001/*
002 *  Copyright 2010 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;
018
019import java.util.regex.Pattern;
020
021import javax.jcr.NoSuchWorkspaceException;
022import javax.jcr.RepositoryException;
023import javax.jcr.Session;
024import javax.jcr.observation.Event;
025import javax.jcr.observation.ObservationManager;
026
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.commons.lang3.ArrayUtils;
030
031import org.ametys.plugins.repository.AmetysObjectResolver;
032import org.ametys.plugins.repository.ModifiableTraversableAmetysObject;
033import org.ametys.plugins.repository.jcr.DefaultTraversableAmetysObject;
034import org.ametys.plugins.repository.provider.AdminSessionProvider;
035import org.ametys.runtime.config.Config;
036import org.ametys.web.live.LiveWorkspaceListener;
037import org.ametys.web.repository.site.Site;
038import org.ametys.web.repository.site.SiteManager;
039import org.ametys.web.synchronization.SynchronizeComponent;
040
041/**
042 * Web plugin init class
043 */
044public class Init extends org.ametys.cms.Init
045{
046    private SiteManager _siteManager;
047    private AdminSessionProvider _adminSessionProvider;
048    private SynchronizeComponent _synchronizeComponent;
049    
050    @Override
051    public void service(ServiceManager manager) throws ServiceException
052    {
053        super.service(manager);
054        _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE);
055        _adminSessionProvider = (AdminSessionProvider) manager.lookup(AdminSessionProvider.ROLE);
056        _synchronizeComponent = (SynchronizeComponent) manager.lookup(SynchronizeComponent.ROLE);
057    }
058    
059    @Override
060    public void init() throws Exception
061    {
062        DefaultTraversableAmetysObject root = _resolver.resolveByPath("/");
063
064        Session session = _adminSessionProvider.getAdminSession();
065        ObservationManager manager = session.getWorkspace().getObservationManager();
066        
067        // Create Live workspace if needed
068        createLiveWorkspace();
069        
070        // Create archives workspace if needed
071        createArchiveWorkspace();
072        
073        // Register a non-recursive live workspace listener for root nodes
074        manager.addEventListener(new LiveWorkspaceListener(_repository, _synchronizeComponent, getLogger()), 
075                                 Event.NODE_ADDED
076                                 | Event.NODE_REMOVED
077                                 | Event.NODE_MOVED
078                                 | Event.PROPERTY_ADDED
079                                 | Event.PROPERTY_CHANGED
080                                 | Event.PROPERTY_REMOVED, 
081                                 root.getNode().getPath(), false, null, null, false);
082        
083        // Create plugins root node if needed
084        createPluginsRootNode(root);
085        
086        // Create sites root node if needed
087        createSitesRootNode(root);
088        
089        // Register a live workspace listener for plugins, excluding root contents and ACL nodes
090        manager.addEventListener(new LiveWorkspaceListener(_repository, _synchronizeComponent, getLogger(), ArrayUtils.toArray(Pattern.compile("/ametys:root/ametys:plugins/[^/]+/ametys:contents.*"), Pattern.compile("/ametys:root/ametys:plugins/.+/ametys-internal:acl.*"))), 
091                                 Event.NODE_ADDED
092                                 | Event.NODE_REMOVED
093                                 | Event.NODE_MOVED
094                                 | Event.PROPERTY_ADDED
095                                 | Event.PROPERTY_CHANGED
096                                 | Event.PROPERTY_REMOVED, 
097                                 root.getNode().getPath() + "/ametys:plugins", true, null, null, false);
098        
099        // Create contents root node if needed
100        createContentsRootNode(root);
101        
102        // Create resources root node if needed
103        createResourcesRootNode(root);
104        
105        // Create shared resources (for all sites) root node if needed
106        createSharedResourcesNode(root);
107        
108        root.getNode().getSession().logout();
109        
110        for (Site site : _siteManager.getSites())
111        {
112            // Register a live workspace listener for plugins on each site, excluding root contents
113            manager.addEventListener(new LiveWorkspaceListener(_repository, _synchronizeComponent, getLogger(), ArrayUtils.toArray(Pattern.compile("/ametys:root/ametys-internal:sites/[^:]+/ametys-internal:plugins/[^/]+/ametys:contents.*"), Pattern.compile("/ametys:root/ametys-internal:sites/[^:]+/ametys-internal:plugins/.+/ametys-internal:acl.*"))), 
114                                     Event.NODE_ADDED
115                                     | Event.NODE_REMOVED
116                                     | Event.NODE_MOVED
117                                     | Event.PROPERTY_ADDED
118                                     | Event.PROPERTY_CHANGED
119                                     | Event.PROPERTY_REMOVED, 
120                                     site.getNode().getPath() + "/ametys-internal:plugins", true, null, null, false);
121            
122            // Creates local plugins space at "<site>/ametys-internal:plugins" if needed
123            if (!site.hasChild("ametys-internal:plugins"))
124            {
125                site.createChild("ametys-internal:plugins", "ametys:unstructured");
126            }
127
128            // Register a live workspace listener for resources explorer on each site
129            manager.addEventListener(new LiveWorkspaceListener(_repository, _synchronizeComponent, getLogger()), 
130                                     Event.NODE_ADDED
131                                     | Event.NODE_REMOVED
132                                     | Event.NODE_MOVED
133                                     | Event.PROPERTY_ADDED
134                                     | Event.PROPERTY_CHANGED
135                                     | Event.PROPERTY_REMOVED, 
136                                     site.getNode().getPath() + "/ametys-internal:resources", true, null, null, false);
137            
138            // Creates local resources space at "<site>/ametys-internal:resources" if needed
139            if (!site.hasChild("ametys-internal:resources"))
140            {
141                site.createChild("ametys-internal:resources", "ametys:resources-collection");
142            }
143
144            if (site.needsSave())
145            {
146                site.saveChanges();
147            }
148        }
149    }
150    
151    /**
152     * Create the live workspace.
153     * @throws RepositoryException if a repository error occurred.
154     */
155    protected void createLiveWorkspace() throws RepositoryException
156    {
157        Session session = null;
158        Session liveSession = null;
159        
160        try
161        {
162            // Try to login 
163            liveSession = _repository.login(WebConstants.LIVE_WORKSPACE);
164        }
165        catch (NoSuchWorkspaceException e)
166        {
167            getLogger().info("Live workspace does not exist, creating it...");
168            
169            session = _repository.login();
170            session.getWorkspace().createWorkspace(WebConstants.LIVE_WORKSPACE);
171            liveSession = _repository.login(WebConstants.LIVE_WORKSPACE);
172
173            liveSession.getRootNode().addNode(AmetysObjectResolver.ROOT_REPO, AmetysObjectResolver.ROOT_TYPE);
174            liveSession.save();
175        }
176        finally
177        {
178            if (session != null)
179            {
180                session.logout();
181            }
182            if (liveSession != null)
183            {
184                liveSession.logout();
185            }
186        }
187    }
188    
189    /**
190     * Create the JCR root node for sites storage if needed
191     * @param rootNode The JCR root node
192     * @throws RepositoryException Thrown if the node cannot be created
193     */
194    protected void createSitesRootNode(ModifiableTraversableAmetysObject rootNode) throws RepositoryException
195    {
196        if (!rootNode.hasChild(SiteManager.ROOT_SITES))
197        {
198            rootNode.createChild(SiteManager.ROOT_SITES, "ametys:collection");
199            rootNode.saveChanges();
200        }
201    }
202    
203    /**
204     * Create the JCR root node for shared resources storage if needed
205     * @param rootNode The JCR root node
206     * @throws RepositoryException Thrown if the node cannot be created
207     */
208    protected void createSharedResourcesNode(ModifiableTraversableAmetysObject rootNode) throws RepositoryException
209    {
210        // Creates web-explorer space at "/ametys:plugins/web-explorer" if needed
211        DefaultTraversableAmetysObject pluginsNode = (DefaultTraversableAmetysObject) rootNode.getChild("ametys:plugins");
212        if (!pluginsNode.hasChild("web-explorer"))
213        {
214            pluginsNode.createChild("web-explorer", "ametys:unstructured");
215        }
216        
217        if (pluginsNode.needsSave())
218        {
219            pluginsNode.saveChanges();
220        }
221
222        // Creates shared resources space at "/ametys:plugins/web-explorer/shared-resources" if needed
223        boolean useSharedResources = Config.getInstance().getValueAsBoolean("resources.shared.folder");
224        DefaultTraversableAmetysObject explorerNode = (DefaultTraversableAmetysObject) pluginsNode.getChild("web-explorer");
225        
226        if (useSharedResources)
227        {
228            DefaultTraversableAmetysObject sharedRoot;
229            
230            if (!explorerNode.hasChild("shared-resources"))
231            {
232                sharedRoot = (DefaultTraversableAmetysObject) explorerNode.createChild("shared-resources", "ametys:unstructured");
233            }
234            else
235            {
236                sharedRoot = (DefaultTraversableAmetysObject) explorerNode.getChild("shared-resources");
237            }
238            
239            // Also automatically create the "all" share, available to all sites
240            if (!sharedRoot.hasChild("all"))
241            {
242                sharedRoot.createChild("all", "ametys:resources-collection");
243            }
244            
245            if (explorerNode.needsSave())
246            {
247                explorerNode.saveChanges();
248            }
249        }
250    }
251}