View Javadoc
1   /**
2    * This file Copyright (c) 2003-2015 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.repository;
35  
36  import info.magnolia.audit.MgnlAuditLoggingContentDecorator;
37  import info.magnolia.audit.MgnlAuditLoggingContentDecoratorSessionWrapper;
38  import info.magnolia.cms.core.SystemProperty;
39  import info.magnolia.cms.core.version.MgnlVersioningSession;
40  import info.magnolia.cms.security.AccessDeniedException;
41  import info.magnolia.cms.util.ConfigUtil;
42  import info.magnolia.context.MgnlContext;
43  import info.magnolia.jcr.RuntimeRepositoryException;
44  import info.magnolia.jcr.predicate.AbstractPredicate;
45  import info.magnolia.jcr.util.NodeTypes;
46  import info.magnolia.jcr.util.NodeUtil;
47  import info.magnolia.jcr.wrapper.MgnlPropertySettingContentDecorator;
48  import info.magnolia.objectfactory.Classes;
49  import info.magnolia.objectfactory.Components;
50  import info.magnolia.repository.definition.RepositoryDefinition;
51  import info.magnolia.repository.definition.RepositoryMappingDefinition;
52  import info.magnolia.repository.definition.RepositoryMappingDefinitionReader;
53  import info.magnolia.repository.definition.WorkspaceMappingDefinition;
54  import info.magnolia.repository.mbean.TrackingSessionWrapper;
55  import info.magnolia.stats.JCRStats;
56  
57  import java.io.InputStream;
58  import java.util.Collection;
59  
60  import javax.inject.Singleton;
61  import javax.jcr.Credentials;
62  import javax.jcr.NoSuchWorkspaceException;
63  import javax.jcr.Node;
64  import javax.jcr.Repository;
65  import javax.jcr.RepositoryException;
66  import javax.jcr.Session;
67  import javax.jcr.Workspace;
68  
69  import org.apache.commons.io.IOUtils;
70  import org.slf4j.Logger;
71  import org.slf4j.LoggerFactory;
72  
73  /**
74   * Manages all used Repositories.
75   */
76  @Singleton
77  public final class DefaultRepositoryManager implements RepositoryManager {
78  
79      private static final Logger log = LoggerFactory.getLogger(DefaultRepositoryManager.class);
80  
81      private final WorkspaceMapping workspaceMapping = new WorkspaceMapping();
82  
83      @Override
84      public void init() {
85          log.info("Loading JCR");
86          workspaceMapping.clearRepositories();
87          try {
88              loadRepositories();
89              log.debug("JCR loaded");
90          } catch (Exception e) {
91              log.error(e.getMessage(), e);
92          }
93      }
94  
95      @Override
96      public void shutdown() {
97          log.info("Shutting down JCR");
98          for (RepositoryDefinition repositoryDefinition : workspaceMapping.getRepositoryDefinitions()) {
99              Provider provider = workspaceMapping.getRepositoryProvider(repositoryDefinition.getName());
100             provider.shutdownRepository();
101         }
102         workspaceMapping.clearAll();
103     }
104 
105     @Override
106     public boolean checkIfInitialized() throws AccessDeniedException, RepositoryException {
107         Collection<String> workspaceNames = workspaceMapping.getLogicalWorkspaceNames();
108         for (String workspace : workspaceNames) {
109             if (checkIfInitialized(workspace)) {
110                 return true;
111             }
112         }
113         return false;
114     }
115 
116     @Override
117     public boolean checkIfInitialized(String logicalWorkspace) throws RepositoryException, AccessDeniedException {
118         log.debug("Checking [{}] repository.", logicalWorkspace);
119         // TODO cant we login without using the system context?
120         Session session = MgnlContext.getSystemContext().getJCRSession(logicalWorkspace);
121 
122         if (session == null) {
123             throw new RuntimeException("Repository [" + logicalWorkspace + "] not loaded");
124         }
125 
126         Node startPage = session.getRootNode();
127 
128         // return any kind of children
129         Iterable<Node> children = NodeUtil.getNodes(startPage, new AbstractPredicate<Node>() {
130             @Override
131             public boolean evaluateTyped(Node content) {
132                 String name;
133                 try {
134                     name = content.getName();
135                 } catch (RepositoryException e) {
136                     throw new RuntimeRepositoryException(e);
137                 }
138                 return (!name.startsWith(NodeTypes.JCR_PREFIX) && !name.startsWith(NodeTypes.REP_PREFIX));
139             }
140         });
141 
142         if (children.iterator().hasNext()) {
143             log.debug("Content found in [{}].", logicalWorkspace);
144             return true;
145         }
146         return false;
147     }
148 
149     @Override
150     public void reload() {
151 
152         // TODO what exactly is this method supposed to do?!
153 
154         log.info("Reloading JCR");
155         init();
156     }
157 
158     private void loadRepositories() throws Exception {
159         final String path = SystemProperty.getProperty(SystemProperty.MAGNOLIA_REPOSITORIES_CONFIG);
160         if (path == null) {
161             throw new RepositoryNotInitializedException("No value found for property " + SystemProperty.MAGNOLIA_REPOSITORIES_CONFIG + ": can not start repository.");
162         }
163         final String tokenizedConfig = ConfigUtil.getTokenizedConfigFile(path);
164         InputStream stream = IOUtils.toInputStream(tokenizedConfig);
165 
166         RepositoryMappingDefinitionReader reader = new RepositoryMappingDefinitionReader();
167         RepositoryMappingDefinition mapping = reader.read(stream);
168 
169         for (RepositoryDefinition repositoryDefinition : mapping.getRepositories()) {
170             if (repositoryDefinition.getWorkspaces().isEmpty()) {
171                 repositoryDefinition.addWorkspace("default");
172             }
173             workspaceMapping.addRepositoryDefinition(repositoryDefinition);
174             loadRepository(repositoryDefinition);
175         }
176 
177         for (WorkspaceMappingDefinition workspaceMapping : mapping.getWorkspaceMappings()) {
178             this.workspaceMapping.addWorkspaceMappingDefinition(workspaceMapping);
179         }
180 
181         // some workspaces might exist even if they are not mapped ... provider knows about them so we should not exclude them.
182         for (RepositoryDefinition repoDefinition : workspaceMapping.getRepositoryDefinitions()) {
183             String repoName = repoDefinition.getName();
184             Provider provider = workspaceMapping.getRepositoryProvider(repoName);
185             Session session = provider.getSystemSession("default");
186             try {
187                 for (String wksName : session.getWorkspace().getAccessibleWorkspaceNames()) {
188                     if (!hasWorkspace(wksName)) {
189                         loadWorkspace(repoName, wksName);
190                     }
191                 }
192             } finally {
193                 session.logout();
194             }
195         }
196     }
197 
198     @Override
199     public void loadRepository(RepositoryDefinition definition) throws RepositoryNotInitializedException, InstantiationException, IllegalAccessException, ClassNotFoundException {
200         log.info("Loading JCR {}", definition.getName());
201 
202         Class<? extends Provider> providerClass = Classes.getClassFactory().forName(definition.getProvider());
203         Provider provider = Components.getComponentProvider().newInstance(providerClass);
204         provider.init(definition);
205         Repository repository = provider.getUnderlyingRepository();
206         workspaceMapping.setRepository(definition.getName(), repository);
207         workspaceMapping.setRepositoryProvider(definition.getName(), provider);
208 
209         if (definition.isLoadOnStartup()) {
210             for (String workspaceId : definition.getWorkspaces()) {
211                 registerNameSpacesAndNodeTypes(workspaceId, definition, provider);
212             }
213         }
214     }
215 
216     @Override
217     public void loadWorkspace(String repositoryId, String physicalWorkspaceName) throws RepositoryException {
218         log.info("Loading workspace {}", physicalWorkspaceName);
219 
220         workspaceMapping.addWorkspaceMapping(new WorkspaceMappingDefinition(physicalWorkspaceName, repositoryId, physicalWorkspaceName));
221 
222         Provider provider = getRepositoryProvider(repositoryId);
223         provider.registerWorkspace(physicalWorkspaceName);
224         RepositoryDefinition repositoryDefinition = workspaceMapping.getRepositoryDefinition(repositoryId);
225 
226         registerNameSpacesAndNodeTypes(physicalWorkspaceName, repositoryDefinition, provider);
227     }
228 
229     private void registerNameSpacesAndNodeTypes(String physicalWorkspaceName, RepositoryDefinition repositoryDefinition, Provider provider) {
230         try {
231             Session session = provider.getSystemSession(physicalWorkspaceName);
232             try {
233                 provider.registerNamespace(RepositoryConstants.NAMESPACE_PREFIX, RepositoryConstants.NAMESPACE_URI, session.getWorkspace());
234                 provider.registerNodeTypes();
235             } finally {
236                 session.logout();
237             }
238         } catch (RepositoryException e) {
239             log.error("Failed to initialize workspace {} in repository {}", physicalWorkspaceName, repositoryDefinition.getName(), e);
240         }
241     }
242 
243     @Override
244     public Session getSession(String logicalWorkspaceName, Credentials credentials) throws RepositoryException {
245         WorkspaceMappingDefinition mapping = this.workspaceMapping.getWorkspaceMapping(logicalWorkspaceName);
246         if (mapping == null) throw new NoSuchWorkspaceException(logicalWorkspaceName);
247         Repository repository = getRepository(mapping.getRepositoryName());
248         String physicalWorkspaceName = mapping.getPhysicalWorkspaceName();
249 
250         Session session = repository.login(credentials, physicalWorkspaceName);
251         return wrapSession(session, logicalWorkspaceName);
252     }
253 
254     @Override
255     public Session getSystemSession(String logicalWorkspaceName) throws RepositoryException {
256         WorkspaceMappingDefinition mapping = this.workspaceMapping.getWorkspaceMapping(logicalWorkspaceName);
257         if (mapping == null) throw new NoSuchWorkspaceException(logicalWorkspaceName);
258         Provider provider = getRepositoryProvider(mapping.getRepositoryName());
259         return wrapSession(provider.getSystemSession(mapping.getPhysicalWorkspaceName()), logicalWorkspaceName);
260     }
261 
262     public void createWorkspace(String repository, String logicalWorkspaceName) throws RepositoryException {
263         for (WorkspaceMappingDefinition mapping : workspaceMapping.getWorkspaceMappings()) {
264             Session session = getSystemSession(mapping.getLogicalWorkspaceName());
265             try {
266                 if (mapping.getRepositoryName().equals(repository)) {
267                     Workspace workspace = session.getWorkspace();
268                     workspace.createWorkspace(logicalWorkspaceName);
269                     workspaceMapping.addWorkspaceMapping(new WorkspaceMappingDefinition(logicalWorkspaceName, repository, logicalWorkspaceName));
270                     return;
271                 }
272             } finally {
273                 session.logout();
274             }
275         }
276         throw new RepositoryException("Repository [" + repository + "] doesn't exist.");
277     }
278 
279     private Session wrapSession(Session session, String logicalWorkspaceName) {
280         session = new TrackingSessionWrapper(session, JCRStats.getInstance());
281         if ("imaging".equals(logicalWorkspaceName) || "Expressions".equals(logicalWorkspaceName) || "Store".equals(logicalWorkspaceName) || "mgnlSystem".equals(logicalWorkspaceName)) {
282             // don't wrap imaging, expression and store into audit logging session
283             // it's only temporary solution. It will be removed when MAGNOLIA-5012 is resolved.
284             return new MgnlVersioningSession(session);
285         }
286         if (!RepositoryConstants.VERSION_STORE.equals(logicalWorkspaceName)) {
287             //do not wrap version store in versioning session or we get infinite redirect loop and stack overflow
288             session = new MgnlVersioningSession(session);
289         }
290 
291         // VersioningSessionWrapper has to be applied last, as it will end the chain of restore calls (doesn't forward there)
292         session = new MgnlPropertySettingContentDecorator().wrapSession(session);
293         return new MgnlAuditLoggingContentDecoratorSessionWrapper(session, new MgnlAuditLoggingContentDecorator());
294     }
295 
296     @Override
297     public boolean hasRepository(String repositoryId) {
298         return workspaceMapping.getRepositoryDefinition(repositoryId) != null;
299     }
300 
301     @Override
302     public RepositoryDefinition getRepositoryDefinition(String repositoryId) {
303         return workspaceMapping.getRepositoryDefinition(repositoryId);
304     }
305 
306     @Override
307     public Provider getRepositoryProvider(String repositoryId) {
308         return workspaceMapping.getRepositoryProvider(repositoryId);
309     }
310 
311     @Override
312     public Repository getRepository(String repositoryId) {
313         return workspaceMapping.getRepository(repositoryId);
314     }
315 
316     @Override
317     public void addWorkspaceMapping(WorkspaceMappingDefinition mapping) {
318         workspaceMapping.addWorkspaceMapping(mapping);
319     }
320 
321     @Override
322     public boolean hasWorkspace(String logicalWorkspaceName) {
323         return workspaceMapping.getWorkspaceMapping(logicalWorkspaceName) != null;
324     }
325 
326     @Override
327     public Collection<WorkspaceMappingDefinition> getWorkspaceMappings() {
328         return workspaceMapping.getWorkspaceMappings();
329     }
330 
331     @Override
332     public WorkspaceMappingDefinition getWorkspaceMapping(String logicalWorkspaceName) {
333         return workspaceMapping.getWorkspaceMapping(logicalWorkspaceName);
334     }
335 
336     @Override
337     public Collection<String> getWorkspaceNames() {
338         return workspaceMapping.getLogicalWorkspaceNames();
339     }
340 
341 }