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