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