001/*
002 *  Copyright 2015 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.cms.search.solr;
017
018import java.util.HashMap;
019import java.util.Map;
020import java.util.Optional;
021
022import javax.jcr.RepositoryException;
023
024import org.apache.avalon.framework.activity.Disposable;
025import org.apache.avalon.framework.activity.Initializable;
026import org.apache.avalon.framework.component.Component;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.avalon.framework.service.Serviceable;
030import org.apache.commons.io.IOUtils;
031import org.apache.commons.lang3.ArrayUtils;
032import org.apache.solr.client.solrj.SolrClient;
033import org.apache.solr.client.solrj.impl.HttpSolrClient;
034
035import org.ametys.plugins.repository.provider.WorkspaceSelector;
036import org.ametys.runtime.config.Config;
037import org.ametys.runtime.plugin.component.AbstractLogEnabled;
038
039/**
040 * Component acting as a single entry point to get access to Solr clients.
041 */
042public class DefaultSolrClientProvider extends AbstractLogEnabled implements SolrClientProvider, Component, Serviceable, Initializable, Disposable
043{
044    private static final String __SOLR_URL_CONFIG = "cms.solr.core.url"; 
045    private static final String __SOLR_SOCKET_TIMEOUT_CONFIG = "cms.solr.socket.timeout";
046    private static final String __SOLR_CORE_PREFIX_CONFIG = "cms.solr.core.prefix";  
047    
048    /** The workspace selector. */
049    protected WorkspaceSelector _workspaceSelector;
050    
051    /** The Solr read client. */
052    protected SolrClient _solrReadClient;
053    
054    /** The Solr "default" update clients, per workspace */
055    protected Map<String, SolrClient> _solrDefaultUpdateClients;
056    
057    /** The Solr "no auto commit" update clients, per workspace */
058    protected Map<String, SolrClient> _solrNoAutoCommitUpdateClients;
059
060    /** The solr URL. */
061    protected String _solrUrl;
062    
063    /** The solr socket timeout (in millis). */
064    protected Optional<Integer> _solrSocketTimeout;
065    
066    /** The solr core prefix. */
067    protected String _solrCorePrefix;
068    
069    @Override
070    public void service(ServiceManager serviceManager) throws ServiceException
071    {
072        _workspaceSelector = (WorkspaceSelector) serviceManager.lookup(WorkspaceSelector.ROLE);
073    }
074    
075    @Override
076    public void initialize() throws Exception
077    {
078        _solrUrl = Config.getInstance().getValue(__SOLR_URL_CONFIG);
079        _solrSocketTimeout = Optional.of(__SOLR_SOCKET_TIMEOUT_CONFIG)
080                .map(Config.getInstance()::<Long>getValue)
081                .map(Long::intValue)
082                .map(s -> s * 1000);
083        _solrCorePrefix = Config.getInstance().getValue(__SOLR_CORE_PREFIX_CONFIG);
084        
085        HttpSolrClient.Builder solrReadClientBuilder = new HttpSolrClient.Builder(_solrUrl);
086        if (_solrSocketTimeout.isPresent())
087        {
088            solrReadClientBuilder.withSocketTimeout(_solrSocketTimeout.get());
089        }
090        _solrReadClient = solrReadClientBuilder.build();
091        
092        String[] workspaces = _workspaceSelector.getWorkspaces();
093        _solrDefaultUpdateClients = new HashMap<>();
094        _solrNoAutoCommitUpdateClients = new HashMap<>();
095        for (String workspaceName : workspaces)
096        {
097            _solrDefaultUpdateClients.put(workspaceName, _createDefaultUpdateClient(workspaceName));
098            _solrNoAutoCommitUpdateClients.put(workspaceName, _createNoAutoCommitUpdateClient(workspaceName));
099        }
100    }
101    
102    private SolrClient _createDefaultUpdateClient(String workspaceName)
103    {
104        AbstractAmetysConcurrentUpdateClient updateClient = new DefaultUpdateClient(_solrUrl, _solrSocketTimeout, getCollectionName(workspaceName), 10, 4, getLogger());
105        updateClient.setPollQueueTime(10);
106        return updateClient;
107    }
108    
109    private SolrClient _createNoAutoCommitUpdateClient(String workspaceName)
110    {
111        AbstractAmetysConcurrentUpdateClient updateClient = new NoAutoCommitUpdateClient(_solrUrl, _solrSocketTimeout, getCollectionName(workspaceName), 10, 4, getLogger());
112        updateClient.setPollQueueTime(10);
113        return updateClient;
114    }
115    
116    @SuppressWarnings("deprecation")
117    @Override
118    public void dispose()
119    {
120        // Release the solr clients (as a Closeable).
121        IOUtils.closeQuietly(_solrReadClient);
122        _solrReadClient = null;
123        
124        for (SolrClient solrDefaultUpdateClient : _solrDefaultUpdateClients.values())
125        {
126            IOUtils.closeQuietly(solrDefaultUpdateClient);
127        }
128        _solrDefaultUpdateClients.clear();
129        _solrDefaultUpdateClients = null;
130        
131        for (SolrClient solrNoAutoCommitUpdateClient : _solrNoAutoCommitUpdateClients.values())
132        {
133            IOUtils.closeQuietly(solrNoAutoCommitUpdateClient);
134        }
135        _solrNoAutoCommitUpdateClients.clear();
136        _solrNoAutoCommitUpdateClients = null;
137    }
138    
139    @Override
140    public SolrClient getReadClient()
141    {
142        return _solrReadClient;
143    }
144    
145    @Override
146    public SolrClient getUpdateClient(String workspaceName, boolean autoCommit)
147    {
148        Map<String, SolrClient> updateClients = autoCommit ? _solrDefaultUpdateClients : _solrNoAutoCommitUpdateClients;
149        
150        SolrClient updateClient = updateClients.get(_nonNullWorkspaceName(workspaceName));
151        if (updateClient == null)
152        {
153            // Perhaps the workspace was created after initializing this component, try to check if JCR workspace exist
154            try
155            {
156                if (ArrayUtils.contains(_workspaceSelector.getWorkspaces(), workspaceName))
157                {
158                    updateClient = autoCommit ? _createDefaultUpdateClient(workspaceName) : _createNoAutoCommitUpdateClient(workspaceName);
159                    updateClients.put(workspaceName, updateClient);
160                }
161            }
162            catch (RepositoryException e)
163            {
164                getLogger().error("An error occurs while trying to return all JCR workspaces", e);
165            }
166        }
167        
168        return updateClient;
169    }
170    
171    @Override
172    public String getCollectionName()
173    {
174        return getCollectionName(_workspaceSelector.getWorkspace());
175    }
176    
177    @Override
178    public String getCollectionName(String workspaceName)
179    {
180        return _solrCorePrefix + _nonNullWorkspaceName(workspaceName);
181    }
182    
183    private String _nonNullWorkspaceName(String workspaceName)
184    {
185        if (workspaceName == null)
186        {
187            getLogger().debug("Passing null workspace name. Switching to current workspace.");
188            return _workspaceSelector.getWorkspace();
189        }
190        else
191        {
192            return workspaceName;
193        }
194    }
195}