View Javadoc
1   /**
2    * This file Copyright (c) 2003-2016 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.init.MagnoliaConfigurationProperties;
44  import info.magnolia.jcr.RuntimeRepositoryException;
45  import info.magnolia.jcr.predicate.AbstractPredicate;
46  import info.magnolia.jcr.util.NodeTypes;
47  import info.magnolia.jcr.util.NodeUtil;
48  import info.magnolia.jcr.wrapper.MgnlPropertySettingContentDecorator;
49  import info.magnolia.objectfactory.Classes;
50  import info.magnolia.objectfactory.Components;
51  import info.magnolia.repository.definition.RepositoryDefinition;
52  import info.magnolia.repository.definition.RepositoryMappingDefinition;
53  import info.magnolia.repository.definition.RepositoryMappingDefinitionReader;
54  import info.magnolia.repository.definition.WorkspaceMappingDefinition;
55  import info.magnolia.repository.mbean.TrackingSessionWrapper;
56  import info.magnolia.stats.JCRStats;
57  
58  import java.io.InputStream;
59  import java.util.Collection;
60  
61  import javax.inject.Inject;
62  import javax.inject.Singleton;
63  import javax.jcr.Credentials;
64  import javax.jcr.NoSuchWorkspaceException;
65  import javax.jcr.Node;
66  import javax.jcr.Repository;
67  import javax.jcr.RepositoryException;
68  import javax.jcr.Session;
69  import javax.jcr.Workspace;
70  
71  import org.apache.commons.io.IOUtils;
72  import org.apache.commons.lang3.StringUtils;
73  import org.slf4j.Logger;
74  import org.slf4j.LoggerFactory;
75  
76  /**
77   * Manages all used Repositories.
78   */
79  @Singleton
80  public final class DefaultRepositoryManager implements RepositoryManager {
81  
82      private static final Logger log = LoggerFactory.getLogger(DefaultRepositoryManager.class);
83  
84      private final WorkspaceMapping workspaceMapping;
85      private final MagnoliaConfigurationProperties magnoliaConfigurationProperties;
86  
87      /**
88       * @deprecated since 5.3.11 - use {@link DefaultRepositoryManager(MagnoliaConfigurationProperties)} instead.
89       */
90      @Deprecated
91      public DefaultRepositoryManager() {
92          this(Components.getComponent(MagnoliaConfigurationProperties.class), new WorkspaceMapping());
93      }
94  
95      @Inject
96      public DefaultRepositoryManager(MagnoliaConfigurationProperties magnoliaConfigurationProperties, WorkspaceMapping workspaceMapping) {
97          this.magnoliaConfigurationProperties = magnoliaConfigurationProperties;
98          this.workspaceMapping = workspaceMapping;
99      }
100 
101     @Override
102     public void init() {
103         log.info("Loading JCR");
104         workspaceMapping.clearRepositories();
105         try {
106             loadRepositories();
107             log.debug("JCR loaded");
108         } catch (Exception e) {
109             log.error(e.getMessage(), e);
110         }
111     }
112 
113     @Override
114     public void shutdown() {
115         log.info("Shutting down JCR");
116         for (RepositoryDefinition repositoryDefinition : workspaceMapping.getRepositoryDefinitions()) {
117             Provider provider = workspaceMapping.getRepositoryProvider(repositoryDefinition.getName());
118             provider.shutdownRepository();
119         }
120         workspaceMapping.clearAll();
121     }
122 
123     @Override
124     public boolean checkIfInitialized() throws AccessDeniedException, RepositoryException {
125         Collection<String> workspaceNames = workspaceMapping.getLogicalWorkspaceNames();
126         for (String workspace : workspaceNames) {
127             if (checkIfInitialized(workspace)) {
128                 return true;
129             }
130         }
131         return false;
132     }
133 
134     @Override
135     public boolean checkIfInitialized(String logicalWorkspace) throws RepositoryException, AccessDeniedException {
136         log.debug("Checking [{}] repository.", logicalWorkspace);
137         // TODO cant we login without using the system context?
138         Session session = MgnlContext.getSystemContext().getJCRSession(logicalWorkspace);
139 
140         if (session == null) {
141             throw new RuntimeException("Repository [" + logicalWorkspace + "] not loaded");
142         }
143 
144         Node startPage = session.getRootNode();
145 
146         // return any kind of children
147         Iterable<Node> children = NodeUtil.getNodes(startPage, new AbstractPredicate<Node>() {
148             @Override
149             public boolean evaluateTyped(Node content) {
150                 String name;
151                 try {
152                     name = content.getName();
153                 } catch (RepositoryException e) {
154                     throw new RuntimeRepositoryException(e);
155                 }
156                 return (!name.startsWith(NodeTypes.JCR_PREFIX) && !name.startsWith(NodeTypes.REP_PREFIX));
157             }
158         });
159 
160         if (children.iterator().hasNext()) {
161             log.debug("Content found in [{}].", logicalWorkspace);
162             return true;
163         }
164         return false;
165     }
166 
167     @Override
168     public void reload() {
169 
170         // TODO what exactly is this method supposed to do?!
171 
172         log.info("Reloading JCR");
173         init();
174     }
175 
176     private void loadRepositories() throws Exception {
177         final String path = SystemProperty.getProperty(SystemProperty.MAGNOLIA_REPOSITORIES_CONFIG);
178         if (path == null) {
179             throw new RepositoryNotInitializedException("No value found for property " + SystemProperty.MAGNOLIA_REPOSITORIES_CONFIG + ": can not start repository.");
180         }
181         final String tokenizedConfig = ConfigUtil.getTokenizedConfigFile(path);
182         InputStream stream = IOUtils.toInputStream(tokenizedConfig);
183 
184         RepositoryMappingDefinitionReader reader = new RepositoryMappingDefinitionReader();
185         RepositoryMappingDefinition mapping = reader.read(stream);
186 
187         for (RepositoryDefinition repositoryDefinition : mapping.getRepositories()) {
188             if (repositoryDefinition.getWorkspaces().isEmpty()) {
189                 repositoryDefinition.addWorkspace("default");
190             }
191             workspaceMapping.addRepositoryDefinition(repositoryDefinition);
192             loadRepository(repositoryDefinition);
193         }
194 
195         for (WorkspaceMappingDefinition workspaceMapping : mapping.getWorkspaceMappings()) {
196             this.workspaceMapping.addWorkspaceMappingDefinition(workspaceMapping);
197         }
198 
199         // some workspaces might exist even if they are not mapped ... provider knows about them so we should not exclude them.
200         for (RepositoryDefinition repoDefinition : workspaceMapping.getRepositoryDefinitions()) {
201             String repoName = repoDefinition.getName();
202             Provider provider = workspaceMapping.getRepositoryProvider(repoName);
203             Session session = provider.getSystemSession("default");
204             try {
205                 for (String wksName : session.getWorkspace().getAccessibleWorkspaceNames()) {
206                     if (!hasWorkspace(wksName)) {
207                         loadWorkspace(repoName, wksName);
208                     }
209                 }
210             } finally {
211                 session.logout();
212             }
213         }
214     }
215 
216     @Override
217     public void loadRepository(RepositoryDefinition definition) throws RepositoryNotInitializedException, InstantiationException, IllegalAccessException, ClassNotFoundException {
218         log.info("Loading JCR {}", definition.getName());
219 
220         Class<? extends Provider> providerClass = Classes.getClassFactory().forName(definition.getProvider());
221         Provider provider = Components.getComponentProvider().newInstance(providerClass);
222         provider.init(definition);
223         Repository repository = provider.getUnderlyingRepository();
224         workspaceMapping.setRepository(definition.getName(), repository);
225         workspaceMapping.setRepositoryProvider(definition.getName(), provider);
226 
227         if (definition.isLoadOnStartup()) {
228             for (String workspaceId : definition.getWorkspaces()) {
229                 registerNameSpacesAndNodeTypes(workspaceId, definition, provider);
230             }
231         }
232     }
233 
234     @Override
235     public void loadWorkspace(String repositoryId, String physicalWorkspaceName) throws RepositoryException {
236         log.info("Loading workspace {}", physicalWorkspaceName);
237 
238         workspaceMapping.addWorkspaceMapping(new WorkspaceMappingDefinition(physicalWorkspaceName, repositoryId, physicalWorkspaceName));
239 
240         Provider provider = getRepositoryProvider(repositoryId);
241         provider.registerWorkspace(physicalWorkspaceName);
242         RepositoryDefinition repositoryDefinition = workspaceMapping.getRepositoryDefinition(repositoryId);
243 
244         registerNameSpacesAndNodeTypes(physicalWorkspaceName, repositoryDefinition, provider);
245     }
246 
247     private void registerNameSpacesAndNodeTypes(String physicalWorkspaceName, RepositoryDefinition repositoryDefinition, Provider provider) {
248         try {
249             Session session = provider.getSystemSession(physicalWorkspaceName);
250             try {
251                 provider.registerNamespace(RepositoryConstants.NAMESPACE_PREFIX, RepositoryConstants.NAMESPACE_URI, session.getWorkspace());
252                 provider.registerNodeTypes();
253             } finally {
254                 session.logout();
255             }
256         } catch (RepositoryException e) {
257             log.error("Failed to initialize workspace {} in repository {}", physicalWorkspaceName, repositoryDefinition.getName(), e);
258         }
259     }
260 
261     @Override
262     public Session getSession(String logicalWorkspaceName, Credentials credentials) throws RepositoryException {
263         WorkspaceMappingDefinition mapping = this.workspaceMapping.getWorkspaceMapping(logicalWorkspaceName);
264         if (mapping == null) throw new NoSuchWorkspaceException(logicalWorkspaceName);
265         Repository repository = getRepository(mapping.getRepositoryName());
266         String physicalWorkspaceName = mapping.getPhysicalWorkspaceName();
267 
268         Session session = repository.login(credentials, physicalWorkspaceName);
269         return wrapSession(session, logicalWorkspaceName);
270     }
271 
272     @Override
273     public Session getSystemSession(String logicalWorkspaceName) throws RepositoryException {
274         WorkspaceMappingDefinition mapping = this.workspaceMapping.getWorkspaceMapping(logicalWorkspaceName);
275         if (mapping == null) throw new NoSuchWorkspaceException(logicalWorkspaceName);
276         Provider provider = getRepositoryProvider(mapping.getRepositoryName());
277         return wrapSession(provider.getSystemSession(mapping.getPhysicalWorkspaceName()), logicalWorkspaceName);
278     }
279 
280     public void createWorkspace(String repository, String logicalWorkspaceName) throws RepositoryException {
281         for (WorkspaceMappingDefinition mapping : workspaceMapping.getWorkspaceMappings()) {
282             Session session = getSystemSession(mapping.getLogicalWorkspaceName());
283             try {
284                 if (mapping.getRepositoryName().equals(repository)) {
285                     Workspace workspace = session.getWorkspace();
286                     workspace.createWorkspace(logicalWorkspaceName);
287                     workspaceMapping.addWorkspaceMapping(new WorkspaceMappingDefinition(logicalWorkspaceName, repository, logicalWorkspaceName));
288                     return;
289                 }
290             } finally {
291                 session.logout();
292             }
293         }
294         throw new RepositoryException("Repository [" + repository + "] doesn't exist.");
295     }
296 
297     private Session wrapSession(Session session, String logicalWorkspaceName) {
298         session = new TrackingSessionWrapper(session, JCRStats.getInstance());
299         if ("imaging".equals(logicalWorkspaceName) || "Expressions".equals(logicalWorkspaceName) || "Store".equals(logicalWorkspaceName) || "mgnlSystem".equals(logicalWorkspaceName)) {
300             // don't wrap imaging, expression and store into audit logging session
301             // it's only temporary solution. It will be removed when MAGNOLIA-5012 is resolved.
302             return new MgnlVersioningSession(session);
303         }
304         if (!RepositoryConstants.VERSION_STORE.equals(logicalWorkspaceName)) {
305             //do not wrap version store in versioning session or we get infinite redirect loop and stack overflow
306             session = new MgnlVersioningSession(session);
307         }
308 
309         // VersioningSessionWrapper has to be applied last, as it will end the chain of restore calls (doesn't forward there)
310         session = new MgnlPropertySettingContentDecorator().wrapSession(session);
311         return new MgnlAuditLoggingContentDecoratorSessionWrapper(session, new MgnlAuditLoggingContentDecorator());
312     }
313 
314     @Override
315     public boolean hasRepository(String repositoryId) {
316         return workspaceMapping.getRepositoryDefinition(repositoryId) != null;
317     }
318 
319     @Override
320     public RepositoryDefinition getRepositoryDefinition(String repositoryId) {
321         return workspaceMapping.getRepositoryDefinition(repositoryId);
322     }
323 
324     @Override
325     public Provider getRepositoryProvider(String repositoryId) {
326         return workspaceMapping.getRepositoryProvider(repositoryId);
327     }
328 
329     @Override
330     public Repository getRepository(String repositoryId) {
331         return workspaceMapping.getRepository(repositoryId);
332     }
333 
334     @Override
335     public void addWorkspaceMapping(WorkspaceMappingDefinition mapping) {
336         workspaceMapping.addWorkspaceMapping(mapping);
337     }
338 
339     @Override
340     public boolean hasWorkspace(String logicalWorkspaceName) {
341         return workspaceMapping.getWorkspaceMapping(logicalWorkspaceName) != null;
342     }
343 
344     @Override
345     public Collection<WorkspaceMappingDefinition> getWorkspaceMappings() {
346         return workspaceMapping.getWorkspaceMappings();
347     }
348 
349     @Override
350     public WorkspaceMappingDefinition getWorkspaceMapping(String logicalWorkspaceName) {
351         return workspaceMapping.getWorkspaceMapping(logicalWorkspaceName);
352     }
353 
354     @Override
355     public Collection<String> getWorkspaceNames() {
356         return workspaceMapping.getLogicalWorkspaceNames();
357     }
358 
359     /**
360      * Verify that workspace exists in clustered repository.
361      * @param workspace The name of the workspace
362      * @return {@code true} if workspace is a shared workspace that is configured in a clustered repository
363      */
364     public boolean isClusteredWorkspace(String workspace) {
365         String clusterConfigFile = magnoliaConfigurationProperties.getProperty(SystemProperty.MAGNOLIA_REPOSITORIES_CLUSTER_CONFIG);
366         if (StringUtils.isBlank(clusterConfigFile)) {
367             return false;
368         }
369 
370         Collection<RepositoryDefinition> repoDefinitions = workspaceMapping.getRepositoryDefinitions();
371         for (RepositoryDefinition repoDefinition : repoDefinitions) {
372             if (repoDefinition.getParameters().containsValue(clusterConfigFile) && repoDefinition.getWorkspaces().contains(workspace)) {
373                 return true;
374             }
375         }
376 
377         return false;
378     }
379 
380     public boolean isClusterMaster() {
381         return magnoliaConfigurationProperties.hasProperty(SystemProperty.MAGNOLIA_REPOSITORIES_CLUSTER_MASTER) &&
382                 magnoliaConfigurationProperties.getBooleanProperty(SystemProperty.MAGNOLIA_REPOSITORIES_CLUSTER_MASTER);
383     }
384 }