Java tutorial
/** * ========================================================================================== * = JAHIA'S DUAL LICENSING - IMPORTANT INFORMATION = * ========================================================================================== * * http://www.jahia.com * * Copyright (C) 2002-2016 Jahia Solutions Group SA. All rights reserved. * * THIS FILE IS AVAILABLE UNDER TWO DIFFERENT LICENSES: * 1/GPL OR 2/JSEL * * 1/ GPL * ================================================================================== * * IF YOU DECIDE TO CHOOSE THE GPL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS: * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * * 2/ JSEL - Commercial and Supported Versions of the program * =================================================================================== * * IF YOU DECIDE TO CHOOSE THE JSEL LICENSE, YOU MUST COMPLY WITH THE FOLLOWING TERMS: * * Alternatively, commercial and supported versions of the program - also known as * Enterprise Distributions - must be used in accordance with the terms and conditions * contained in a separate written agreement between you and Jahia Solutions Group SA. * * If you are unsure which license is appropriate for your use, * please contact the sales department at sales@jahia.com. */ package org.jahia.modules.modulemanager.flow; import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; import org.dom4j.DocumentException; import org.jahia.bin.Jahia; import org.jahia.commons.Version; import org.jahia.data.templates.JahiaTemplatesPackage; import org.jahia.data.templates.ModuleState; import org.jahia.data.templates.ModulesPackage; import org.jahia.exceptions.JahiaException; import org.jahia.modules.modulemanager.forge.ForgeService; import org.jahia.modules.modulemanager.forge.Module; import org.jahia.osgi.BundleUtils; import org.jahia.osgi.FrameworkService; import org.jahia.security.license.LicenseCheckerService; import org.jahia.services.content.JCRNodeWrapper; import org.jahia.services.content.JCRSessionWrapper; import org.jahia.services.content.decorator.JCRSiteNode; import org.jahia.services.content.nodetypes.ExtendedNodeType; import org.jahia.services.content.nodetypes.NodeTypeRegistry; import org.jahia.services.modulemanager.BundleInfo; import org.jahia.services.modulemanager.Constants; import org.jahia.services.modulemanager.ModuleManagementException; import org.jahia.services.modulemanager.ModuleManager; import org.jahia.services.render.RenderContext; import org.jahia.services.sites.JahiaSitesService; import org.jahia.services.templates.*; import org.jahia.settings.SettingsBean; import org.jahia.utils.i18n.Messages; import org.osgi.framework.Bundle; import org.osgi.framework.BundleException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.binding.message.MessageBuilder; import org.springframework.binding.message.MessageContext; import org.springframework.binding.message.MessageResolver; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.core.io.FileSystemResource; import org.springframework.web.multipart.MultipartFile; import org.springframework.webflow.core.collection.MutableAttributeMap; import org.springframework.webflow.execution.RequestContext; import javax.jcr.RepositoryException; import javax.jcr.nodetype.NodeTypeIterator; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.*; import java.util.Map.Entry; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; /** * WebFlow handler for managing modules. * * @author rincevent */ public class ModuleManagementFlowHandler implements Serializable { private static final long serialVersionUID = -4195379181264451784L; private static final Logger logger = LoggerFactory.getLogger(ModuleManagementFlowHandler.class); @Autowired private transient JahiaTemplateManagerService templateManagerService; @Autowired private transient JahiaSitesService sitesService; @Autowired private transient ForgeService forgeService; @Autowired private transient ModuleManager moduleManager; @Autowired private transient TemplatePackageRegistry templatePackageRegistry; private String moduleName; public boolean isInModule(RenderContext renderContext) { try { if (renderContext.getMainResource().getNode().isNodeType("jnt:module")) { moduleName = renderContext.getMainResource().getNode().getName(); return true; } } catch (RepositoryException e) { } return false; } public boolean isStudio(RenderContext renderContext) { return renderContext.getEditModeConfigName().equals("studiomode") || renderContext.getEditModeConfigName().equals("studiovisualmode"); } public ModuleFile initModuleFile() { return new ModuleFile(); } public boolean installModule(String forgeId, String url, boolean autoStart, MessageContext context) { File file = null; try { file = forgeService.downloadModuleFromForge(forgeId, url); installBundles(file, context, url, false, autoStart); return true; } catch (Exception e) { context.addMessage(new MessageBuilder().source("moduleFile") .code("serverSettings.manageModules.install.failed").arg(e.getMessage()).error().build()); logger.error(e.getMessage(), e); } finally { FileUtils.deleteQuietly(file); } return false; } public boolean uploadModule(MultipartFile moduleFile, MessageContext context, boolean forceUpdate, boolean autoStart) { if (moduleFile == null) { context.addMessage(new MessageBuilder().error().source("moduleFile") .code("serverSettings.manageModules.install.moduleFileRequired").build()); return false; } String originalFilename = moduleFile.getOriginalFilename(); if (!FilenameUtils.isExtension(StringUtils.lowerCase(originalFilename), "jar")) { context.addMessage(new MessageBuilder().error().source("moduleFile") .code("serverSettings.manageModules.install.wrongFormat").build()); return false; } File file = null; try { file = File.createTempFile("module-", "." + StringUtils.substringAfterLast(originalFilename, ".")); moduleFile.transferTo(file); installBundles(file, context, originalFilename, forceUpdate, autoStart); return true; } catch (Exception e) { context.addMessage(new MessageBuilder().source("moduleFile") .code("serverSettings.manageModules.install.failed").arg(e.getMessage()).error().build()); logger.error(e.getMessage(), e); } finally { FileUtils.deleteQuietly(file); } return false; } private void installBundles(File file, MessageContext context, String originalFilename, boolean forceUpdate, boolean autoStart) throws IOException, BundleException { JarFile jarFile = new JarFile(file); try { Attributes manifestAttributes = jarFile.getManifest().getMainAttributes(); String jahiaRequiredVersion = manifestAttributes.getValue(Constants.ATTR_NAME_JAHIA_REQUIRED_VERSION); if (StringUtils.isEmpty(jahiaRequiredVersion)) { context.addMessage(new MessageBuilder().source("moduleFile") .code("serverSettings.manageModules.install.required.version.missing.error").error() .build()); return; } if (new Version(jahiaRequiredVersion).compareTo(new Version(Jahia.VERSION)) > 0) { context.addMessage(new MessageBuilder().source("moduleFile") .code("serverSettings.manageModules.install.required.version.error") .args(jahiaRequiredVersion, Jahia.VERSION).error().build()); return; } if (manifestAttributes.getValue(Constants.ATTR_NAME_JAHIA_PACKAGE_NAME) != null) { handlePackage(jarFile, manifestAttributes, originalFilename, forceUpdate, autoStart, context); } else { ModuleInstallationResult installationResult = installModule(file, context, null, null, forceUpdate, autoStart); if (installationResult != null) { addModuleInstallationMessage(installationResult, context); } } } finally { jarFile.close(); } } private void handlePackage(JarFile jarFile, Attributes manifestAttributes, String originalFilename, boolean forceUpdate, boolean autoStart, MessageContext context) throws IOException, BundleException { // check package name validity String jahiaPackageName = manifestAttributes.getValue(Constants.ATTR_NAME_JAHIA_PACKAGE_NAME); if (jahiaPackageName != null && jahiaPackageName.trim().length() == 0) { context.addMessage(new MessageBuilder().source("moduleFile") .code("serverSettings.manageModules.install.package.name.error").error().build()); return; } //Check license String licenseFeature = manifestAttributes.getValue(Constants.ATTR_NAME_JAHIA_PACKAGE_LICENSE); if (licenseFeature != null && !LicenseCheckerService.Stub.isAllowed(licenseFeature)) { context.addMessage(new MessageBuilder().source("moduleFile") .code("serverSettings.manageModules.install.package.missing.license") .args(originalFilename, licenseFeature).build()); return; } ModulesPackage pack = ModulesPackage.create(jarFile); try { List<String> providedBundles = new ArrayList<String>(pack.getModules().keySet()); Map<Bundle, MessageResolver> collectedResolutionErrors = new LinkedHashMap<>(); List<ModuleInstallationResult> installationResults = new LinkedList<>(); for (ModulesPackage.PackagedModule entry : pack.getModules().values()) { ModuleInstallationResult installationResult = installModule(entry.getModuleFile(), context, providedBundles, collectedResolutionErrors, forceUpdate, autoStart); if (installationResult != null) { installationResults.add(installationResult); } } if (!collectedResolutionErrors.isEmpty()) { // double-check the resolution issues after all bundles were installed for (Iterator<Map.Entry<Bundle, MessageResolver>> resolutionErrorIterator = collectedResolutionErrors .entrySet().iterator(); resolutionErrorIterator.hasNext();) { Entry<Bundle, MessageResolver> resolutionErrorEntry = resolutionErrorIterator.next(); if (resolutionErrorEntry.getKey().getState() >= Bundle.RESOLVED) { // the bundle is successfully resolved now resolutionErrorIterator.remove(); } else { // the bundle still has resolution issue -> add error message context.addMessage(resolutionErrorEntry.getValue()); } } } // add info about installed bundles for (ModuleInstallationResult installationResult : installationResults) { if (!collectedResolutionErrors.containsKey(installationResult)) { addModuleInstallationMessage(installationResult, context); } } } finally { // delete temporary created files of the package for (ModulesPackage.PackagedModule entry : pack.getModules().values()) { FileUtils.deleteQuietly(entry.getModuleFile()); } } } /** * Add module installation result to the message context * @param installationResult module installation result * @param context message context * @throws BundleException thrown exception */ private void addModuleInstallationMessage(ModuleInstallationResult installationResult, MessageContext context) throws BundleException { Bundle bundle = installationResult.getBundle(); context.addMessage(new MessageBuilder().source("moduleFile").code(installationResult.getMessageCode()) .args(bundle.getSymbolicName(), bundle.getVersion().toString()).build()); } private ModuleInstallationResult installModule(File file, MessageContext context, List<String> providedBundles, Map<Bundle, MessageResolver> collectedResolutionErrors, boolean forceUpdate, boolean autoStart) throws IOException, BundleException { JarFile jarFile = new JarFile(file); try { Manifest manifest = jarFile.getManifest(); String symbolicName = manifest.getMainAttributes().getValue(Constants.ATTR_NAME_BUNDLE_SYMBOLIC_NAME); if (symbolicName == null) { symbolicName = manifest.getMainAttributes().getValue(Constants.ATTR_NAME_ROOT_FOLDER); } String version = manifest.getMainAttributes().getValue(Constants.ATTR_NAME_IMPL_VERSION); String groupId = manifest.getMainAttributes().getValue(Constants.ATTR_NAME_GROUP_ID); if (templateManagerService.differentModuleWithSameIdExists(symbolicName, groupId)) { context.addMessage(new MessageBuilder().source("moduleFile") .code("serverSettings.manageModules.install.moduleWithSameIdExists").arg(symbolicName) .error().build()); return null; } ModuleVersion moduleVersion = new ModuleVersion(version); Set<ModuleVersion> allVersions = templatePackageRegistry.getAvailableVersionsForModule(symbolicName); if (!forceUpdate) { if (!moduleVersion.isSnapshot()) { if (allVersions.contains(moduleVersion)) { context.addMessage(new MessageBuilder().source("moduleExists") .code("serverSettings.manageModules.install.moduleExists") .args(symbolicName, version).build()); return null; } } } String successMessage = (autoStart ? "serverSettings.manageModules.install.uploadedAndStarted" : "serverSettings.manageModules.install.uploaded"); String resolutionError = null; boolean shouldAutoStart = autoStart; if (autoStart && !Boolean.valueOf(SettingsBean.getInstance().getPropertiesFile() .getProperty("org.jahia.modules.autoStartOlderVersions"))) { // verify that a newer version is not active already JahiaTemplatesPackage currentActivePackage = templateManagerService.getTemplatePackageRegistry() .lookupById(symbolicName); ModuleVersion currentVersion = currentActivePackage != null ? currentActivePackage.getVersion() : null; if (currentActivePackage != null && moduleVersion.compareTo(currentVersion) < 0) { // we do not start the uploaded older version automatically shouldAutoStart = false; successMessage = "serverSettings.manageModules.install.uploadedNotStartedDueToNewerVersionActive"; } } try { moduleManager.install(new FileSystemResource(file), null, shouldAutoStart); } catch (ModuleManagementException e) { Throwable cause = e.getCause(); if (cause != null && cause instanceof BundleException && ((BundleException) cause).getType() == BundleException.RESOLVE_ERROR) { // we are dealing with unresolved dependencies here resolutionError = cause.getMessage(); } else { // re-throw the exception throw e; } } Bundle bundle = BundleUtils.getBundle(symbolicName, version); JahiaTemplatesPackage module = BundleUtils.getModule(bundle); if (module.getState().getState() == ModuleState.State.WAITING_TO_BE_IMPORTED) { // This only can happen in a cluster. successMessage = "serverSettings.manageModules.install.waitingToBeImported"; } if (resolutionError != null) { List<String> missingDeps = getMissingDependenciesFrom(module.getDepends(), providedBundles); if (!missingDeps.isEmpty()) { createMessageForMissingDependencies(context, missingDeps); } else { MessageResolver errorMessage = new MessageBuilder().source("moduleFile") .code("serverSettings.manageModules.resolutionError").arg(resolutionError).error() .build(); if (collectedResolutionErrors != null) { // we just collect the resolution errors for multiple module to double-check them after all modules are installed collectedResolutionErrors.put(bundle, errorMessage); return new ModuleInstallationResult(bundle, successMessage); } else { // we directly add error message context.addMessage(errorMessage); } } } else if (module.getState().getState() == ModuleState.State.ERROR_WITH_DEFINITIONS) { context.addMessage(new MessageBuilder().source("moduleFile") .code("serverSettings.manageModules.errorWithDefinitions") .arg(((Exception) module.getState().getDetails()).getCause().getMessage()).error().build()); } else { return new ModuleInstallationResult(bundle, successMessage); } } finally { IOUtils.closeQuietly(jarFile); } return null; } private void createMessageForMissingDependencies(MessageContext context, List<String> missingDeps) { logger.warn("Missing dependencies : " + missingDeps); context.addMessage(new MessageBuilder().source("moduleFile") .code("serverSettings.manageModules.install.missingDependencies") .arg(StringUtils.join(missingDeps, ",")).error().build()); } private List<String> getMissingDependenciesFrom(List<String> deps, List<String> providedDependencies) { List<String> missingDeps = new ArrayList<String>(deps.size()); for (String dep : deps) { if (providedDependencies != null && providedDependencies.indexOf(dep) != -1) { // we have the dependency continue; } if (templateManagerService.getTemplatePackageById(dep) == null && templateManagerService.getTemplatePackage(dep) == null) { missingDeps.add(dep); } } return missingDeps; } public void loadModuleInformation(RequestContext context) { String selectedModuleName = moduleName != null ? moduleName : (String) context.getFlowScope().get("selectedModule"); Map<ModuleVersion, JahiaTemplatesPackage> selectedModule = templateManagerService .getTemplatePackageRegistry().getAllModuleVersions().get(selectedModuleName); if (selectedModule != null) { if (selectedModule.size() > 1) { boolean foundActiveVersion = false; for (Map.Entry<ModuleVersion, JahiaTemplatesPackage> entry : selectedModule.entrySet()) { JahiaTemplatesPackage value = entry.getValue(); if (value.isActiveVersion()) { foundActiveVersion = true; populateActiveVersion(context, value); } } if (!foundActiveVersion) { // there is no active version take information from most recent installed version LinkedList<ModuleVersion> sortedVersions = new LinkedList<ModuleVersion>( selectedModule.keySet()); Collections.sort(sortedVersions); populateActiveVersion(context, selectedModule.get(sortedVersions.getFirst())); } } else { populateActiveVersion(context, selectedModule.values().iterator().next()); } context.getRequestScope().put("otherVersions", selectedModule); } else { // module is not yet parsed probably because it depends on unavailable modules so look for it in module states final Map<Bundle, ModuleState> moduleStates = templateManagerService.getModuleStates(); for (Bundle bundle : moduleStates.keySet()) { JahiaTemplatesPackage module = BundleUtils.getModule(bundle); if (module.getId().equals(selectedModuleName)) { populateActiveVersion(context, module); final List<String> missing = getMissingDependenciesFrom(module.getDepends(), null); if (!missing.isEmpty()) { createMessageForMissingDependencies(context.getMessageContext(), missing); } break; } } } populateSitesInformation(context); Set<String> systemSiteRequiredModules = getSystemSiteRequiredModules(); context.getRequestScope().put("systemSiteRequiredModules", systemSiteRequiredModules); // Get list of definitions NodeTypeIterator nodeTypes = NodeTypeRegistry.getInstance().getNodeTypes(selectedModuleName); Map<String, Boolean> booleanMap = new TreeMap<String, Boolean>(); while (nodeTypes.hasNext()) { ExtendedNodeType nodeType = (ExtendedNodeType) nodeTypes.next(); booleanMap.put(nodeType.getLabel(LocaleContextHolder.getLocale()), nodeType.isNodeType("jmix:droppableContent")); } context.getRequestScope().put("nodeTypes", booleanMap); } public void populateSitesInformation(RequestContext context) { //populate information about sites List<String> siteKeys = new ArrayList<String>(); Map<String, List<String>> directSiteDep = new HashMap<String, List<String>>(); Map<String, List<String>> templateSiteDep = new HashMap<String, List<String>>(); Map<String, List<String>> transitiveSiteDep = new HashMap<String, List<String>>(); try { List<JCRSiteNode> sites = sitesService.getSitesNodeList(); for (JCRSiteNode site : sites) { siteKeys.add(site.getSiteKey()); List<JahiaTemplatesPackage> directDependencies = templateManagerService .getInstalledModulesForSite(site.getSiteKey(), false, true, false); for (JahiaTemplatesPackage directDependency : directDependencies) { if (!directSiteDep.containsKey(directDependency.getId())) { directSiteDep.put(directDependency.getId(), new ArrayList<String>()); } directSiteDep.get(directDependency.getId()).add(site.getSiteKey()); } if (site.getTemplatePackage() != null) { if (!templateSiteDep.containsKey(site.getTemplatePackage().getId())) { templateSiteDep.put(site.getTemplatePackage().getId(), new ArrayList<String>()); } templateSiteDep.get(site.getTemplatePackage().getId()).add(site.getSiteKey()); } List<JahiaTemplatesPackage> transitiveDependencies = templateManagerService .getInstalledModulesForSite(site.getSiteKey(), true, false, true); for (JahiaTemplatesPackage transitiveDependency : transitiveDependencies) { if (!transitiveSiteDep.containsKey(transitiveDependency.getId())) { transitiveSiteDep.put(transitiveDependency.getId(), new ArrayList<String>()); } transitiveSiteDep.get(transitiveDependency.getId()).add(site.getSiteKey()); } } } catch (JahiaException e) { logger.error(e.getMessage(), e); } catch (RepositoryException e) { logger.error(e.getMessage(), e); } context.getRequestScope().put("sites", siteKeys); context.getRequestScope().put("sitesDirect", directSiteDep); context.getRequestScope().put("sitesTemplates", templateSiteDep); context.getRequestScope().put("sitesTransitive", transitiveSiteDep); populateModuleVersionStateInfo(context, directSiteDep, templateSiteDep, transitiveSiteDep); populateModulesWithNodetypesInfo(context); } /** * Returns a map, keyed by the module name, with the sorted map (by version ascending) of {@link JahiaTemplatesPackage} objects. * * @return a map, keyed by the module name, with the sorted map (by version ascending) of {@link JahiaTemplatesPackage} objects */ public Map<String, SortedMap<ModuleVersion, JahiaTemplatesPackage>> getAllModuleVersions() { Map<String, SortedMap<ModuleVersion, JahiaTemplatesPackage>> result = new TreeMap<String, SortedMap<ModuleVersion, JahiaTemplatesPackage>>(); Map<Bundle, ModuleState> moduleStatesByBundle = templateManagerService.getModuleStates(); for (Bundle bundle : moduleStatesByBundle.keySet()) { JahiaTemplatesPackage module = BundleUtils.getModule(bundle); SortedMap<ModuleVersion, JahiaTemplatesPackage> modulesByVersion = result.get(module.getId()); if (modulesByVersion == null) { modulesByVersion = new TreeMap<ModuleVersion, JahiaTemplatesPackage>(); result.put(module.getId(), modulesByVersion); } modulesByVersion.put(module.getVersion(), module); } return result; } /** * Returns a map, keyed by the module name, with all available module updates. * * @return a map, keyed by the module name, with all available module updates */ public Map<String, Module> getAvailableUpdates() { Map<String, Module> availableUpdate = new HashMap<String, Module>(); Map<String, SortedMap<ModuleVersion, JahiaTemplatesPackage>> moduleStates = templateManagerService .getTemplatePackageRegistry().getAllModuleVersions(); for (String key : moduleStates.keySet()) { SortedMap<ModuleVersion, JahiaTemplatesPackage> moduleVersions = moduleStates.get(key); Module forgeModule = forgeService.findModule(key, moduleVersions.get(moduleVersions.firstKey()).getGroupId()); if (forgeModule != null) { ModuleVersion forgeVersion = new ModuleVersion(forgeModule.getVersion()); if (!isSameOrNewerVersionPresent(key, forgeVersion)) { availableUpdate.put(key, forgeModule); } } } return availableUpdate; } private boolean isSameOrNewerVersionPresent(String symbolicName, ModuleVersion forgeVersion) { for (Bundle bundle : FrameworkService.getBundleContext().getBundles()) { String n = bundle.getSymbolicName(); if (StringUtils.equals(n, symbolicName) && forgeVersion.compareTo(new ModuleVersion(BundleUtils.getModuleVersion(bundle))) <= 0) { // we've found either same or a new version present return true; } } return false; } private void populateModuleVersionStateInfo(RequestContext context, Map<String, List<String>> directSiteDep, Map<String, List<String>> templateSiteDep, Map<String, List<String>> transitiveSiteDep) { Map<String, Map<ModuleVersion, ModuleVersionState>> states = new TreeMap<String, Map<ModuleVersion, ModuleVersionState>>(); Map<String, String> errors = new TreeMap<String, String>(); Set<String> systemSiteRequiredModules = getSystemSiteRequiredModules(); context.getRequestScope().put("systemSiteRequiredModules", systemSiteRequiredModules); for (Map.Entry<String, SortedMap<ModuleVersion, JahiaTemplatesPackage>> entry : getAllModuleVersions() .entrySet()) { Map<ModuleVersion, ModuleVersionState> moduleVersions = states.get(entry.getKey()); if (moduleVersions == null) { moduleVersions = new TreeMap<ModuleVersion, ModuleVersionState>(); states.put(entry.getKey(), moduleVersions); } if (BundleUtils.getContextStartException(entry.getKey()) != null) { errors.put(entry.getKey(), BundleUtils.getContextStartException(entry.getKey()).getLocalizedMessage()); } for (Map.Entry<ModuleVersion, JahiaTemplatesPackage> moduleVersionEntry : entry.getValue().entrySet()) { ModuleVersionState state = getModuleVersionState(context, moduleVersionEntry.getKey(), moduleVersionEntry.getValue(), entry.getValue().size() > 1, directSiteDep, templateSiteDep, transitiveSiteDep, systemSiteRequiredModules, errors); moduleVersions.put(moduleVersionEntry.getKey(), state); } } context.getRequestScope().put("moduleStates", states); context.getRequestScope().put("errors", errors); } private void populateModulesWithNodetypesInfo(RequestContext context) { Set<String> modulesWithNodetypes = new HashSet<String>(); NodeTypeRegistry nodeTypeRegistry = NodeTypeRegistry.getInstance(); for (String moduleId : getAllModuleVersions().keySet()) { if (nodeTypeRegistry.getNodeTypes(moduleId).hasNext()) { modulesWithNodetypes.add(moduleId); } } context.getRequestScope().put("modulesWithNodetypes", modulesWithNodetypes); } private ModuleVersionState getModuleVersionState(RequestContext context, ModuleVersion moduleVersion, JahiaTemplatesPackage pkg, boolean multipleVersionsOfModuleInstalled, Map<String, List<String>> directSiteDep, Map<String, List<String>> templateSiteDep, Map<String, List<String>> transitiveSiteDep, Set<String> systemSiteRequiredModules, Map<String, String> errors) { ModuleVersionState state = new ModuleVersionState(); Map<String, JahiaTemplatesPackage> registeredModules = templateManagerService.getTemplatePackageRegistry() .getRegisteredModules(); String moduleId = pkg.getId(); // check for unresolved dependencies if (!pkg.getDepends().isEmpty()) { for (String dependency : pkg.getDepends()) { if (templateManagerService.getTemplatePackageRegistry().getAvailableVersionsForModule(dependency) .isEmpty()) { state.getUnresolvedDependencies().add(dependency); } } } List<JahiaTemplatesPackage> dependantModules = templateManagerService.getTemplatePackageRegistry() .getDependantModules(pkg); for (JahiaTemplatesPackage dependant : dependantModules) { state.getDependencies().add(dependant.getId()); } // check site usage and system dependency if (templateSiteDep.containsKey(moduleId)) { state.getUsedInSites().addAll(templateSiteDep.get(moduleId)); } if (directSiteDep.containsKey(moduleId)) { state.getUsedInSites().addAll(directSiteDep.get(moduleId)); } if (transitiveSiteDep.containsKey(moduleId)) { state.getUsedInSites().addAll(transitiveSiteDep.get(moduleId)); } state.setSystemDependency(systemSiteRequiredModules.contains(moduleId)); Object details = pkg.getState().getDetails(); if (registeredModules.containsKey(moduleId) && registeredModules.get(moduleId).getVersion().equals(moduleVersion) && pkg.getState().getState() == ModuleState.State.STARTED) { // this is the currently active version of a module state.setCanBeStopped(!state.isSystemDependency()); if (details != null) { String dspMsg = Messages.getWithArgs("resources.ModuleManager", "serverSettings.manageModules.startError", LocaleContextHolder.getLocale(), details.toString()); addError(moduleVersion, errors, moduleId, dspMsg); } } else { // not currently active version of a module if (pkg.getState().getState() == ModuleState.State.INCOMPATIBLE_VERSION) { state.setCanBeStarted(false); state.setInstalled(false); state.setCanBeUninstalled(state.getUsedInSites().isEmpty() || multipleVersionsOfModuleInstalled); if (details != null) { String dspMsg = Messages.getWithArgs("resources.ModuleManager", "serverSettings.manageModules.incompatibleVersion", LocaleContextHolder.getLocale(), details.toString()); addError(moduleVersion, errors, moduleId, dspMsg); } } else if (pkg.getState().getState() == ModuleState.State.ERROR_WITH_DEFINITIONS) { state.setCanBeStarted(false); state.setInstalled(false); state.setCanBeUninstalled(state.getUsedInSites().isEmpty() || multipleVersionsOfModuleInstalled); state.setCanBeReinstalled(true); String dspMsg = Messages.getWithArgs("resources.ModuleManager", "serverSettings.manageModules.errorWithDefinitions", LocaleContextHolder.getLocale(), ((Exception) details).getCause().getMessage()); addError(moduleVersion, errors, moduleId, dspMsg); } else if (pkg.getState().getState() == ModuleState.State.WAITING_TO_BE_IMPORTED) { state.setCanBeStarted(false); state.setCanBeUninstalled(state.getUsedInSites().isEmpty() || multipleVersionsOfModuleInstalled); state.setCanBeReinstalled(true); String dspMsg = Messages.getWithArgs("resources.ModuleManager", "serverSettings.manageModules.waitingToBeImported", LocaleContextHolder.getLocale()); addError(moduleVersion, errors, moduleId, dspMsg); } else if (state.getUnresolvedDependencies().isEmpty()) { // no unresolved dependencies -> can start module version state.setCanBeStarted(true); // if the module is not used in sites or this is not the only version of a module installed -> allow to uninstall it state.setCanBeUninstalled(state.getUsedInSites().isEmpty() || multipleVersionsOfModuleInstalled); } else { state.setCanBeUninstalled(!state.isSystemDependency()); } } return state; } private void addError(ModuleVersion moduleVersion, Map<String, String> errors, String moduleId, String dspMsg) { if (errors.containsKey(moduleId)) { errors.put(moduleId, errors.get(moduleId) + "\n\n" + moduleVersion + " : " + dspMsg); } else { errors.put(moduleId, moduleVersion + " : " + dspMsg); } } private void populateActiveVersion(RequestContext context, JahiaTemplatesPackage value) { context.getRequestScope().put("activeVersion", value); Map<String, String> bundleInfo = new HashMap<String, String>(); Dictionary<String, String> dictionary = value.getBundle().getHeaders(); Enumeration<String> keys = dictionary.keys(); while (keys.hasMoreElements()) { String s = keys.nextElement(); bundleInfo.put(s, dictionary.get(s)); } context.getRequestScope().put("bundleInfo", bundleInfo); context.getRequestScope().put("activeVersionDate", new Date(value.getBundle().getLastModified())); context.getRequestScope().put("dependantModules", templateManagerService.getTemplatePackageRegistry().getDependantModules(value)); } private Set<String> getSystemSiteRequiredModules() { Set<String> modules = new TreeSet<String>(); for (String module : templateManagerService.getNonManageableModules()) { JahiaTemplatesPackage pkg = templateManagerService.getTemplatePackageById(module); if (pkg != null) { modules.add(pkg.getId()); for (JahiaTemplatesPackage dep : pkg.getDependencies()) { modules.add(dep.getId()); } } } return modules; } public Date getLastModulesUpdateTime() { return new Date(forgeService.getLastUpdateTime()); } public void initModules(RequestContext requestContext, RenderContext renderContext) { // generate tables ids, used by datatable jquery plugin to store the state of a table in the user localestorage. // new flow = new ids reloadTablesUUIDFromSession(requestContext); if (!requestContext.getFlowScope().contains("adminModuleTableUUID")) { requestContext.getFlowScope().put("adminModuleTableUUID", UUID.randomUUID().toString()); requestContext.getFlowScope().put("forgeModuleTableUUID", UUID.randomUUID().toString()); } if (!isStudio(renderContext)) { forgeService.loadModules(); final Long startedBundleId = (Long) requestContext.getExternalContext().getSessionMap() .get("moduleHasBeenStarted"); if (startedBundleId != null) { Bundle b = BundleUtils.getBundle(startedBundleId.longValue()); JahiaTemplatesPackage module = BundleUtils.getModule(b); String msgKey = "serverSettings.manageModules.module.started"; if (module != null && module.getState().getState() == ModuleState.State.WAITING_TO_BE_IMPORTED) { msgKey = "serverSettings.manageModules.start.waitingToBeImported"; } requestContext.getMessageContext().addMessage(new MessageBuilder().info().source(startedBundleId) .code(msgKey).arg(b.getSymbolicName()).build()); requestContext.getExternalContext().getSessionMap().remove("moduleHasBeenStarted"); } final Object stoppedBundleId = requestContext.getExternalContext().getSessionMap() .get("moduleHasBeenStopped"); if (stoppedBundleId != null) { requestContext.getMessageContext().addMessage(new MessageBuilder().info().source(stoppedBundleId) .code("serverSettings.manageModules.module.stopped").arg(stoppedBundleId).build()); requestContext.getExternalContext().getSessionMap().remove("moduleHasBeenStopped"); } final List<String> missingDependencies = (List<String>) requestContext.getExternalContext() .getSessionMap().get("missingDependencies"); if (missingDependencies != null) { createMessageForMissingDependencies(requestContext.getMessageContext(), missingDependencies); requestContext.getExternalContext().getSessionMap().remove("missingDependencies"); } } } public void reloadModules() { forgeService.flushModules(); forgeService.loadModules(); } public List<Module> getForgeModules() { List<Module> installedModule = new ArrayList<Module>(); List<Module> newModules = new ArrayList<Module>(); for (Module module : forgeService.getModules()) { module.setInstallable( !templateManagerService.differentModuleWithSameIdExists(module.getId(), module.getGroupId())); JahiaTemplatesPackage pkg = templateManagerService.getTemplatePackageRegistry() .lookupById(module.getId()); if (pkg != null && pkg.getGroupId().equals(module.getGroupId())) { installedModule.add(module); } else { newModules.add(module); } } newModules.addAll(installedModule); return newModules; } /** * Logs the specified exception details. * * @param e the occurred exception to be logged */ public void logError(Exception e) { logger.error(e.getMessage(), e); } public void handleError(Exception exception, MutableAttributeMap flowScope, MessageContext messageContext) { if (exception instanceof ScmUnavailableModuleIdException) { messageContext.addMessage(new MessageBuilder().error() .code("serverSettings.manageModules.duplicateModuleError.moduleExists") .arg(flowScope.get("newModuleName")).build()); } else if (exception instanceof ScmWrongVersionException) { messageContext.addMessage(new MessageBuilder().error() .code("serverSettings.manageModules.downloadSourcesError.wrongVersion").build()); } else if (exception instanceof SourceControlException) { messageContext.addMessage( new MessageBuilder().error().code("serverSettings.manageModules.downloadSourcesError") .arg(flowScope.get("version")).build()); } else { String message = exception.getLocalizedMessage(); if (StringUtils.isBlank(message)) { message = exception.toString(); } messageContext.addMessage(new MessageBuilder().error().defaultText(message).build()); } } public void startModule(String moduleId, String version, RequestContext requestContext) throws RepositoryException, BundleException { Bundle bundle = BundleUtils.getBundle(moduleId, version); moduleManager.start(new BundleInfo(BundleUtils.getModuleGroupId(bundle), bundle.getSymbolicName(), bundle.getVersion().toString()).getKey(), null); if (bundle.getState() == Bundle.ACTIVE) { requestContext.getExternalContext().getSessionMap().put("moduleHasBeenStarted", Long.valueOf(bundle.getBundleId())); } storeTablesUUID(requestContext); } public void stopModule(String moduleId, RequestContext requestContext) throws RepositoryException, BundleException { JahiaTemplatesPackage module = templatePackageRegistry.lookupById(moduleId); if (module != null) { moduleManager.stop(module.getBundleKey(), null); } else { Bundle bundle = BundleUtils.getBundleBySymbolicName(moduleId, null); throw new ModuleManagementException(bundle == null || bundle.getState() == Bundle.ACTIVE ? "Module '" + moduleId + "' could not stopped as it was not found in the registry." : "Module '" + moduleId + "' was already stopped"); } requestContext.getExternalContext().getSessionMap().put("moduleHasBeenStopped", moduleId); storeTablesUUID(requestContext); } public void storeTablesUUID(RequestContext requestContext) { requestContext.getExternalContext().getSessionMap().put("adminModuleTableUUID", requestContext.getFlowScope().get("adminModuleTableUUID")); requestContext.getExternalContext().getSessionMap().put("forgeModuleTableUUID", requestContext.getFlowScope().get("forgeModuleTableUUID")); } private void reloadTablesUUIDFromSession(RequestContext requestContext) { if (requestContext.getExternalContext().getSessionMap().contains("adminModuleTableUUID") && !requestContext.getFlowScope().contains("adminModuleTableUUID")) { requestContext.getFlowScope().put("adminModuleTableUUID", requestContext.getExternalContext().getSessionMap().get("adminModuleTableUUID")); requestContext.getFlowScope().put("forgeModuleTableUUID", requestContext.getExternalContext().getSessionMap().get("forgeModuleTableUUID")); requestContext.getExternalContext().getSessionMap().remove("adminModuleTableUUID"); requestContext.getExternalContext().getSessionMap().remove("forgeModuleTableUUID"); } } public String[] getModuleNodetypes(String moduleId) { ArrayList<String> typeNames = new ArrayList<String>(); NodeTypeRegistry.JahiaNodeTypeIterator it = NodeTypeRegistry.getInstance().getNodeTypes(moduleId); while (it.hasNext()) { typeNames.add(it.nextNodeType().getName()); } return typeNames.toArray(new String[typeNames.size()]); } public Map<String, String> listBranchOrTags(String moduleVersion, String scmURI) throws IOException { if (moduleVersion.endsWith("-SNAPSHOT")) { return templateManagerService.listBranches(scmURI); } else { return templateManagerService.listTags(scmURI); } } public String guessBranchOrTag(String moduleVersion, String scmURI, Map<String, String> branchOrTags, String defaultBranchOrTag) { String branchOrTag = templateManagerService.guessBranchOrTag(moduleVersion, StringUtils.substringBefore(StringUtils.removeStart(scmURI, "scm:"), ":"), branchOrTags.keySet()); return branchOrTag != null ? branchOrTag : defaultBranchOrTag; } public void validateScmInfo(String scmUri, String branchOrTag, String moduleVersion, MutableAttributeMap flashScope) throws IOException { if ((StringUtils.startsWith(scmUri, "scm:git:") && StringUtils.isBlank(branchOrTag)) || (StringUtils.startsWith(scmUri, "scm:svn:") && StringUtils.contains(scmUri, "/trunk/"))) { Map<String, String> branchTagInfos = listBranchOrTags(moduleVersion, scmUri); flashScope.put("branchTagInfos", branchTagInfos); branchOrTag = guessBranchOrTag(moduleVersion, scmUri, branchTagInfos, null); flashScope.put("branchOrTag", branchOrTag); } } public JCRNodeWrapper checkoutModule(MutableAttributeMap flowScope, JCRSessionWrapper session) throws RepositoryException, XmlPullParserException, DocumentException, IOException, BundleException { String scmUri = (String) flowScope.get("scmUri"); String branchOrTag = (String) flowScope.get("branchOrTag"); String module = (String) flowScope.get("module"); String version = (String) flowScope.get("version"); try { return templateManagerService.checkoutModule(null, scmUri, branchOrTag, module, version, session); } catch (SourceControlException e) { Map<String, String> branchTagInfos = listBranchOrTags(version, scmUri); String newBranchOrTag = guessBranchOrTag(version, scmUri, branchTagInfos, branchOrTag); String newScmUri = branchTagInfos.get(newBranchOrTag); if (newScmUri != null && newBranchOrTag != null && (!newBranchOrTag.equals(branchOrTag) || newScmUri.equals(scmUri))) { flowScope.put("scmUri", newScmUri); flowScope.put("branchTagInfos", branchTagInfos); flowScope.put("branchOrTag", newBranchOrTag); return templateManagerService.checkoutModule(null, newScmUri, newBranchOrTag, module, version, session); } throw e; } } public File checkoutTempModule(MutableAttributeMap flowScope) throws RepositoryException, XmlPullParserException, DocumentException, IOException { String scmUri = (String) flowScope.get("scmUri"); String branchOrTag = (String) flowScope.get("branchOrTag"); String module = (String) flowScope.get("module"); String version = (String) flowScope.get("version"); try { return templateManagerService.checkoutTempModule(scmUri, branchOrTag, module, version); } catch (SourceControlException e) { Map<String, String> branchTagInfos = listBranchOrTags(version, scmUri); String newBranchOrTag = guessBranchOrTag(version, scmUri, branchTagInfos, branchOrTag); String newScmUri = branchTagInfos.get(newBranchOrTag); if (newScmUri != null && newBranchOrTag != null && (!newBranchOrTag.equals(branchOrTag) || newScmUri.equals(scmUri))) { flowScope.put("newScmUri", newScmUri); flowScope.put("branchTagInfos", branchTagInfos); flowScope.put("branchOrTag", newBranchOrTag); return templateManagerService.checkoutTempModule(newScmUri, newBranchOrTag, module, version); } throw e; } } public void deleteTempSources(File tempSources) { if (tempSources != null && tempSources.exists()) { FileUtils.deleteQuietly(tempSources); } } public void updateModule(String id, String version) throws RepositoryException { Bundle bundle = BundleUtils.getBundle(id, version); if (bundle != null) { try { bundle.update(); } catch (BundleException e) { logger.error("Cannot update module", e); } } } public void uninstallModule(String moduleId, String moduleVersion, RequestContext requestContext) throws RepositoryException, BundleException { moduleManager.uninstall(BundleInfo.fromModuleInfo(moduleId, moduleVersion).getKey(), null); } private static class ModuleInstallationResult { private Bundle bundle; private String messageCode; public ModuleInstallationResult(Bundle bundle, String messageCode) { this.bundle = bundle; this.messageCode = messageCode; } public Bundle getBundle() { return bundle; } public String getMessageCode() { return messageCode; } } }