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