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}