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.setup.for5_0;
35  
36  import info.magnolia.module.InstallContext;
37  import info.magnolia.module.delta.AbstractRepositoryTask;
38  import info.magnolia.module.delta.TaskExecutionException;
39  
40  import java.util.HashMap;
41  import java.util.LinkedList;
42  import java.util.List;
43  import java.util.Set;
44  
45  import javax.jcr.RepositoryException;
46  import javax.jcr.nodetype.NodeTypeDefinition;
47  import javax.jcr.nodetype.NodeTypeManager;
48  
49  import org.apache.commons.lang.StringUtils;
50  import org.apache.jackrabbit.core.nodetype.NodeTypeManagerImpl;
51  import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
52  import org.apache.jackrabbit.spi.Name;
53  import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
54  import org.slf4j.Logger;
55  import org.slf4j.LoggerFactory;
56  
57  /**
58   * Task allowing to register or unregister nodeTypes.<br>
59   * <br>
60   * <b>For registration</b>, override {@link AbstractNodeTypeRegistrationTask#getNodeTypesToRegister(NodeTypeManager)}. <br>
61   * - For every {@link NodeTypeDefinition}, create them if not present or update the existing one's.<br>
62   * <b>Warning:</b> Be careful of the List order of {@link NodeTypeDefinition} <br>
63   * - Always put the parent NodeType first, or newly reference NodeType.<br>
64   * -- For example, you may want to create a new NodeType A that is referenced as Super by an Updated NodeType B, the list <br>
65   * -- should be [A,B] so A is created before B. Otherwise, A will not be updated.<br>
66   * In this case, the NodeType A will not be registered and a warn log is displayed.<br>
67   * <br>
68   * <b>For unregister</b>, override {@link AbstractNodeTypeRegistrationTask#getNodeTypesToUnregister(NodeTypeManager)}. <br>
69   * - For every NodeType defined by their name, unregister them. <br>
70   * <b>Warning:</b> A NodeType will be unregister only if no dependency issue is found<br>
71   * Dependency issue:<br>
72   * - The NodeType is referenced as child node by one ore more other NodeType definition<br>
73   * - The NodeType is defined as super by one ore more other NodeType definition<br>
74   * In this case, the NodeType will not be unregistered and a warn log is displayed.
75   */
76  public abstract class AbstractNodeTypeRegistrationTask extends AbstractRepositoryTask {
77  
78      private final Logger log = LoggerFactory.getLogger(AbstractNodeTypeRegistrationTask.class);
79  
80      private String workspaceName;
81  
82      public AbstractNodeTypeRegistrationTask(String name, String description, String workspaceName) {
83          super(name, description);
84          this.workspaceName = workspaceName;
85      }
86  
87      @Override
88      protected void doExecute(InstallContext installContext) throws RepositoryException, TaskExecutionException {
89          if (StringUtils.isBlank(workspaceName)) {
90              log.warn("No workspace defined. This task will resume");
91              return;
92          }
93  
94          NodeTypeManager nodeTypeManager = installContext.getJCRSession(workspaceName).getWorkspace().getNodeTypeManager();
95  
96          // Register nodeTypes
97          List<NodeTypeDefinition> nodeTypeToRegister = getNodeTypesToRegister(nodeTypeManager);
98          if (nodeTypeToRegister == null || nodeTypeToRegister.isEmpty()) {
99              log.info("No NodeType defined to register for the following workspace '{}'", workspaceName);
100         } else {
101             registerNodeTypes(nodeTypeManager, nodeTypeToRegister, installContext);
102         }
103         // Unregister nodeTypes
104         List<String> nodeTypeToUnregister = getNodeTypesToUnregister(nodeTypeManager);
105         if (nodeTypeToUnregister == null || nodeTypeToUnregister.isEmpty()) {
106             log.info("No NodeType defined to unregister for the following workspace '{}'", workspaceName);
107         } else {
108             unregisterNodeTypes(nodeTypeManager, nodeTypeToUnregister, installContext);
109         }
110 
111     }
112 
113     /**
114      * Create a List of {@link NodeTypeDefinition} to register or update (in case they are already created).
115      */
116     public abstract List<NodeTypeDefinition> getNodeTypesToRegister(NodeTypeManager nodeTypeManager) throws RepositoryException;
117 
118     /**
119      * Create a List of {@link NodeTypeDefinition#getName()} to unregister.
120      */
121     public abstract List<String> getNodeTypesToUnregister(NodeTypeManager nodeTypeManager) throws RepositoryException;
122 
123 
124     /**
125      * Register a NodeType List.<br>
126      * - In case of new NodeType definition, simply register it.
127      * - In case of the NodeType definition already exist: <br>
128      * -- Get all NodeType definition that have a dependency to the current definition <br>
129      * -- Unregister the complete list of dependent NodeType (including the current one) <br>
130      * -- Register the complete list of dependent NodeType, including the new NodeType definition <br>
131      * -- In case of exception, re register the original NodeType definition and log the exception.
132      */
133     private void registerNodeTypes(NodeTypeManager nodeTypeManager, List<NodeTypeDefinition> nodeTypesToRegister, InstallContext installContext) throws RepositoryException {
134         NodeTypeRegistry registry = ((NodeTypeManagerImpl) nodeTypeManager).getNodeTypeRegistry();
135         NamePathResolver namePathResolver = ((NodeTypeManagerImpl) nodeTypeManager).getNamePathResolver();
136         // Iterate the nodeTypeToRegister List
137         for (NodeTypeDefinition nodeType : nodeTypesToRegister) {
138             // NodeType already registered, Update (unregister, register)
139             if (nodeTypeManager.hasNodeType(nodeType.getName())) {
140                 // Get the original NodeType definition. Useful in case of dependency issue (the original NodeType is re-registered in case of exception).
141                 NodeTypeDefinition originalNodeType = nodeTypeManager.getNodeType(nodeType.getName());
142                 // Get the list of NodeType that reference the current nodeType
143                 HashMap<String, NodeTypeDefinition> nodeTypeToUnregister = getDependentNodeTypes(nodeType, registry, namePathResolver, nodeTypeManager);
144                 // UNREGISTRATION
145                 NodeTypeRegistry.disableCheckForReferencesInContentException = true;
146                 nodeTypeManager.unregisterNodeTypes(nodeTypeToUnregister.keySet().toArray(new String[nodeTypeToUnregister.keySet().size()]));
147                 log.info("In order to register '" + nodeType.getName() + "', the following depending node types had to be unregistered first", nodeTypeToUnregister.keySet().toArray());
148                 NodeTypeRegistry.disableCheckForReferencesInContentException = false;
149                 // REGISTRATION
150                 try {
151                     nodeTypeManager.registerNodeTypes(nodeTypeToUnregister.values().toArray(new NodeTypeDefinition[nodeTypeToUnregister.values().size()]), true);
152                     log.info("Registering the following '" + nodeType.getName() + "' node type. Reregister the following dependency", nodeTypeToUnregister.keySet().toArray());
153                 } catch (RepositoryException re) {
154                     // Register the previous version of the NodeType
155                     nodeTypeManager.registerNodeType(originalNodeType, false);
156                     // Register the dependent child
157                     nodeTypeToUnregister.remove(nodeType.getName());
158                     nodeTypeManager.registerNodeTypes(nodeTypeToUnregister.values().toArray(new NodeTypeDefinition[nodeTypeToUnregister.values().size()]), true);
159                     installContext.warn("Registration (update of an existing NodeType) of the following NodeType generated exceptions '" + nodeType.getName() + "'.The original version of the definition is kept. Please check the NodeType dependency and the input list order");
160                     log.warn("Not able to register the following node type {}. The original definition is kept.", nodeType.getName(), re);
161                 }
162             } else {
163                 // NodeType is new, Create (register)
164                 nodeTypeManager.registerNodeType(nodeType, false);
165                 log.info("Registering new '{}' node type.", nodeType.getName());
166             }
167         }
168     }
169 
170     /**
171      * Create a Map containing all child NodeType of.<br>
172      * - key = NodeType name.<br>
173      * - value = {@link NodeTypeDefinition}.
174      */
175     protected HashMap<String, NodeTypeDefinition> getDependentNodeTypes(NodeTypeDefinition nodeType, NodeTypeRegistry registry, NamePathResolver namePathResolver, NodeTypeManager nodeTypeManager) throws RepositoryException {
176 
177         HashMap<String, NodeTypeDefinition> dependentNodeType = new HashMap<String, NodeTypeDefinition>();
178         // Get the Name in order to get the Dependent Node type
179         Name qName = namePathResolver.getQName(nodeType.getName());
180         Set<Name> dependentNodesName = registry.getDependentNodeTypes(qName);
181         // for every dependent node type, call recursively in order to get all the dependency tree.
182         if (dependentNodesName != null && !dependentNodesName.isEmpty()) {
183             for (Name name : dependentNodesName) {
184                 dependentNodeType.putAll(getDependentNodeTypes(nodeTypeManager.getNodeType(namePathResolver.getJCRName(name)), registry, namePathResolver, nodeTypeManager));
185             }
186         }
187 
188         dependentNodeType.put(nodeType.getName(), nodeType);
189         return dependentNodeType;
190 
191     }
192 
193     /**
194      * Unregister all NodeType.
195      */
196     private void unregisterNodeTypes(NodeTypeManager nodeTypeManager, List<String> nodeTypesToUnregister, InstallContext installContext) throws RepositoryException {
197         LinkedList<String> filteredNodeTypeToUnregister = getFilteredNodesToUnregister(nodeTypeManager, nodeTypesToUnregister);
198         try {
199             NodeTypeRegistry.disableCheckForReferencesInContentException = true;
200             nodeTypeManager.unregisterNodeTypes(filteredNodeTypeToUnregister.toArray(new String[filteredNodeTypeToUnregister.size()]));
201             log.info("Unregistered the following NodeTypes '{}'", filteredNodeTypeToUnregister.toArray());
202             NodeTypeRegistry.disableCheckForReferencesInContentException = false;
203         } catch (RepositoryException re) {
204             // IN CASE OF EXCEPTION:
205             installContext.warn("Unregistration of NodeTypes generated exceptions. NodeType will not be Unregistered. Please check the NodeType dependency");
206             log.warn("Not able to unregister the following node type {}. The original definition is kept.", filteredNodeTypeToUnregister.toArray(), re);
207         }
208     }
209 
210     /**
211      * Filter the user defined NodeType to unregister.<br>
212      * Only keep the currently registered NodeType.
213      */
214     private LinkedList<String> getFilteredNodesToUnregister(NodeTypeManager nodeTypeManager, List<String> nodeTypeToUnregister) throws RepositoryException {
215         LinkedList<String> filteredNodeTypes = new LinkedList<String>();
216         for (String nodeTypeName : nodeTypeToUnregister) {
217             if (nodeTypeManager.hasNodeType(nodeTypeName)) {
218                 filteredNodeTypes.add(nodeTypeName);
219             } else {
220                 log.warn("Node type '{}' is not registered . No unregistration will be performed", nodeTypeName);
221             }
222         }
223         return filteredNodeTypes;
224     }
225 }