View Javadoc

1   /**
2    * This file Copyright (c) 2013 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.googlesitemap.setup.migration;
35  
36  import info.magnolia.cms.util.QueryUtil;
37  import info.magnolia.jcr.util.NodeTypes;
38  import info.magnolia.jcr.util.NodeUtil;
39  import info.magnolia.jcr.wrapper.JCRMgnlPropertiesFilteringNodeWrapper;
40  import info.magnolia.jcr.wrapper.JCRPropertiesFilteringNodeWrapper;
41  import info.magnolia.module.InstallContext;
42  import info.magnolia.module.delta.AbstractRepositoryTask;
43  import info.magnolia.module.delta.TaskExecutionException;
44  import info.magnolia.module.googlesitemap.GoogleSiteMapConfiguration;
45  import info.magnolia.repository.RepositoryConstants;
46  
47  import java.util.ArrayList;
48  import java.util.Iterator;
49  import java.util.List;
50  
51  import javax.jcr.Node;
52  import javax.jcr.NodeIterator;
53  import javax.jcr.Property;
54  import javax.jcr.PropertyIterator;
55  import javax.jcr.RepositoryException;
56  import javax.jcr.Session;
57  import javax.jcr.query.Query;
58  
59  import org.apache.commons.lang.StringUtils;
60  import org.slf4j.Logger;
61  import org.slf4j.LoggerFactory;
62  
63  
64  /**
65   * Google site map dedicated migration task.<br>
66   * Implemented logic: <br>
67   * - Search all siteMaps definition from the specified workspace.<br>
68   * - For every siteMapDefinition <br>
69   * -- Create a folders representing the siteMap definition path (demo-project/about/siteMapDefinitoin --> demo-project/about) <br>
70   * -- Create a new M5 siteMap definition based on the M4.5 one.<br>
71   * -- Remove the M4.5 siteMap definition.<br>
72   */
73  public class SiteMapDefinitionMigrationTask extends AbstractRepositoryTask {
74  
75      private final Logger log = LoggerFactory.getLogger(SiteMapDefinitionMigrationTask.class);
76  
77      private final String sourceWorkspace;
78      private final String searchRootPath;
79      private final String templateName = "google-sitemap:pages/siteMapsConfiguration";
80      private final String siteDefinitionTemplateName = "google-sitemap:components/content/siteComponent";
81      private final String virtualUriTemplateName = "google-sitemap:components/content/virtualUriComponent";
82      private Session siteMapSession;
83  
84  
85      public SiteMapDefinitionMigrationTask(String name, String description, String sourceWorkspace, String searchRootPath) {
86          super(name, description);
87          this.sourceWorkspace = (StringUtils.isNotBlank(sourceWorkspace)) ? sourceWorkspace : RepositoryConstants.WEBSITE;
88          this.searchRootPath = (StringUtils.isNotBlank(searchRootPath)) ? searchRootPath : "/";
89      }
90  
91      @Override
92      protected void doExecute(InstallContext installContext) throws RepositoryException, TaskExecutionException {
93          // Init
94          siteMapSession = installContext.getJCRSession(GoogleSiteMapConfiguration.WORKSPACE);
95          try {
96              // Perform the query
97              NodeIterator nodeIterator = getQueryResult();
98              // Handle result
99              while (nodeIterator.hasNext()) {
100                 handleSimeMapDefinitionMigration(nodeIterator.nextNode());
101             }
102 
103         } catch (RepositoryException re) {
104             installContext.error("Unable to perform Migration task " + getName(), re);
105             throw new TaskExecutionException(re.getMessage());
106         }
107     }
108 
109     private void handleSimeMapDefinitionMigration(Node siteMapNodeDefinition) throws RepositoryException {
110         // Create path in the siteMap workspace
111         Node targetRootSiteMapFolder = getOrCreateRootSiteMapDefinitionNode(siteMapNodeDefinition);
112 
113         // Create an equivalent M5 siteMap definition
114         Node targetSiteMapDefinition = migrateSiteMapDefinition(siteMapNodeDefinition, targetRootSiteMapFolder);
115         log.info("New M5 siteMapDefinition created {} based on the previous definition {} ", targetSiteMapDefinition.getPath(), siteMapNodeDefinition.getPath());
116 
117         // Remove the original siteMapDefinition
118         siteMapNodeDefinition.remove();
119     }
120 
121     /**
122      * Create a M5 siteMapDefinition based on a M4.5 definition.<br>
123      * Steps: <br>
124      * - Create a folder tree to the sitMapDefinition node if required<br>
125      * - Create a new siteMapDefinition nodeTye <br>
126      * - Create a new sites contentNode child
127      * - Copy all site definition from M4.5 into the newly created child node.
128      */
129     private Node migrateSiteMapDefinition(Node siteMapNodeDefinition, Node targetRootSiteMapFolder) throws RepositoryException {
130         // Create the new M5 definition
131         Node targetSiteMapDefinition = targetRootSiteMapFolder.addNode(siteMapNodeDefinition.getName(), GoogleSiteMapConfiguration.NODE_TYPE);
132         // Copy all root properties
133         copyOrUpdateProperty(siteMapNodeDefinition, targetSiteMapDefinition);
134 
135         // Copy all properties from M4.5 definition into 5.2 format
136         populateSiteMapDefinition(targetSiteMapDefinition, siteMapNodeDefinition);
137 
138         return targetSiteMapDefinition;
139     }
140 
141     private void populateSiteMapDefinition(Node targetSiteMapDefinition, Node sourceSiteMapNodeDefinition) throws RepositoryException {
142         List<String> siteMapDefinitionList = new ArrayList<String>();
143         boolean insertVirtualUri = false;
144         Iterable<Node> children = NodeUtil.collectAllChildren(sourceSiteMapNodeDefinition);
145         Iterator<Node> childrenIterator = children.iterator();
146         // Iterate the child nodes and extract the configuration
147         while (childrenIterator.hasNext()) {
148             Node child = childrenIterator.next();
149             if (StringUtils.isNotBlank(NodeTypes.Renderable.getTemplate(child))) {
150                 String templateName = NodeTypes.Renderable.getTemplate(child);
151                 if (virtualUriTemplateName.equals(templateName)) {
152                     insertVirtualUri = true;
153                 } else if (siteDefinitionTemplateName.equals(templateName)) {
154                     siteMapDefinitionList.addAll(extractSiteMapDefinition(child));
155                 }
156             }
157         }
158 
159         // Set includeVirtualURIMappings property
160         targetSiteMapDefinition.setProperty(GoogleSiteMapConfiguration.INCLUDE_VIRTUAL_URI_MAPPINGS_PROPERTIES, insertVirtualUri);
161         // Set siteMaps definition
162         if (!siteMapDefinitionList.isEmpty()) {
163             Node sites = targetSiteMapDefinition.addNode("sites", NodeTypes.ContentNode.NAME);
164             int pos = 0;
165             for (String definition : siteMapDefinitionList) {
166                 sites.setProperty("" + pos, definition);
167                 pos += 1;
168             }
169         }
170     }
171 
172     private List<String> extractSiteMapDefinition(Node parent) throws RepositoryException {
173         List<String> siteDefinitions = new ArrayList<String>();
174         if (parent.hasNode(GoogleSiteMapConfiguration.SITE_DIALOG_CONFIGURATION_NAME)) {
175             Node filteredNode = new JCRMgnlPropertiesFilteringNodeWrapper(parent.getNode(GoogleSiteMapConfiguration.SITE_DIALOG_CONFIGURATION_NAME));
176             PropertyIterator iterator = filteredNode.getProperties();
177             while (iterator.hasNext()) {
178                 Property property = iterator.nextProperty();
179                 siteDefinitions.add(property.getString());
180             }
181         } else {
182             log.info("Node '{}' do not have a 'sites' child node.", parent.getPath());
183         }
184         return siteDefinitions;
185     }
186 
187 
188     /**
189      * Copy all non jcr: properties from the source to the target node.
190      */
191     private void copyOrUpdateProperty(Node source, Node target) throws RepositoryException {
192         Node filteredSource = new JCRPropertiesFilteringNodeWrapper(source);
193         PropertyIterator iterator = filteredSource.getProperties();
194         while (iterator.hasNext()) {
195             Property property = iterator.nextProperty();
196             if (target.hasProperty(property.getName())) {
197                 target.getProperty(property.getName()).setValue(property.getValue());
198             } else {
199                 target.setProperty(property.getName(), property.getValue());
200             }
201         }
202     }
203 
204     /**
205      * Create the site map definition parent folder.
206      */
207     Node getOrCreateRootSiteMapDefinitionNode(Node siteMapNodeDefinition) throws RepositoryException {
208         // If the definition is at the root level, return the root node.
209         if (siteMapNodeDefinition.getDepth() <= 1) {
210             return siteMapSession.getRootNode();
211         }
212         // If the path already exist return the referenced node..
213         String targetPath = siteMapNodeDefinition.getParent().getPath();
214         if (siteMapSession.nodeExists(targetPath)) {
215             return siteMapSession.getNode(targetPath);
216         }
217         // If the path do not already exist, create it.
218         return NodeUtil.createPath(siteMapSession.getRootNode(), targetPath, NodeTypes.Folder.NAME, false);
219     }
220 
221     /**
222      * Search all node within 'sourceWorkspace' that are under the 'searchRootPath' containing a template definition equal to 'templateName'.
223      */
224     private NodeIterator getQueryResult() throws RepositoryException {
225         NodeIterator nodeIterator = null;
226         String query = "SELECT * FROM [nt:base] AS t WHERE (ISSAMENODE(t, '" + searchRootPath + "') OR ISDESCENDANTNODE(t, '" + searchRootPath + "')) " +
227                 "AND t.[mgnl:template] is not null AND contains(t.[" + NodeTypes.Renderable.TEMPLATE + "],'" + templateName + "')";
228         nodeIterator = QueryUtil.search(sourceWorkspace, query, Query.JCR_SQL2);
229         log.info("{} google site map definitions will be handled", nodeIterator.getSize());
230         return nodeIterator;
231     }
232 }