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.core.observation.AsyncObserver; 034import org.ametys.core.observation.Event; 035import org.ametys.plugins.explorer.ObservationConstants; 036import org.ametys.plugins.explorer.resources.Resource; 037import org.ametys.plugins.explorer.resources.ResourceCollection; 038import org.ametys.plugins.repository.AmetysObject; 039import org.ametys.plugins.repository.AmetysObjectResolver; 040import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 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 = _getHandledObject(resourceId); 140 if (resource != null) 141 { 142 onResourceCreated(resource, workspaceName); 143 } 144 } 145 } 146 } 147 else if (event.getId().equals(ObservationConstants.EVENT_RESOURCE_UPDATED) 148 || event.getId().equals(ObservationConstants.EVENT_RESOURCE_RENAMED) 149 || event.getId().equals(ObservationConstants.EVENT_RESOURCE_MOVED)) 150 { 151 // A resource was modified, renamed or moved: reindex the corresponding document. 152 String resourceId = (String) event.getArguments().get(ObservationConstants.ARGS_ID); 153 Resource resource = _getHandledObject(resourceId); 154 if (resource != null) 155 { 156 onResourceUpdated(resource, workspaceName); 157 } 158 } 159 else if (event.getId().equals(ObservationConstants.EVENT_COLLECTION_RENAMED) 160 || event.getId().equals(ObservationConstants.EVENT_COLLECTION_MOVED)) 161 { 162 // A collection was moved: recursively reindex all resources in it. 163 String collectionId = (String) event.getArguments().get(ObservationConstants.ARGS_ID); 164 165 ResourceCollection resourceCollection = _getHandledObject(collectionId); 166 if (resourceCollection != null) 167 { 168 onCollectionRenamedOrMoved(resourceCollection, workspaceName); 169 } 170 } 171 } 172 catch (Exception e) 173 { 174 String error = String.format("Failed to index some resources in workspace %s", workspaceName); 175 getLogger().error(error, e); 176 throw new IndexingException(error, e); 177 } 178 finally 179 { 180 // Restore context 181 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 182 } 183 } 184 185 private <A extends AmetysObject> A _getHandledObject(String id) 186 { 187 if (_resolver.hasAmetysObjectForId(id)) 188 { 189 A object = _resolver.resolveById(id); 190 if (isHandledResource(object)) 191 { 192 return object; 193 } 194 } 195 196 return null; 197 } 198 199 /** 200 * Returns true if it is a {@link Resource} or {@link ResourceCollection} handled by this observer 201 * @param object The resource or collection 202 * @return true if it is handled by this observer 203 */ 204 protected abstract boolean isHandledResource(AmetysObject object); 205 206 /** 207 * Method called when a resource handled by this observer is created. Typically for indexing the created resource. 208 * @param resource The created resource 209 * @param workspaceName The workspace name 210 * @throws Exception if an error occurs 211 */ 212 protected abstract void onResourceCreated(Resource resource, String workspaceName) throws Exception; 213 214 /** 215 * Method called when a resource handled by this observer is updated, moved or renamed. Typically for indexing the updated resource. 216 * @param resource The updated resource 217 * @param workspaceName The workspace name 218 * @throws Exception if an error occurs 219 */ 220 protected abstract void onResourceUpdated(Resource resource, String workspaceName) throws Exception; 221 222 /** 223 * Method called when a resource collection handled by this observer is renamed or moved. Typically for indexing the children of the resource collection. 224 * @param resourceCollection The resource collection 225 * @param workspaceName The workspace name 226 * @throws Exception if an error occurs 227 */ 228 protected abstract void onCollectionRenamedOrMoved(ResourceCollection resourceCollection, String workspaceName) throws Exception; 229}