/*
* Copyright 2015 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ametys.cms.indexing.solr;
import java.io.IOException;
import java.util.Set;
import javax.jcr.Repository;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.avalon.framework.component.Component;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.context.ContextException;
import org.apache.avalon.framework.context.Contextualizable;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.cocoon.components.ContextHelper;
import org.apache.cocoon.environment.Request;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.ametys.cms.content.indexing.solr.SolrIndexer;
import org.ametys.cms.indexing.IndexingException;
import org.ametys.cms.indexing.WorkspaceIndexer;
import org.ametys.cms.search.solr.SolrClientProvider;
import org.ametys.core.ui.Callable;
import org.ametys.plugins.repository.RepositoryConstants;
import org.ametys.plugins.repository.provider.AbstractRepository;
import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector;
import org.ametys.plugins.repository.provider.WorkspaceSelector;
import org.ametys.runtime.plugin.component.AbstractLogEnabled;
/**
* Component indexing a workspace in a Solr server.
*/
public class SolrWorkspaceIndexer extends AbstractLogEnabled implements WorkspaceIndexer, Component, Serviceable, Contextualizable
{
/** The repository. */
protected Repository _repository;
/** The solr indexer. */
protected SolrIndexer _solrIndexer;
/** The workspace selector. */
protected WorkspaceSelector _workspaceSelector;
/** Additional documents provider extension point. */
protected DocumentProviderExtensionPoint _docProviderEP;
/** The Solr client provider */
protected SolrClientProvider _solrClientProvider;
/** The component context. */
protected Context _context;
@Override
public void service(ServiceManager manager) throws ServiceException
{
_repository = (Repository) manager.lookup(AbstractRepository.ROLE);
_solrIndexer = (SolrIndexer) manager.lookup(SolrIndexer.ROLE);
_workspaceSelector = (WorkspaceSelector) manager.lookup(WorkspaceSelector.ROLE);
_docProviderEP = (DocumentProviderExtensionPoint) manager.lookup(DocumentProviderExtensionPoint.ROLE);
_solrClientProvider = (SolrClientProvider) manager.lookup(SolrClientProvider.ROLE);
}
@Override
public void contextualize(Context context) throws ContextException
{
_context = context;
}
@Override
@Callable
public void indexAllWorkspaces() throws IndexingException
{
String[] workspaceNames;
try
{
// Get all the workspaces and reindex them.
Session session = _repository.login();
workspaceNames = session.getWorkspace().getAccessibleWorkspaceNames();
}
catch (RepositoryException e)
{
getLogger().error("Error while indexing the workspaces.", e);
throw new IndexingException("Error while indexing the workspaces.", e);
}
// Before sending schema, all cores need to exist
ensureCoresExists(workspaceNames);
_sendSchema();
for (String workspaceName : workspaceNames)
{
_index(workspaceName, false, false); // no need to ensure core exists, nor sending schema, as it was done just before
}
}
// Send schema (using default update Solr client)
private void _sendSchema() throws IndexingException
{
Request request = ContextHelper.getRequest(_context);
// Retrieve the current workspace.
String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
try
{
// Force the default workspace.
RequestAttributeWorkspaceSelector.setForcedWorkspace(request, RepositoryConstants.DEFAULT_WORKSPACE);
_solrIndexer.sendSchema();
}
catch (IOException | SolrServerException e)
{
getLogger().error("Error while sending schema.", e);
throw new IndexingException("Error while sending schema.", e);
}
finally
{
// Restore context
RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp);
}
}
@Override
public void index(String workspaceName) throws IndexingException
{
_index(workspaceName, true, true);
}
private void _index(String workspaceName, boolean ensureCoreExists, boolean sendSchema) throws IndexingException
{
if (ensureCoreExists)
{
// Create the core corresponding to the workspace if it doesn't exist.
ensureCoreExists(workspaceName);
}
getLogger().info("Start indexing workspace {}...", workspaceName);
_forceWorkspaceAndDoIndex(workspaceName, sendSchema);
getLogger().info("Successfully indexed workspace {}", workspaceName);
}
private void _forceWorkspaceAndDoIndex(String workspaceName, boolean sendSchema) throws IndexingException
{
Request request = ContextHelper.getRequest(_context);
// Retrieve the current workspace.
String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
try
{
// Force the workspace.
RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName);
if (sendSchema)
{
try
{
_solrIndexer.sendSchema();
}
catch (IOException | SolrServerException e)
{
getLogger().error("Error while sending schema.", e);
throw new IndexingException("Error while sending schema.", e);
}
}
doIndex(workspaceName);
}
finally
{
// Restore context
RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp);
}
}
/**
* Index the given workspace.
* @param workspaceName The workspace name.
* @throws IndexingException If an error occurs indexing the workspace.
*/
protected void doIndex(String workspaceName) throws IndexingException
{
try
{
SolrClient solrClient = _solrClientProvider.getUpdateClient(workspaceName, false);
// First, unindex all documents.
_solrIndexer.unindexAllDocuments(workspaceName, solrClient);
// Index all contents.
_solrIndexer.indexAllContents(workspaceName, true, solrClient);
// Index all resources.
_solrIndexer.indexAllResources(workspaceName, solrClient);
// Eventually index additional documents.
indexAdditionalDocuments(workspaceName, solrClient);
// Commit changes
_solrIndexer.commit(workspaceName, solrClient);
// When done indexing, optimize.
_solrIndexer.optimize(workspaceName, solrClient);
}
catch (Exception e)
{
getLogger().error("Error indexing the workspace '" + workspaceName + "'.", e);
throw new IndexingException("Error indexing the workspace '" + workspaceName + "'.", e);
}
}
/**
* Index additional documents provided by the extensions.
* @param workspaceName The workspace name.
* @param solrClient The solr client to use
* @throws IndexingException If an error occurs while indexing.
*/
protected void indexAdditionalDocuments(String workspaceName, SolrClient solrClient) throws IndexingException
{
for (String id : _docProviderEP.getExtensionsIds())
{
DocumentProvider docProvider = _docProviderEP.getExtension(id);
docProvider.indexDocuments(workspaceName, solrClient);
}
}
/**
* Create the given cores if they do not exist.
* @param coreNames The core names.
* @throws IndexingException If an error occurs while checking if cores exist.
*/
protected void ensureCoresExists(String[] coreNames) throws IndexingException
{
for (String coreName : coreNames)
{
ensureCoreExists(coreName);
}
}
/**
* Create the given core if it doesn't exist.
* @param coreName The core name.
* @throws IndexingException If an error occurs while checking if core exists.
*/
protected void ensureCoreExists(String coreName) throws IndexingException
{
try
{
Set<String> coreNames = _solrIndexer.getCoreNames();
if (!coreNames.contains(coreName))
{
_solrIndexer.createCore(coreName);
}
}
catch (IOException | SolrServerException e)
{
String msg = String.format("Error while checking if core '%s' exists.", coreName);
getLogger().error(msg, e);
throw new IndexingException(msg, e);
}
}
}