001/* 002 * Copyright 2017 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.explorer; 017 018import java.util.Map; 019 020import org.apache.avalon.framework.context.Context; 021import org.apache.avalon.framework.context.ContextException; 022import org.apache.avalon.framework.context.Contextualizable; 023import org.apache.avalon.framework.service.ServiceException; 024import org.apache.avalon.framework.service.ServiceManager; 025import org.apache.avalon.framework.service.Serviceable; 026import org.apache.cocoon.components.ContextHelper; 027import org.apache.cocoon.environment.Request; 028import org.apache.commons.lang.BooleanUtils; 029 030import org.ametys.cms.content.indexing.solr.SolrIndexer; 031import org.ametys.cms.content.indexing.solr.observation.ObserverHelper; 032import org.ametys.cms.indexing.IndexingException; 033import org.ametys.cms.repository.RequestAttributeWorkspaceSelector; 034import org.ametys.core.observation.AsyncObserver; 035import org.ametys.core.observation.Event; 036import org.ametys.plugins.explorer.ObservationConstants; 037import org.ametys.plugins.explorer.resources.Resource; 038import org.ametys.plugins.explorer.resources.ResourceCollection; 039import org.ametys.plugins.repository.AmetysObject; 040import org.ametys.plugins.repository.AmetysObjectResolver; 041import org.ametys.runtime.plugin.component.AbstractLogEnabled; 042 043/** 044 * Abstract observer in charge of indexing resources when created, modified, moved... 045 */ 046public abstract class AbstractSolrIndexResourceObserver extends AbstractLogEnabled implements AsyncObserver, Contextualizable, Serviceable 047{ 048 /** Request attribute disabling content indexation when set to <code>true</code>. */ 049 public static final String DISABLE_INDEXING_KEY = SolrIndexResourceObserver.class.getName() + "$disableIndexing"; 050 051 /** The Solr indexer. */ 052 protected SolrIndexer _solrIndexer; 053 054 /** The AmetysObject resolver. */ 055 protected AmetysObjectResolver _resolver; 056 057 /** The component context. */ 058 protected Context _context; 059 060 @Override 061 public void contextualize(Context context) throws ContextException 062 { 063 _context = context; 064 } 065 066 @Override 067 public void service(ServiceManager serviceManager) throws ServiceException 068 { 069 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 070 _solrIndexer = (SolrIndexer) serviceManager.lookup(SolrIndexer.ROLE); 071 } 072 073 @Override 074 public boolean supports(Event event) 075 { 076 // If the resource is moved or renamed, the document stays the same, only its data changes 077 // (there is no need to unindex a document and index a new one). 078 return event.getId().equals(ObservationConstants.EVENT_RESOURCE_CREATED) 079 || event.getId().equals(ObservationConstants.EVENT_RESOURCE_UPDATED) 080 || event.getId().equals(ObservationConstants.EVENT_RESOURCE_RENAMED) 081 || event.getId().equals(ObservationConstants.EVENT_RESOURCE_MOVED) 082 || event.getId().equals(ObservationConstants.EVENT_COLLECTION_RENAMED) 083 || event.getId().equals(ObservationConstants.EVENT_COLLECTION_MOVED); 084 } 085 086 @Override 087 public int getPriority(Event event) 088 { 089 return MAX_PRIORITY + 3000; 090 } 091 092 @Override 093 public void observe(Event event, Map<String, Object> transientVars) throws Exception 094 { 095 Request request = ContextHelper.getRequest(_context); 096 Boolean disableIndexingAttr = (Boolean) request.getAttribute(DISABLE_INDEXING_KEY); 097 098 if (ObserverHelper.isNotSuspendedObservationForIndexation() && BooleanUtils.isNotTrue(disableIndexingAttr)) 099 { 100 String[] workspaces = getWorkspacesToIndex(); 101 for (String workspace : workspaces) 102 { 103 _index(event, workspace); 104 } 105 } 106 } 107 108 /** 109 * Get the list of workspace to index 110 * @return An array of workspace names 111 */ 112 protected abstract String[] getWorkspacesToIndex(); 113 114 /** 115 * Perform the indexation process 116 * @param event The current event 117 * @param workspaceName The workspace name for the indexation 118 * @throws IndexingException if an error occurs during indexation. 119 */ 120 protected void _index(Event event, String workspaceName) throws IndexingException 121 { 122 Request request = ContextHelper.getRequest(_context); 123 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 124 125 try 126 { 127 // Force the workspace. 128 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, workspaceName); 129 130 if (event.getId().equals(ObservationConstants.EVENT_RESOURCE_CREATED)) 131 { 132 // Resources were created: index each one. 133 @SuppressWarnings("unchecked") 134 Map<String, Resource> resources = (Map<String, Resource>) event.getArguments().get(ObservationConstants.ARGS_RESOURCES); 135 if (resources != null) 136 { 137 for (String resourceId : resources.keySet()) 138 { 139 Resource resource = _resolver.resolveById(resourceId); 140 if (isHandledResource(resource)) 141 { 142 onResourceCreated(resource, workspaceName); 143 } 144 } 145 146 _solrIndexer.commit(workspaceName); 147 } 148 } 149 else if (event.getId().equals(ObservationConstants.EVENT_RESOURCE_UPDATED) 150 || event.getId().equals(ObservationConstants.EVENT_RESOURCE_RENAMED) 151 || event.getId().equals(ObservationConstants.EVENT_RESOURCE_MOVED)) 152 { 153 // A resource was modified, renamed or moved: reindex the corresponding document. 154 String resourceId = (String) event.getArguments().get(ObservationConstants.ARGS_ID); 155 Resource resource = _resolver.resolveById(resourceId); 156 if (isHandledResource(resource)) 157 { 158 onResourceUpdated(resource, workspaceName); 159 _solrIndexer.commit(workspaceName); 160 } 161 } 162 else if (event.getId().equals(ObservationConstants.EVENT_COLLECTION_RENAMED) 163 || event.getId().equals(ObservationConstants.EVENT_COLLECTION_MOVED)) 164 { 165 // A collection was moved: recursively reindex all resources in it. 166 String collectionId = (String) event.getArguments().get(ObservationConstants.ARGS_ID); 167 ResourceCollection resourceCollection = _resolver.resolveById(collectionId); 168 169 if (isHandledResource(resourceCollection)) 170 { 171 onCollectionRenamedOrMoved(resourceCollection, workspaceName); 172 _solrIndexer.commit(workspaceName); 173 } 174 } 175 } 176 catch (Exception e) 177 { 178 String error = String.format("Failed to index some resources in workspace %s", workspaceName); 179 getLogger().error(error, e); 180 throw new IndexingException(error, e); 181 } 182 finally 183 { 184 // Restore context 185 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 186 } 187 } 188 189 /** 190 * Returns true if it is a {@link Resource} or {@link ResourceCollection} handled by this observer 191 * @param object The resource or collection 192 * @return true if it is handled by this observer 193 */ 194 protected abstract boolean isHandledResource(AmetysObject object); 195 196 /** 197 * Method called when a resource handled by this observer is created. Typically for indexing the created resource. 198 * @param resource The created resource 199 * @param workspaceName The workspace name 200 * @throws Exception if an error occurs 201 */ 202 protected abstract void onResourceCreated(Resource resource, String workspaceName) throws Exception; 203 204 /** 205 * Method called when a resource handled by this observer is updated, moved or renamed. Typically for indexing the updated resource. 206 * @param resource The updated resource 207 * @param workspaceName The workspace name 208 * @throws Exception if an error occurs 209 */ 210 protected abstract void onResourceUpdated(Resource resource, String workspaceName) throws Exception; 211 212 /** 213 * Method called when a resource collection handled by this observer is renamed or moved. Typically for indexing the children of the resource collection. 214 * @param resourceCollection The resource collection 215 * @param workspaceName The workspace name 216 * @throws Exception if an error occurs 217 */ 218 protected abstract void onCollectionRenamedOrMoved(ResourceCollection resourceCollection, String workspaceName) throws Exception; 219}