001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software GmbH, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.module; 029 030import org.opencms.configuration.CmsConfigurationException; 031import org.opencms.configuration.CmsConfigurationManager; 032import org.opencms.configuration.CmsModuleConfiguration; 033import org.opencms.db.CmsExportPoint; 034import org.opencms.file.CmsObject; 035import org.opencms.file.CmsProject; 036import org.opencms.file.CmsResource; 037import org.opencms.file.CmsResourceFilter; 038import org.opencms.file.CmsVfsResourceNotFoundException; 039import org.opencms.i18n.CmsMessageContainer; 040import org.opencms.importexport.CmsImportExportManager; 041import org.opencms.lock.CmsLock; 042import org.opencms.lock.CmsLockException; 043import org.opencms.lock.CmsLockFilter; 044import org.opencms.main.CmsException; 045import org.opencms.main.CmsIllegalArgumentException; 046import org.opencms.main.CmsIllegalStateException; 047import org.opencms.main.CmsLog; 048import org.opencms.main.CmsRuntimeException; 049import org.opencms.main.OpenCms; 050import org.opencms.report.I_CmsReport; 051import org.opencms.security.CmsRole; 052import org.opencms.security.CmsRoleViolationException; 053import org.opencms.security.CmsSecurityException; 054import org.opencms.util.CmsStringUtil; 055 056import java.io.File; 057import java.util.ArrayList; 058import java.util.Collections; 059import java.util.HashMap; 060import java.util.HashSet; 061import java.util.Hashtable; 062import java.util.Iterator; 063import java.util.List; 064import java.util.Map; 065import java.util.Set; 066 067import org.apache.commons.logging.Log; 068 069/** 070 * Manages the modules of an OpenCms installation.<p> 071 * 072 * @since 6.0.0 073 */ 074public class CmsModuleManager { 075 076 /** Indicates dependency check for module deletion. */ 077 public static final int DEPENDENCY_MODE_DELETE = 0; 078 079 /** Indicates dependency check for module import. */ 080 public static final int DEPENDENCY_MODE_IMPORT = 1; 081 082 /** The log object for this class. */ 083 private static final Log LOG = CmsLog.getLog(CmsModuleManager.class); 084 085 /** The list of module export points. */ 086 private Set<CmsExportPoint> m_moduleExportPoints; 087 088 /** The map of configured modules. */ 089 private Map<String, CmsModule> m_modules; 090 091 /** 092 * Basic constructor.<p> 093 * 094 * @param configuredModules the list of configured modules 095 */ 096 public CmsModuleManager(List<CmsModule> configuredModules) { 097 098 if (CmsLog.INIT.isInfoEnabled()) { 099 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_MOD_MANAGER_CREATED_0)); 100 } 101 102 m_modules = new Hashtable<String, CmsModule>(); 103 for (int i = 0; i < configuredModules.size(); i++) { 104 CmsModule module = configuredModules.get(i); 105 m_modules.put(module.getName(), module); 106 if (CmsLog.INIT.isInfoEnabled()) { 107 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_MOD_CONFIGURED_1, module.getName())); 108 } 109 } 110 111 if (CmsLog.INIT.isInfoEnabled()) { 112 CmsLog.INIT.info(Messages.get().getBundle().key( 113 Messages.INIT_NUM_MODS_CONFIGURED_1, 114 new Integer(m_modules.size()))); 115 } 116 m_moduleExportPoints = Collections.emptySet(); 117 } 118 119 /** 120 * Returns a map of dependencies.<p> 121 * 122 * The module dependencies are get from the installed modules or 123 * from the module manifest.xml files found in the given FRS path.<p> 124 * 125 * Two types of dependency lists can be generated:<br> 126 * <ul> 127 * <li>Forward dependency lists: a list of modules that depends on a module</li> 128 * <li>Backward dependency lists: a list of modules that a module depends on</li> 129 * </ul> 130 * 131 * @param rfsAbsPath a RFS absolute path to search for modules, or <code>null</code> to use the installed modules 132 * @param mode if <code>true</code> a list of forward dependency is build, is not a list of backward dependency 133 * 134 * @return a Map of module names as keys and a list of dependency names as values 135 * 136 * @throws CmsConfigurationException if something goes wrong 137 */ 138 public static Map<String, List<String>> buildDepsForAllModules(String rfsAbsPath, boolean mode) 139 throws CmsConfigurationException { 140 141 Map<String, List<String>> ret = new HashMap<String, List<String>>(); 142 List<CmsModule> modules; 143 if (rfsAbsPath == null) { 144 modules = OpenCms.getModuleManager().getAllInstalledModules(); 145 } else { 146 modules = new ArrayList<CmsModule>(getAllModulesFromPath(rfsAbsPath).keySet()); 147 } 148 Iterator<CmsModule> itMods = modules.iterator(); 149 while (itMods.hasNext()) { 150 CmsModule module = itMods.next(); 151 152 // if module a depends on module b, and module c depends also on module b: 153 // build a map with a list containing "a" and "c" keyed by "b" to get a 154 // list of modules depending on module "b"... 155 Iterator<CmsModuleDependency> itDeps = module.getDependencies().iterator(); 156 while (itDeps.hasNext()) { 157 CmsModuleDependency dependency = itDeps.next(); 158 // module dependency package name 159 String moduleDependencyName = dependency.getName(); 160 161 if (mode) { 162 // get the list of dependent modules 163 List<String> moduleDependencies = ret.get(moduleDependencyName); 164 if (moduleDependencies == null) { 165 // build a new list if "b" has no dependent modules yet 166 moduleDependencies = new ArrayList<String>(); 167 ret.put(moduleDependencyName, moduleDependencies); 168 } 169 // add "a" as a module depending on "b" 170 moduleDependencies.add(module.getName()); 171 } else { 172 List<String> moduleDependencies = ret.get(module.getName()); 173 if (moduleDependencies == null) { 174 moduleDependencies = new ArrayList<String>(); 175 ret.put(module.getName(), moduleDependencies); 176 } 177 moduleDependencies.add(dependency.getName()); 178 } 179 } 180 } 181 itMods = modules.iterator(); 182 while (itMods.hasNext()) { 183 CmsModule module = itMods.next(); 184 if (ret.get(module.getName()) == null) { 185 ret.put(module.getName(), new ArrayList<String>()); 186 } 187 } 188 return ret; 189 } 190 191 /** 192 * Returns a map of dependencies between the given modules.<p> 193 * 194 * The module dependencies are get from the installed modules or 195 * from the module manifest.xml files found in the given FRS path.<p> 196 * 197 * Two types of dependency lists can be generated:<br> 198 * <ul> 199 * <li>Forward dependency lists: a list of modules that depends on a module</li> 200 * <li>Backward dependency lists: a list of modules that a module depends on</li> 201 * </ul> 202 * 203 * @param moduleNames a list of module names 204 * @param rfsAbsPath a RFS absolute path to search for modules, or <code>null</code> to use the installed modules 205 * @param mode if <code>true</code> a list of forward dependency is build, is not a list of backward dependency 206 * 207 * @return a Map of module names as keys and a list of dependency names as values 208 * 209 * @throws CmsConfigurationException if something goes wrong 210 */ 211 public static Map<String, List<String>> buildDepsForModulelist( 212 List<String> moduleNames, 213 String rfsAbsPath, 214 boolean mode) throws CmsConfigurationException { 215 216 Map<String, List<String>> ret = buildDepsForAllModules(rfsAbsPath, mode); 217 Iterator<CmsModule> itMods; 218 if (rfsAbsPath == null) { 219 itMods = OpenCms.getModuleManager().getAllInstalledModules().iterator(); 220 } else { 221 itMods = getAllModulesFromPath(rfsAbsPath).keySet().iterator(); 222 } 223 while (itMods.hasNext()) { 224 CmsModule module = itMods.next(); 225 if (!moduleNames.contains(module.getName())) { 226 Iterator<List<String>> itDeps = ret.values().iterator(); 227 while (itDeps.hasNext()) { 228 List<String> dependencies = itDeps.next(); 229 dependencies.remove(module.getName()); 230 } 231 ret.remove(module.getName()); 232 } 233 } 234 return ret; 235 } 236 237 /** 238 * Returns a map of modules found in the given RFS absolute path.<p> 239 * 240 * @param rfsAbsPath the path to look for module distributions 241 * 242 * @return a map of <code>{@link CmsModule}</code> objects for keys and filename for values 243 * 244 * @throws CmsConfigurationException if something goes wrong 245 */ 246 public static Map<CmsModule, String> getAllModulesFromPath(String rfsAbsPath) throws CmsConfigurationException { 247 248 Map<CmsModule, String> modules = new HashMap<CmsModule, String>(); 249 if (rfsAbsPath == null) { 250 return modules; 251 } 252 File folder = new File(rfsAbsPath); 253 if (folder.exists()) { 254 // list all child resources in the given folder 255 File[] folderFiles = folder.listFiles(); 256 if (folderFiles != null) { 257 for (int i = 0; i < folderFiles.length; i++) { 258 File moduleFile = folderFiles[i]; 259 if (moduleFile.isFile() && !(moduleFile.getAbsolutePath().toLowerCase().endsWith(".zip"))) { 260 // skip non-ZIP files 261 continue; 262 } 263 if (moduleFile.isDirectory()) { 264 File manifest = new File(moduleFile, CmsImportExportManager.EXPORT_MANIFEST); 265 if (!manifest.exists() || !manifest.canRead()) { 266 // skip unused directories 267 continue; 268 } 269 } 270 modules.put( 271 CmsModuleImportExportHandler.readModuleFromImport(moduleFile.getAbsolutePath()), 272 moduleFile.getName()); 273 } 274 } 275 } 276 return modules; 277 } 278 279 /** 280 * Sorts a given list of module names by dependencies, 281 * so that the resulting list can be imported in that given order, 282 * that means modules without dependencies first.<p> 283 * 284 * The module dependencies are get from the installed modules or 285 * from the module manifest.xml files found in the given FRS path.<p> 286 * 287 * @param moduleNames a list of module names 288 * @param rfsAbsPath a RFS absolute path to search for modules, or <code>null</code> to use the installed modules 289 * 290 * @return a sorted list of module names 291 * 292 * @throws CmsConfigurationException if something goes wrong 293 */ 294 public static List<String> topologicalSort(List<String> moduleNames, String rfsAbsPath) 295 throws CmsConfigurationException { 296 297 List<String> modules = new ArrayList<String>(moduleNames); 298 List<String> retList = new ArrayList<String>(); 299 Map<String, List<String>> moduleDependencies = buildDepsForModulelist(moduleNames, rfsAbsPath, true); 300 boolean finished = false; 301 while (!finished) { 302 finished = true; 303 Iterator<String> itMods = modules.iterator(); 304 while (itMods.hasNext()) { 305 String moduleName = itMods.next(); 306 List<String> deps = moduleDependencies.get(moduleName); 307 if ((deps == null) || deps.isEmpty()) { 308 retList.add(moduleName); 309 Iterator<List<String>> itDeps = moduleDependencies.values().iterator(); 310 while (itDeps.hasNext()) { 311 List<String> dependencies = itDeps.next(); 312 dependencies.remove(moduleName); 313 } 314 finished = false; 315 itMods.remove(); 316 } 317 } 318 } 319 if (!modules.isEmpty()) { 320 throw new CmsIllegalStateException(Messages.get().container( 321 Messages.ERR_MODULE_DEPENDENCY_CYCLE_1, 322 modules.toString())); 323 } 324 Collections.reverse(retList); 325 return retList; 326 } 327 328 /** 329 * Adds a new module to the module manager.<p> 330 * 331 * @param cms must be initialized with "Admin" permissions 332 * @param module the module to add 333 * 334 * @throws CmsSecurityException if the required permissions are not available (i.e. no "Admin" CmsObject has been provided) 335 * @throws CmsConfigurationException if a module with this name is already configured 336 */ 337 public synchronized void addModule(CmsObject cms, CmsModule module) 338 throws CmsSecurityException, CmsConfigurationException { 339 340 // check the role permissions 341 OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER); 342 343 if (m_modules.containsKey(module.getName())) { 344 // module is currently configured, no create possible 345 throw new CmsConfigurationException(Messages.get().container( 346 Messages.ERR_MODULE_ALREADY_CONFIGURED_1, 347 module.getName())); 348 349 } 350 351 if (LOG.isInfoEnabled()) { 352 LOG.info(Messages.get().getBundle().key(Messages.LOG_CREATE_NEW_MOD_1, module.getName())); 353 } 354 355 // initialize the module 356 module.initialize(cms); 357 358 m_modules.put(module.getName(), module); 359 360 try { 361 I_CmsModuleAction moduleAction = module.getActionInstance(); 362 // handle module action instance if initialized 363 if (moduleAction != null) { 364 moduleAction.moduleUpdate(module); 365 } 366 } catch (Throwable t) { 367 LOG.error(Messages.get().getBundle().key(Messages.LOG_MOD_UPDATE_ERR_1, module.getName()), t); 368 } 369 370 // initialize the export points 371 initModuleExportPoints(); 372 373 // update the configuration 374 updateModuleConfiguration(); 375 } 376 377 /** 378 * Checks if a modules dependencies are fulfilled.<p> 379 * 380 * The possible values for the <code>mode</code> parameter are:<dl> 381 * <dt>{@link #DEPENDENCY_MODE_DELETE}</dt> 382 * <dd>Check for module deleting, i.e. are other modules dependent on the 383 * given module?</dd> 384 * <dt>{@link #DEPENDENCY_MODE_IMPORT}</dt> 385 * <dd>Check for module importing, i.e. are all dependencies required by the given 386 * module available?</dd></dl> 387 * 388 * @param module the module to check the dependencies for 389 * @param mode the dependency check mode 390 * @return a list of dependencies that are not fulfilled, if empty all dependencies are fulfilled 391 */ 392 public List<CmsModuleDependency> checkDependencies(CmsModule module, int mode) { 393 394 List<CmsModuleDependency> result = new ArrayList<CmsModuleDependency>(); 395 396 if (mode == DEPENDENCY_MODE_DELETE) { 397 // delete mode, check if other modules depend on this module 398 Iterator<CmsModule> i = m_modules.values().iterator(); 399 while (i.hasNext()) { 400 CmsModule otherModule = i.next(); 401 CmsModuleDependency dependency = otherModule.checkDependency(module); 402 if (dependency != null) { 403 // dependency found, add to list 404 result.add(new CmsModuleDependency(otherModule.getName(), otherModule.getVersion())); 405 } 406 } 407 408 } else if (mode == DEPENDENCY_MODE_IMPORT) { 409 // import mode, check if all module dependencies are fulfilled 410 Iterator<CmsModule> i = m_modules.values().iterator(); 411 // add all dependencies that must be found 412 result.addAll(module.getDependencies()); 413 while (i.hasNext() && (result.size() > 0)) { 414 CmsModule otherModule = i.next(); 415 CmsModuleDependency dependency = module.checkDependency(otherModule); 416 if (dependency != null) { 417 // dependency found, remove from list 418 result.remove(dependency); 419 } 420 } 421 } else { 422 // invalid mode selected 423 throw new CmsRuntimeException(Messages.get().container( 424 Messages.ERR_CHECK_DEPENDENCY_INVALID_MODE_1, 425 new Integer(mode))); 426 } 427 428 return result; 429 } 430 431 /** 432 * Checks the module selection list for consistency, that means 433 * that if a module is selected, all its dependencies are also selected.<p> 434 * 435 * The module dependencies are get from the installed modules or 436 * from the module manifest.xml files found in the given FRS path.<p> 437 * 438 * @param moduleNames a list of module names 439 * @param rfsAbsPath a RFS absolute path to search for modules, or <code>null</code> to use the installed modules 440 * @param forDeletion there are two modes, one for installation of modules, and one for deletion. 441 * 442 * @throws CmsIllegalArgumentException if the module list is not consistent 443 * @throws CmsConfigurationException if something goes wrong 444 */ 445 public void checkModuleSelectionList(List<String> moduleNames, String rfsAbsPath, boolean forDeletion) 446 throws CmsIllegalArgumentException, CmsConfigurationException { 447 448 Map<String, List<String>> moduleDependencies = buildDepsForAllModules(rfsAbsPath, forDeletion); 449 Iterator<String> itMods = moduleNames.iterator(); 450 while (itMods.hasNext()) { 451 String moduleName = itMods.next(); 452 List<String> dependencies = moduleDependencies.get(moduleName); 453 if (dependencies != null) { 454 List<String> depModules = new ArrayList<String>(dependencies); 455 depModules.removeAll(moduleNames); 456 if (!depModules.isEmpty()) { 457 throw new CmsIllegalArgumentException(Messages.get().container( 458 Messages.ERR_MODULE_SELECTION_INCONSISTENT_2, 459 moduleName, 460 depModules.toString())); 461 } 462 } 463 } 464 } 465 466 /** 467 * Deletes a module from the configuration.<p> 468 * 469 * @param cms must be initialized with "Admin" permissions 470 * @param moduleName the name of the module to delete 471 * @param replace indicates if the module is replaced (true) or finally deleted (false) 472 * @param report the report to print progress messages to 473 * 474 * @throws CmsRoleViolationException if the required module manager role permissions are not available 475 * @throws CmsConfigurationException if a module with this name is not available for deleting 476 * @throws CmsLockException if the module resources can not be locked 477 */ 478 public synchronized void deleteModule(CmsObject cms, String moduleName, boolean replace, I_CmsReport report) 479 throws CmsRoleViolationException, CmsConfigurationException, CmsLockException { 480 481 // check for module manager role permissions 482 OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER); 483 484 if (!m_modules.containsKey(moduleName)) { 485 // module is not currently configured, no update possible 486 throw new CmsConfigurationException(Messages.get().container( 487 Messages.ERR_MODULE_NOT_CONFIGURED_1, 488 moduleName)); 489 } 490 491 if (LOG.isInfoEnabled()) { 492 LOG.info(Messages.get().getBundle().key(Messages.LOG_DEL_MOD_1, moduleName)); 493 } 494 495 CmsModule module = m_modules.get(moduleName); 496 497 if (!replace) { 498 // module is deleted, not replaced 499 500 // perform dependency check 501 List<CmsModuleDependency> dependencies = checkDependencies(module, DEPENDENCY_MODE_DELETE); 502 if (!dependencies.isEmpty()) { 503 StringBuffer message = new StringBuffer(); 504 Iterator<CmsModuleDependency> it = dependencies.iterator(); 505 while (it.hasNext()) { 506 message.append(" ").append(it.next().getName()).append("\r\n"); 507 } 508 throw new CmsConfigurationException(Messages.get().container( 509 Messages.ERR_MOD_DEPENDENCIES_2, 510 moduleName, 511 message.toString())); 512 } 513 try { 514 I_CmsModuleAction moduleAction = module.getActionInstance(); 515 // handle module action instance if initialized 516 if (moduleAction != null) { 517 moduleAction.moduleUninstall(module); 518 } 519 } catch (Throwable t) { 520 LOG.error(Messages.get().getBundle().key(Messages.LOG_MOD_UNINSTALL_ERR_1, moduleName), t); 521 report.println( 522 Messages.get().container(Messages.LOG_MOD_UNINSTALL_ERR_1, moduleName), 523 I_CmsReport.FORMAT_WARNING); 524 } 525 } 526 527 boolean removeResourceTypes = !module.getResourceTypes().isEmpty(); 528 if (removeResourceTypes) { 529 // mark the resource manager to reinitialize if necessary 530 OpenCms.getWorkplaceManager().removeExplorerTypeSettings(module); 531 } 532 533 CmsProject previousProject = cms.getRequestContext().getCurrentProject(); 534 // try to create a new offline project for deletion 535 CmsProject deleteProject = null; 536 try { 537 // try to read a (leftover) module delete project 538 deleteProject = cms.readProject(Messages.get().getBundle(cms.getRequestContext().getLocale()).key( 539 Messages.GUI_DELETE_MODULE_PROJECT_NAME_1, 540 new Object[] {moduleName})); 541 } catch (CmsException e) { 542 try { 543 // create a Project to delete the module 544 deleteProject = cms.createProject( 545 Messages.get().getBundle(cms.getRequestContext().getLocale()).key( 546 Messages.GUI_DELETE_MODULE_PROJECT_NAME_1, 547 new Object[] {moduleName}), 548 Messages.get().getBundle(cms.getRequestContext().getLocale()).key( 549 Messages.GUI_DELETE_MODULE_PROJECT_DESC_1, 550 new Object[] {moduleName}), 551 OpenCms.getDefaultUsers().getGroupAdministrators(), 552 OpenCms.getDefaultUsers().getGroupAdministrators(), 553 CmsProject.PROJECT_TYPE_TEMPORARY); 554 } catch (CmsException e1) { 555 throw new CmsConfigurationException(e1.getMessageContainer(), e1); 556 } 557 } 558 559 try { 560 cms.getRequestContext().setCurrentProject(deleteProject); 561 562 // check locks 563 List<String> lockedResources = new ArrayList<String>(); 564 CmsLockFilter filter1 = CmsLockFilter.FILTER_ALL.filterNotLockableByUser(cms.getRequestContext().getCurrentUser()); 565 CmsLockFilter filter2 = CmsLockFilter.FILTER_INHERITED; 566 List<String> moduleResources = module.getResources(); 567 for (int iLock = 0; iLock < moduleResources.size(); iLock++) { 568 String resourceName = moduleResources.get(iLock); 569 try { 570 lockedResources.addAll(cms.getLockedResources(resourceName, filter1)); 571 lockedResources.addAll(cms.getLockedResources(resourceName, filter2)); 572 } catch (CmsException e) { 573 // may happen if the resource has already been deleted 574 if (LOG.isDebugEnabled()) { 575 LOG.debug(e.getMessageContainer(), e); 576 } 577 report.println(e.getMessageContainer(), I_CmsReport.FORMAT_WARNING); 578 } 579 } 580 if (!lockedResources.isEmpty()) { 581 CmsMessageContainer msg = Messages.get().container( 582 Messages.ERR_DELETE_MODULE_CHECK_LOCKS_2, 583 moduleName, 584 CmsStringUtil.collectionAsString(lockedResources, ",")); 585 report.addError(msg.key(cms.getRequestContext().getLocale())); 586 report.println(msg); 587 cms.getRequestContext().setCurrentProject(previousProject); 588 try { 589 cms.deleteProject(deleteProject.getUuid()); 590 } catch (CmsException e1) { 591 throw new CmsConfigurationException(e1.getMessageContainer(), e1); 592 } 593 throw new CmsLockException(msg); 594 } 595 } finally { 596 cms.getRequestContext().setCurrentProject(previousProject); 597 } 598 599 // now remove the module 600 module = m_modules.remove(moduleName); 601 602 try { 603 cms.getRequestContext().setCurrentProject(deleteProject); 604 605 // copy the module resources to the project 606 List<String> projectFiles = module.getResources(); 607 for (int i = 0; i < projectFiles.size(); i++) { 608 String resourceName = projectFiles.get(i); 609 if (cms.existsResource(resourceName, CmsResourceFilter.ALL)) { 610 try { 611 cms.copyResourceToProject(resourceName); 612 } catch (CmsException e) { 613 // may happen if the resource has already been deleted 614 if (LOG.isDebugEnabled()) { 615 LOG.debug(Messages.get().getBundle().key(Messages.LOG_MOVE_RESOURCE_FAILED_1, resourceName)); 616 } 617 report.println(e.getMessageContainer(), I_CmsReport.FORMAT_WARNING); 618 } 619 } 620 } 621 622 report.print(Messages.get().container(Messages.RPT_DELETE_MODULE_BEGIN_0), I_CmsReport.FORMAT_HEADLINE); 623 report.println( 624 org.opencms.report.Messages.get().container( 625 org.opencms.report.Messages.RPT_ARGUMENT_HTML_ITAG_1, 626 moduleName), 627 I_CmsReport.FORMAT_HEADLINE); 628 629 // move through all module resources and delete them 630 for (int i = 0; i < module.getResources().size(); i++) { 631 String currentResource = null; 632 try { 633 currentResource = module.getResources().get(i); 634 if (LOG.isDebugEnabled()) { 635 LOG.debug(Messages.get().getBundle().key(Messages.LOG_DEL_MOD_RESOURCE_1, currentResource)); 636 } 637 CmsResource resource = null; 638 try { 639 resource = cms.readResource(currentResource, CmsResourceFilter.ALL); 640 } catch (CmsVfsResourceNotFoundException e) { 641 // ignore 642 } 643 if (resource != null) { 644 CmsLock lock = cms.getLock(currentResource); 645 if (lock.isUnlocked()) { 646 // lock the resource 647 cms.lockResource(currentResource); 648 } else if (lock.isLockableBy(cms.getRequestContext().getCurrentUser())) { 649 // steal the resource 650 cms.changeLock(currentResource); 651 } 652 if (!resource.getState().isDeleted()) { 653 // delete the resource 654 cms.deleteResource(currentResource, CmsResource.DELETE_PRESERVE_SIBLINGS); 655 } 656 // update the report 657 report.print(Messages.get().container(Messages.RPT_DELETE_0), I_CmsReport.FORMAT_NOTE); 658 report.println(org.opencms.report.Messages.get().container( 659 org.opencms.report.Messages.RPT_ARGUMENT_1, 660 currentResource)); 661 if (!resource.getState().isNew()) { 662 // unlock the resource (so it gets deleted with next publish) 663 cms.unlockResource(currentResource); 664 } 665 } 666 } catch (CmsException e) { 667 // ignore the exception and delete the next resource 668 LOG.error(Messages.get().getBundle().key(Messages.LOG_DEL_MOD_EXC_1, currentResource), e); 669 report.println(e.getMessageContainer(), I_CmsReport.FORMAT_WARNING); 670 } 671 } 672 673 report.println(Messages.get().container(Messages.RPT_PUBLISH_PROJECT_BEGIN_0), I_CmsReport.FORMAT_HEADLINE); 674 675 // now unlock and publish the project 676 cms.unlockProject(deleteProject.getUuid()); 677 OpenCms.getPublishManager().publishProject(cms, report); 678 OpenCms.getPublishManager().waitWhileRunning(); 679 680 report.println(Messages.get().container(Messages.RPT_PUBLISH_PROJECT_END_0), I_CmsReport.FORMAT_HEADLINE); 681 report.println(Messages.get().container(Messages.RPT_DELETE_MODULE_END_0), I_CmsReport.FORMAT_HEADLINE); 682 } catch (CmsException e) { 683 throw new CmsConfigurationException(e.getMessageContainer(), e); 684 } finally { 685 cms.getRequestContext().setCurrentProject(previousProject); 686 } 687 688 // initialize the export points (removes export points from deleted module) 689 initModuleExportPoints(); 690 691 // update the configuration 692 updateModuleConfiguration(); 693 694 // reinit the manager is necessary 695 if (removeResourceTypes) { 696 OpenCms.getResourceManager().initialize(cms); 697 } 698 } 699 700 /** 701 * Returns a list of installed modules.<p> 702 * 703 * @return a list of <code>{@link CmsModule}</code> objects 704 */ 705 public List<CmsModule> getAllInstalledModules() { 706 707 return new ArrayList<CmsModule>(m_modules.values()); 708 } 709 710 /** 711 * Returns the (immutable) list of configured module export points.<p> 712 * 713 * @return the (immutable) list of configured module export points 714 * @see CmsExportPoint 715 */ 716 public Set<CmsExportPoint> getExportPoints() { 717 718 return m_moduleExportPoints; 719 } 720 721 /** 722 * Returns the module with the given module name, 723 * or <code>null</code> if no module with the given name is configured.<p> 724 * 725 * @param name the name of the module to return 726 * @return the module with the given module name 727 */ 728 public CmsModule getModule(String name) { 729 730 return m_modules.get(name); 731 } 732 733 /** 734 * Returns the set of names of all the installed modules.<p> 735 * 736 * @return the set of names of all the installed modules 737 */ 738 public Set<String> getModuleNames() { 739 740 synchronized (m_modules) { 741 return new HashSet<String>(m_modules.keySet()); 742 } 743 } 744 745 /** 746 * Checks if this module manager has a module with the given name installed.<p> 747 * 748 * @param name the name of the module to check 749 * @return true if this module manager has a module with the given name installed 750 */ 751 public boolean hasModule(String name) { 752 753 return m_modules.containsKey(name); 754 } 755 756 /** 757 * Initializes all module instance classes managed in this module manager.<p> 758 * 759 * @param cms an initialized CmsObject with "manage modules" role permissions 760 * @param configurationManager the initialized OpenCms configuration manager 761 * 762 * @throws CmsRoleViolationException if the provided OpenCms context does not have "manage modules" role permissions 763 */ 764 public synchronized void initialize(CmsObject cms, CmsConfigurationManager configurationManager) 765 throws CmsRoleViolationException { 766 767 if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_1_CORE_OBJECT) { 768 // certain test cases won't have an OpenCms context 769 OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER); 770 } 771 772 Iterator<String> it; 773 int count = 0; 774 it = m_modules.keySet().iterator(); 775 while (it.hasNext()) { 776 // get the module description 777 CmsModule module = m_modules.get(it.next()); 778 779 if (module.getActionClass() != null) { 780 // create module instance class 781 I_CmsModuleAction moduleAction = module.getActionInstance(); 782 if (module.getActionClass() != null) { 783 try { 784 moduleAction = (I_CmsModuleAction)Class.forName(module.getActionClass()).newInstance(); 785 } catch (Exception e) { 786 CmsLog.INIT.info( 787 Messages.get().getBundle().key(Messages.INIT_CREATE_INSTANCE_FAILED_1, module.getName()), 788 e); 789 } 790 } 791 if (moduleAction != null) { 792 count++; 793 module.setActionInstance(moduleAction); 794 if (CmsLog.INIT.isInfoEnabled()) { 795 CmsLog.INIT.info(Messages.get().getBundle().key( 796 Messages.INIT_INITIALIZE_MOD_CLASS_1, 797 moduleAction.getClass().getName())); 798 } 799 try { 800 // create a copy of the adminCms so that each module instance does have 801 // it's own context, a shared context might introduce side - effects 802 CmsObject adminCmsCopy = OpenCms.initCmsObject(cms); 803 // initialize the module 804 moduleAction.initialize(adminCmsCopy, configurationManager, module); 805 } catch (Throwable t) { 806 LOG.error( 807 Messages.get().getBundle().key( 808 Messages.LOG_INSTANCE_INIT_ERR_1, 809 moduleAction.getClass().getName()), 810 t); 811 } 812 } 813 } 814 } 815 816 // initialize the export points 817 initModuleExportPoints(); 818 819 if (CmsLog.INIT.isInfoEnabled()) { 820 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_NUM_CLASSES_INITIALIZED_1, new Integer(count))); 821 } 822 } 823 824 /** 825 * Shuts down all module instance classes managed in this module manager.<p> 826 */ 827 public synchronized void shutDown() { 828 829 int count = 0; 830 Iterator<String> it = getModuleNames().iterator(); 831 while (it.hasNext()) { 832 String moduleName = it.next(); 833 // get the module 834 CmsModule module = m_modules.get(moduleName); 835 if (module == null) { 836 continue; 837 } 838 // get the module action instance 839 I_CmsModuleAction moduleAction = module.getActionInstance(); 840 if (moduleAction == null) { 841 continue; 842 } 843 844 count++; 845 if (CmsLog.INIT.isInfoEnabled()) { 846 CmsLog.INIT.info(Messages.get().getBundle().key( 847 Messages.INIT_SHUTDOWN_MOD_CLASS_1, 848 moduleAction.getClass().getName())); 849 } 850 try { 851 // shut down the module 852 moduleAction.shutDown(module); 853 } catch (Throwable t) { 854 LOG.error( 855 Messages.get().getBundle().key( 856 Messages.LOG_INSTANCE_SHUTDOWN_ERR_1, 857 moduleAction.getClass().getName()), 858 t); 859 } 860 } 861 862 if (CmsLog.INIT.isInfoEnabled()) { 863 CmsLog.INIT.info(Messages.get().getBundle().key( 864 Messages.INIT_SHUTDOWN_NUM_MOD_CLASSES_1, 865 new Integer(count))); 866 } 867 868 if (CmsLog.INIT.isInfoEnabled()) { 869 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SHUTDOWN_1, this.getClass().getName())); 870 } 871 } 872 873 /** 874 * Updates a already configured module with new values.<p> 875 * 876 * @param cms must be initialized with "Admin" permissions 877 * @param module the module to update 878 * 879 * @throws CmsRoleViolationException if the required module manager role permissions are not available 880 * @throws CmsConfigurationException if a module with this name is not available for updating 881 */ 882 public synchronized void updateModule(CmsObject cms, CmsModule module) 883 throws CmsRoleViolationException, CmsConfigurationException { 884 885 // check for module manager role permissions 886 OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER); 887 888 CmsModule oldModule = m_modules.get(module.getName()); 889 890 if (oldModule == null) { 891 // module is not currently configured, no update possible 892 throw new CmsConfigurationException(Messages.get().container(Messages.ERR_OLD_MOD_ERR_1, module.getName())); 893 } 894 895 if (LOG.isInfoEnabled()) { 896 LOG.info(Messages.get().getBundle().key(Messages.LOG_MOD_UPDATE_1, module.getName())); 897 } 898 899 // indicate that the version number was recently updated 900 module.getVersion().setUpdated(true); 901 902 // initialize (freeze) the module 903 module.initialize(cms); 904 905 // replace old version of module with new version 906 m_modules.put(module.getName(), module); 907 908 try { 909 I_CmsModuleAction moduleAction = oldModule.getActionInstance(); 910 // handle module action instance if initialized 911 if (moduleAction != null) { 912 moduleAction.moduleUpdate(module); 913 // set the old action instance 914 // the new action instance will be used after a system restart 915 module.setActionInstance(moduleAction); 916 } 917 } catch (Throwable t) { 918 LOG.error(Messages.get().getBundle().key(Messages.LOG_INSTANCE_UPDATE_ERR_1, module.getName()), t); 919 } 920 921 // initialize the export points 922 initModuleExportPoints(); 923 924 // update the configuration 925 updateModuleConfiguration(); 926 } 927 928 /** 929 * Initializes the list of export points from all configured modules.<p> 930 */ 931 private synchronized void initModuleExportPoints() { 932 933 Set<CmsExportPoint> exportPoints = new HashSet<CmsExportPoint>(); 934 Iterator<CmsModule> i = m_modules.values().iterator(); 935 while (i.hasNext()) { 936 CmsModule module = i.next(); 937 List<CmsExportPoint> moduleExportPoints = module.getExportPoints(); 938 for (int j = 0; j < moduleExportPoints.size(); j++) { 939 CmsExportPoint point = moduleExportPoints.get(j); 940 if (exportPoints.contains(point)) { 941 if (LOG.isWarnEnabled()) { 942 LOG.warn(Messages.get().getBundle().key( 943 Messages.LOG_DUPLICATE_EXPORT_POINT_2, 944 point, 945 module.getName())); 946 } 947 } else { 948 exportPoints.add(point); 949 if (LOG.isDebugEnabled()) { 950 LOG.debug(Messages.get().getBundle().key( 951 Messages.LOG_ADD_EXPORT_POINT_2, 952 point, 953 module.getName())); 954 } 955 } 956 } 957 } 958 m_moduleExportPoints = Collections.unmodifiableSet(exportPoints); 959 } 960 961 /** 962 * Updates the module configuration.<p> 963 */ 964 private void updateModuleConfiguration() { 965 966 OpenCms.writeConfiguration(CmsModuleConfiguration.class); 967 } 968}