1 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 ; 30 import java.io.IOException ; 31 import java.io.StringReader ; 32 import java.io.StringWriter ; 33 import java.net.URL ; 34 import java.util.ArrayList ; 35 import java.util.Collections ; 36 import java.util.Comparator ; 37 import java.util.Iterator ; 38 import java.util.List ; 39 import java.util.regex.Matcher ; 40 import java.util.regex.Pattern ; 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 66 public class ModuleRegistration { 67 68 71 private boolean initialized = false; 72 73 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 87 private Logger log = LoggerFactory.getLogger(ModuleRegistration.class); 88 89 92 private OrderedMap moduleDefinitions = new LinkedMap(); 93 94 97 private boolean restartNeeded = false; 98 99 102 public ModuleRegistration() { 103 } 104 105 108 public void init() throws MissingDependencyException { 109 readModuleDefinitions(); 111 112 checkDependencies(); 114 115 sortByDependencyLevel(); 117 118 initialized = true; 119 } 120 121 protected File getModuleRoot(String magnoliaModuleXml) { 122 URL xmlUrl = getClass().getResource(magnoliaModuleXml); 123 124 return getModuleRoot(xmlUrl); 125 } 126 127 131 protected File getModuleRoot(URL xmlUrl) { 132 final String path = xmlUrl.getFile(); 133 134 final boolean withinJar = StringUtils.contains(path, ".jar!"); 135 if (withinJar) { 136 final String jarPath = StringUtils.substringBefore(path, ".jar!") + ".jar"; 137 return new File (jarPath); 138 } else { 139 final File xmlFile = new File (path); 140 return xmlFile.getParentFile().getParentFile().getParentFile(); 142 } 143 } 144 145 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 String [] defResources = ClasspathResourcesUtil.findResources(new ClasspathResourcesUtil.Filter() { 157 158 public boolean accept(String name) { 159 return name.startsWith("/META-INF/magnolia") && name.endsWith(".xml"); 160 } 161 }); 162 163 for (int j = 0; j < defResources.length; j++) { 165 String name = defResources[j]; 166 File 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 (getXML(name))); 172 def.setModuleRoot(moduleRoot); 173 this.moduleDefinitions.put(def.getName(), def); 174 } 175 catch (Exception e) { 176 throw new ConfigurationException("can't read the module definition file [" + name + "].", e); 177 } 178 } 179 180 } 181 catch (Exception e) { 182 throw new ConfigurationException("can't read the module definition files.", e); 183 } 184 } 185 186 protected void addAdHocDefinitions() { 187 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 iter = modulesNode.getChildren().iterator(); iter.hasNext();) { 198 Content moduleNode = (Content) iter.next(); 199 200 String name = moduleNode.getName(); 201 String version = NodeDataUtil.getString(moduleNode, "version", ""); 202 String 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 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 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 241 private void sortByDependencyLevel() { 242 List modules = new ArrayList (); 244 245 for (MapIterator iter = this.moduleDefinitions.mapIterator(); iter.hasNext();) { 247 iter.next(); 248 modules.add(iter.getValue()); 249 } 250 251 Collections.sort(modules, new Comparator () { 252 253 public int compare(Object arg1, Object arg2) { 254 ModuleDefinition def1 = (ModuleDefinition) arg1; 255 ModuleDefinition def2 = (ModuleDefinition) arg2; 256 int level1 = calcDependencyLevel(def1); 257 int level2 = calcDependencyLevel(def2); 258 259 int dif = level1 - level2; 261 if (dif != 0) { 262 return dif; 263 } 264 266 return def1.getName().compareTo(def2.getName()); 267 268 } 269 }); 270 271 this.moduleDefinitions.clear(); 273 274 for (Iterator 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 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 e) { 302 log.error("can't register modules", e); } 304 } 305 306 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 if (!def.getVersion().equals(moduleNode.getNodeData("version").getString())) { registerState = Module.REGISTER_STATE_NEW_VERSION; 325 } 326 } 327 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 if (registerState != Module.REGISTER_STATE_NONE) { 340 log.info("start registration of module {}", def.getName()); 341 } 342 343 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()); } 352 modulesNode.save(); 353 354 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); break; 368 case Module.REGISTER_STATE_NEW_VERSION: 369 log.error("can't update module [" + def.getName() + "] to version " + def.getVersion(), e); 371 break; 372 default: 373 log.error("error during registering an already installed module [" + def.getName() 375 + "]", e); break; 377 } 378 } 379 } 380 381 catch (Exception e) { 382 log.error("can't register module [" + def.getName() 384 + "] due to a " + e.getClass().getName() 386 + " exception: " + e.getMessage(), e); 388 } 389 } 390 391 398 protected int calcDependencyLevel(ModuleDefinition def) { 399 if (def.getDependencies() == null || def.getDependencies().size() == 0) { 400 return 0; 401 } 402 List dependencyLevels = new ArrayList (); 403 for (Iterator 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 ("Missing definition for module:" + dep.getName()); 408 } else if (depDef != null){ 409 dependencyLevels.add(new Integer (calcDependencyLevel(depDef))); 410 } 411 } 412 return ((Integer ) Collections.max(dependencyLevels)).intValue() + 1; 413 } 414 415 419 public ModuleDefinition getModuleDefinition(String moduleName) { 420 return (ModuleDefinition) this.moduleDefinitions.get(moduleName); 421 } 422 423 426 public OrderedMap getModuleDefinitions() { 427 return this.moduleDefinitions; 428 } 429 430 437 private String getXML(String name) throws IOException , JDOMException { 438 URL dtdUrl = getClass().getResource("/info/magnolia/cms/module/module.dtd"); 439 440 String content = IOUtils.toString(getClass().getResourceAsStream(name)); 441 442 Pattern pattern = Pattern.compile("<!DOCTYPE .*>"); 444 Matcher matcher = pattern.matcher(content); 445 content = matcher.replaceFirst(""); 446 447 Document doc = new SAXBuilder().build(new StringReader (content)); 449 doc.setDocType(new DocType("module", dtdUrl.toString())); 450 XMLOutputter outputter = new XMLOutputter(); 452 StringWriter writer = new StringWriter (); 453 outputter.output(doc, writer); 454 return writer.toString(); 455 } 456 457 461 protected void postBootstrapModule(final String moduleName) { 462 Bootstrapper.bootstrapRepository(ContentRepository.CONFIG, new Bootstrapper.BootstrapFilter() { 463 464 public boolean accept(String filename) { 465 return filename.startsWith("config.modules." + moduleName); 466 } 467 }); 468 } 469 470 473 public boolean isRestartNeeded() { 474 return this.restartNeeded; 475 } 476 477 480 public void setRestartNeeded(boolean b) { 481 AlertUtil.setMessage("system.restart", MgnlContext.getSystemContext()); 482 this.restartNeeded = b; 483 } 484 } | Popular Tags |