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.core.observation.Event;
036import org.ametys.core.observation.Observer;
037import org.ametys.plugins.repository.AmetysObjectResolver;
038import org.ametys.plugins.repository.UnknownAmetysObjectException;
039import org.ametys.plugins.repository.jcr.JCRAmetysObject;
040import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector;
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);
171                        }
172                    }
173                }
174                catch (Exception e)
175                {
176                    getLogger().error("Error indexing page documents for content " + contentId, e);
177                }
178            }
179        }
180        finally
181        {
182            // Restore context
183            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp);
184        }
185    }
186    
187    /**
188     * Deletes Solr page documents associated with a content.
189     * @param contentId the content id.
190     * @throws Exception if an error occurs.
191     */
192    protected void _deletePageDocumentsForContent(String contentId) throws Exception
193    {
194        Request request = ContextHelper.getRequest(_context);
195        
196        // Retrieve current workspace
197        String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
198        
199        try
200        {
201            // Use live workspace
202            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, WebConstants.LIVE_WORKSPACE);
203            Content liveContent = null;
204            
205            try
206            {
207                // Retrieve page in the live workspace
208                liveContent = _resolver.resolveById(contentId);
209            }
210            catch (UnknownAmetysObjectException e)
211            {
212                // Page is not synchronized
213            }
214            
215            if (liveContent != null)
216            {
217                PropertyIterator itReferences = ((JCRAmetysObject) liveContent).getNode().getReferences();
218                
219                try
220                {
221                    while (itReferences.hasNext())
222                    {
223                        Node refererNode = itReferences.nextProperty().getParent();
224                        
225                        if (refererNode.getPrimaryNodeType().getName().equals(DefaultZone.ZONEITEM_NODE_NAME))
226                        {
227                            Node pageNode = refererNode.getParent().getParent().getParent().getParent();
228                            Page livePage = _resolver.resolve(pageNode, false);
229                            
230                            if (getLogger().isInfoEnabled())
231                            {
232                                getLogger().info("Updating solr document with id: " + livePage.getId());
233                            }
234                            
235                            // Delete the page document.
236                            _solrPageIndexer.unindexPage(livePage.getId(), WebConstants.LIVE_WORKSPACE, true, true);
237                        }
238                    }
239                }
240                catch (Exception e)
241                {
242                    getLogger().error("Error unindexing page documents for content " + contentId, e);
243                }
244            }
245        }
246        finally
247        {
248            // Restore context
249            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp);
250        }
251    }
252    
253    ///////////////////////////////////////////////////////////////////////////
254    
255    /**
256     * Updates a Solr document referencing a page.
257     * @param pageId the page id.
258     * @throws Exception if an error occurs.
259     */
260    protected void _updatePageDocument(String pageId) throws Exception
261    {
262        _updatePageDocument(pageId, false);
263    }
264    
265    /**
266     * Updates a Solr document referencing a page.
267     * @param pageId the page id.
268     * @param indexRecursively to also process children pages.
269     * @throws Exception if an error occurs.
270     */
271    protected void _updatePageDocument(String pageId, boolean indexRecursively) throws Exception
272    {
273        _solrPageIndexer.reindexPage(pageId, WebConstants.LIVE_WORKSPACE, indexRecursively, false);
274    }
275    
276    /**
277     * Returns the page in the live workspace corresponding to a given id.
278     * @param pageId the page id.
279     * @return the page in the live workspace or null if not present.
280     */
281    protected Page _getPageInLive(String pageId)
282    {
283        Request request = ContextHelper.getRequest(_context);
284
285        // Retrieve current workspace
286        String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request);
287        
288        try
289        {
290            // Use live workspace
291            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, WebConstants.LIVE_WORKSPACE);
292            Page livePage = null;
293            
294            try
295            {
296                // Retrieve page in the live workspace
297                livePage = _resolver.resolveById(pageId);
298            }
299            catch (UnknownAmetysObjectException e)
300            {
301                // Page is not synchronized
302            }
303            
304            return livePage;
305        }
306        finally
307        {
308            // Restore context
309            RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp);
310        }
311    }
312    
313}