KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > info > magnolia > cms > beans > config > ModuleRegistration


1 /**
2  *
3  * Magnolia and its source-code is licensed under the LGPL.
4  * You may copy, adapt, and redistribute this file for commercial or non-commercial use.
5  * When copying, adapting, or redistributing this document in keeping with the guidelines above,
6  * you are required to provide proper attribution to obinary.
7  * If you reproduce or distribute the document without making any substantive modifications to its content,
8  * please use the following attribution line:
9  *
10  * Copyright 1993-2005 obinary Ltd. (http://www.obinary.com) All rights reserved.
11  *
12  */

13 package info.magnolia.cms.beans.config;
14
15 import info.magnolia.cms.core.Content;
16 import info.magnolia.cms.module.DependencyDefinition;
17 import info.magnolia.cms.module.Module;
18 import info.magnolia.cms.module.ModuleDefinition;
19 import info.magnolia.cms.module.ModuleUtil;
20 import info.magnolia.cms.module.RegisterException;
21 import info.magnolia.cms.security.AccessDeniedException;
22 import info.magnolia.cms.util.AlertUtil;
23 import info.magnolia.cms.util.ClassUtil;
24 import info.magnolia.cms.util.ClasspathResourcesUtil;
25 import info.magnolia.cms.util.FactoryUtil;
26 import info.magnolia.cms.util.NodeDataUtil;
27 import info.magnolia.context.MgnlContext;
28
29 import java.io.File JavaDoc;
30 import java.io.IOException JavaDoc;
31 import java.io.StringReader JavaDoc;
32 import java.io.StringWriter JavaDoc;
33 import java.net.URL JavaDoc;
34 import java.util.ArrayList JavaDoc;
35 import java.util.Collections JavaDoc;
36 import java.util.Comparator JavaDoc;
37 import java.util.Iterator JavaDoc;
38 import java.util.List JavaDoc;
39 import java.util.regex.Matcher JavaDoc;
40 import java.util.regex.Pattern JavaDoc;
41
42 import javax.jcr.PathNotFoundException;
43 import javax.jcr.RepositoryException;
44
45 import org.apache.commons.betwixt.io.BeanReader;
46 import org.apache.commons.collections.MapIterator;
47 import org.apache.commons.collections.OrderedMap;
48 import org.apache.commons.collections.map.LinkedMap;
49 import org.apache.commons.io.IOUtils;
50 import org.apache.commons.lang.StringUtils;
51 import org.jdom.DocType;
52 import org.jdom.Document;
53 import org.jdom.JDOMException;
54 import org.jdom.input.SAXBuilder;
55 import org.jdom.output.XMLOutputter;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59
60 /**
61  * Executes the registration of the modules. It searches the META-INF/magnolia/*.xml module descriptors, instantiate the
62  * engine object and calls the register method on it.
63  * @author Philipp Bracher
64  * @version $Revision: 7392 $ ($Author: gjoseph $)
65  */

66 public class ModuleRegistration {
67     
68     /**
69      * getInstance() used this flag to init the instance if not yet done.
70      */

71     private boolean initialized = false;
72
73     /**
74      * @return Returns the instance.
75      */

76     public synchronized static ModuleRegistration getInstance() {
77         ModuleRegistration registration = (ModuleRegistration) FactoryUtil.getSingleton(ModuleRegistration.class);
78         if(!registration.initialized) {
79             registration.init();
80         }
81         return registration;
82     }
83
84     /**
85      * Logger
86      */

87     private Logger log = LoggerFactory.getLogger(ModuleRegistration.class);
88
89     /**
90      * All the module definitions.
91      */

92     private OrderedMap moduleDefinitions = new LinkedMap();
93
94     /**
95      * If this flag is set a restart of the system is needed. The reason could be a servlet registration for example.
96      */

97     private boolean restartNeeded = false;
98
99     /**
100      * Don't instantiate! Warum dann nicht private?????
101      */

102     public ModuleRegistration() {
103     }
104
105     /**
106      * Get's the META-INF/magnolia/*.xml module descriptors and registers the modules.
107      */

108     public void init() throws MissingDependencyException {
109         // read the definitions from the xml files in the classpath
110
readModuleDefinitions();
111
112         // check the dependecies: before odering!
113
checkDependencies();
114
115         // order them by dependency level
116
sortByDependencyLevel();
117
118         initialized = true;
119     }
120
121     protected File JavaDoc getModuleRoot(String JavaDoc magnoliaModuleXml) {
122         URL JavaDoc xmlUrl = getClass().getResource(magnoliaModuleXml);
123
124         return getModuleRoot(xmlUrl);
125     }
126
127     /**
128      * @param xmlUrl
129      * @return module root
130      */

131     protected File JavaDoc getModuleRoot(URL JavaDoc xmlUrl) {
132         final String JavaDoc path = xmlUrl.getFile();
133
134         final boolean withinJar = StringUtils.contains(path, ".jar!");
135         if (withinJar) {
136             final String JavaDoc jarPath = StringUtils.substringBefore(path, ".jar!") + ".jar";
137             return new File JavaDoc(jarPath);
138         } else {
139             final File JavaDoc xmlFile = new File JavaDoc(path);
140             // module.jar!:META-INF/magnolia/module-name.xml
141
return xmlFile.getParentFile().getParentFile().getParentFile();
142         }
143     }
144
145     /**
146      * Read the xml files and make the objects
147      */

148     protected void readModuleDefinitions() {
149         try {
150             BeanReader beanReader = new BeanReader();
151             beanReader.registerBeanClass(ModuleDefinition.class);
152
153             log.info("Reading module definition");
154
155             // get the xml files
156
String JavaDoc[] defResources = ClasspathResourcesUtil.findResources(new ClasspathResourcesUtil.Filter() {
157
158                 public boolean accept(String JavaDoc name) {
159                     return name.startsWith("/META-INF/magnolia") && name.endsWith(".xml");
160                 }
161             });
162
163             // parse the xml files
164
for (int j = 0; j < defResources.length; j++) {
165                 String JavaDoc name = defResources[j];
166                 File JavaDoc moduleRoot = getModuleRoot(name);
167
168                 log.info("Parsing module file {} for module @ {}", name, moduleRoot.getAbsolutePath());
169
170                 try {
171                     ModuleDefinition def = (ModuleDefinition) beanReader.parse(new StringReader JavaDoc(getXML(name)));
172                     def.setModuleRoot(moduleRoot);
173                     this.moduleDefinitions.put(def.getName(), def);
174                 }
175                 catch (Exception JavaDoc e) {
176                     throw new ConfigurationException("can't read the module definition file [" + name + "].", e);
177                 }
178             }
179
180         }
181         catch (Exception JavaDoc e) {
182             throw new ConfigurationException("can't read the module definition files.", e);
183         }
184     }
185
186     protected void addAdHocDefinitions() {
187         // add adhoc definitions for already registered modules without an xml file (pseudo modules)
188
Content modulesNode;
189         try {
190             modulesNode = ModuleLoader.getInstance().getModulesNode();
191         }
192         catch (RepositoryException e) {
193             log.error("can't add ad hoc module definitions", e);
194             return;
195         }
196
197         for (Iterator JavaDoc iter = modulesNode.getChildren().iterator(); iter.hasNext();) {
198             Content moduleNode = (Content) iter.next();
199
200             String JavaDoc name = moduleNode.getName();
201             String JavaDoc version = NodeDataUtil.getString(moduleNode, "version", "");
202             String JavaDoc className = NodeDataUtil.getString(ContentRepository.CONFIG, moduleNode.getHandle()
203                 + "/Register/class", "");
204
205             if (!this.moduleDefinitions.containsKey(name)) {
206                 log.warn("no proper module definition file found for [{}]: will add an adhoc definition", name);
207                 ModuleDefinition def = new ModuleDefinition(name, version, className);
208                 this.moduleDefinitions.put(def.getName(), def);
209             }
210         }
211     }
212
213     /**
214      * Check if the dependencies are ok
215      * @throws MissingDependencyException
216      */

217     private void checkDependencies() throws MissingDependencyException {
218         for (MapIterator iter = this.moduleDefinitions.orderedMapIterator(); iter.hasNext();) {
219             iter.next();
220             ModuleDefinition def = (ModuleDefinition) iter.getValue();
221
222             for (Iterator JavaDoc iterator = def.getDependencies().iterator(); iterator.hasNext();) {
223                 DependencyDefinition dep = (DependencyDefinition) iterator.next();
224                 if (!dep.isOptional()) {
225                     if (!this.moduleDefinitions.containsKey(dep.getName())
226                         || !dep.getVersion().equals(this.getModuleDefinition(dep.getName()).getVersion())) {
227                         throw new MissingDependencyException("missing dependency: module ["
228                             + def.getName()
229                             + "] needs ["
230                             + dep.getName()
231                             + "]");
232                     }
233                 }
234             }
235         }
236     }
237
238     /**
239      * Sort all the definitions by the dependency level
240      */

241     private void sortByDependencyLevel() {
242         // order by dependencies
243
List JavaDoc modules = new ArrayList JavaDoc();
244
245         // make a list for sorting
246
for (MapIterator iter = this.moduleDefinitions.mapIterator(); iter.hasNext();) {
247             iter.next();
248             modules.add(iter.getValue());
249         }
250
251         Collections.sort(modules, new Comparator JavaDoc() {
252
253             public int compare(Object JavaDoc arg1, Object JavaDoc arg2) {
254                 ModuleDefinition def1 = (ModuleDefinition) arg1;
255                 ModuleDefinition def2 = (ModuleDefinition) arg2;
256                 int level1 = calcDependencyLevel(def1);
257                 int level2 = calcDependencyLevel(def2);
258
259                 // lower level first
260
int dif = level1 - level2;
261                 if (dif != 0) {
262                     return dif;
263                 }
264                 // rest is ordered alphabetically
265

266                 return def1.getName().compareTo(def2.getName());
267
268             }
269         });
270
271         // clear the not yet ordered entries
272
this.moduleDefinitions.clear();
273
274         // register the sorted defs
275
for (Iterator JavaDoc iterator = modules.iterator(); iterator.hasNext();) {
276             ModuleDefinition def = (ModuleDefinition) iterator.next();
277             if (log.isDebugEnabled()) {
278                 log.debug("add module definition [{}]", def.getName());
279             }
280             this.moduleDefinitions.put(def.getName(), def);
281         }
282     }
283
284     /**
285      * Register the modules. This is separated of the init method to allow the usage of the descripors in advance.
286      */

287     public void registerModules() {
288         
289         addAdHocDefinitions();
290         
291         try {
292             Content modulesNode = ModuleLoader.getInstance().getModulesNode();
293
294             for (MapIterator iter = this.moduleDefinitions.orderedMapIterator(); iter.hasNext();) {
295                 iter.next();
296                 ModuleDefinition def = (ModuleDefinition) iter.getValue();
297                 registerModule(modulesNode, def);
298             }
299
300         }
301         catch (Exception JavaDoc e) {
302             log.error("can't register modules", e); //$NON-NLS-1$
303
}
304     }
305
306     /**
307      * Regsiter a module
308      * @param modulesNode the module is or wil get placed under this node
309      * @param def the definition of the module to register
310      */

311     protected void registerModule(Content modulesNode, ModuleDefinition def) {
312         try {
313
314             Module module = (Module) ClassUtil.newInstance(def.getClassName());
315             int registerState = Module.REGISTER_STATE_NONE;
316             ModuleLoader.getInstance().addModuleInstance(def.getName(), module);
317
318             Content moduleNode;
319
320             try {
321                 moduleNode = modulesNode.getContent(def.getName());
322                 // node exists: is it a new version ?
323
if (!def.getVersion().equals(moduleNode.getNodeData("version").getString())) { //$NON-NLS-1$
324
registerState = Module.REGISTER_STATE_NEW_VERSION;
325                 }
326             }
327             // first installation
328
catch (PathNotFoundException e1) {
329                 moduleNode = modulesNode.createContent(def.getName());
330                 ModuleUtil.createMinimalConfiguration(moduleNode, def.getName(), def.getDisplayName(), def
331                     .getClassName(), def.getVersion());
332                 registerState = Module.REGISTER_STATE_INSTALLATION;
333             }
334
335             try {
336
337                 long startTime = System.currentTimeMillis();
338                 // do only log if the register state is not none
339
if (registerState != Module.REGISTER_STATE_NONE) {
340                     log.info("start registration of module {}", def.getName());
341                 }
342
343                 // call register: this is always done not only during the first startup
344
module.register(def, moduleNode, registerState);
345                 if (module.isRestartNeeded()) {
346                     this.restartNeeded = true;
347                 }
348
349                 if (registerState == Module.REGISTER_STATE_NEW_VERSION) {
350                     moduleNode.createNodeData("version").setValue(def.getVersion()); //$NON-NLS-1$
351
}
352                 modulesNode.save();
353
354                 // execute now the post bootstrap if module specific configuration files found
355
if (registerState == Module.REGISTER_STATE_INSTALLATION) {
356                     postBootstrapModule(def.getName());
357                 }
358
359                 log.info("Registration of module {} completed in {} second(s)", def.getName(), Long.toString((System
360                     .currentTimeMillis() - startTime) / 1000));
361
362             }
363             catch (RegisterException e) {
364                 switch (registerState) {
365                     case Module.REGISTER_STATE_INSTALLATION:
366                         log.error("can't install module [" + def.getName() + "]" + def.getVersion(), e); //$NON-NLS-1$ //$NON-NLS-2$
367
break;
368                     case Module.REGISTER_STATE_NEW_VERSION:
369                         log.error("can't update module [" + def.getName() + "] to version " + def.getVersion(), //$NON-NLS-1$ //$NON-NLS-2$
370
e);
371                         break;
372                     default:
373                         log.error("error during registering an already installed module [" //$NON-NLS-1$
374
+ def.getName()
375                             + "]", e); //$NON-NLS-1$
376
break;
377                 }
378             }
379         }
380
381         catch (Exception JavaDoc e) {
382             log.error("can't register module [" //$NON-NLS-1$
383
+ def.getName()
384                 + "] due to a " //$NON-NLS-1$
385
+ e.getClass().getName()
386                 + " exception: " //$NON-NLS-1$
387
+ e.getMessage(), e);
388         }
389     }
390
391     /**
392      * Calculates the level of dependency. 0 means no dependency. If no of the dependencies has itself dependencies is
393      * this level 1. If one or more of the dependencies has a dependencies has a dependency it would return 2. And so on
394      * ...
395      * @param def module definition
396      * @return the level
397      */

398     protected int calcDependencyLevel(ModuleDefinition def) {
399         if (def.getDependencies() == null || def.getDependencies().size() == 0) {
400             return 0;
401         }
402         List JavaDoc dependencyLevels = new ArrayList JavaDoc();
403         for (Iterator JavaDoc iter = def.getDependencies().iterator(); iter.hasNext();) {
404             DependencyDefinition dep = (DependencyDefinition) iter.next();
405             ModuleDefinition depDef = this.getModuleDefinition(dep.getName());
406             if (depDef == null && !dep.isOptional()) {
407                 throw new RuntimeException JavaDoc("Missing definition for module:" + dep.getName());
408             } else if (depDef != null){
409                 dependencyLevels.add(new Integer JavaDoc(calcDependencyLevel(depDef)));
410             }
411         }
412         return ((Integer JavaDoc) Collections.max(dependencyLevels)).intValue() + 1;
413     }
414
415     /**
416      * Returns the definition of this module
417      * @param moduleName
418      */

419     public ModuleDefinition getModuleDefinition(String JavaDoc moduleName) {
420         return (ModuleDefinition) this.moduleDefinitions.get(moduleName);
421     }
422
423     /**
424      * @return Returns the moduleDefinitions.
425      */

426     public OrderedMap getModuleDefinitions() {
427         return this.moduleDefinitions;
428     }
429
430     /**
431      * Changes the doctype to the correct dtd path (in the classpath)
432      * @param name name of the xml resource
433      * @return the reader for passing to the beanReader
434      * @throws IOException
435      * @throws JDOMException
436      */

437     private String JavaDoc getXML(String JavaDoc name) throws IOException JavaDoc, JDOMException {
438         URL JavaDoc dtdUrl = getClass().getResource("/info/magnolia/cms/module/module.dtd");
439
440         String JavaDoc content = IOUtils.toString(getClass().getResourceAsStream(name));
441
442         // remove doctype
443
Pattern JavaDoc pattern = Pattern.compile("<!DOCTYPE .*>");
444         Matcher JavaDoc matcher = pattern.matcher(content);
445         content = matcher.replaceFirst("");
446
447         // set doctype to the dtd
448
Document doc = new SAXBuilder().build(new StringReader JavaDoc(content));
449         doc.setDocType(new DocType("module", dtdUrl.toString()));
450         // write the xml to the string
451
XMLOutputter outputter = new XMLOutputter();
452         StringWriter JavaDoc writer = new StringWriter JavaDoc();
453         outputter.output(doc, writer);
454         return writer.toString();
455     }
456
457     /**
458      * Bootstrap module specifig bootstrap file after the registration to load custom settings
459      * @param moduleName
460      */

461     protected void postBootstrapModule(final String JavaDoc moduleName) {
462         Bootstrapper.bootstrapRepository(ContentRepository.CONFIG, new Bootstrapper.BootstrapFilter() {
463
464             public boolean accept(String JavaDoc filename) {
465                 return filename.startsWith("config.modules." + moduleName);
466             }
467         });
468     }
469
470     /**
471      * @return Returns the restartNeeded.
472      */

473     public boolean isRestartNeeded() {
474         return this.restartNeeded;
475     }
476
477     /**
478      * @param b
479      */

480     public void setRestartNeeded(boolean b) {
481         AlertUtil.setMessage("system.restart", MgnlContext.getSystemContext());
482         this.restartNeeded = b;
483     }
484 }
Popular Tags