View Javadoc
1   /**
2    * This file Copyright (c) 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.module.resources.setup;
35  
36  import info.magnolia.jcr.RuntimeRepositoryException;
37  import info.magnolia.jcr.predicate.AbstractPredicate;
38  import info.magnolia.jcr.util.NodeTypes;
39  import info.magnolia.jcr.util.NodeUtil;
40  import info.magnolia.jcr.util.PropertyUtil;
41  import info.magnolia.module.InstallContext;
42  import info.magnolia.module.delta.AbstractRepositoryTask;
43  import info.magnolia.module.delta.TaskExecutionException;
44  import info.magnolia.resourceloader.jcr.JcrResourceOrigin;
45  
46  import java.util.HashMap;
47  import java.util.Iterator;
48  import java.util.Map;
49  
50  import javax.jcr.Node;
51  import javax.jcr.RepositoryException;
52  import javax.jcr.Session;
53  
54  import org.apache.commons.lang3.StringUtils;
55  import org.slf4j.Logger;
56  import org.slf4j.LoggerFactory;
57  
58  import com.google.common.collect.ArrayListMultimap;
59  import com.google.common.collect.ListMultimap;
60  
61  /**
62   * We update references in a second phase (upon success of {@linkplain ResourceCleanUpTask}):
63   *
64   * <ul>
65   * <li> We query for resources with template resources:reference and check if the path in reference property was part of performed renames.</li>
66   * <li> We don't rename those reference resources themselves</li>
67   * <li> We may optionally look up the config workspace for theme resources which might have been renamed as well (nice-have)</li>
68   * </ul>
69   */
70  public class UpdateResourceReferencesTask extends AbstractRepositoryTask {
71  
72      private static final Logger log = LoggerFactory.getLogger(UpdateResourceReferencesTask.class);
73  
74      private final String[] workspaces;
75  
76      private Map<String, String> changedPaths = new HashMap<>();
77  
78      public UpdateResourceReferencesTask(String name, String description, String... workspaces) {
79          super(name, description);
80          this.workspaces = workspaces;
81      }
82  
83      public UpdateResourceReferencesTask(String name) {
84          this(name, String.format("Update 'reference' property for resource with template 'resource:reference' in workspace '%s'", JcrResourceOrigin.RESOURCES_WORKSPACE), JcrResourceOrigin.RESOURCES_WORKSPACE);
85      }
86  
87      public void setChangedPaths(Map<String, String> changedPaths) {
88          this.changedPaths = changedPaths;
89      }
90  
91      @Override
92      protected void doExecute(InstallContext installContext) throws RepositoryException, TaskExecutionException {
93          if (workspaces != null) {
94              final ListMultimap<UpdateResourceMessage, String[]> messages = ArrayListMultimap.create();
95  
96              for (String workspace : workspaces) {
97                  try {
98                      Session workspaceSession = installContext.getJCRSession(workspace);
99  
100                     final Iterator<Node> nodeIterator = NodeUtil.collectAllChildren(workspaceSession.getRootNode(), new JCRResourceReferencesPredicate()).iterator();
101 
102                     // Update new reference path for all matched resource
103                     while (nodeIterator.hasNext()) {
104                         Node resource = nodeIterator.next();
105                         String referencePath = PropertyUtil.getString(resource, ResourceCleanUpTask.PROPERTY_REFERENCE);
106                         if (changedPaths.containsKey(referencePath)) {
107                             resource.setProperty(ResourceCleanUpTask.PROPERTY_REFERENCE, changedPaths.get(referencePath));
108                             messages.put(UpdateResourceMessage.REFERENCE_UPDATED, new String[] { resource.getPath(), referencePath, changedPaths.get(referencePath) });
109                         }
110                     }
111                 } catch (RepositoryException e) {
112                     messages.put(UpdateResourceMessage.CANNOT_GET_SESSION, new String[] { workspace });
113                 }
114             }
115 
116             UpdateResourceMessage.logMessages(installContext, log, messages);
117         }
118     }
119 
120     /**
121      * Simple predicate implementation to find resource content with property mgnl:template=resources:reference.
122      */
123     protected class JCRResourceReferencesPredicate extends AbstractPredicate<Node> {
124 
125         @Override
126         public boolean evaluateTyped(Node resource) {
127             try {
128                 return NodeUtil.isNodeType(resource, NodeTypes.Content.NAME)
129                         && resource.hasProperty(ResourceCleanUpTask.PROPERTY_TEMPLATE)
130                         && resource.hasProperty(ResourceCleanUpTask.PROPERTY_REFERENCE)
131                         && StringUtils.equals(ResourceCleanUpTask.REFERENCES_RESOURCES_TEMPLATE, PropertyUtil.getString(resource, ResourceCleanUpTask.PROPERTY_TEMPLATE));
132             } catch (RepositoryException e) {
133                 throw new RuntimeRepositoryException(e);
134             }
135         }
136     }
137 
138 }