001/* 002 * Copyright 2014 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.web.indexing.observation; 017 018import java.util.Map; 019 020import javax.jcr.Node; 021import javax.jcr.PropertyIterator; 022 023import org.apache.avalon.framework.context.Context; 024import org.apache.avalon.framework.context.ContextException; 025import org.apache.avalon.framework.context.Contextualizable; 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.avalon.framework.service.Serviceable; 029import org.apache.cocoon.components.ContextHelper; 030import org.apache.cocoon.environment.Request; 031 032import org.ametys.cms.content.indexing.solr.SolrIndexer; 033import org.ametys.cms.content.indexing.solr.observation.ObserverHelper; 034import org.ametys.cms.repository.Content; 035import org.ametys.cms.repository.RequestAttributeWorkspaceSelector; 036import org.ametys.core.observation.Event; 037import org.ametys.core.observation.Observer; 038import org.ametys.plugins.repository.AmetysObjectResolver; 039import org.ametys.plugins.repository.UnknownAmetysObjectException; 040import org.ametys.plugins.repository.jcr.JCRAmetysObject; 041import org.ametys.runtime.plugin.component.AbstractLogEnabled; 042import org.ametys.web.WebConstants; 043import org.ametys.web.indexing.solr.SolrPageIndexer; 044import org.ametys.web.repository.page.Page; 045import org.ametys.web.repository.page.jcr.DefaultZone; 046 047/** 048 * Abstract {@link Observer} for synchronizing the live solr index. 049 */ 050public abstract class AbstractLiveSolrObserver extends AbstractLogEnabled implements Observer, Contextualizable, Serviceable 051{ 052 /** The Avalon context. */ 053 protected Context _context; 054 /** The ametys object resolver. */ 055 protected AmetysObjectResolver _resolver; 056 /** The Solr indexer. */ 057 protected SolrIndexer _solrIndexer; 058 /** The Solr page indexer. */ 059 protected SolrPageIndexer _solrPageIndexer; 060 061 @Override 062 public void contextualize(Context context) throws ContextException 063 { 064 _context = context; 065 } 066 067 @Override 068 public void service(ServiceManager manager) throws ServiceException 069 { 070 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 071 _solrIndexer = (SolrIndexer) manager.lookup(SolrIndexer.ROLE); 072 _solrPageIndexer = (SolrPageIndexer) manager.lookup(SolrPageIndexer.ROLE); 073 } 074 075 @Override 076 public int getPriority(Event event) 077 { 078 // Will be processed after live synchronization observers 079 return MAX_PRIORITY + 3000; 080 } 081 082 @Override 083 public void observe(Event event, Map<String, Object> transientVars) throws Exception 084 { 085 if (ObserverHelper.isNotSuspendedObservationForIndexation()) 086 { 087 Request request = ContextHelper.getRequest(_context); 088 089 // Retrieve current workspace 090 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 091 092 try 093 { 094 // Use live workspace 095 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, WebConstants.LIVE_WORKSPACE); 096 097 _updateIndex(event, transientVars); 098 } 099 catch (Exception e) 100 { 101 getLogger().error("Unable to create or update index for event: " + event, e); 102 } 103 finally 104 { 105 // Restore context 106 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 107 } 108 } 109 } 110 111 /** 112 * Update the index from the event observed. 113 * @param event the event. 114 * @param transientVars the transient vars passed from one Observer to another when processing a single Event. 115 * @throws Exception if an error occurs. 116 */ 117 protected abstract void _updateIndex(Event event, Map<String, Object> transientVars) throws Exception; 118 119 ///////////////////////////////////////////////////////////////////////// 120 121 /** 122 * Updates Solr page documents associated with a content. 123 * @param contentId the content id. 124 * @throws Exception if an error occurs. 125 */ 126 protected void _updatePageDocumentsForContent(String contentId) throws Exception 127 { 128 Request request = ContextHelper.getRequest(_context); 129 130 // Retrieve current workspace 131 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 132 133 try 134 { 135 // Use live workspace 136 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, WebConstants.LIVE_WORKSPACE); 137 Content liveContent = null; 138 139 try 140 { 141 // Retrieve page in the live workspace 142 liveContent = _resolver.resolveById(contentId); 143 } 144 catch (UnknownAmetysObjectException e) 145 { 146 // Content is not in the live workspace 147 } 148 149 if (liveContent != null) 150 { 151 PropertyIterator itReferences = ((JCRAmetysObject) liveContent).getNode().getReferences(); 152 153 try 154 { 155 while (itReferences.hasNext()) 156 { 157 Node refererNode = itReferences.nextProperty().getParent(); 158 159 if (refererNode.getPrimaryNodeType().getName().equals(DefaultZone.ZONEITEM_NODE_NAME)) 160 { 161 Node pageNode = refererNode.getParent().getParent().getParent().getParent(); 162 Page livePage = _resolver.resolve(pageNode, false); 163 164 if (getLogger().isInfoEnabled()) 165 { 166 getLogger().info("Updating solr document with id: " + livePage.getId()); 167 } 168 169 // Updates the document by first deleting it 170 _solrPageIndexer.indexPage(livePage.getId(), WebConstants.LIVE_WORKSPACE, false, false, false); 171 } 172 } 173 174 _solrIndexer.commit(WebConstants.LIVE_WORKSPACE); 175 } 176 catch (Exception e) 177 { 178 _solrIndexer.rollback(WebConstants.LIVE_WORKSPACE); 179 180 getLogger().error("Error indexing page documents for content " + contentId, e); 181 } 182 } 183 } 184 finally 185 { 186 // Restore context 187 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 188 } 189 } 190 191 /** 192 * Deletes Solr page documents associated with a content. 193 * @param contentId the content id. 194 * @throws Exception if an error occurs. 195 */ 196 protected void _deletePageDocumentsForContent(String contentId) throws Exception 197 { 198 Request request = ContextHelper.getRequest(_context); 199 200 // Retrieve current workspace 201 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 202 203 try 204 { 205 // Use live workspace 206 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, WebConstants.LIVE_WORKSPACE); 207 Content liveContent = null; 208 209 try 210 { 211 // Retrieve page in the live workspace 212 liveContent = _resolver.resolveById(contentId); 213 } 214 catch (UnknownAmetysObjectException e) 215 { 216 // Page is not synchronized 217 } 218 219 if (liveContent != null) 220 { 221 PropertyIterator itReferences = ((JCRAmetysObject) liveContent).getNode().getReferences(); 222 223 try 224 { 225 while (itReferences.hasNext()) 226 { 227 Node refererNode = itReferences.nextProperty().getParent(); 228 229 if (refererNode.getPrimaryNodeType().getName().equals(DefaultZone.ZONEITEM_NODE_NAME)) 230 { 231 Node pageNode = refererNode.getParent().getParent().getParent().getParent(); 232 Page livePage = _resolver.resolve(pageNode, false); 233 234 if (getLogger().isInfoEnabled()) 235 { 236 getLogger().info("Updating solr document with id: " + livePage.getId()); 237 } 238 239 // Delete the page document. 240 _solrPageIndexer.unindexPage(livePage.getId(), WebConstants.LIVE_WORKSPACE, true, true, false); 241 } 242 } 243 244 _solrIndexer.commit(WebConstants.LIVE_WORKSPACE); 245 } 246 catch (Exception e) 247 { 248 _solrIndexer.rollback(WebConstants.LIVE_WORKSPACE); 249 250 getLogger().error("Error unindexing page documents for content " + contentId, e); 251 } 252 } 253 } 254 finally 255 { 256 // Restore context 257 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 258 } 259 } 260 261 /////////////////////////////////////////////////////////////////////////// 262 263 /** 264 * Updates a Solr document referencing a page. 265 * @param pageId the page id. 266 * @throws Exception if an error occurs. 267 */ 268 protected void _updatePageDocument(String pageId) throws Exception 269 { 270 _updatePageDocument(pageId, false); 271 } 272 273 /** 274 * Updates a Solr document referencing a page. 275 * @param pageId the page id. 276 * @param indexRecursively to also process children pages. 277 * @throws Exception if an error occurs. 278 */ 279 protected void _updatePageDocument(String pageId, boolean indexRecursively) throws Exception 280 { 281 _solrPageIndexer.reindexPage(pageId, WebConstants.LIVE_WORKSPACE, indexRecursively, false, true); 282 } 283 284 /** 285 * Returns the page in the live workspace corresponding to a given id. 286 * @param pageId the page id. 287 * @return the page in the live workspace or null if not present. 288 */ 289 protected Page _getPageInLive(String pageId) 290 { 291 Request request = ContextHelper.getRequest(_context); 292 293 // Retrieve current workspace 294 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 295 296 try 297 { 298 // Use live workspace 299 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, WebConstants.LIVE_WORKSPACE); 300 Page livePage = null; 301 302 try 303 { 304 // Retrieve page in the live workspace 305 livePage = _resolver.resolveById(pageId); 306 } 307 catch (UnknownAmetysObjectException e) 308 { 309 // Page is not synchronized 310 } 311 312 return livePage; 313 } 314 finally 315 { 316 // Restore context 317 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 318 } 319 } 320 321}