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