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.i18n.CmsMessageContainer; 038import org.opencms.importexport.CmsImportExportManager; 039import org.opencms.lock.CmsLock; 040import org.opencms.lock.CmsLockException; 041import org.opencms.lock.CmsLockFilter; 042import org.opencms.main.CmsException; 043import org.opencms.main.CmsIllegalArgumentException; 044import org.opencms.main.CmsIllegalStateException; 045import org.opencms.main.CmsLog; 046import org.opencms.main.CmsRuntimeException; 047import org.opencms.main.OpenCms; 048import org.opencms.report.I_CmsReport; 049import org.opencms.security.CmsRole; 050import org.opencms.security.CmsRoleViolationException; 051import org.opencms.security.CmsSecurityException; 052import org.opencms.util.CmsStringUtil; 053 054import java.io.File; 055import java.util.ArrayList; 056import java.util.Collections; 057import java.util.HashMap; 058import java.util.HashSet; 059import java.util.Hashtable; 060import java.util.Iterator; 061import java.util.List; 062import java.util.Map; 063import java.util.Set; 064 065import org.apache.commons.logging.Log; 066 067/** 068 * Manages the modules of an OpenCms installation.<p> 069 * 070 * @since 6.0.0 071 */ 072public class CmsModuleManager { 073 074 /** Indicates dependency check for module deletion. */ 075 public static final int DEPENDENCY_MODE_DELETE = 0; 076 077 /** Indicates dependency check for module import. */ 078 public static final int DEPENDENCY_MODE_IMPORT = 1; 079 080 /** The log object for this class. */ 081 private static final Log LOG = CmsLog.getLog(CmsModuleManager.class); 082 083 /** The import/export repository. */ 084 private CmsModuleImportExportRepository m_importExportRepository = new CmsModuleImportExportRepository(); 085 086 /** The list of module export points. */ 087 private Set<CmsExportPoint> m_moduleExportPoints; 088 089 /** The map of configured modules. */ 090 private Map<String, CmsModule> m_modules; 091 092 /** 093 * Basic constructor.<p> 094 * 095 * @param configuredModules the list of configured modules 096 */ 097 public CmsModuleManager(List<CmsModule> configuredModules) { 098 099 if (CmsLog.INIT.isInfoEnabled()) { 100 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_MOD_MANAGER_CREATED_0)); 101 } 102 103 m_modules = new Hashtable<String, CmsModule>(); 104 for (int i = 0; i < configuredModules.size(); i++) { 105 CmsModule module = configuredModules.get(i); 106 m_modules.put(module.getName(), module); 107 if (CmsLog.INIT.isInfoEnabled()) { 108 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_MOD_CONFIGURED_1, module.getName())); 109 } 110 } 111 112 if (CmsLog.INIT.isInfoEnabled()) { 113 CmsLog.INIT.info( 114 Messages.get().getBundle().key(Messages.INIT_NUM_MODS_CONFIGURED_1, 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( 321 Messages.get().container(Messages.ERR_MODULE_DEPENDENCY_CYCLE_1, modules.toString())); 322 } 323 Collections.reverse(retList); 324 return retList; 325 } 326 327 /** 328 * Adds a new module to the module manager.<p> 329 * 330 * @param cms must be initialized with "Admin" permissions 331 * @param module the module to add 332 * 333 * @throws CmsSecurityException if the required permissions are not available (i.e. no "Admin" CmsObject has been provided) 334 * @throws CmsConfigurationException if a module with this name is already configured 335 */ 336 public synchronized void addModule(CmsObject cms, CmsModule module) 337 throws CmsSecurityException, CmsConfigurationException { 338 339 // check the role permissions 340 OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER); 341 342 if (m_modules.containsKey(module.getName())) { 343 // module is currently configured, no create possible 344 throw new CmsConfigurationException( 345 Messages.get().container(Messages.ERR_MODULE_ALREADY_CONFIGURED_1, module.getName())); 346 347 } 348 349 if (LOG.isInfoEnabled()) { 350 LOG.info(Messages.get().getBundle().key(Messages.LOG_CREATE_NEW_MOD_1, module.getName())); 351 } 352 353 // initialize the module 354 module.initialize(cms); 355 356 m_modules.put(module.getName(), module); 357 358 try { 359 I_CmsModuleAction moduleAction = module.getActionInstance(); 360 // handle module action instance if initialized 361 if (moduleAction != null) { 362 moduleAction.moduleUpdate(module); 363 } 364 } catch (Throwable t) { 365 LOG.error(Messages.get().getBundle().key(Messages.LOG_MOD_UPDATE_ERR_1, module.getName()), t); 366 } 367 368 // initialize the export points 369 initModuleExportPoints(); 370 371 // update the configuration 372 updateModuleConfiguration(); 373 } 374 375 /** 376 * Checks if a modules dependencies are fulfilled.<p> 377 * 378 * The possible values for the <code>mode</code> parameter are:<dl> 379 * <dt>{@link #DEPENDENCY_MODE_DELETE}</dt> 380 * <dd>Check for module deleting, i.e. are other modules dependent on the 381 * given module?</dd> 382 * <dt>{@link #DEPENDENCY_MODE_IMPORT}</dt> 383 * <dd>Check for module importing, i.e. are all dependencies required by the given 384 * module available?</dd></dl> 385 * 386 * @param module the module to check the dependencies for 387 * @param mode the dependency check mode 388 * @return a list of dependencies that are not fulfilled, if empty all dependencies are fulfilled 389 */ 390 public List<CmsModuleDependency> checkDependencies(CmsModule module, int mode) { 391 392 List<CmsModuleDependency> result = new ArrayList<CmsModuleDependency>(); 393 394 if (mode == DEPENDENCY_MODE_DELETE) { 395 // delete mode, check if other modules depend on this module 396 Iterator<CmsModule> i = m_modules.values().iterator(); 397 while (i.hasNext()) { 398 CmsModule otherModule = i.next(); 399 CmsModuleDependency dependency = otherModule.checkDependency(module); 400 if (dependency != null) { 401 // dependency found, add to list 402 result.add(new CmsModuleDependency(otherModule.getName(), otherModule.getVersion())); 403 } 404 } 405 406 } else if (mode == DEPENDENCY_MODE_IMPORT) { 407 // import mode, check if all module dependencies are fulfilled 408 Iterator<CmsModule> i = m_modules.values().iterator(); 409 // add all dependencies that must be found 410 result.addAll(module.getDependencies()); 411 while (i.hasNext() && (result.size() > 0)) { 412 CmsModule otherModule = i.next(); 413 CmsModuleDependency dependency = module.checkDependency(otherModule); 414 if (dependency != null) { 415 // dependency found, remove from list 416 result.remove(dependency); 417 } 418 } 419 } else { 420 // invalid mode selected 421 throw new CmsRuntimeException( 422 Messages.get().container(Messages.ERR_CHECK_DEPENDENCY_INVALID_MODE_1, new Integer(mode))); 423 } 424 425 return result; 426 } 427 428 /** 429 * Checks the module selection list for consistency, that means 430 * that if a module is selected, all its dependencies are also selected.<p> 431 * 432 * The module dependencies are get from the installed modules or 433 * from the module manifest.xml files found in the given FRS path.<p> 434 * 435 * @param moduleNames a list of module names 436 * @param rfsAbsPath a RFS absolute path to search for modules, or <code>null</code> to use the installed modules 437 * @param forDeletion there are two modes, one for installation of modules, and one for deletion. 438 * 439 * @throws CmsIllegalArgumentException if the module list is not consistent 440 * @throws CmsConfigurationException if something goes wrong 441 */ 442 public void checkModuleSelectionList(List<String> moduleNames, String rfsAbsPath, boolean forDeletion) 443 throws CmsIllegalArgumentException, CmsConfigurationException { 444 445 Map<String, List<String>> moduleDependencies = buildDepsForAllModules(rfsAbsPath, forDeletion); 446 Iterator<String> itMods = moduleNames.iterator(); 447 while (itMods.hasNext()) { 448 String moduleName = itMods.next(); 449 List<String> dependencies = moduleDependencies.get(moduleName); 450 if (dependencies != null) { 451 List<String> depModules = new ArrayList<String>(dependencies); 452 depModules.removeAll(moduleNames); 453 if (!depModules.isEmpty()) { 454 throw new CmsIllegalArgumentException( 455 Messages.get().container( 456 Messages.ERR_MODULE_SELECTION_INCONSISTENT_2, 457 moduleName, 458 depModules.toString())); 459 } 460 } 461 } 462 } 463 464 /** 465 * Deletes a module from the configuration.<p> 466 * 467 * @param cms must be initialized with "Admin" permissions 468 * @param moduleName the name of the module to delete 469 * @param replace indicates if the module is replaced (true) or finally deleted (false) 470 * @param report the report to print progress messages to 471 * 472 * @throws CmsRoleViolationException if the required module manager role permissions are not available 473 * @throws CmsConfigurationException if a module with this name is not available for deleting 474 * @throws CmsLockException if the module resources can not be locked 475 */ 476 public synchronized void deleteModule( 477 CmsObject cms, 478 String moduleName, 479 boolean replace, 480 boolean preserveLibs, 481 I_CmsReport report) throws CmsRoleViolationException, CmsConfigurationException, CmsLockException { 482 483 // check for module manager role permissions 484 OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER); 485 486 if (!m_modules.containsKey(moduleName)) { 487 // module is not currently configured, no update possible 488 throw new CmsConfigurationException( 489 Messages.get().container(Messages.ERR_MODULE_NOT_CONFIGURED_1, moduleName)); 490 } 491 492 if (LOG.isInfoEnabled()) { 493 LOG.info(Messages.get().getBundle().key(Messages.LOG_DEL_MOD_1, moduleName)); 494 } 495 496 CmsModule module = m_modules.get(moduleName); 497 String importSite = module.getImportSite(); 498 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(importSite)) { 499 CmsObject newCms; 500 try { 501 newCms = OpenCms.initCmsObject(cms); 502 newCms.getRequestContext().setSiteRoot(importSite); 503 cms = newCms; 504 } catch (CmsException e) { 505 LOG.error(e.getLocalizedMessage(), e); 506 } 507 } 508 509 if (!replace) { 510 // module is deleted, not replaced 511 512 // perform dependency check 513 List<CmsModuleDependency> dependencies = checkDependencies(module, DEPENDENCY_MODE_DELETE); 514 if (!dependencies.isEmpty()) { 515 StringBuffer message = new StringBuffer(); 516 Iterator<CmsModuleDependency> it = dependencies.iterator(); 517 while (it.hasNext()) { 518 message.append(" ").append(it.next().getName()).append("\r\n"); 519 } 520 throw new CmsConfigurationException( 521 Messages.get().container(Messages.ERR_MOD_DEPENDENCIES_2, moduleName, message.toString())); 522 } 523 try { 524 I_CmsModuleAction moduleAction = module.getActionInstance(); 525 // handle module action instance if initialized 526 if (moduleAction != null) { 527 moduleAction.moduleUninstall(module); 528 } 529 } catch (Throwable t) { 530 LOG.error(Messages.get().getBundle().key(Messages.LOG_MOD_UNINSTALL_ERR_1, moduleName), t); 531 report.println( 532 Messages.get().container(Messages.LOG_MOD_UNINSTALL_ERR_1, moduleName), 533 I_CmsReport.FORMAT_WARNING); 534 } 535 } 536 537 boolean removeResourceTypes = !module.getResourceTypes().isEmpty(); 538 if (removeResourceTypes) { 539 // mark the resource manager to reinitialize if necessary 540 OpenCms.getWorkplaceManager().removeExplorerTypeSettings(module); 541 } 542 543 CmsProject previousProject = cms.getRequestContext().getCurrentProject(); 544 // try to create a new offline project for deletion 545 CmsProject deleteProject = null; 546 try { 547 // try to read a (leftover) module delete project 548 deleteProject = cms.readProject( 549 Messages.get().getBundle(cms.getRequestContext().getLocale()).key( 550 Messages.GUI_DELETE_MODULE_PROJECT_NAME_1, 551 new Object[] {moduleName})); 552 } catch (CmsException e) { 553 try { 554 // create a Project to delete the module 555 deleteProject = cms.createProject( 556 Messages.get().getBundle(cms.getRequestContext().getLocale()).key( 557 Messages.GUI_DELETE_MODULE_PROJECT_NAME_1, 558 new Object[] {moduleName}), 559 Messages.get().getBundle(cms.getRequestContext().getLocale()).key( 560 Messages.GUI_DELETE_MODULE_PROJECT_DESC_1, 561 new Object[] {moduleName}), 562 OpenCms.getDefaultUsers().getGroupAdministrators(), 563 OpenCms.getDefaultUsers().getGroupAdministrators(), 564 CmsProject.PROJECT_TYPE_TEMPORARY); 565 } catch (CmsException e1) { 566 throw new CmsConfigurationException(e1.getMessageContainer(), e1); 567 } 568 } 569 570 try { 571 cms.getRequestContext().setCurrentProject(deleteProject); 572 573 // check locks 574 List<String> lockedResources = new ArrayList<String>(); 575 CmsLockFilter filter1 = CmsLockFilter.FILTER_ALL.filterNotLockableByUser( 576 cms.getRequestContext().getCurrentUser()); 577 CmsLockFilter filter2 = CmsLockFilter.FILTER_INHERITED; 578 List<String> moduleResources = module.getResources(); 579 for (int iLock = 0; iLock < moduleResources.size(); iLock++) { 580 String resourceName = moduleResources.get(iLock); 581 try { 582 lockedResources.addAll(cms.getLockedResources(resourceName, filter1)); 583 lockedResources.addAll(cms.getLockedResources(resourceName, filter2)); 584 } catch (CmsException e) { 585 // may happen if the resource has already been deleted 586 if (LOG.isDebugEnabled()) { 587 LOG.debug(e.getMessageContainer(), e); 588 } 589 report.println(e.getMessageContainer(), I_CmsReport.FORMAT_WARNING); 590 } 591 } 592 if (!lockedResources.isEmpty()) { 593 CmsMessageContainer msg = Messages.get().container( 594 Messages.ERR_DELETE_MODULE_CHECK_LOCKS_2, 595 moduleName, 596 CmsStringUtil.collectionAsString(lockedResources, ",")); 597 report.addError(msg.key(cms.getRequestContext().getLocale())); 598 report.println(msg); 599 cms.getRequestContext().setCurrentProject(previousProject); 600 try { 601 cms.deleteProject(deleteProject.getUuid()); 602 } catch (CmsException e1) { 603 throw new CmsConfigurationException(e1.getMessageContainer(), e1); 604 } 605 throw new CmsLockException(msg); 606 } 607 } finally { 608 cms.getRequestContext().setCurrentProject(previousProject); 609 } 610 611 // now remove the module 612 module = m_modules.remove(moduleName); 613 614 if (preserveLibs) { 615 // to preserve the module libs, remove the responsible export points, before deleting module resources 616 Set<CmsExportPoint> exportPoints = new HashSet<CmsExportPoint>(m_moduleExportPoints); 617 Iterator<CmsExportPoint> it = exportPoints.iterator(); 618 while (it.hasNext()) { 619 CmsExportPoint point = it.next(); 620 if ((point.getUri().endsWith(module.getName() + "/lib/") 621 || point.getUri().endsWith(module.getName() + "/lib")) 622 && point.getConfiguredDestination().equals("WEB-INF/lib/")) { 623 it.remove(); 624 } 625 } 626 627 m_moduleExportPoints = Collections.unmodifiableSet(exportPoints); 628 } 629 630 try { 631 cms.getRequestContext().setCurrentProject(deleteProject); 632 633 // copy the module resources to the project 634 List<CmsResource> moduleResources = CmsModule.calculateModuleResources(cms, module); 635 for (CmsResource resource : moduleResources) { 636 try { 637 cms.copyResourceToProject(resource); 638 } catch (CmsException e) { 639 // may happen if the resource has already been deleted 640 if (LOG.isDebugEnabled()) { 641 LOG.debug( 642 Messages.get().getBundle().key( 643 Messages.LOG_MOVE_RESOURCE_FAILED_1, 644 cms.getSitePath(resource))); 645 } 646 report.println(e.getMessageContainer(), I_CmsReport.FORMAT_WARNING); 647 } 648 } 649 650 report.print(Messages.get().container(Messages.RPT_DELETE_MODULE_BEGIN_0), I_CmsReport.FORMAT_HEADLINE); 651 report.println( 652 org.opencms.report.Messages.get().container( 653 org.opencms.report.Messages.RPT_ARGUMENT_HTML_ITAG_1, 654 moduleName), 655 I_CmsReport.FORMAT_HEADLINE); 656 657 // move through all module resources and delete them 658 for (CmsResource resource : moduleResources) { 659 String sitePath = cms.getSitePath(resource); 660 try { 661 if (LOG.isDebugEnabled()) { 662 LOG.debug(Messages.get().getBundle().key(Messages.LOG_DEL_MOD_RESOURCE_1, sitePath)); 663 } 664 CmsLock lock = cms.getLock(resource); 665 if (lock.isUnlocked()) { 666 // lock the resource 667 cms.lockResource(resource); 668 } else if (lock.isLockableBy(cms.getRequestContext().getCurrentUser())) { 669 // steal the resource 670 cms.changeLock(resource); 671 } 672 if (!resource.getState().isDeleted()) { 673 // delete the resource 674 cms.deleteResource(sitePath, CmsResource.DELETE_PRESERVE_SIBLINGS); 675 } 676 // update the report 677 report.print(Messages.get().container(Messages.RPT_DELETE_0), I_CmsReport.FORMAT_NOTE); 678 report.println( 679 org.opencms.report.Messages.get().container( 680 org.opencms.report.Messages.RPT_ARGUMENT_1, 681 sitePath)); 682 if (!resource.getState().isNew()) { 683 // unlock the resource (so it gets deleted with next publish) 684 cms.unlockResource(resource); 685 } 686 } catch (CmsException e) { 687 // ignore the exception and delete the next resource 688 LOG.error(Messages.get().getBundle().key(Messages.LOG_DEL_MOD_EXC_1, sitePath), e); 689 report.println(e.getMessageContainer(), I_CmsReport.FORMAT_WARNING); 690 } 691 } 692 693 report.println(Messages.get().container(Messages.RPT_PUBLISH_PROJECT_BEGIN_0), I_CmsReport.FORMAT_HEADLINE); 694 695 // now unlock and publish the project 696 cms.unlockProject(deleteProject.getUuid()); 697 OpenCms.getPublishManager().publishProject(cms, report); 698 OpenCms.getPublishManager().waitWhileRunning(); 699 700 report.println(Messages.get().container(Messages.RPT_PUBLISH_PROJECT_END_0), I_CmsReport.FORMAT_HEADLINE); 701 report.println(Messages.get().container(Messages.RPT_DELETE_MODULE_END_0), I_CmsReport.FORMAT_HEADLINE); 702 } catch (CmsException e) { 703 throw new CmsConfigurationException(e.getMessageContainer(), e); 704 } finally { 705 cms.getRequestContext().setCurrentProject(previousProject); 706 } 707 708 // initialize the export points (removes export points from deleted module) 709 initModuleExportPoints(); 710 711 // update the configuration 712 updateModuleConfiguration(); 713 714 // reinit the manager is necessary 715 if (removeResourceTypes) { 716 OpenCms.getResourceManager().initialize(cms); 717 } 718 } 719 720 /** 721 * Deletes a module from the configuration.<p> 722 * 723 * @param cms must be initialized with "Admin" permissions 724 * @param moduleName the name of the module to delete 725 * @param replace indicates if the module is replaced (true) or finally deleted (false) 726 * @param report the report to print progress messages to 727 * 728 * @throws CmsRoleViolationException if the required module manager role permissions are not available 729 * @throws CmsConfigurationException if a module with this name is not available for deleting 730 * @throws CmsLockException if the module resources can not be locked 731 */ 732 public synchronized void deleteModule(CmsObject cms, String moduleName, boolean replace, I_CmsReport report) 733 throws CmsRoleViolationException, CmsConfigurationException, CmsLockException { 734 735 deleteModule(cms, moduleName, replace, false, report); 736 } 737 738 /** 739 * Returns a list of installed modules.<p> 740 * 741 * @return a list of <code>{@link CmsModule}</code> objects 742 */ 743 public List<CmsModule> getAllInstalledModules() { 744 745 return new ArrayList<CmsModule>(m_modules.values()); 746 } 747 748 /** 749 * Returns the (immutable) list of configured module export points.<p> 750 * 751 * @return the (immutable) list of configured module export points 752 * @see CmsExportPoint 753 */ 754 public Set<CmsExportPoint> getExportPoints() { 755 756 return m_moduleExportPoints; 757 } 758 759 /** 760 * Returns the importExportRepository.<p> 761 * 762 * @return the importExportRepository 763 */ 764 public CmsModuleImportExportRepository getImportExportRepository() { 765 766 return m_importExportRepository; 767 } 768 769 /** 770 * Returns the module with the given module name, 771 * or <code>null</code> if no module with the given name is configured.<p> 772 * 773 * @param name the name of the module to return 774 * @return the module with the given module name 775 */ 776 public CmsModule getModule(String name) { 777 778 return m_modules.get(name); 779 } 780 781 /** 782 * Returns the set of names of all the installed modules.<p> 783 * 784 * @return the set of names of all the installed modules 785 */ 786 public Set<String> getModuleNames() { 787 788 synchronized (m_modules) { 789 return new HashSet<String>(m_modules.keySet()); 790 } 791 } 792 793 /** 794 * Checks if this module manager has a module with the given name installed.<p> 795 * 796 * @param name the name of the module to check 797 * @return true if this module manager has a module with the given name installed 798 */ 799 public boolean hasModule(String name) { 800 801 return m_modules.containsKey(name); 802 } 803 804 /** 805 * Initializes all module instance classes managed in this module manager.<p> 806 * 807 * @param cms an initialized CmsObject with "manage modules" role permissions 808 * @param configurationManager the initialized OpenCms configuration manager 809 * 810 * @throws CmsRoleViolationException if the provided OpenCms context does not have "manage modules" role permissions 811 */ 812 public synchronized void initialize(CmsObject cms, CmsConfigurationManager configurationManager) 813 throws CmsRoleViolationException { 814 815 if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_1_CORE_OBJECT) { 816 // certain test cases won't have an OpenCms context 817 OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER); 818 } 819 820 Iterator<String> it; 821 int count = 0; 822 it = m_modules.keySet().iterator(); 823 while (it.hasNext()) { 824 // get the module description 825 CmsModule module = m_modules.get(it.next()); 826 827 if (module.getActionClass() != null) { 828 // create module instance class 829 I_CmsModuleAction moduleAction = module.getActionInstance(); 830 if (module.getActionClass() != null) { 831 try { 832 moduleAction = (I_CmsModuleAction)Class.forName(module.getActionClass()).newInstance(); 833 } catch (Exception e) { 834 CmsLog.INIT.info( 835 Messages.get().getBundle().key(Messages.INIT_CREATE_INSTANCE_FAILED_1, module.getName()), 836 e); 837 } 838 } 839 if (moduleAction != null) { 840 count++; 841 module.setActionInstance(moduleAction); 842 if (CmsLog.INIT.isInfoEnabled()) { 843 CmsLog.INIT.info( 844 Messages.get().getBundle().key( 845 Messages.INIT_INITIALIZE_MOD_CLASS_1, 846 moduleAction.getClass().getName())); 847 } 848 try { 849 // create a copy of the adminCms so that each module instance does have 850 // it's own context, a shared context might introduce side - effects 851 CmsObject adminCmsCopy = OpenCms.initCmsObject(cms); 852 // initialize the module 853 moduleAction.initialize(adminCmsCopy, configurationManager, module); 854 } catch (Throwable t) { 855 LOG.error( 856 Messages.get().getBundle().key( 857 Messages.LOG_INSTANCE_INIT_ERR_1, 858 moduleAction.getClass().getName()), 859 t); 860 } 861 } 862 } 863 } 864 865 // initialize the export points 866 initModuleExportPoints(); 867 m_importExportRepository.initialize(cms); 868 869 if (CmsLog.INIT.isInfoEnabled()) { 870 CmsLog.INIT.info( 871 Messages.get().getBundle().key(Messages.INIT_NUM_CLASSES_INITIALIZED_1, new Integer(count))); 872 } 873 } 874 875 /** 876 * Shuts down all module instance classes managed in this module manager.<p> 877 */ 878 public synchronized void shutDown() { 879 880 int count = 0; 881 Iterator<String> it = getModuleNames().iterator(); 882 while (it.hasNext()) { 883 String moduleName = it.next(); 884 // get the module 885 CmsModule module = m_modules.get(moduleName); 886 if (module == null) { 887 continue; 888 } 889 // get the module action instance 890 I_CmsModuleAction moduleAction = module.getActionInstance(); 891 if (moduleAction == null) { 892 continue; 893 } 894 895 count++; 896 if (CmsLog.INIT.isInfoEnabled()) { 897 CmsLog.INIT.info( 898 Messages.get().getBundle().key( 899 Messages.INIT_SHUTDOWN_MOD_CLASS_1, 900 moduleAction.getClass().getName())); 901 } 902 try { 903 // shut down the module 904 moduleAction.shutDown(module); 905 } catch (Throwable t) { 906 LOG.error( 907 Messages.get().getBundle().key( 908 Messages.LOG_INSTANCE_SHUTDOWN_ERR_1, 909 moduleAction.getClass().getName()), 910 t); 911 } 912 } 913 914 if (CmsLog.INIT.isInfoEnabled()) { 915 CmsLog.INIT.info( 916 Messages.get().getBundle().key(Messages.INIT_SHUTDOWN_NUM_MOD_CLASSES_1, new Integer(count))); 917 } 918 919 if (CmsLog.INIT.isInfoEnabled()) { 920 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_SHUTDOWN_1, this.getClass().getName())); 921 } 922 } 923 924 /** 925 * Updates a already configured module with new values.<p> 926 * 927 * @param cms must be initialized with "Admin" permissions 928 * @param module the module to update 929 * 930 * @throws CmsRoleViolationException if the required module manager role permissions are not available 931 * @throws CmsConfigurationException if a module with this name is not available for updating 932 */ 933 public synchronized void updateModule(CmsObject cms, CmsModule module) 934 throws CmsRoleViolationException, CmsConfigurationException { 935 936 // check for module manager role permissions 937 OpenCms.getRoleManager().checkRole(cms, CmsRole.DATABASE_MANAGER); 938 939 CmsModule oldModule = m_modules.get(module.getName()); 940 941 if (oldModule == null) { 942 // module is not currently configured, no update possible 943 throw new CmsConfigurationException(Messages.get().container(Messages.ERR_OLD_MOD_ERR_1, module.getName())); 944 } 945 946 if (LOG.isInfoEnabled()) { 947 LOG.info(Messages.get().getBundle().key(Messages.LOG_MOD_UPDATE_1, module.getName())); 948 } 949 950 // indicate that the version number was recently updated 951 module.getVersion().setUpdated(true); 952 953 // initialize (freeze) the module 954 module.initialize(cms); 955 956 // replace old version of module with new version 957 m_modules.put(module.getName(), module); 958 959 try { 960 I_CmsModuleAction moduleAction = oldModule.getActionInstance(); 961 // handle module action instance if initialized 962 if (moduleAction != null) { 963 moduleAction.moduleUpdate(module); 964 // set the old action instance 965 // the new action instance will be used after a system restart 966 module.setActionInstance(moduleAction); 967 } 968 } catch (Throwable t) { 969 LOG.error(Messages.get().getBundle().key(Messages.LOG_INSTANCE_UPDATE_ERR_1, module.getName()), t); 970 } 971 972 // initialize the export points 973 initModuleExportPoints(); 974 975 // update the configuration 976 updateModuleConfiguration(); 977 } 978 979 /** 980 * Initializes the list of export points from all configured modules.<p> 981 */ 982 private synchronized void initModuleExportPoints() { 983 984 Set<CmsExportPoint> exportPoints = new HashSet<CmsExportPoint>(); 985 Iterator<CmsModule> i = m_modules.values().iterator(); 986 while (i.hasNext()) { 987 CmsModule module = i.next(); 988 List<CmsExportPoint> moduleExportPoints = module.getExportPoints(); 989 for (int j = 0; j < moduleExportPoints.size(); j++) { 990 CmsExportPoint point = moduleExportPoints.get(j); 991 if (exportPoints.contains(point)) { 992 if (LOG.isWarnEnabled()) { 993 LOG.warn( 994 Messages.get().getBundle().key( 995 Messages.LOG_DUPLICATE_EXPORT_POINT_2, 996 point, 997 module.getName())); 998 } 999 } else { 1000 exportPoints.add(point); 1001 if (LOG.isDebugEnabled()) { 1002 LOG.debug( 1003 Messages.get().getBundle().key(Messages.LOG_ADD_EXPORT_POINT_2, point, module.getName())); 1004 } 1005 } 1006 } 1007 } 1008 m_moduleExportPoints = Collections.unmodifiableSet(exportPoints); 1009 } 1010 1011 /** 1012 * Updates the module configuration.<p> 1013 */ 1014 private void updateModuleConfiguration() { 1015 1016 OpenCms.writeConfiguration(CmsModuleConfiguration.class); 1017 } 1018}