/* * This library is part of OpenCms - * the Open Source Content Management System * * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * For further information about Alkacon Software GmbH, please see the * company website: http://www.alkacon.com * * For further information about OpenCms, please see the * project website: http://www.opencms.org * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.opencms.module; import org.opencms.configuration.CmsVfsConfiguration; import org.opencms.configuration.CmsWorkplaceConfiguration; import org.opencms.configuration.I_CmsConfigurationParameterHandler; import org.opencms.configuration.I_CmsXmlConfiguration; import org.opencms.db.CmsExportPoint; import org.opencms.file.types.I_CmsResourceType; import org.opencms.main.CmsLog; import org.opencms.util.CmsDateUtil; import org.opencms.util.CmsStringUtil; import org.opencms.workplace.CmsWorkplace; import org.opencms.workplace.explorer.CmsExplorerTypeSettings; import java.text.ParseException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.SortedMap; import org.apache.commons.digester.Digester; import org.apache.commons.logging.Log; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; /** * Adds the XML handler rules for import and export of a single module.<p> * * @since 6.0.0 */ public class CmsModuleXmlHandler { /** The "name" attribute. */ public static final String A_NAME = "name"; /** The "version" attribute. */ public static final String A_VERSION = "version"; /** The node name for the authoremail node. */ public static final String N_AUTHOREMAIL = "authoremail"; /** The node name for the authorname node. */ public static final String N_AUTHORNAME = "authorname"; /** The node name for the class node. */ public static final String N_CLASS = "class"; /** The node name for the datecreated node. */ public static final String N_DATECREATED = "datecreated"; /** The node name for the date installed node. */ public static final String N_DATEINSTALLED = "dateinstalled"; /** The node name for the dependencies node. */ public static final String N_DEPENDENCIES = "dependencies"; /** The node name for the dependency node. */ public static final String N_DEPENDENCY = "dependency"; /** The node name for the description node. */ public static final String N_DESCRIPTION = "description"; /** The node name for the group node. */ public static final String N_GROUP = "group"; /** The node name for a module. */ public static final String N_MODULE = "module"; /** The node name for the name node. */ public static final String N_NAME = "name"; /** The node name for the nicename node. */ public static final String N_NICENAME = "nicename"; /** The "param" node name for generic parameters. */ public static final String N_PARAM = "param"; /** The node name for the parameters node. */ public static final String N_PARAMETERS = "parameters"; /** The node name for the resources node. */ public static final String N_RESOURCES = "resources"; /** The node name for the user installed node. */ public static final String N_USERINSTALLED = "userinstalled"; /** The node name for the version node. */ public static final String N_VERSION = "version"; /** The log object for this class. */ private static final Log LOG = CmsLog.getLog(CmsModuleXmlHandler.class); /** The list of dependencies for a module. */ private List<CmsModuleDependency> m_dependencies; /** The explorer type settings. */ private List<CmsExplorerTypeSettings> m_explorerTypeSettings; /** The list of export points for a module. */ private List<CmsExportPoint> m_exportPoints; /** The generated module. */ private CmsModule m_module; /** Indicates if the module was an old (5.0.x) style module. */ private boolean m_oldModule; /** The module parameters. */ private Map<String, String> m_parameters; /** The list of resources for a module. */ private List<String> m_resources; /** The list of additional resource types. */ private List<I_CmsResourceType> m_resourceTypes; /** * Public constructor, will be called by digester during import.<p> */ public CmsModuleXmlHandler() { m_exportPoints = new ArrayList<CmsExportPoint>(); m_dependencies = new ArrayList<CmsModuleDependency>(); m_resources = new ArrayList<String>(); m_parameters = new HashMap<String, String>(); m_resourceTypes = new ArrayList<I_CmsResourceType>(); m_explorerTypeSettings = new ArrayList<CmsExplorerTypeSettings>(); } /** * Adds the XML digester rules for a single module.<p> * * @param digester the digester to add the rules to */ public static void addXmlDigesterRules(Digester digester) { // add class generation rule digester.addObjectCreate("*/" + N_MODULE, CmsModuleXmlHandler.class); digester.addSetNext("*/" + N_MODULE, "setModule"); // add rules for base module information digester.addCallMethod("*/" + N_MODULE, "createdModule", 11); digester.addCallParam("*/" + N_MODULE + "/" + N_NAME, 0); digester.addCallParam("*/" + N_MODULE + "/" + N_NICENAME, 1); digester.addCallParam("*/" + N_MODULE + "/" + N_GROUP, 2); digester.addCallParam("*/" + N_MODULE + "/" + N_CLASS, 3); digester.addCallParam("*/" + N_MODULE + "/" + N_DESCRIPTION, 4); digester.addCallParam("*/" + N_MODULE + "/" + N_VERSION, 5); digester.addCallParam("*/" + N_MODULE + "/" + N_AUTHORNAME, 6); digester.addCallParam("*/" + N_MODULE + "/" + N_AUTHOREMAIL, 7); digester.addCallParam("*/" + N_MODULE + "/" + N_DATECREATED, 8); digester.addCallParam("*/" + N_MODULE + "/" + N_USERINSTALLED, 9); digester.addCallParam("*/" + N_MODULE + "/" + N_DATEINSTALLED, 10); // add rules for module dependencies digester.addCallMethod("*/" + N_MODULE + "/" + N_DEPENDENCIES + "/" + N_DEPENDENCY, "addDependency", 2); digester.addCallParam( "*/" + N_MODULE + "/" + N_DEPENDENCIES + "/" + N_DEPENDENCY, 0, I_CmsXmlConfiguration.A_NAME); digester.addCallParam("*/" + N_MODULE + "/" + N_DEPENDENCIES + "/" + N_DEPENDENCY, 1, A_VERSION); // add rules for the module export points digester.addCallMethod("*/" + N_MODULE + "/" + I_CmsXmlConfiguration.N_EXPORTPOINTS + "/" + I_CmsXmlConfiguration.N_EXPORTPOINT, "addExportPoint", 2); digester.addCallParam("*/" + N_MODULE + "/" + I_CmsXmlConfiguration.N_EXPORTPOINTS + "/" + I_CmsXmlConfiguration.N_EXPORTPOINT, 0, I_CmsXmlConfiguration.A_URI); digester.addCallParam("*/" + N_MODULE + "/" + I_CmsXmlConfiguration.N_EXPORTPOINTS + "/" + I_CmsXmlConfiguration.N_EXPORTPOINT, 1, I_CmsXmlConfiguration.A_DESTINATION); // add rules for the module resources digester.addCallMethod( "*/" + N_MODULE + "/" + N_RESOURCES + "/" + I_CmsXmlConfiguration.N_RESOURCE, "addResource", 1); digester.addCallParam( "*/" + N_MODULE + "/" + N_RESOURCES + "/" + I_CmsXmlConfiguration.N_RESOURCE, 0, I_CmsXmlConfiguration.A_URI); // add rules for the module parameters digester.addCallMethod( "*/" + N_MODULE + "/" + N_PARAMETERS + "/" + I_CmsXmlConfiguration.N_PARAM, "addParameter", 2); digester.addCallParam( "*/" + N_MODULE + "/" + N_PARAMETERS + "/" + I_CmsXmlConfiguration.N_PARAM, 0, I_CmsXmlConfiguration.A_NAME); digester.addCallParam("*/" + N_MODULE + "/" + N_PARAMETERS + "/" + I_CmsXmlConfiguration.N_PARAM, 1); // generic <param> parameter rules digester.addCallMethod( "*/" + I_CmsXmlConfiguration.N_PARAM, I_CmsConfigurationParameterHandler.ADD_PARAMETER_METHOD, 2); digester.addCallParam("*/" + I_CmsXmlConfiguration.N_PARAM, 0, I_CmsXmlConfiguration.A_NAME); digester.addCallParam("*/" + I_CmsXmlConfiguration.N_PARAM, 1); // add resource type rules from VFS CmsVfsConfiguration.addResourceTypeXmlRules(digester); // add explorer type rules from workplace CmsWorkplaceConfiguration.addExplorerTypeXmlRules(digester); // finally add all rules for backward compatibility with OpenCms 5.0 addXmlDigesterRulesForVersion5Modules(digester); } /** * Generates a detached XML element for a module.<p> * * @param module the module to generate the XML element for * * @return the detached XML element for the module */ public static Element generateXml(CmsModule module) { Document doc = DocumentHelper.createDocument(); Element moduleElement = doc.addElement(N_MODULE); moduleElement.addElement(N_NAME).setText(module.getName()); if (!module.getName().equals(module.getNiceName())) { moduleElement.addElement(N_NICENAME).addCDATA(module.getNiceName()); } else { moduleElement.addElement(N_NICENAME); } if (CmsStringUtil.isNotEmpty(module.getGroup())) { moduleElement.addElement(N_GROUP).setText(module.getGroup()); } if (CmsStringUtil.isNotEmpty(module.getActionClass())) { moduleElement.addElement(N_CLASS).setText(module.getActionClass()); } else { moduleElement.addElement(N_CLASS); } if (CmsStringUtil.isNotEmpty(module.getDescription())) { moduleElement.addElement(N_DESCRIPTION).addCDATA(module.getDescription()); } else { moduleElement.addElement(N_DESCRIPTION); } moduleElement.addElement(N_VERSION).setText(module.getVersion().toString()); if (CmsStringUtil.isNotEmpty(module.getAuthorName())) { moduleElement.addElement(N_AUTHORNAME).addCDATA(module.getAuthorName()); } else { moduleElement.addElement(N_AUTHORNAME); } if (CmsStringUtil.isNotEmpty(module.getAuthorEmail())) { moduleElement.addElement(N_AUTHOREMAIL).addCDATA(module.getAuthorEmail()); } else { moduleElement.addElement(N_AUTHOREMAIL); } if (module.getDateCreated() != CmsModule.DEFAULT_DATE) { moduleElement.addElement(N_DATECREATED).setText(CmsDateUtil.getHeaderDate(module.getDateCreated())); } else { moduleElement.addElement(N_DATECREATED); } if (CmsStringUtil.isNotEmpty(module.getUserInstalled())) { moduleElement.addElement(N_USERINSTALLED).setText(module.getUserInstalled()); } else { moduleElement.addElement(N_USERINSTALLED); } if (module.getDateInstalled() != CmsModule.DEFAULT_DATE) { moduleElement.addElement(N_DATEINSTALLED).setText(CmsDateUtil.getHeaderDate(module.getDateInstalled())); } else { moduleElement.addElement(N_DATEINSTALLED); } Element dependenciesElement = moduleElement.addElement(N_DEPENDENCIES); for (int i = 0; i < module.getDependencies().size(); i++) { CmsModuleDependency dependency = module.getDependencies().get(i); dependenciesElement.addElement(N_DEPENDENCY).addAttribute( I_CmsXmlConfiguration.A_NAME, dependency.getName()).addAttribute(A_VERSION, dependency.getVersion().toString()); } Element exportpointsElement = moduleElement.addElement(I_CmsXmlConfiguration.N_EXPORTPOINTS); for (int i = 0; i < module.getExportPoints().size(); i++) { CmsExportPoint point = module.getExportPoints().get(i); exportpointsElement.addElement(I_CmsXmlConfiguration.N_EXPORTPOINT).addAttribute( I_CmsXmlConfiguration.A_URI, point.getUri()).addAttribute(I_CmsXmlConfiguration.A_DESTINATION, point.getConfiguredDestination()); } Element resourcesElement = moduleElement.addElement(N_RESOURCES); for (int i = 0; i < module.getResources().size(); i++) { String resource = module.getResources().get(i); resourcesElement.addElement(I_CmsXmlConfiguration.N_RESOURCE).addAttribute( I_CmsXmlConfiguration.A_URI, resource); } Element parametersElement = moduleElement.addElement(N_PARAMETERS); SortedMap<String, String> parameters = module.getParameters(); if (parameters != null) { List<String> names = new ArrayList<String>(parameters.keySet()); Collections.sort(names); for (String name : names) { String value = parameters.get(name).toString(); Element paramNode = parametersElement.addElement(I_CmsXmlConfiguration.N_PARAM); paramNode.addAttribute(I_CmsXmlConfiguration.A_NAME, name); paramNode.addText(value); } } // add resource types List<I_CmsResourceType> resourceTypes = module.getResourceTypes(); if (resourceTypes.size() > 0) { Element resourcetypesElement = moduleElement.addElement(CmsVfsConfiguration.N_RESOURCETYPES); CmsVfsConfiguration.generateResourceTypeXml(resourcetypesElement, resourceTypes, true); } List<CmsExplorerTypeSettings> explorerTypes = module.getExplorerTypes(); if (explorerTypes.size() > 0) { Element explorerTypesElement = moduleElement.addElement(CmsWorkplaceConfiguration.N_EXPLORERTYPES); CmsWorkplaceConfiguration.generateExplorerTypesXml(explorerTypesElement, explorerTypes, true); } // return the modules node moduleElement.detach(); return moduleElement; } /** * Generates a (hopefully) valid Java class name from an invalid class name.<p> * * All invalid characters are replaced by an underscore "_". * This is for example used to make sure old (5.0) modules can still be imported, * by converting the name to a valid class name.<p> * * @param className the class name to make valid * * @return a valid Java class name from an invalid class name */ public static String makeValidJavaClassName(String className) { StringBuffer result = new StringBuffer(className.length()); int length = className.length(); boolean nodot = true; for (int i = 0; i < length; i++) { char ch = className.charAt(i); if (nodot) { if (ch == '.') { // ignore, remove } else if (Character.isJavaIdentifierStart(ch)) { nodot = false; result.append(ch); } else { result.append('_'); } } else { if (ch == '.') { nodot = true; result.append(ch); } else if (Character.isJavaIdentifierPart(ch)) { nodot = false; result.append(ch); } else { result.append('_'); } } } return result.toString(); } /** * Adds the digester rules for OpenCms version 5 modules.<p> * * @param digester the digester to add the rules to */ private static void addXmlDigesterRulesForVersion5Modules(Digester digester) { // mark method digester.addCallMethod("*/" + N_MODULE + "/author", "setOldModule"); // base module information digester.addCallParam("*/" + N_MODULE + "/author", 6); digester.addCallParam("*/" + N_MODULE + "/email", 7); digester.addCallParam("*/" + N_MODULE + "/creationdate", 8); // dependencies digester.addCallParam("*/" + N_MODULE + "/dependencies/dependency/name", 0); digester.addCallParam("*/" + N_MODULE + "/dependencies/dependency/minversion", 1); // export points digester.addCallMethod("*/" + N_MODULE + "/exportpoint", "addExportPoint", 2); digester.addCallParam("*/" + N_MODULE + "/exportpoint/source", 0); digester.addCallParam("*/" + N_MODULE + "/exportpoint/destination", 1); // parameters digester.addCallMethod("*/" + N_MODULE + "/parameters/para", "addParameter", 2); digester.addCallParam("*/" + N_MODULE + "/parameters/para/name", 0); digester.addCallParam("*/" + N_MODULE + "/parameters/para/value", 1); } /** * Adds a module dependency to the current module.<p> * * @param name the module name of the dependency * @param version the module version of the dependency */ public void addDependency(String name, String version) { CmsModuleVersion moduleVersion = new CmsModuleVersion(version); CmsModuleDependency dependency = new CmsModuleDependency(name, moduleVersion); m_dependencies.add(dependency); if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_ADD_MOD_DEPENDENCY_2, name, version)); } } /** * Adds an explorer type setting object to the list of type settings.<p> * * Adds the type setting as well to a map with the resource type name as key. * * @param settings the explorer type settings */ public void addExplorerTypeSetting(CmsExplorerTypeSettings settings) { settings.setAddititionalModuleExplorerType(true); m_explorerTypeSettings.add(settings); } /** * Adds an export point to the module configuration.<p> * * @param uri the export point uri * @param destination the export point destination */ public void addExportPoint(String uri, String destination) { CmsExportPoint point = new CmsExportPoint(uri, destination); m_exportPoints.add(point); if (CmsLog.INIT.isInfoEnabled()) { CmsLog.INIT.info(Messages.get().getBundle().key( Messages.INIT_ADD_EXPORT_POINT_2, point.getUri(), point.getConfiguredDestination())); } } /** * Adds a module parameter to the module configuration.<p> * * @param key the parameter key * @param value the parameter value */ public void addParameter(String key, String value) { if (CmsStringUtil.isNotEmpty(key)) { key = key.trim(); } if (CmsStringUtil.isNotEmpty(value)) { value = value.trim(); } m_parameters.put(key, value); if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_ADD_MOD_PARAM_KEY_2, key, value)); } } /** * Adds a resource to the list module resources.<p> * * @param resource a resources uri in the OpenCms VFS */ public void addResource(String resource) { if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_ADD_MOD_RESOURCE_1, resource)); } m_resources.add(resource); } /** * Adds a new resource type to the internal list of loaded resource types.<p> * * @param resourceType the resource type to add * * @see I_CmsResourceType#ADD_RESOURCE_TYPE_METHOD */ public void addResourceType(I_CmsResourceType resourceType) { resourceType.setAdditionalModuleResourceType(true); m_resourceTypes.add(resourceType); } /** * Created a new module from the provided parameters.<p> * * @param name the name of this module, usually looks like a java package name * @param niceName the "nice" display name of this module * @param group the group of the module * @param actionClass the (optional) module action class name * @param description the description of this module * @param version the version of this module * @param authorName the name of the author of this module * @param authorEmail the email of the module author * @param dateCreated the date this module was created by the author * @param userInstalled the name of the user who uploaded this module * @param dateInstalled the date this module was uploaded */ public void createdModule( String name, String niceName, String group, String actionClass, String description, String version, String authorName, String authorEmail, String dateCreated, String userInstalled, String dateInstalled) { String moduleName; if (!CmsStringUtil.isValidJavaClassName(name)) { // ensure backward compatibility with old (5.0) module names LOG.error(Messages.get().getBundle().key(Messages.LOG_INVALID_MOD_NAME_IMPORTED_1, name)); moduleName = makeValidJavaClassName(name); LOG.error(Messages.get().getBundle().key(Messages.LOG_CORRECTED_MOD_NAME_1, moduleName)); } else { moduleName = name; } // parse the module version CmsModuleVersion moduleVersion = new CmsModuleVersion(version); // parse date created long moduleDateCreated = CmsModule.DEFAULT_DATE; if (dateCreated != null) { try { moduleDateCreated = CmsDateUtil.parseHeaderDate(dateCreated); } catch (ParseException e) { // noop } } // parse date installed long moduleDateInstalled = CmsModule.DEFAULT_DATE; if (dateInstalled != null) { try { moduleDateInstalled = CmsDateUtil.parseHeaderDate(dateInstalled); } catch (ParseException e1) { // noop } } if (m_oldModule) { // make sure module path is added to resources for "old" (5.0.x) modules String modulePath = CmsWorkplace.VFS_PATH_MODULES + name + "/"; m_resources.add(modulePath); } // now create the module m_module = new CmsModule( moduleName, niceName, group, actionClass, description, moduleVersion, authorName, authorEmail, moduleDateCreated, userInstalled, moduleDateInstalled, m_dependencies, m_exportPoints, m_resources, m_parameters); // store module name in the additional resource types List<I_CmsResourceType> moduleResourceTypes = new ArrayList<I_CmsResourceType>(m_resourceTypes.size()); for (Iterator<I_CmsResourceType> i = m_resourceTypes.iterator(); i.hasNext();) { I_CmsResourceType resType = i.next(); resType.setModuleName(moduleName); moduleResourceTypes.add(resType); } // set the additional resource types; m_module.setResourceTypes(moduleResourceTypes); // set the additional explorer types m_module.setExplorerTypes(m_explorerTypeSettings); } /** * Returns the generated module.<p> * * @return the generated module */ public CmsModule getModule() { return m_module; } /** * Sets the current imported module to an old (5.0.x) style module. */ public void setOldModule() { m_oldModule = true; if (LOG.isDebugEnabled()) { LOG.debug(Messages.get().getBundle().key(Messages.LOG_OLD_MODULE_IMPORTED_0)); } } }