View Javadoc
1   /**
2    * This file Copyright (c) 2012-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.dam.app.setup.migration;
35  
36  import info.magnolia.cms.util.QueryUtil;
37  import info.magnolia.dam.api.ItemKey;
38  import info.magnolia.dam.jcr.DamConstants;
39  import info.magnolia.jcr.util.NodeUtil;
40  import info.magnolia.module.InstallContext;
41  import info.magnolia.module.delta.AbstractRepositoryTask;
42  import info.magnolia.module.delta.TaskExecutionException;
43  
44  import java.util.List;
45  
46  import javax.jcr.Node;
47  import javax.jcr.NodeIterator;
48  import javax.jcr.Property;
49  import javax.jcr.RepositoryException;
50  import javax.jcr.Session;
51  import javax.jcr.query.Query;
52  
53  import org.apache.commons.lang.StringUtils;
54  import org.slf4j.Logger;
55  import org.slf4j.LoggerFactory;
56  
57  /**
58   * This task is responsible for changing the old DMS/Data/... references to DAM
59   * references.<br>
60   * Steps:<br>
61   * 
62   * <pre>
63   * Iterate the list of contentPathsList,
64   *  Perform a JCR query that search for properties named 'imgUUID' and return the related node.
65   *  Iterate the query result list
66   *  For every Node,
67   *   If the identifier exist in the dam repository as an Asset.
68   *    Transform the identifier as an Asset composite id (assetProviderId:AssetIdentifier)
69   *    Update the 'img' link to the corresponding path.
70   *   Else
71   *    Remove the property.
72   * </pre>
73   * 
74   * <pre>
75   * <b>Normal handling</b>:
76   * Move from:
77   *  property sv:name="img" sv:type="String"
78   *  value = /demo-project/img/bk/Stage/v-light-4
79   *  property sv:name="imgUUID" sv:type="String"
80   *  value = 4c291aa5-9807-4bbe-b372-ce523f82e600
81   * To:
82   *  property sv:name="imgUUID" sv:type="String"
83   *  value = jcr:4c291aa5-9807-4bbe-b372-ce523f82e600
84   * 
85   * OR
86   * Move from:
87   *  property sv:name="img" sv:type="String"
88   *  value = dms
89   *  property sv:name="imgUUID" sv:type="String"
90   *  value = 4c291aa5-9807-4bbe-b372-ce523f82e600
91   * To:
92   *  property sv:name="img" sv:type="String"
93   *  value = jcr:4c291aa5-9807-4bbe-b372-ce523f82e600
94   * 
95   * <b>Specific handling</b>:
96   *  In case of the UUID does not exist any more, remove properties.
97   * </pre>
98   **/
99  public class ChangeDataDmsReferenceToDamMigrationTask extends AbstractRepositoryTask {
100 
101     private static final Logger log = LoggerFactory.getLogger(ChangeDataDmsReferenceToDamMigrationTask.class);
102 
103     private final List<String> contentPathsList;
104     private final String contentRepository;
105     private final String identifierPropertyName;
106     private final String pathPropertyName;
107     private Session contentSession;
108     private Session damSession;
109 
110     /**
111      * Default constructor.
112      */
113     public ChangeDataDmsReferenceToDamMigrationTask(String taskName, String taskDescription, String contentRepository, List<String> contentPathsList, String identifierPropertyName, String pathPropertyName) {
114         super(taskName, taskDescription);
115         this.contentPathsList = contentPathsList;
116         this.contentRepository = contentRepository;
117         this.identifierPropertyName = StringUtils.isNotBlank(identifierPropertyName) ? identifierPropertyName : "imgUUID";
118         this.pathPropertyName = StringUtils.isNotBlank(pathPropertyName) ? pathPropertyName : "img";
119         ;
120     }
121 
122     @Override
123     public void doExecute(InstallContext ctx) throws TaskExecutionException {
124         log.info("Start to update DATA reference to DAM for the following repository '{}'", this.contentRepository);
125         try {
126             // Init
127             contentSession = ctx.getJCRSession(contentRepository);
128             damSession = ctx.getJCRSession(DamConstants.WORKSPACE);
129             for (String path : this.contentPathsList) {
130                 if (!contentSession.nodeExists(path)) {
131                     log.warn("'{}' path do not exist for the following repository: '{}' No Data migration will be performed ", path, contentRepository);
132                     continue;
133                 }
134                 // Handle path
135                 handlePath(path);
136             }
137         } catch (Exception e) {
138             log.error("Unable to execute update of DATA reference to DAM", e);
139             ctx.error("Unable to perform Migration task " + getName(), e);
140             throw new TaskExecutionException(e.getMessage());
141         }
142         log.info("Successfully execute update of DATA reference to DAM for the following repository '{}'", this.contentRepository);
143     }
144 
145     /**
146      * Perform the changing reference task on the passed path and sub path.
147      */
148     private void handlePath(String path) throws RepositoryException {
149         final String queryString = "SELECT * FROM [nt:base] AS t WHERE (ISSAMENODE(t, '" + path + "') OR ISDESCENDANTNODE(t, '" + path + "')) AND t." + this.identifierPropertyName + " is not null";
150         NodeIterator iterator = QueryUtil.search(this.contentRepository, queryString, Query.JCR_SQL2);
151         while (iterator.hasNext()) {
152             Node node = iterator.nextNode();
153             handleDamReferenceForNode(node);
154         }
155     }
156 
157     /**
158      * Handle the property related to the founded node.
159      */
160     private void handleDamReferenceForNode(Node node) throws RepositoryException {
161         Property identifierProperty = node.getProperty(this.identifierPropertyName);
162         Property pathProperty = null;
163         if (node.hasProperty(this.pathPropertyName)) {
164             pathProperty = node.getProperty(this.pathPropertyName);
165         }
166 
167         // Check if the Identifier exist in the DAM workspace
168         Node assetNode = getAssetNode(identifierProperty.getString());
169         if (assetNode != null) {
170             String compositeId = new ItemKey(DamConstants.DEFAULT_JCR_PROVIDER_ID, identifierProperty.getString()).asString();
171             if (pathProperty != null && StringUtils.equals("dms", pathProperty.getString())) {
172                 handleDmsReference(identifierProperty, pathProperty, assetNode, compositeId);
173             } else if (pathProperty != null && StringUtils.equals("upload", pathProperty.getString())) {
174                 log.info("Uploaded content not handled by this task");
175             } else {
176                 handleDataReference(identifierProperty, pathProperty, assetNode, compositeId);
177             }
178         } else {
179             // log
180             log.info("No Asset found for the following identifier '{}'. The property named '{}' will be ignore", identifierProperty.getString(), this.identifierPropertyName);
181         }
182     }
183 
184     /**
185      * Set the composite id to the identifierProperty (imgUUID) value.
186      */
187     protected void handleDataReference(Property identifierProperty, Property pathProperty, Node assetNode, String compositeId) throws RepositoryException {
188         identifierProperty.setValue(compositeId);
189         if (pathProperty != null) {
190             pathProperty.setValue(assetNode.getPath());
191             log.info("The value of the property '{}' was changed to point to the following composite asset path '{}' .", pathProperty.getName(), NodeUtil.getPathIfPossible(assetNode));
192         }
193         log.info("The value of the property '{}' was changed to the following composite id '{}' referencing '{}' asset.", identifierProperty.getName(), compositeId, NodeUtil.getPathIfPossible(assetNode));
194     }
195 
196     /**
197      * Set the composite id to the pathProperty (img) value, and remove the identifierProperty (imgUUID).
198      */
199     protected void handleDmsReference(Property identifierProperty, Property pathProperty, Node assetNode, String compositeId) throws RepositoryException {
200         // Set the composite key to the img property. Remove the imgUUID property.
201         pathProperty.setValue(compositeId);
202         identifierProperty.remove();
203         log.info("The value of the property '{}' was changed from 'dms' to the following composite id '{}' referencing '{}' asset.", pathProperty.getName(), compositeId, NodeUtil.getPathIfPossible(assetNode));
204     }
205 
206     /**
207      * Utility method. Check if the identifier exist in the DAM repository.
208      */
209     private Node getAssetNode(String identifier) {
210         try {
211             return damSession.getNodeByIdentifier(identifier);
212         } catch (RepositoryException re) {
213             return null;
214         }
215     }
216 }