View Javadoc
1   /**
2    * This file Copyright (c) 2017 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.virtualuri.setup.delta;
35  
36  import com.google.common.collect.ImmutableMap;
37  import com.google.common.collect.Maps;
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.TaskExecutionException;
42  import info.magnolia.repository.RepositoryConstants;
43  import info.magnolia.virtualuri.VirtualUriMapping;
44  import info.magnolia.virtualuri.mapping.DefaultVirtualUriMapping;
45  import info.magnolia.virtualuri.mapping.HostBasedRegexpVirtualUriMapping;
46  import info.magnolia.virtualuri.mapping.HostBasedVirtualUriMapping;
47  import info.magnolia.virtualuri.mapping.RegexpVirtualUriMapping;
48  import info.magnolia.virtualuri.mapping.RotatingVirtualUriMapping;
49  import org.apache.commons.lang3.ClassUtils;
50  import org.slf4j.Logger;
51  import org.slf4j.LoggerFactory;
52  
53  import javax.jcr.Node;
54  import javax.jcr.NodeIterator;
55  import javax.jcr.RepositoryException;
56  import javax.jcr.Session;
57  import java.util.Map;
58  
59  /**
60   * Update virtualUriMappings records with following changes:
61   * <ul>
62   *     <li>Update 'class' property to new value</li>
63   *     <li>Rename 'toURI' property to 'toUri'</li>
64   *     <li>Rename 'fromURI' property to 'fromUri'</li>
65   * </ul>
66   * Default deprecated classes (info.magnolia.cms.beans.config.*) will be replaced automatically:
67   * <ul>
68   *     <li>info.magnolia.cms.beans.config.DefaultVirtualURIMapping updated to info.magnolia.virtualuri.mapping.DefaultVirtualUriMapping</li>
69   *     <li>info.magnolia.cms.beans.config.RegexpVirtualURIMapping updated to info.magnolia.virtualuri.mapping.RegexpVirtualUriMapping</li>
70   *     <li>info.magnolia.cms.beans.config.RotatingVirtualURIMapping updated to info.magnolia.virtualuri.mapping.RotatingVirtualUriMapping</li>
71   *     <li>info.magnolia.cms.beans.config.HostBasedVirtualURIMapping updated to info.magnolia.virtualuri.mapping.HostBasedVirtualUriMapping</li>
72   *     <li>info.magnolia.cms.beans.config.HostBasedRegexpVirtualURIMapping updated to info.magnolia.virtualuri.mapping.HostBasedRegexpVirtualUriMapping.</li>
73   * </ul>
74   * Parameter additionalMappingClasses contains customized old-new VirtualUriMappings that will be replaced.
75   * If there are nodes with deprecated customized mapping classes (implements info.magnolia.cms.beans.config.VirtualURIMapping), warnings will be thrown.
76   */
77  public class UpdateMappingClassesAndPropertiesTask extends AbstractRepositoryTask {
78  
79      private static final Logger log = LoggerFactory.getLogger(UpdateMappingClassesAndPropertiesTask.class);
80  
81      protected static final String CLASS_PROPERTY = "class";
82      protected static final String FROM_URI_OLD_PROPERTY = "fromURI";
83      protected static final String TO_URI_OLD_PROPERTY = "toURI";
84      protected static final String FROM_URI_NEW_PROPERTY = "fromUri";
85      protected static final String TO_URI_NEW_PROPERTY = "toUri";
86      protected static final String DOCUMENT_URL = "https://documentation.magnolia-cms.com/display/DOCS/URI+mapping#URImapping-VirtualURImapping";
87  
88      private final Map<String, Class<? extends VirtualUriMapping>> defaultMappings = ImmutableMap.of(
89              "info.magnolia.cms.beans.config.DefaultVirtualURIMapping", DefaultVirtualUriMapping.class,
90              "info.magnolia.cms.beans.config.RegexpVirtualURIMapping", RegexpVirtualUriMapping.class,
91              "info.magnolia.cms.beans.config.RotatingVirtualURIMapping", RotatingVirtualUriMapping.class,
92              "info.magnolia.cms.beans.config.HostBasedVirtualURIMapping", HostBasedVirtualUriMapping.class,
93              "info.magnolia.cms.beans.config.HostBasedRegexpVirtualURIMapping", HostBasedRegexpVirtualUriMapping.class
94      );
95  
96      private final String virtualUriMappingPath;
97  
98      private Map<String, Class<? extends VirtualUriMapping>> mappingClasses;
99      private InstallContext installContext;
100 
101     public UpdateMappingClassesAndPropertiesTask(String name, String virtualUriMappingPath, Map<String, Class<? extends VirtualUriMapping>> additionalMappingClasses) {
102         super(name, "Task to update VirtualUriMapping class to new implementation, property's name fromURI/toURI to new fromUri/toUri.");
103         this.virtualUriMappingPath = virtualUriMappingPath;
104         this.mappingClasses = Maps.newHashMap(defaultMappings);
105         if (additionalMappingClasses != null) {
106             this.mappingClasses.putAll(additionalMappingClasses);
107         }
108     }
109 
110     @Override
111     protected void doExecute(InstallContext installContext) throws RepositoryException, TaskExecutionException {
112         this.installContext = installContext;
113 
114         Session session = installContext.getJCRSession(RepositoryConstants.CONFIG);
115 
116         NodeIterator nodeIterator = session.getNode(virtualUriMappingPath).getNodes();
117 
118         while (nodeIterator.hasNext()) {
119             updateMapping(nodeIterator.nextNode());
120         }
121     }
122 
123     protected void updateMapping(Node node) throws RepositoryException {
124         String oldClass = PropertyUtil.getString(node, CLASS_PROPERTY, "");
125         Class<? extends VirtualUriMapping> newClass = mappingClasses.get(oldClass);
126         if (newClass != null) {
127             node.getProperty(CLASS_PROPERTY).setValue(newClass.getName());
128 
129             // We consider that even additional mappings typically extend the default mapping.
130             // So if additional mappings are given, we also update properties.
131             updateProperties(node);
132 
133             // update host mappings if applicable
134             if (HostBasedVirtualUriMapping.class.isAssignableFrom(newClass) && node.hasNode("mappings")) {
135                 Node mappings = node.getNode("mappings");
136                 NodeIterator childrenIterator = mappings.getNodes();
137                 while (childrenIterator.hasNext()) {
138                     Node hostMapping = childrenIterator.nextNode();
139                     updateProperties(hostMapping);
140                 }
141             }
142         } else if (isExtendDeprecatedClass(oldClass)) {
143             String message = String.format("Virtual URI mapping {%s} extends a deprecated class and should be updated. Please refer to the new module magnolia-virtual-uri; for more information, see: %s", oldClass, DOCUMENT_URL);
144             if (oldClass.startsWith("info.magnolia")) {
145                 log.warn(message);
146             } else {
147                 installContext.warn(message);
148             }
149         }
150     }
151 
152     private boolean isExtendDeprecatedClass(String oldClass) {
153         try {
154             return ClassUtils.getClass("info.magnolia.cms.beans.config.VirtualURIMapping").isAssignableFrom(ClassUtils.getClass(oldClass));
155         } catch (ClassNotFoundException e) {
156             log.warn("Class {} not found.", oldClass);
157         }
158 
159         return false;
160     }
161 
162     protected void updateProperties(Node node) throws RepositoryException {
163         if (node.hasProperty(FROM_URI_OLD_PROPERTY)) {
164             PropertyUtil.renameProperty(node.getProperty(FROM_URI_OLD_PROPERTY), FROM_URI_NEW_PROPERTY);
165         }
166 
167         if (node.hasProperty(TO_URI_OLD_PROPERTY)) {
168             PropertyUtil.renameProperty(node.getProperty(TO_URI_OLD_PROPERTY), TO_URI_NEW_PROPERTY);
169         }
170     }
171 
172 }