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