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.indexing.solr;
017
018import java.io.IOException;
019import java.util.Set;
020
021import javax.jcr.Repository;
022import javax.jcr.RepositoryException;
023import javax.jcr.Session;
024
025import org.apache.avalon.framework.component.Component;
026import org.apache.avalon.framework.context.Context;
027import org.apache.avalon.framework.context.ContextException;
028import org.apache.avalon.framework.context.Contextualizable;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.avalon.framework.service.Serviceable;
032import org.apache.cocoon.components.ContextHelper;
033import org.apache.cocoon.environment.Request;
034import org.apache.solr.client.solrj.SolrServerException;
035
036import org.ametys.cms.content.indexing.solr.SolrIndexer;
037import org.ametys.cms.indexing.IndexingException;
038import org.ametys.cms.indexing.WorkspaceIndexer;
039import org.ametys.core.ui.Callable;
040import org.ametys.plugins.repository.RepositoryConstants;
041import org.ametys.plugins.repository.provider.AbstractRepository;
042import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector;
043import org.ametys.plugins.repository.provider.WorkspaceSelector;
044import org.ametys.runtime.plugin.component.AbstractLogEnabled;
045
046/**
047 * Component indexing a workspace in a Solr server.
048 */
049public class SolrWorkspaceIndexer extends AbstractLogEnabled implements WorkspaceIndexer, Component, Serviceable, Contextualizable
050{
051    /** The repository. */
052    protected Repository _repository;
053    
054    /** The solr indexer. */
055    protected SolrIndexer _solrIndexer;
056    
057    /** The workspace selector. */
058    protected WorkspaceSelector _workspaceSelector;
059    
060    /** Additional documents provider extension point. */
061    protected DocumentProviderExtensionPoint _docProviderEP;
062    
063    /** The component context. */
064    protected Context _context;
065    
066    @Override
067    public void service(ServiceManager manager) throws ServiceException
068    {
069        _repository = (Repository) manager.lookup(AbstractRepository.ROLE);
070        _solrIndexer = (SolrIndexer) manager.lookup(SolrIndexer.ROLE);
071        _workspaceSelector = (WorkspaceSelector) manager.lookup(WorkspaceSelector.ROLE);
072        _docProviderEP = (DocumentProviderExtensionPoint) manager.lookup(DocumentProviderExtensionPoint.ROLE);
073    }
074    
075    @Override
076    public void contextualize(Context context) throws ContextException
077    {
078        _context = context;
079    }
080    
081    @Override
082    @Callable
083    public void indexAllWorkspaces() throws IndexingException
084    {
085        String[] workspaceNames;
086        try
087        {
088            // Get all the workspaces and reindex them.
089            Session session = _repository.login();
090            workspaceNames = session.getWorkspace().getAccessibleWorkspaceNames();
091        }
092        catch (RepositoryException e)
093        {
094            getLogger().error("Error while indexing the workspaces.", e);
095            throw new IndexingException("Error while indexing the workspaces.", e);
096        }
097        
098        // Before sending schema, all cores need to exist
099        ensureCoresExists(workspaceNames);
100        
101        _sendSchema();
102        
103        for (String workspaceName : workspaceNames)
104        {
105            _index(workspaceName, false, false); // no need to ensure core exists, nor sending schema, as it was done just before
106        }
107        
108        reloadAclCache();
109    }
110    
111    // Send schema (using default update Solr client)
112    private void _sendSchema() throws IndexingException
113    {
114        Request request = ContextHelper.getRequest(_context);
115        // Retrieve the current workspace.
116        String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
117        try
118        {
119            // Force the default workspace.
120            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, RepositoryConstants.DEFAULT_WORKSPACE);
121            _solrIndexer.sendSchema();
122        }
123        catch (IOException | SolrServerException e)
124        {
125            getLogger().error("Error while sending schema.", e);
126            throw new IndexingException("Error while sending schema.", e);
127        }
128        finally
129        {
130            // Restore context
131            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp);
132        }
133    }
134    
135    @Override
136    public void index(String workspaceName) throws IndexingException
137    {
138        _index(workspaceName, true, true);
139    }
140    
141    private void _index(String workspaceName, boolean ensureCoreExists, boolean sendSchema) throws IndexingException
142    {
143        if (ensureCoreExists)
144        {
145            // Create the core corresponding to the workspace if it doesn't exist.
146            ensureCoreExists(workspaceName);
147        }
148        
149        getLogger().info("Start indexing workspace {}...", workspaceName);
150        
151        _forceWorkspaceAndDoIndex(workspaceName, sendSchema);
152        
153        getLogger().info("Successfully indexed workspace {}", workspaceName);
154    }
155    
156    private void _forceWorkspaceAndDoIndex(String workspaceName, boolean sendSchema) throws IndexingException
157    {
158        Request request = ContextHelper.getRequest(_context);
159        
160        // Retrieve the current workspace.
161        String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
162        
163        try
164        {
165            // Force the workspace.
166            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName);
167            
168            if (sendSchema)
169            {
170                try
171                {
172                    _solrIndexer.sendSchema();
173                }
174                catch (IOException | SolrServerException e)
175                {
176                    getLogger().error("Error while sending schema.", e);
177                    throw new IndexingException("Error while sending schema.", e);
178                }
179            }
180            
181            doIndex(workspaceName);
182        }
183        finally
184        {
185            // Restore context
186            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp);
187        }
188        
189    }
190    
191    /**
192     * Index the given workspace.
193     * @param workspaceName The workspace name.
194     * @throws IndexingException If an error occurs indexing the workspace.
195     */
196    protected void doIndex(String workspaceName) throws IndexingException
197    {
198        try
199        {
200            // First, unindex all documents.
201            _solrIndexer.unindexAllDocuments(workspaceName, false);
202            
203            // Index all contents.
204            _solrIndexer.indexAllContents(workspaceName, true, false);
205            
206            // Index all resources.
207            _solrIndexer.indexAllResources(workspaceName, false);
208            
209            // Eventually index additional documents.
210            indexAdditionalDocuments(workspaceName);
211            
212            // Commit changes
213            _solrIndexer.commit(workspaceName);
214            // When done indexing, optimize.
215            _solrIndexer.optimize(workspaceName);
216        }
217        catch (Exception e)
218        {
219            getLogger().error("Error indexing the workspace '" + workspaceName + "'.", e);
220            throw new IndexingException("Error indexing the workspace '" + workspaceName + "'.", e);
221        }
222    }
223    
224    /**
225     * Asks Solr server to reload ACL Cache
226     */
227    protected void reloadAclCache()
228    {
229        getLogger().info("Starting reloading Solr ACL Cache");
230        long start = System.currentTimeMillis();
231        
232        // Reload the ACL cache
233        try
234        {
235            _solrIndexer.reloadAclCache();
236        }
237        catch (SolrServerException | IOException | RepositoryException e)
238        {
239            getLogger().error("An error occured when reloading the ACL cache", e);
240        }
241        
242        long end = System.currentTimeMillis();
243        getLogger().info("Reloading of Solr ACL Cache ended, the process took {} milliseconds.", end - start);
244    }
245    
246    /**
247     * Index additional documents provided by the extensions.
248     * @param workspaceName The workspace name.
249     * @throws IndexingException If an error occurs while indexing.
250     */
251    protected void indexAdditionalDocuments(String workspaceName) throws IndexingException
252    {
253        for (String id : _docProviderEP.getExtensionsIds())
254        {
255            DocumentProvider docProvider = _docProviderEP.getExtension(id);
256            docProvider.indexDocuments(workspaceName);
257        }
258    }
259    
260    /**
261     * Create the given cores if they do not exist.
262     * @param coreNames The core names.
263     * @throws IndexingException If an error occurs while checking if cores exist.
264     */
265    protected void ensureCoresExists(String[] coreNames) throws IndexingException
266    {
267        for (String coreName : coreNames)
268        {
269            ensureCoreExists(coreName);
270        }
271    }
272    
273    /**
274     * Create the given core if it doesn't exist.
275     * @param coreName The core name.
276     * @throws IndexingException If an error occurs while checking if core exists.
277     */
278    protected void ensureCoreExists(String coreName) throws IndexingException
279    {
280        try
281        {
282            Set<String> coreNames = _solrIndexer.getCoreNames();
283            if (!coreNames.contains(coreName))
284            {
285                _solrIndexer.createCore(coreName);
286            }
287        }
288        catch (IOException | SolrServerException e)
289        {
290            String msg = String.format("Error while checking if core '%s' exists.", coreName);
291            getLogger().error(msg, e);
292            throw new IndexingException(msg, e);
293        }
294    }
295}