org.tolven.assembler.war.WARAssembler.java Source code

Java tutorial

Introduction

Here is the source code for org.tolven.assembler.war.WARAssembler.java

Source

/*
 * Copyright (C) 2009 Tolven Inc
    
 * 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.
 *
 * Contact: info@tolvenhealth.com 
 *
 * @author Joseph Isaac
 * @version $Id$
 */
package org.tolven.assembler.war;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.java.plugin.registry.Extension;
import org.java.plugin.registry.Extension.Parameter;
import org.java.plugin.registry.ExtensionPoint;
import org.java.plugin.registry.ExtensionPoint.ParameterDefinition;
import org.java.plugin.registry.PluginDescriptor;
import org.tolven.plugin.TolvenCommandPlugin;
import org.tolven.plugin.repository.ConfigPluginsWrapper;
import org.tolven.plugin.repository.bean.PluginDetail;
import org.tolven.plugin.repository.bean.PluginPropertyDetail;
import org.tolven.plugin.repository.bean.Plugins;
import org.tolven.security.hash.TolvenMessageDigest;
import org.tolven.tools.ant.TolvenJar;
import org.tolven.tools.ant.TolvenReplaceRegExp;
import org.tolven.tools.ant.TolvenZip;

/**
 * This plugin assembles a war file.
 * 
 * @author Joseph Isaac
 *
 */
public class WARAssembler extends TolvenCommandPlugin {

    public static final String CMD_CONTEXT_ID_OPTION = "contextId";
    public static final String CMD_DESTDIR_OPTION = "destDir";
    public static final String CMD_WAR_PLUGIN_OPTION = "warPlugin";
    public static final String CMD_WEB_URI_OPTION = "webURI";

    public static final String EXN_FACES_CONFIG_ASSEMBLER = "facesConfigAssemblerExtension";
    public static final String EXN_WEBXML_ASSEMBLER = "webXMLAssemblerExtension";

    public static final String EXNPT_ABSTRACT_WAR = "abstractWAR";
    public static final String EXNPT_CLASSES = "classes";
    public static final String EXNPT_FILE_INCLUDE = "fileInclude";
    public static final String EXTENSIONPOINT_SCRIPT_SORT_ORDER = "scriptsOrder";
    public static final String EXNPT_ID = "extension-point";
    public static final String EXNPT_METAINF = "META-INF";
    public static final String EXNPT_REMOTE_FILE = "remoteFile";
    public static final String EXNPT_REMOTE_INCLUDES = "remoteIncludes";
    public static final String EXNPT_REMOTE_PRODUCT_DIR = "remoteProductDir";
    public static final String EXNPT_SRC_PLUGIN_ID = "source-plugin-id";
    public static final String EXNPT_WAR_PROPERTY = "property";
    public static final String EXNPT_WAR_PROPERTY_SEQUENCE = "propertySequence";
    public static final String EXNPT_WARMODULE_DECL = "warModule-declaration";
    @Deprecated
    public static final String EXNPT_WEBDIRECTORY = "webDirectory";
    public static final String EXNPT_WEBDIRECTORY_ADPTR = "webDirectory-adaptor";
    public static final String EXNPT_WEBINFCLASSES_ADPTR = "WEB-INF-CLASSES-adaptor";
    @Deprecated
    public static final String EXNPT_WEBINFLIB = "WEB-INF-LIB";
    public static final String EXNPT_WEBINFLIB_ADPTR = "WEB-INF-LIB-adaptor";
    public static final String EXNPT_WEBROOTFILES_ADPTR = "WEB-ROOT-FILES-adaptor";

    public static final String MESSAGE_DIGEST_ALGORITHM = "md5";
    public static final String TAGLIB_EXTENSIONPOINT = "taglib";

    private Logger logger = Logger.getLogger(WARAssembler.class);

    protected void addRemoteJars(PluginDescriptor pd) {
        File myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
        PluginDescriptor abstractWARPD = getAbstractWARPluginDescriptor();
        ExtensionPoint remoteProductDirExnPt = abstractWARPD.getExtensionPoint(EXNPT_REMOTE_PRODUCT_DIR);
        if (remoteProductDirExnPt == null) {
            throw new RuntimeException(abstractWARPD.getUniqueId() + " does not have a " + EXNPT_REMOTE_PRODUCT_DIR
                    + " extension point");
        }
        for (ExtensionPoint remoteProductDirDescendentExnPt : remoteProductDirExnPt.getDescendants()) {
            ParameterDefinition pluginIdParamDef = remoteProductDirDescendentExnPt
                    .getParameterDefinition("target-plugin-id");
            if (pluginIdParamDef == null || pluginIdParamDef.getDefaultValue() == null
                    || pluginIdParamDef.getDefaultValue().trim().length() == 0) {
                throw new RuntimeException(remoteProductDirDescendentExnPt.getUniqueId()
                        + " does not have a default parameter-def target-plugin-id value");
            }
            if (pluginIdParamDef.getDefaultValue().equals(pd.getId())) {
                ParameterDefinition destParamDef = remoteProductDirDescendentExnPt
                        .getParameterDefinition("destination");
                if (destParamDef == null || destParamDef.getDefaultValue() == null
                        || destParamDef.getDefaultValue().trim().length() == 0) {
                    throw new RuntimeException(remoteProductDirDescendentExnPt.getUniqueId()
                            + " does not have a default parameter-def destination value");
                }
                String destName = destParamDef.getDefaultValue();
                for (Extension remoteProductDirExn : remoteProductDirDescendentExnPt.getConnectedExtensions()) {
                    String srcPluginId = remoteProductDirExn.getParameter("source-plugin-id").valueAsString();
                    if (srcPluginId.trim().length() == 0) {
                        throw new RuntimeException(remoteProductDirExn.getUniqueId()
                                + " must have a source-plugin-id parameter value");
                    }
                    PluginDescriptor srcPD = getManager().getRegistry().getPluginDescriptor(srcPluginId);
                    String srcExnPtId = remoteProductDirExn.getParameter("source-extension-point-id")
                            .valueAsString();
                    if (srcExnPtId.trim().length() == 0) {
                        throw new RuntimeException(remoteProductDirExn.getUniqueId()
                                + " must have a source-extension-point-id parameter value");
                    }
                    ExtensionPoint srcExnPt = srcPD.getExtensionPoint(srcExnPtId);
                    String srcDirParam = remoteProductDirExn.getParameter("source-dir-parameter").valueAsString();
                    if (srcDirParam.trim().length() == 0) {
                        throw new RuntimeException(
                                srcExnPt.getUniqueId() + " must have a source-dir-parameter parameter value");
                    }
                    String dir = srcExnPt.getParameterDefinition(srcDirParam).getDefaultValue();
                    if (dir.trim().length() == 0) {
                        throw new RuntimeException(
                                srcExnPt.getUniqueId() + " must have a " + srcDirParam + " parameter value");
                    }
                    String evaluatedDir = evaluate(dir, srcPD);
                    File productDir = new File(evaluatedDir);
                    File destDir = new File(myWARPluginDir, destName);
                    destDir.mkdirs();
                    try {
                        FileUtils.copyDirectory(productDir, destDir);
                    } catch (IOException ex) {
                        throw new RuntimeException(
                                "Could not copy the stage file " + productDir + " to " + myWARPluginDir, ex);
                    }
                }
            }
        }
    }

    protected void addTagConverter(PluginDescriptor pd, XMLStreamWriter writer) throws XMLStreamException {
        ExtensionPoint converterExnPt = pd.getExtensionPoint("tagConverter");
        if (converterExnPt != null) {
            for (Extension extension : converterExnPt.getConnectedExtensions()) {
                writer.writeStartElement("tag");
                writer.writeCharacters("\n");
                writer.writeStartElement("tag-name");
                writer.writeCharacters(extension.getParameter("tag-name").valueAsString());
                writer.writeEndElement();
                writer.writeCharacters("\n");
                writer.writeStartElement("converter");
                writer.writeCharacters("\n");
                writer.writeStartElement("converter-id");
                writer.writeCharacters(extension.getParameter("converter-id").valueAsString());
                writer.writeEndElement();
                writer.writeCharacters("\n");
                Parameter handlerClass = extension.getParameter("handler-class");
                if (handlerClass != null) {
                    writer.writeStartElement("handler-class");
                    writer.writeCharacters(handlerClass.valueAsString());
                    writer.writeEndElement();
                    writer.writeCharacters("\n");
                }
                writer.writeEndElement();
                writer.writeCharacters("\n");
                writer.writeEndElement();
                writer.writeCharacters("\n");
            }
        }
    }

    protected void addTagInfo(Extension taglibExn, File myWARPluginDir) throws IOException, XMLStreamException {
        File metaInfTagDir = new File(myWARPluginDir.getPath() + "/META-INF/tags");
        metaInfTagDir.mkdirs();
        StringWriter tagLibWriter = new StringWriter();
        XMLOutputFactory factory = XMLOutputFactory.newInstance();
        XMLStreamWriter writer = null;
        try {
            writer = factory.createXMLStreamWriter(tagLibWriter);
            writer.writeStartDocument("UTF-8", "1.0");
            writer.writeCharacters("\n");
            writer.writeDTD(
                    "<!DOCTYPE facelet-taglib PUBLIC \"-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN\" \"http://java.sun.com/dtd/facelet-taglib_1_0.dtd\">");
            writer.writeCharacters("\n");
            writer.writeStartElement("facelet-taglib");
            writer.writeCharacters("\n");
            String namespace = taglibExn.getParameter("namespace").valueAsString();
            writer.writeStartElement("namespace");
            writer.writeCharacters(namespace);
            writer.writeEndElement();
            writer.writeCharacters("\n");
            PluginDescriptor taglibPD = taglibExn.getDeclaringPluginDescriptor();
            addTagSource(taglibPD, metaInfTagDir, writer);
            addTagValidator(taglibPD, writer);
            addTagConverter(taglibPD, writer);
            writer.writeEndElement();
            writer.writeCharacters("\n");
            writer.writeEndDocument();
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
        String tagFilename = taglibExn.getParameter("tag-filename").valueAsString();
        File metaInfTagFile = new File(metaInfTagDir, tagFilename);
        logger.debug("Write tagLib string to " + metaInfTagFile.getPath());
        FileUtils.writeStringToFile(metaInfTagFile, tagLibWriter.toString());
    }

    protected void addTagSource(File dir, XMLStreamWriter writer) throws XMLStreamException {
        for (File file : dir.listFiles()) {
            String src = dir.getName() + "/" + file.getName();
            writer.writeStartElement("tag");
            writer.writeCharacters("\n");
            writer.writeStartElement("tag-name");
            int index = file.getName().lastIndexOf(".");
            if (index == -1) {
                writer.writeCharacters(file.getName());
            } else {
                writer.writeCharacters(file.getName().substring(0, index));
            }
            writer.writeEndElement();
            writer.writeCharacters("\n");
            writer.writeStartElement("source");
            writer.writeCharacters(src);
            writer.writeEndElement();
            writer.writeCharacters("\n");
            writer.writeEndElement();
            writer.writeCharacters("\n");
        }
    }

    protected void addTagSource(PluginDescriptor pd, File metaInfTagDir, XMLStreamWriter writer)
            throws IOException, XMLStreamException {
        ExtensionPoint tagSrcDirectoryExnPt = pd.getExtensionPoint("tagSourceDirectory");
        if (tagSrcDirectoryExnPt != null) {
            for (Extension tagSrcDirectoryExn : tagSrcDirectoryExnPt.getConnectedExtensions()) {
                String dirname = tagSrcDirectoryExn.getParameter("source-directory").valueAsString();
                File dir = getFilePath(tagSrcDirectoryExn.getDeclaringPluginDescriptor(), dirname);
                logger.debug("Copy " + dir.getPath() + " to " + metaInfTagDir.getPath());
                FileUtils.copyDirectoryToDirectory(dir, metaInfTagDir);
                addTagSource(dir, writer);
            }
        }
    }

    protected void addTagValidator(PluginDescriptor pd, XMLStreamWriter writer) throws XMLStreamException {
        ExtensionPoint validatorExnPt = pd.getExtensionPoint("tagValidator");
        if (validatorExnPt != null) {
            for (Extension exn : validatorExnPt.getConnectedExtensions()) {
                writer.writeStartElement("tag");
                writer.writeCharacters("\n");
                writer.writeStartElement("tag-name");
                writer.writeCharacters(exn.getParameter("tag-name").valueAsString());
                writer.writeEndElement();
                writer.writeCharacters("\n");
                writer.writeStartElement("validator");
                writer.writeCharacters("\n");
                writer.writeStartElement("validator-id");
                writer.writeCharacters(exn.getParameter("validator-id").valueAsString());
                writer.writeEndElement();
                writer.writeCharacters("\n");
                Parameter handlerClass = exn.getParameter("handler-class");
                if (handlerClass != null) {
                    writer.writeStartElement("handler-class");
                    writer.writeCharacters(handlerClass.valueAsString());
                    writer.writeEndElement();
                    writer.writeCharacters("\n");
                }
                writer.writeEndElement();
                writer.writeCharacters("\n");
                writer.writeEndElement();
                writer.writeCharacters("\n");
            }
        }
    }

    @Deprecated
    protected void assembleClasses(PluginDescriptor pd) throws IOException {
        File myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
        //The following loop is where class contributions are not directly attached to pd.getId()
        for (Extension exn : getExtensions(pd, EXNPT_CLASSES)) {
            assembleWARClasses(exn, myWARPluginDir);
        }
        for (ExtensionPoint classesExPt : pd.getExtensionPoints()) {
            if (EXNPT_CLASSES.equals(classesExPt.getParentExtensionPointId())
                    && classesExPt.getParentPluginId().equals(getDescriptor().getId())) {
                myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
                for (Extension exn : classesExPt.getConnectedExtensions()) {
                    assembleWARClasses(exn, myWARPluginDir);
                }
            }
        }
    }

    protected void assembleMetaInf(PluginDescriptor pd) throws IOException {
        File myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
        for (ExtensionPoint exnPt : pd.getExtensionPoints()) {
            if (EXNPT_METAINF.equals(exnPt.getParentExtensionPointId())
                    && exnPt.getParentPluginId().equals(getDescriptor().getId())) {
                myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
                for (Extension metaInfExn : exnPt.getConnectedExtensions()) {
                    String dirname = metaInfExn.getParameter("dir").valueAsString();
                    File srcDir = getFilePath(metaInfExn.getDeclaringPluginDescriptor(), dirname);
                    File destDir = new File(myWARPluginDir + "/META-INF");
                    logger.debug("Copy " + srcDir.getPath() + " to " + destDir.getPath());
                    FileUtils.copyDirectory(srcDir, destDir);
                }
            }
        }
    }

    protected void assembleProperties(PluginDescriptor pd) {
        File myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
        Map<String, Map<String, Set<String>>> propertyClassMapping = new HashMap<String, Map<String, Set<String>>>();
        Map<String, List<String>> propertySequenceMapping = propertySequenceMapping(pd);
        Set<Extension> classesExns = new HashSet<Extension>();
        classesExns.addAll(getChildExtensions(EXNPT_WAR_PROPERTY, pd));
        PluginDescriptor abstractWARPD = getAbstractWARPluginDescriptor();
        ExtensionPoint propertyExnPt = abstractWARPD.getExtensionPoint(EXNPT_WAR_PROPERTY);
        for (Extension exn : propertyExnPt.getConnectedExtensions()) {
            Parameter pluginIdParam = exn.getParameter("target-plugin-id");
            if (pluginIdParam == null || pluginIdParam.valueAsString().trim().length() == 0) {
                throw new RuntimeException(exn.getUniqueId() + " does not have a target-plugin-id parameter value");
            }
            if (pd.getId().equals(pluginIdParam.valueAsString())) {
                classesExns.add(exn);
            }
        }
        for (Extension classesExn : classesExns) {
            String classname = classesExn.getParameter("class").valueAsString();
            File classFile = new File(myWARPluginDir, "WEB-INF/classes/" + classname.replace(".", "/") + ".class");
            if (!classFile.exists()) {
                throw new RuntimeException(
                        "Plugin " + classesExn.getUniqueId() + " requires properties for non-existent class: "
                                + classname + " (looked in: " + classFile.getPath());
            }
            Map<String, Set<String>> propertySetMap = propertyClassMapping.get(classname);
            if (propertySetMap == null) {
                propertySetMap = new HashMap<String, Set<String>>();
                String propertiesFilename = classname.replace(".", "/") + ".properties";
                File propertiesFile = new File(propertiesFilename);
                File previousPropertiesFile = new File(myWARPluginDir, propertiesFile.getPath());
                if (previousPropertiesFile.exists()) {
                    Properties props = loadProperties(previousPropertiesFile);
                    for (Object key : props.keySet()) {
                        Set<String> valueSet = new HashSet<String>();
                        String[] values = ((String) props.get(key)).split(",");
                        for (String v : values) {
                            valueSet.add(v);
                        }
                        propertySetMap.put((String) key, valueSet);
                    }
                }
                propertyClassMapping.put(classname, propertySetMap);
            }
            String name = classesExn.getParameter("name").valueAsString();
            Set<String> valueSet = propertySetMap.get(name);
            if (valueSet == null) {
                valueSet = new HashSet<String>();
                propertySetMap.put(name, valueSet);
            }
            valueSet.add(classesExn.getParameter("value").valueAsString());
        }
        for (String classname : propertyClassMapping.keySet()) {
            Properties props = new Properties();
            Map<String, Set<String>> propertySetMap = propertyClassMapping.get(classname);
            for (String name : propertySetMap.keySet()) {
                final List<String> sequence = propertySequenceMapping.get(classname + "." + name);
                List<String> orderedValues = new ArrayList<String>();
                List<String> unorderedValues = new ArrayList<String>();
                for (String value : propertySetMap.get(name)) {
                    if (sequence != null && sequence.contains(value)) {
                        orderedValues.add(value);
                    } else {
                        unorderedValues.add(value);
                    }
                }
                if (sequence != null) {
                    Comparator<String> comparator = new Comparator<String>() {
                        public int compare(String string1, String string2) {
                            return new Integer(sequence.indexOf(string1))
                                    .compareTo(new Integer(sequence.indexOf(string2)));
                        };
                    };
                    Collections.sort(orderedValues, comparator);
                }
                List<String> allValues = new ArrayList<String>();
                allValues.addAll(orderedValues);
                allValues.addAll(unorderedValues);
                Iterator<String> it = allValues.iterator();
                StringBuffer buff = new StringBuffer();
                while (it.hasNext()) {
                    buff.append(it.next());
                    if (it.hasNext()) {
                        buff.append(",");
                    }
                }
                String value = buff.toString();
                props.put(name, value);
            }
            String propertiesFilename = classname.replace(".", "/") + ".properties";
            File propertiesFile = new File(myWARPluginDir, "WEB-INF/classes/" + propertiesFilename);
            logger.debug("Write " + propertiesFile.getPath());
            propertiesFile.getParentFile().mkdirs();
            storeProperties(props, propertiesFile);
        }
    }

    protected void assembleRemoteContributions(PluginDescriptor pd) {
        ExtensionPoint remoteProductDirExnPt = pd.getExtensionPoint(EXNPT_REMOTE_PRODUCT_DIR);
        if (remoteProductDirExnPt != null
                && getAbstractWARPluginDescriptor().getId().equals(remoteProductDirExnPt.getParentPluginId())) {
            String dirname = remoteProductDirExnPt.getParameterDefinition("dir").getDefaultValue();
            String evaluatedDirname = evaluate(dirname, pd);
            if (evaluatedDirname == null) {
                throw new RuntimeException(
                        remoteProductDirExnPt.getUniqueId() + " default dir parameter evaluated to null");
            }
            File myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
            File dir = new File(myWARPluginDir, evaluatedDirname);
            dir.mkdirs();
            assembleRemoteFiles(pd, dir);
            assembleRemoteZips(pd, dir);
        }
    }

    protected void assembleRemoteFiles(PluginDescriptor pd, File remoteProductDir) {
        ExtensionPoint remoteFileExnPt = pd.getExtensionPoint(EXNPT_REMOTE_FILE);
        if (remoteFileExnPt != null
                && getAbstractWARPluginDescriptor().getId().equals(remoteFileExnPt.getParentPluginId())) {
            for (Extension exn : remoteFileExnPt.getConnectedExtensions()) {
                for (Parameter fileParam : exn.getParameters("file")) {
                    String filePath = fileParam.valueAsString();
                    if (filePath == null) {
                        throw new RuntimeException(exn.getUniqueId() + " requires a file parameter value");
                    }
                    File jar = getFilePath(exn.getDeclaringPluginDescriptor(), filePath);
                    try {
                        FileUtils.copyFileToDirectory(jar, remoteProductDir);
                        colocateChecksum(new File(remoteProductDir, jar.getName()));
                    } catch (IOException ex) {
                        throw new RuntimeException("Could not copy the remote file " + jar.getPath() + " to: "
                                + remoteProductDir.getPath(), ex);
                    }
                }
            }
        }
    }

    protected void assembleRemoteIncludes(PluginDescriptor pd, File remoteProductDir) {
        ExtensionPoint remoteIncludesExnPt = pd.getExtensionPoint(EXNPT_REMOTE_INCLUDES);
        if (remoteIncludesExnPt != null
                && getDescriptor().getId().equals(remoteIncludesExnPt.getParentPluginId())) {
            if (remoteIncludesExnPt.getParameterDefinition("jar") == null) {
                throw new RuntimeException(remoteIncludesExnPt.getUniqueId() + " must have a jar parameter");
            }
            String jarname = remoteIncludesExnPt.getParameterDefinition("jar").getDefaultValue();
            String evaluatedJarname = evaluate(jarname);
            File remoteFile = new File(remoteProductDir, evaluatedJarname);
            if (remoteFile.exists()) {
                remoteFile.delete();
            }
            remoteFile.getParentFile().mkdirs();
            for (Extension exn : remoteIncludesExnPt.getConnectedExtensions()) {
                String dirname = exn.getParameter("sourceDir").valueAsString();
                if (dirname == null) {
                    throw new RuntimeException(exn.getUniqueId() + " requires a dir parameter value");
                }
                File srcDir = getFilePath(exn.getDeclaringPluginDescriptor(), dirname);
                String includes = null;
                if (exn.getParameter("includes") != null) {
                    includes = exn.getParameter("includes").valueAsString();
                }
                List<String> include = new ArrayList<String>();
                for (Parameter includeParam : exn.getParameters("include")) {
                    String inc = includeParam.valueAsString();
                    if (inc == null) {
                        throw new RuntimeException(exn.getUniqueId() + " requires an include parameter value");
                    }
                    include.add(inc);
                }
                String excludes = null;
                if (exn.getParameter("excludes") != null) {
                    excludes = exn.getParameter("excludes").valueAsString();
                }
                List<String> exclude = new ArrayList<String>();
                for (Parameter excludeParam : exn.getParameters("exclude")) {
                    String exc = excludeParam.valueAsString();
                    if (exc == null) {
                        throw new RuntimeException(exn.getUniqueId() + " requires an exclude parameter value");
                    }
                    exclude.add(exc);
                }
                TolvenJar.jarDir(srcDir, remoteFile, includes, include, excludes, exclude, true);
            }
            if (remoteFile.exists() && remoteFile.length() > 0) {
                colocateChecksum(remoteFile);
            } else if (remoteFile.exists() && remoteFile.length() == 0) {
                remoteFile.delete();
            }
        }
    }

    protected void assembleRemoteZips(PluginDescriptor pd, File remoteProductDir) {
        ExtensionPoint remoteFileExnPt = pd.getExtensionPoint(EXNPT_REMOTE_FILE);
        if (remoteFileExnPt != null
                && getAbstractWARPluginDescriptor().getId().equals(remoteFileExnPt.getParentPluginId())) {
            for (Extension exn : remoteFileExnPt.getConnectedExtensions()) {
                for (Parameter zipParam : exn.getParameters("zip")) {
                    String srcDirname = zipParam.getSubParameter("sourceDir").valueAsString();
                    File srcDir = getFilePath(exn.getDeclaringPluginDescriptor(), srcDirname);
                    if (srcDir == null) {
                        throw new RuntimeException(exn.getUniqueId() + " requires a sourceDir parameter value");
                    }
                    String includes = null;
                    if (zipParam.getSubParameter("includes") != null) {
                        includes = zipParam.getSubParameter("includes").valueAsString();
                    }
                    List<String> include = new ArrayList<String>();
                    for (Parameter includeParam : zipParam.getSubParameters("include")) {
                        String inc = includeParam.valueAsString();
                        if (inc == null) {
                            throw new RuntimeException(exn.getUniqueId() + " requires an include parameter value");
                        }
                        include.add(inc);
                    }
                    String excludes = null;
                    if (zipParam.getSubParameter("excludes") != null) {
                        excludes = zipParam.getSubParameter("excludes").valueAsString();
                    }
                    List<String> exclude = new ArrayList<String>();
                    for (Parameter excludeParam : zipParam.getSubParameters("exclude")) {
                        String exc = excludeParam.valueAsString();
                        if (exc == null) {
                            throw new RuntimeException(exn.getUniqueId() + " requires an exclude parameter value");
                        }
                        exclude.add(exc);
                    }
                    String destFilename = zipParam.getSubParameter("destFile").valueAsString();
                    if (destFilename == null) {
                        throw new RuntimeException(exn.getUniqueId() + " requires a file parameter value");
                    }
                    File dest = new File(remoteProductDir, destFilename);
                    TolvenZip.zip(srcDir, dest, includes, include, excludes, exclude);
                    colocateChecksum(dest);
                }
            }
        }
    }

    /**
     * An inclusion tags for java scripts
     * 
     * @param pd
     * @throws IOException
     */
    protected void assembleScriptIncludes(PluginDescriptor pd) throws IOException {
        File myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
        Map<File, Map<String, Object>> fileIncludesMap = new HashMap<File, Map<String, Object>>();
        for (ExtensionPoint fileIncludeExnPt : pd.getExtensionPoints()) {
            if (EXNPT_FILE_INCLUDE.equals(fileIncludeExnPt.getParentExtensionPointId())
                    && fileIncludeExnPt.getParentPluginId().equals(getDescriptor().getId())) {
                String matchExp = fileIncludeExnPt.getParameterDefinition("matchExp").getDefaultValue();
                if (matchExp == null) {
                    throw new RuntimeException(
                            fileIncludeExnPt.getUniqueId() + " must supply a default value for matchExp");
                }
                String targetFilename = fileIncludeExnPt.getParameterDefinition("targetFile").getDefaultValue();
                if (targetFilename == null) {
                    throw new RuntimeException(
                            fileIncludeExnPt.getUniqueId() + " must supply a default value for targetFile");
                }
                File targetFile = new File(myWARPluginDir, targetFilename);
                if (!targetFile.exists()) {
                    throw new RuntimeException(targetFilename + " is not available for the war file");
                }
                //sort the java script file includes
                PluginPropertyDetail propertyDetail = getPluginProperty(pd.getId(),
                        EXTENSIONPOINT_SCRIPT_SORT_ORDER);
                Collection<Extension> extensions = fileIncludeExnPt.getConnectedExtensions();
                if (propertyDetail != null && propertyDetail.getValue() != null
                        && propertyDetail.getValue().trim().length() > 0)
                    extensions = sortScriptIncludes(extensions, propertyDetail);
                for (Extension exn : extensions) {
                    String includeFilename = exn.getParameter("includeFile").valueAsString();
                    if (includeFilename == null) {
                        throw new RuntimeException(exn.getUniqueId() + " must supply a value for includeFile");
                    }
                    File includeFile = getFilePath(exn.getDeclaringPluginDescriptor(), includeFilename);
                    if (!includeFile.exists()) {
                        throw new RuntimeException("Could not locate: " + includeFile.getPath());
                    }
                    Map<String, Object> info = fileIncludesMap.get(targetFile);
                    if (info == null) {
                        info = new HashMap<String, Object>();
                        info.put("matchExp", matchExp);
                        List<File> includeFiles = new ArrayList<File>();
                        info.put("includeFiles", includeFiles);
                        fileIncludesMap.put(targetFile, info);
                    }
                    List<File> includeFiles = (List<File>) info.get("includeFiles");
                    includeFiles.add(includeFile);
                }
            }
        }
        for (File targetFile : fileIncludesMap.keySet()) {
            StringBuffer buff = new StringBuffer();
            Map<String, Object> info = fileIncludesMap.get(targetFile);
            List<File> includeFiles = (List<File>) info.get("includeFiles");
            //Collections.sort(includeFiles);
            for (File includeFile : includeFiles) {
                buff.append(FileUtils.readFileToString(includeFile) + "\n");
            }
            if (buff.toString().length() > 0) {
                TolvenReplaceRegExp.replaceregexp(targetFile, (String) info.get("matchExp"), buff.toString());
            }
        }
    }

    protected void assembleTagLibs(PluginDescriptor pd) throws IOException, XMLStreamException {
        File myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
        PluginDescriptor tagLibPD = getTagLibPluginDescriptor();
        ExtensionPoint tagLibExnPt = tagLibPD.getExtensionPoint("taglib");
        if (tagLibExnPt == null) {
            throw new RuntimeException(tagLibPD.getUniqueId() + " must have a taglib extension point");
        }
        for (Extension taglibExn : tagLibExnPt.getConnectedExtensions()) {
            if (pd.getId().equals(taglibExn.getParameter("target-plugin-id").valueAsString())) {
                addTagInfo(taglibExn, myWARPluginDir);
            }
        }
    }

    private void assembleWARClasses(Extension exn, File pluginWARDir) throws IOException {
        String srcDirname = exn.getParameter("dir").valueAsString();
        File srcDir = getFilePath(exn.getDeclaringPluginDescriptor(), srcDirname);
        assembleWARClasses(srcDir, pluginWARDir);
    }

    private void assembleWARClasses(File srcDir, File pluginWARDir) throws IOException {
        File destDir = new File(pluginWARDir + "/WEB-INF/classes");
        logger.debug("Copy " + srcDir.getPath() + " to " + destDir.getPath());
        destDir.mkdirs();
        FileUtils.copyDirectory(srcDir, destDir);
    }

    @Deprecated
    protected void assembleWebDirectory(Extension exn, File destDir) throws IOException {
        Parameter srcDirnameParam = exn.getParameter("sourceDirectory");
        if (srcDirnameParam == null || srcDirnameParam.valueAsString().trim().length() == 0) {
            throw new RuntimeException(exn.getUniqueId() + " does not have a sourceDirectory parameter value");
        }
        String srcDirname = srcDirnameParam.valueAsString();
        File srcDir = getFilePath(exn.getDeclaringPluginDescriptor(), srcDirname);
        logger.debug("Copy " + srcDir.getPath() + " to " + destDir.getPath());
        FileUtils.copyDirectory(srcDir, destDir);
    }

    @Deprecated
    protected void assembleWebDirectory(PluginDescriptor pd) throws IOException {
        File myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
        PluginDescriptor abstractWarPD = getAbstractWARPluginDescriptor();
        ExtensionPoint abstractExnPt = abstractWarPD.getExtensionPoint("webDirectory");
        if (abstractExnPt == null) {
            throw new RuntimeException(
                    abstractWarPD.getUniqueId() + " does not have a webDirectory extension point");
        }
        /*
         * Extensions directly connected to the webDirectory extension-point
         * These contribute a new webDirectory to the war file
         */
        for (Extension exn : abstractExnPt.getConnectedExtensions()) {
            Parameter pluginIdParam = exn.getParameter("target-plugin-id");
            if (pluginIdParam == null || pluginIdParam.valueAsString() == null
                    || pluginIdParam.valueAsString().trim().length() == 0) {
                throw new RuntimeException(
                        exn.getUniqueId() + " does not have a default parameter target-plugin-id value");
            }
            if (pluginIdParam.valueAsString().equals(pd.getId())) {
                Parameter functionParam = exn.getParameter("function");
                if (functionParam == null || functionParam.valueAsString() == null
                        || functionParam.valueAsString().trim().length() == 0) {
                    throw new RuntimeException(exn.getUniqueId() + " does not have a parameter function value");
                }
                File destDir = new File(myWARPluginDir, functionParam.valueAsString());
                assembleWebDirectory(exn, destDir);
            }
        }
        /*
         * Extensions directly connected to the descendent of the webDirectory extension-point
         * These are webDirectories offered directly by the war file
         */
        for (ExtensionPoint descendentExPt : abstractExnPt.getDescendants()) {
            ParameterDefinition pluginIdParamDef = descendentExPt.getParameterDefinition("target-plugin-id");
            if (pluginIdParamDef == null || pluginIdParamDef.getDefaultValue() == null
                    || pluginIdParamDef.getDefaultValue().trim().length() == 0) {
                throw new RuntimeException(descendentExPt.getUniqueId()
                        + " does not have a default parameter-def target-plugin-id value");
            }
            if (pluginIdParamDef.getDefaultValue().equals(pd.getId())) {
                ParameterDefinition functionParamDef = descendentExPt.getParameterDefinition("function");
                if (functionParamDef == null || functionParamDef.getDefaultValue() == null
                        || functionParamDef.getDefaultValue().trim().length() == 0) {
                    throw new RuntimeException(
                            descendentExPt.getUniqueId() + " does not have a default parameter-def function value");
                }
                String defaultWebDirname = functionParamDef.getDefaultValue();
                File destDir = new File(myWARPluginDir, defaultWebDirname);
                for (Extension exn : descendentExPt.getConnectedExtensions()) {
                    assembleWebDirectory(exn, destDir);
                }
            }
        }
    }

    protected void assembleWebDirectoryAdaptor(Extension exn, File destDir) throws IOException {
        for (File srcDir : getAdaptorFiles(exn)) {
            logger.debug("Copy " + srcDir.getPath() + " to " + destDir.getPath());
            FileUtils.copyDirectory(srcDir, destDir);
        }
    }

    protected void assembleWebDirectoryAdaptors(PluginDescriptor pd, String contextId) throws IOException {
        File myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
        PluginDescriptor abstractWarPD = getAbstractWARPluginDescriptor();
        ExtensionPoint abstractExnPt = abstractWarPD.getExtensionPoint(EXNPT_WEBDIRECTORY_ADPTR);
        for (ExtensionPoint descendentExPt : abstractExnPt.getDescendants()) {
            if (descendentExPt.getDeclaringPluginDescriptor().getId().equals(pd.getId())) {
                for (Extension exn : descendentExPt.getConnectedExtensions()) {
                    String targetDirectory = exn.getParameter("targetWebDirectory").valueAsString();
                    if (targetDirectory == null || targetDirectory.trim().length() == 0) {
                        throw new RuntimeException(
                                exn.getUniqueId() + " does not have a value for a targetDirectory paramater");
                    }
                    File destDir = new File(myWARPluginDir, targetDirectory);
                    assembleWebDirectoryAdaptor(exn, destDir);
                }
            }
        }
    }

    protected void assembleWebInfClassesAdaptors(PluginDescriptor pd, String contextId) throws IOException {
        ExtensionPoint exnPt = pd.getExtensionPoint(EXNPT_WEBINFCLASSES_ADPTR);
        if (exnPt != null) {
            File myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
            for (Extension exn : exnPt.getConnectedExtensions()) {
                for (File srcDir : getAdaptorFiles(exn)) {
                    assembleWARClasses(srcDir, myWARPluginDir);
                }
            }
        }
    }

    protected void assembleWebInfLibAdaptors(PluginDescriptor pd, String contextId) throws IOException {
        ExtensionPoint exnPt = pd.getExtensionPoint(EXNPT_WEBINFLIB_ADPTR);
        if (exnPt != null) {
            File myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
            File destDir = new File(myWARPluginDir + "/WEB-INF/lib");
            destDir.getParentFile().mkdirs();
            for (Extension exn : exnPt.getConnectedExtensions()) {
                for (File src : getAdaptorFiles(exn)) {
                    logger.debug("Copy " + src.getPath() + " to " + destDir.getPath());
                    FileUtils.copyFileToDirectory(src, destDir);
                }
            }
        }
    }

    @Deprecated
    protected void assembleWebInfLibs(PluginDescriptor pd) throws IOException {
        File myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
        File destDir = new File(myWARPluginDir + "/WEB-INF/lib");
        destDir.getParentFile().mkdirs();
        for (ExtensionPoint extnPt : pd.getExtensionPoints()) {
            if (EXNPT_WEBINFLIB.equals(extnPt.getParentExtensionPointId())
                    && extnPt.getParentPluginId().equals(getDescriptor().getId())) {
                for (Extension exn : extnPt.getConnectedExtensions()) {
                    String srcName = exn.getParameter("jar").valueAsString();
                    File src = getFilePath(exn.getDeclaringPluginDescriptor(), srcName);
                    logger.debug("Copy " + src.getPath() + " to " + destDir.getPath());
                    FileUtils.copyFileToDirectory(src, destDir);
                }
            }
        }
    }

    protected void assembleWebRootFilesAdaptors(PluginDescriptor pd, String contextId) throws IOException {
        ExtensionPoint exnPt = pd.getExtensionPoint(EXNPT_WEBROOTFILES_ADPTR);
        if (exnPt != null) {
            File myWARPluginDir = new File(getPluginTmpDir(), pd.getId());
            //For the root directory, only copy files. Otherwise a webdirectory can be used
            FileFilter ff = new FileFilter() {
                public boolean accept(File file) {
                    return file.isFile();
                }
            };
            for (Extension exn : exnPt.getConnectedExtensions()) {
                for (File srcDir : getAdaptorFiles(exn)) {
                    File destDir = new File(myWARPluginDir + "/");
                    logger.debug("Copy " + srcDir.getPath() + " to " + destDir.getPath());
                    destDir.mkdirs();
                    FileUtils.copyDirectory(srcDir, destDir, ff);
                }
            }
        }
    }

    private void colocateChecksum(File file) {
        String md5sum;
        try {
            md5sum = TolvenMessageDigest.checksum(file.toURI().toURL(), MESSAGE_DIGEST_ALGORITHM);
        } catch (MalformedURLException ex) {
            throw new RuntimeException("Could not create URL for file: " + file.getPath(), ex);
        }
        File checksumFile = new File(file.getParentFile(), file.getName() + ".md5");
        try {
            FileUtils.writeStringToFile(checksumFile, md5sum);
        } catch (IOException ex) {
            throw new RuntimeException("Could not write checksum file: " + file.getPath(), ex);
        }
    }

    @Override
    protected void doStart() throws Exception {
        logger.debug("*** start ***");
        logger.debug("deleting: " + getPluginTmpDir());
        FileUtils.deleteDirectory(getPluginTmpDir());
        getPluginTmpDir().mkdirs();
    }

    @Override
    protected void doStop() throws Exception {
        logger.debug("*** stop ***");
    }

    @Override
    public void execute(String[] args) throws Exception {
        logger.debug("*** execute ***");
        CommandLine commandLine = getCommandLine(args);
        String warPluginId = commandLine.getOptionValue(CMD_WAR_PLUGIN_OPTION);
        String webURI = commandLine.getOptionValue(CMD_WEB_URI_OPTION);
        String contextId = commandLine.getOptionValue(CMD_CONTEXT_ID_OPTION);
        String destDirname = commandLine.getOptionValue(CMD_DESTDIR_OPTION);
        if (destDirname == null) {
            destDirname = new File(getStageDir(), warPluginId).getPath();
        }
        PluginDescriptor warPD = getManager().getRegistry().getPluginDescriptor(warPluginId);
        List<ExtensionPoint> declExnPts = new ArrayList<ExtensionPoint>();
        for (ExtensionPoint exnPt : warPD.getExtensionPoints()) {
            if (EXNPT_WARMODULE_DECL.equals(exnPt.getParentExtensionPointId())) {
                declExnPts.add(exnPt);
            }
        }
        if (declExnPts.isEmpty()) {
            throw new RuntimeException(
                    warPD.getUniqueId() + " does not define a warModule-declaration extension-point");
        }
        if (contextId == null) {
            if (declExnPts.size() > 1) {
                throw new RuntimeException(
                        warPD.getId() + " has more than one warModule-declaration. A context-id must be provided");
            }
            if (declExnPts.get(0).getParameterDefinition("context-id") == null) {
                throw new RuntimeException(declExnPts.get(0).getUniqueId() + " must define a context-id parameter");
            }
            contextId = declExnPts.get(0).getParameterDefinition("context-id").getDefaultValue();
        }
        File myPluginTmpDir = getPluginTmpDir();
        File myWARPluginDir = new File(myPluginTmpDir, warPD.getId());
        File srcWARFile = getFilePath(warPD, webURI);
        if (srcWARFile.exists()) {
            logger.debug("Unjar " + srcWARFile.getPath() + " to " + myWARPluginDir.getPath());
            FileUtils.deleteDirectory(myWARPluginDir);
            myWARPluginDir.mkdirs();
            TolvenJar.unjar(srcWARFile, myWARPluginDir);
        } else {
            logger.debug(srcWARFile + " does not exist");
        }
        executeRequiredPlugins(warPluginId);
        assembleWebDirectory(warPD);
        assembleWebRootFilesAdaptors(warPD, contextId);
        assembleWebDirectoryAdaptors(warPD, contextId);
        assembleScriptIncludes(warPD);
        assembleTagLibs(warPD);
        assembleMetaInf(warPD);
        assembleWebInfLibs(warPD);
        assembleWebInfLibAdaptors(warPD, contextId);
        assembleClasses(warPD);
        assembleWebInfClassesAdaptors(warPD, contextId);
        assembleProperties(warPD);
        assembleRemoteContributions(warPD);
        //addRemoteJars(warPD);
        File destinationWARFile = new File(destDirname, webURI);
        destinationWARFile.getParentFile().mkdirs();
        logger.debug("Jar " + myWARPluginDir.getPath() + " to " + destinationWARFile.getPath());
        TolvenJar.jar(myWARPluginDir, destinationWARFile);
    }

    protected void executeRequiredPlugins(String warPluginId) throws Exception {
        File warPluginTmpDir = new File(getPluginTmpDir(), warPluginId);
        File destDir = new File(warPluginTmpDir, "WEB-INF");
        destDir.mkdirs();
        String argString = "-warPlugin " + warPluginId + " -destDir " + destDir;
        execute("org.tolven.assembler.webxml", argString.split(" "));
        execute("org.tolven.assembler.facesconfig", argString.split(" "));
    }

    protected PluginDescriptor getAbstractWARPluginDescriptor() {
        ExtensionPoint exnPt = getDescriptor().getExtensionPoint(EXNPT_ABSTRACT_WAR);
        String parentPluginId = exnPt.getParentPluginId();
        PluginDescriptor pd = getManager().getRegistry().getPluginDescriptor(parentPluginId);
        return pd;
    }

    private List<File> getAdaptorFiles(Extension exn) {
        String pluginId = exn.getParameter(EXNPT_SRC_PLUGIN_ID).valueAsString();
        if (pluginId == null || pluginId.trim().length() == 0) {
            throw new RuntimeException(
                    "No parameter value for " + EXNPT_SRC_PLUGIN_ID + " found in " + exn.getUniqueId());
        }
        String exnPtId = exn.getParameter(EXNPT_ID).valueAsString();
        if (exnPtId == null || exnPtId.trim().length() == 0) {
            throw new RuntimeException("No parameter value for " + EXNPT_ID + " found in " + exn.getUniqueId());
        }
        ExtensionPoint exnPt = getManager().getRegistry().getExtensionPoint(pluginId + "@" + exnPtId);
        List<File> files = new ArrayList<File>();
        for (ParameterDefinition paramDef : exnPt.getParameterDefinitions()) {
            String filename = paramDef.getDefaultValue();
            if (filename == null || filename.trim().length() == 0) {
                throw new RuntimeException("No default-value for parameter-def found in " + exnPt.getUniqueId());
            }
            File src = getFilePath(exnPt.getDeclaringPluginDescriptor(), filename);
            files.add(src);
        }
        return files;
    }

    private CommandLine getCommandLine(String[] args) {
        GnuParser parser = new GnuParser();
        try {
            return parser.parse(getCommandOptions(), args, true);
        } catch (ParseException ex) {
            HelpFormatter formatter = new HelpFormatter();
            formatter.printHelp(getClass().getName(), getCommandOptions());
            throw new RuntimeException("Could not parse command line for: " + getClass().getName(), ex);
        }
    }

    private Options getCommandOptions() {
        Options cmdLineOptions = new Options();
        Option warPluginOption = new Option(CMD_WAR_PLUGIN_OPTION, CMD_WAR_PLUGIN_OPTION, true, "war plugin");
        warPluginOption.setRequired(true);
        cmdLineOptions.addOption(warPluginOption);
        Option webURIPluginOption = new Option(CMD_WEB_URI_OPTION, CMD_WEB_URI_OPTION, true, "web-uri");
        webURIPluginOption.setRequired(true);
        cmdLineOptions.addOption(webURIPluginOption);
        Option contextIdOption = new Option(CMD_CONTEXT_ID_OPTION, CMD_CONTEXT_ID_OPTION, true,
                CMD_CONTEXT_ID_OPTION);
        cmdLineOptions.addOption(contextIdOption);
        Option destDirPluginOption = new Option(CMD_DESTDIR_OPTION, CMD_DESTDIR_OPTION, true,
                "destination directory");
        cmdLineOptions.addOption(destDirPluginOption);
        return cmdLineOptions;
    }

    protected PluginDescriptor getTagLibPluginDescriptor() {
        ExtensionPoint exnPt = getDescriptor().getExtensionPoint(TAGLIB_EXTENSIONPOINT);
        String parentPluginId = exnPt.getParentPluginId();
        PluginDescriptor pd = getManager().getRegistry().getPluginDescriptor(parentPluginId);
        return pd;
    }

    private Map<String, List<String>> propertySequenceMapping(PluginDescriptor pd) {
        Map<String, List<String>> propertySequenceMapping = new HashMap<String, List<String>>();
        Set<Extension> classesExns = new HashSet<Extension>();
        classesExns.addAll(getChildExtensions(EXNPT_WAR_PROPERTY_SEQUENCE, pd));
        PluginDescriptor abstractWARPD = getAbstractWARPluginDescriptor();
        ExtensionPoint propertyExnPt = abstractWARPD.getExtensionPoint(EXNPT_WAR_PROPERTY_SEQUENCE);
        classesExns.addAll(propertyExnPt.getConnectedExtensions());
        for (Extension classesExn : classesExns) {
            Parameter pluginIdParam = classesExn.getParameter("target-plugin-id");
            if (pluginIdParam == null || pluginIdParam.valueAsString().trim().length() == 0) {
                throw new RuntimeException(
                        classesExn.getUniqueId() + " does not have a target-plugin-id parameter value");
            }
            if (pd.getId().equals(pluginIdParam.valueAsString())) {
                String classname = classesExn.getParameter("class").valueAsString();
                String name = classesExn.getParameter("name").valueAsString();
                String key = classname + "." + name;
                if (propertySequenceMapping.get(key) == null) {
                    String sequence = null;
                    if (classesExn.getParameter("sequence") != null) {
                        sequence = classesExn.getParameter("sequence").valueAsString();
                    }
                    String evaluatedSequence = null;
                    if (sequence != null) {
                        evaluatedSequence = evaluate(sequence, classesExn.getDeclaringPluginDescriptor());
                    }
                    if (evaluatedSequence == null && classesExn.getParameter("defaultSequence") != null) {
                        String defaultSequence = classesExn.getParameter("defaultSequence").valueAsString();
                        evaluatedSequence = evaluate(defaultSequence, classesExn.getDeclaringPluginDescriptor());
                    }
                    if (evaluatedSequence != null) {
                        List<String> list = new ArrayList<String>();
                        for (String string : evaluatedSequence.split(",")) {
                            list.add(string);
                        }
                        propertySequenceMapping.put(key, list);
                    }
                } else {
                    throw new RuntimeException(classesExn.getUniqueId() + " is redefining a propertySequence for: "
                            + classname + " when one exists for: " + key);
                }
            }
        }
        return propertySequenceMapping;
    }

    /** Sort the javascript file includes based on the order defined in tolven-config/plugins.xml
     * an example sorting order configuration would be
     * <plugin id="org.tolven.component.tolvenweb">
    <root />
      <property name="scriptsOrder" value="org.tolven.component.tolvenweb@prototype,org.tolven.component.tolvenweb@datastructures"/>
       </plugin> 
        
     * @param extensions
     * @param propertyDetail
     * @return
     */
    public List<Extension> sortScriptIncludes(Collection<Extension> extensions,
            PluginPropertyDetail propertyDetail) {
        List<Extension> sortedExtensions = new ArrayList<Extension>(extensions.size());
        Map<String, Extension> namedExtensions = getNamedExtensions(extensions);
        String[] scriptsOrder = propertyDetail.getValue().split(",");
        if (scriptsOrder != null) {
            for (String detail : scriptsOrder) {
                if (namedExtensions.containsKey(detail)) {
                    sortedExtensions.add(namedExtensions.get(detail));
                    namedExtensions.remove(detail);
                }
            }
        }
        //add unordered extensions at the bottom
        sortedExtensions.addAll(namedExtensions.values());
        return sortedExtensions;
    }

    private Map<String, Extension> getNamedExtensions(Collection<Extension> extensions) {
        Map<String, Extension> namedExtensions = new HashMap<String, Extension>(extensions.size());
        for (Extension extension : extensions) {
            namedExtensions.put(extension.getUniqueId(), extension);
        }
        return namedExtensions;
    }

    /** Method to find a property in a plugin in tolven-config/plugins.xml. 
     * This method can find the property only if it exists at one level deep. 
     * @param pluginName
     * @param propertyName
     * @return
     */
    private PluginPropertyDetail getPluginProperty(String pluginName, String propertyName) {
        ConfigPluginsWrapper pluginsWrapper = getPluginsWrapper();
        Plugins plugins = pluginsWrapper.getPlugins();
        for (PluginDetail pluginDetail : plugins.getPlugin()) {
            if (pluginDetail.getId().equalsIgnoreCase(pluginName)) {
                for (PluginPropertyDetail propertyDetail : pluginDetail.getProperty()) {
                    if (propertyDetail.getName().equalsIgnoreCase(propertyName))
                        return propertyDetail;
                }
            }
        }
        return null;
    }
}