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}