Clover icon

Magnolia Resources Module 2.4.2

  1. Project Clover database Fri Nov 6 2015 16:15:26 CET
  2. Package info.magnolia.module.resources.setup

File TemplateMigrationTask.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

34
67
7
1
297
158
34
0.51
9.57
7
4.86

Classes

Class Line # Actions
TemplateMigrationTask 84 67 0% 34 5
0.953703795.4%
 

Contributing tests

This file is covered by 22 tests. .

Source view

1    /**
2    * This file Copyright (c) 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.module.resources.setup;
35   
36    import info.magnolia.jcr.util.NodeTypes;
37    import info.magnolia.jcr.util.NodeUtil;
38    import info.magnolia.jcr.util.PropertyUtil;
39    import info.magnolia.module.InstallContext;
40    import info.magnolia.module.delta.AbstractRepositoryTask;
41    import info.magnolia.module.delta.Task;
42    import info.magnolia.module.delta.TaskExecutionException;
43    import info.magnolia.objectfactory.Components;
44    import info.magnolia.repository.RepositoryManager;
45    import info.magnolia.resourceloader.Resource;
46    import info.magnolia.resourceloader.ResourceOrigin;
47    import info.magnolia.resourceloader.jcr.JcrResourceOrigin;
48    import info.magnolia.resourceloader.layered.LayeredResource;
49    import info.magnolia.resourceloader.layered.LayeredResourceOrigin;
50   
51    import java.io.IOException;
52    import java.io.Reader;
53    import java.util.Iterator;
54   
55    import javax.inject.Provider;
56    import javax.jcr.Node;
57    import javax.jcr.Property;
58    import javax.jcr.PropertyIterator;
59    import javax.jcr.RepositoryException;
60    import javax.jcr.Session;
61   
62    import org.apache.commons.io.IOUtils;
63    import org.apache.commons.lang3.ObjectUtils;
64    import org.apache.commons.lang3.StringUtils;
65    import org.apache.jackrabbit.commons.predicate.NodeTypePredicate;
66    import org.slf4j.Logger;
67    import org.slf4j.LoggerFactory;
68   
69    /**
70    * Migrates all templates from <code>{@value #TEMPLATES_WORKSPACE}</code> to the
71    * <code>{@value JcrResourceOrigin#RESOURCES_WORKSPACE}</code> workspace.
72    *
73    * <p>Templates will only be processed when they have the <code>{@value #PROPERTY_ENABLED}</code> property.</p>
74    *
75    * <p>When a template has <code>{@value #PROPERTY_AUTO_IMPORT}</code> set to <code>true</code> we check if there is an
76    * existing resource in the {@link ResourceOrigin} and migrate the template if there is none. Otherwise no action is
77    * taken.</p>
78    *
79    * <p>When a template has <code>{@value #PROPERTY_AUTO_IMPORT}</code> set to <code>false</code> we check whether there
80    * is a hot-fixed version of that template in {@link JcrResourceOrigin#RESOURCES_WORKSPACE}. If there is, contents
81    * are compared and when equal, no action is taken, otherwise this task will fail! If there is no hot-fix or no
82    * resource what so ever, the template will be migrated.</p>
83    */
 
84    public class TemplateMigrationTask extends AbstractRepositoryTask {
85   
86    private static final Logger log = LoggerFactory.getLogger(TemplateMigrationTask.class);
87   
88    protected static final String TEMPLATES_WORKSPACE = "templates";
89   
90    protected static final String PROPERTY_AUTO_IMPORT = "autoImport";
91    protected static final String PROPERTY_TEXT = "text";
92    protected static final String PROPERTY_ENABLED = "enabled";
93   
94    private final Provider<ResourceOrigin> resourceOriginProvider;
95   
96    private Session templatesSession = null;
97    private Session resourcesSession = null;
98   
99    private final Task onSuccess;
100   
 
101  24 toggle public TemplateMigrationTask(Task onSuccess) {
102  24 super("Migrates templates from 'templates' workspace", "Migrates templates from 'templates' workspace to 'resources' workspace");
103   
104  24 this.resourceOriginProvider = new Provider<ResourceOrigin>() {
 
105  11 toggle @Override
106    public ResourceOrigin get() {
107  11 return Components.getComponent(ResourceOrigin.class);
108    }
109    };
110   
111  24 this.onSuccess = onSuccess;
112    }
113   
 
114  13 toggle public TemplateMigrationTask() {
115  13 this(null);
116    }
117   
 
118  22 toggle @Override
119    protected void doExecute(InstallContext installContext) throws RepositoryException, TaskExecutionException {
120  22 final RepositoryManager repositoryManager = Components.getComponent(RepositoryManager.class);
121  22 String infoMessage = null;
122  22 if (repositoryManager.hasWorkspace(TEMPLATES_WORKSPACE)) {
123  12 try {
124  12 templatesSession = installContext.getJCRSession(TEMPLATES_WORKSPACE);
125    } catch (RepositoryException e) {
126  1 infoMessage = String.format("Could not get session for workspace '%s'. " +
127    "Not running template migration task.", TEMPLATES_WORKSPACE);
128    }
129    } else {
130  10 infoMessage = String.format("Inplace-templating might not be installed. Not running template migration task.");
131    }
132   
133  22 if (infoMessage != null) {
134  11 installContext.info(infoMessage);
135  11 log.info(infoMessage);
136   
137    // We won't exit here as there might be an onSuccessTask to be executed below. Not having to do anything
138    // with the templates workspace doesn't mean we are done yet.
139    }
140   
141  22 if (templatesSession != null) {
142  11 resourcesSession = installContext.getJCRSession(JcrResourceOrigin.RESOURCES_WORKSPACE);
143   
144  11 final LayeredResourceOrigin resourceOrigin = (LayeredResourceOrigin) resourceOriginProvider.get();
145   
146  11 final Iterator<Node> nodeIterator = NodeUtil.collectAllChildren(templatesSession.getRootNode(),
147    new NodeTypePredicate(NodeTypes.Content.NAME, false)).iterator();
148   
149  19 while (nodeIterator.hasNext()) {
150  10 final Node templateNode = nodeIterator.next();
151   
152    // We consider no enabled property to be equal to enabled=false
153    // All templates used to be considered as disabled, also see deprecated
154    // info.magnolia.module.inplacetemplating.JcrRepoTemplateLoader
155  10 final boolean enabled = templateNode.hasProperty(PROPERTY_ENABLED) && templateNode.getProperty(PROPERTY_ENABLED).getBoolean();
156  10 if (!enabled) {
157  1 continue;
158    }
159   
160    // We consider no autoImport property to be equal to autoImport=false
161  9 final boolean autoImport = templateNode.hasProperty(PROPERTY_AUTO_IMPORT) && templateNode.getProperty(PROPERTY_AUTO_IMPORT).getBoolean();
162   
163  9 try {
164   
165  9 final String resourceTemplatePath = templateNode.getPath() + ".ftl";
166   
167    // autoImport=true means we can safely override edited resources
168    // autoImport=false means we cannot
169  9 if (autoImport) {
170  3 if (!resourceOrigin.hasPath(resourceTemplatePath)) {
171    // Template doesn't exists in resource origin so we can migrate it
172  2 migrateTemplate(templateNode, resourceTemplatePath);
173    }
174    } else {
175    // When template exists in resource origin check whether it was 'hot-fixed' or not
176  6 if (resourceOrigin.hasPath(resourceTemplatePath)) {
177  4 final LayeredResource resource = resourceOrigin.getByPath(resourceTemplatePath);
178   
179    // When there is a hot-fixed resource
180  4 if (resource.getFirst().getOrigin() instanceof JcrResourceOrigin) {
181    // Compare contents
182  3 if (!templateEqualsResource(templateNode, resource)) {
183    // There is no way we can handle this scenario:
184    // autoImport=false (i.e. do not override) but the very same template exists in jcr
185    // resource origin with different content!
186  2 throw new TaskExecutionException(String.format("Contents of '%s:%s' and resource '%s:%s' were not equal but autoImport was set to 'false'. " +
187    "Cannot proceed with template migration.", TEMPLATES_WORKSPACE, resourceTemplatePath,
188    JcrResourceOrigin.RESOURCES_WORKSPACE, resource.getPath()));
189    }
190    // Otherwise do nothing with template; it's already in jcr with the same content
191    // why bother migrating it...!?
192    } else {
193    // No jcr hotfix available means we can safely migrate
194  1 migrateTemplate(templateNode, resourceTemplatePath);
195    }
196    } else {
197    // No resource available at all also means we can safely migrate the template to jcr
198  2 migrateTemplate(templateNode, resourceTemplatePath);
199    }
200    }
201   
202    } catch (RepositoryException e) {
203  0 log.info("An error occurred when handling template '{}:{}'. Will not migrate this template.",
204    TEMPLATES_WORKSPACE, NodeUtil.getPathIfPossible(templateNode), e);
205    }
206    }
207   
208    }
209   
210    // If we reach this point, no errors have happened!
211    // Any additional task given, will now be executed...
212  20 if (onSuccess != null) {
213  10 onSuccess.execute(installContext);
214    }
215    }
216   
217    /**
218    * Compares the template contents of a {@link Node} from <code>{@value #TEMPLATES_WORKSPACE}</code> workspace and a
219    * {@link Resource} from {@link ResourceOrigin}. Both contents being {@code null} are considered equal.
220    */
 
221  3 toggle private boolean templateEqualsResource(final Node templateNode, final Resource resource) {
222  3 try (final Reader reader = resource.openReader()) {
223  3 final String contentResource = IOUtils.toString(reader);
224  3 final String contentTemplate = PropertyUtil.getString(templateNode, PROPERTY_TEXT, null);
225   
226  3 return ObjectUtils.equals(contentResource, contentTemplate);
227    } catch (IOException e) {
228  0 log.error("Cannot read contents of '{}:{}'.", resource.getOrigin().getName(), resource.getName(), e);
229    }
230   
231  0 return false;
232    }
233   
234    /**
235    * Copies the template {@link Node} to the {@value JcrResourceOrigin#RESOURCES_WORKSPACE} workspace. Also creates
236    * parent folders if necessary.
237    */
 
238  5 toggle private void migrateTemplate(final Node templateNode, final String newTemplatePath) throws RepositoryException {
239  5 final String parentPath = StringUtils.substringBeforeLast(newTemplatePath, "/");
240  5 if (StringUtils.isNotBlank(parentPath) && !resourcesSession.nodeExists(parentPath)) {
241  1 NodeUtil.createPath(resourcesSession.getRootNode(), parentPath, NodeTypes.Folder.NAME);
242   
243  1 log.info("Created path '{}:{}'.", JcrResourceOrigin.RESOURCES_WORKSPACE, parentPath);
244    }
245   
246  5 copyTemplateNode(templateNode, newTemplatePath);
247   
248  5 log.info("Migrated template '{}:{}' to '{}:{}'.", TEMPLATES_WORKSPACE, NodeUtil.getPathIfPossible(templateNode),
249    JcrResourceOrigin.RESOURCES_WORKSPACE, newTemplatePath);
250    }
251   
252    /**
253    * Copies {@link Node} to destination <code>newTemplatePath</code> and also copies all properties (except
254    * properties suffixed with <code>{@value NodeTypes#REP_PREFIX}</code> or
255    * <code>{@value NodeTypes#JCR_PREFIX}</code>) without saving the session.
256    *
257    * <p>Note: {@link javax.jcr.Workspace#clone(String, String, String, boolean)} or
258    * {@link javax.jcr.Workspace#copy(String, String, String)} would have needed the parent folders to be persisted
259    * ({@link Session#save()}) which is not desired in {@link info.magnolia.module.delta.Task}s!</p>
260    */
 
261  5 toggle private void copyTemplateNode(final Node templateNode, final String newTemplatePath) throws RepositoryException {
262  5 final Node newTemplateNode = NodeUtil.createPath(resourcesSession.getRootNode(), newTemplatePath, NodeTypes.Content.NAME);
263  5 final PropertyIterator propertyIterator = templateNode.getProperties();
264   
265  24 while (propertyIterator.hasNext()) {
266  19 final Property property = propertyIterator.nextProperty();
267  19 final String propertyName = property.getName();
268   
269    // We do not migrate property enabled
270  19 if (PROPERTY_ENABLED.equals(propertyName)) {
271  5 continue;
272    }
273   
274    // Properties prefixed with jcr:/rep: should not be migrated
275  14 if (propertyName.startsWith(NodeTypes.REP_PREFIX) || propertyName.startsWith(NodeTypes.JCR_PREFIX)) {
276  0 continue;
277    }
278   
279    // We want to maintain the last modified date/actor of above, new node
280    // Creation date/actor will be migrated
281  14 if (NodeTypes.LastModified.LAST_MODIFIED.equals(propertyName) || NodeTypes.LastModified.LAST_MODIFIED_BY.equals(propertyName)) {
282  2 continue;
283    }
284   
285    // We also do not want to copy properties related to activation. This makes sure the new nodes do not have
286    // the activation status + information until they are indeed properly activated.
287    // This is also "in-sync" with potentially created parent folders that have the not-activated status.
288  12 if (NodeTypes.Activatable.LAST_ACTIVATED.equals(propertyName) || NodeTypes.Activatable.LAST_ACTIVATED_BY.equals(propertyName) ||
289    NodeTypes.Activatable.ACTIVATION_STATUS.equals(propertyName)) {
290  2 continue;
291    }
292   
293  10 newTemplateNode.setProperty(propertyName, property.getValue());
294    }
295    }
296   
297    }