001/*
002 *  Copyright 2013 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 */
016
017package org.ametys.web.live;
018
019import java.io.PrintWriter;
020import java.io.StringWriter;
021import java.util.ArrayList;
022import java.util.List;
023
024import javax.jcr.Repository;
025import javax.jcr.Session;
026
027import org.apache.avalon.framework.component.Component;
028import org.apache.avalon.framework.service.ServiceException;
029import org.apache.avalon.framework.service.ServiceManager;
030import org.apache.avalon.framework.service.Serviceable;
031import org.apache.commons.lang.StringUtils;
032
033import org.ametys.cms.indexing.IndexingException;
034import org.ametys.cms.indexing.WorkspaceIndexer;
035import org.ametys.plugins.repository.AmetysObjectIterable;
036import org.ametys.runtime.plugin.component.AbstractLogEnabled;
037import org.ametys.web.WebConstants;
038import org.ametys.web.cache.CacheHelper;
039import org.ametys.web.cache.pageelement.PageElementCache;
040import org.ametys.web.indexing.SiteIndexer;
041import org.ametys.web.repository.site.Site;
042import org.ametys.web.repository.site.SiteManager;
043import org.ametys.web.skin.Skin;
044import org.ametys.web.skin.SkinsManager;
045
046/**
047 * Component for rebuild the live workspace, reindex all sitemaps and reset cache.
048 * Provide a way to rebuild the full live workspace, or only the live of a site.
049 */
050public class RebuildLiveComponent extends AbstractLogEnabled implements Component, Serviceable
051{
052    /** Avalon Role. */
053    public static final String ROLE = RebuildLiveComponent.class.getName();
054    
055    private Repository _repository;
056    private LivePopulatorExtensionPoint _livePopulatorExtensionPoint;
057    private SitePopulator _sitePopulator;
058    private SiteIndexer _siteIndexer;
059    private WorkspaceIndexer _workspaceIndexer;
060    private SiteManager _siteManager;
061    private SkinsManager _skinsManager;
062    private PageElementCache _inputDataCache;
063    private PageElementCache _zoneItemCache;
064    
065    @Override
066    public void service(ServiceManager manager) throws ServiceException
067    {
068        _siteIndexer = (SiteIndexer) manager.lookup(SiteIndexer.ROLE);
069        _repository = (Repository) manager.lookup(Repository.class.getName());
070        _livePopulatorExtensionPoint = (LivePopulatorExtensionPoint)  manager.lookup(LivePopulatorExtensionPoint.ROLE);
071        _sitePopulator = (SitePopulator) manager.lookup(SitePopulator.ROLE);
072        _workspaceIndexer = (WorkspaceIndexer) manager.lookup(WorkspaceIndexer.ROLE);
073        _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE);
074        _skinsManager = (SkinsManager) manager.lookup(SkinsManager.ROLE);
075        _inputDataCache = (PageElementCache) manager.lookup(PageElementCache.ROLE + "/inputData");
076        _zoneItemCache = (PageElementCache) manager.lookup(PageElementCache.ROLE + "/zoneItem");
077    }
078    
079    /**
080     * Rebuild live workspace, index all sitemaps and reset cache.
081     * @throws Exception if an error occurs.
082     */
083    public void rebuildLiveWorkspace() throws Exception
084    {
085        List<String> errors = new ArrayList<>();
086        
087        try
088        {
089            getLogger().info("Rebuilding live workspace started");
090
091            long t0 = System.currentTimeMillis();
092        
093            errors.addAll(_populateLiveWorkspace());
094            errors.addAll(_reindexLiveWorkspace());
095            
096            // Clear every site's cache.
097            AmetysObjectIterable<Site> siteIterable = _siteManager.getSites();
098            for (Site site : siteIterable)
099            {
100                try
101                {
102                    // TODO Clear only the live workspace?
103                    _clearSiteCache(site);
104                }
105                catch (Throwable t)
106                {
107                    errors.add("Failed to clear cache of site '" + site.getName() + "'\n" + t.getMessage());
108                    getLogger().error("Failed to clear cache of site '" + site.getName() + "'", t);
109                }
110            }
111            
112            getLogger().info("Rebuilding live workspace ended in {} ms with {} error(s)", System.currentTimeMillis() - t0, errors.size());
113        }
114        catch (Exception e)
115        {
116            throw new Exception("Failed to rebuild live workspace", e);
117        }
118        
119        if (errors.size() > 0)
120        {
121            throw new Exception("Failed to rebuild live workspace with " + errors.size() + " error(s)\n\n--------------------------------------\n" + StringUtils.join(errors, "\n\n") + "\n--------------------------------------");
122        }
123    }
124    
125    private List<String> _populateLiveWorkspace()
126    {
127        List<String> errors = new ArrayList<>();
128
129        Session session = null;
130        Session liveSession = null;
131        
132        try
133        {
134            getLogger().info("Populating live workspace started");
135            
136            long t0 = System.currentTimeMillis();
137            
138            session = _repository.login();
139            liveSession = _repository.login(WebConstants.LIVE_WORKSPACE);
140            
141            // Synchronize other data
142            for (String id : _livePopulatorExtensionPoint.getExtensionsIds())
143            {
144                getLogger().info("Populating live workspace using LivePopulator {} started", id);
145             
146                try
147                {
148                    LivePopulator populator = _livePopulatorExtensionPoint.getExtension(id);
149                    
150                    errors.addAll(populator.populate(session, liveSession));
151                    
152                    if (liveSession.hasPendingChanges())
153                    {
154                        liveSession.save();
155                    }
156                }
157                catch (Throwable t)
158                {
159                    errors.add("Failed to populate live workspace with populator '" + id + "'\n" + t.getMessage());
160                    getLogger().error("Failed to populate live workspace with populator '" + id + "'", t);
161                }
162                
163                getLogger().info("Populating live workspace using LivePopulator {} ended", id);
164            }
165            
166            getLogger().info("Populating live workspace ended in {} ms", System.currentTimeMillis() - t0);
167        }
168        catch (Throwable t)
169        {
170            StringWriter sw = new StringWriter();
171            t.printStackTrace(new PrintWriter(sw));
172            
173            errors.add("Failed to populate live workspace\n" + sw.toString());
174            getLogger().error("Failed to populate live workspace", t);
175        }
176        finally
177        {
178            if (session != null)
179            {
180                session.logout();
181            }
182            
183            if (liveSession != null)
184            {
185                liveSession.logout();
186            }
187        }
188        
189        return errors;
190    }
191    
192    private List<String> _reindexLiveWorkspace()
193    {
194        List<String> errors = new ArrayList<>();
195        
196        try
197        {
198            getLogger().info("Indexing Live workspace started");
199            
200            long t0 = System.currentTimeMillis();
201            
202            // Index the whole live workspace and not just every site.
203            _workspaceIndexer.index(WebConstants.LIVE_WORKSPACE);
204            
205            getLogger().info("Indexing live workspace ended in {} ms", System.currentTimeMillis() - t0);
206        }
207        catch (IndexingException e)
208        {
209            errors.add("Failed to index live workspace\n" + e.getMessage());
210            getLogger().error("Failed to index live workspace", e);
211        }
212        
213        return errors;
214    }
215    
216    /**
217     * Rebuild the live of a site, index all sitemaps and reset cache.
218     * @param site The site to be rebuilt
219     * @throws Exception if an error occurs.
220     */
221    public void rebuildLive(Site site) throws Exception
222    {
223        String siteName = "";
224        try
225        {
226            siteName = site.getName();
227            
228            Skin skin = _skinsManager.getSkin(site.getSkinId());
229            assert skin != null;
230            
231            getLogger().info("Rebuilding site {} started", siteName);
232
233            long t0 = System.currentTimeMillis();
234            
235            _populateSite(site, skin);
236            _reindexSite(site);
237            _clearSiteCache(site);
238            
239            getLogger().info("Rebuilding site {} ended in {} ms", siteName, System.currentTimeMillis() - t0);
240        }
241        catch (Exception e)
242        {
243            throw new Exception("Failed to rebuild live workspace for site '" + siteName + "'", e);
244        }
245    }
246    
247    private void _populateSite(Site site, Skin skin) throws Exception
248    {
249        String siteName = site.getName();
250
251        getLogger().info("Populating site {} started", siteName);
252        
253        long t0 = System.currentTimeMillis();
254        
255        _sitePopulator.populate(site, skin);
256        
257        getLogger().info("Populating site {} ended in {} ms", siteName, System.currentTimeMillis() - t0);
258    }
259    
260    private void _reindexSite(Site site) throws Exception
261    {
262        String siteName = site.getName();
263        
264        getLogger().info("Indexing site '{}' started", siteName);
265        
266        long t0 = System.currentTimeMillis();
267        
268        // Reindex the site in the live workspace.
269        _siteIndexer.indexSite(site.getName(), WebConstants.LIVE_WORKSPACE);
270        
271        getLogger().info("Indexing of site '{}' ended in {} ms", siteName, System.currentTimeMillis() - t0);
272    }
273    
274    private void _clearSiteCache(Site site) throws Exception
275    {
276        String siteName = site.getName();
277        
278        getLogger().info("Clearing cache for site {} started", siteName);
279        
280        long t0 = System.currentTimeMillis();
281        
282        CacheHelper.invalidateCache(site, getLogger());
283        _inputDataCache.clear(null, siteName);
284        _zoneItemCache.clear(null, siteName);
285        
286        getLogger().info("Clearing cache for site '{}' ended in {} ms", siteName, System.currentTimeMillis() - t0);
287    }
288}