You are viewing a plain text version of this content. The canonical link for it is here.
Posted to pluto-scm@portals.apache.org by dd...@apache.org on 2004/10/17 05:47:39 UTC

svn commit: rev 54948 - in portals/pluto/branches/pluto-1.1/deploy: . src src/bin src/java src/java/org src/java/org/apache src/java/org/apache/pluto src/java/org/apache/pluto/deploy src/java/org/apache/pluto/deploy/ant src/java/org/apache/pluto/deploy/cli src/java/org/apache/pluto/deploy/maven

Author: ddewolf
Date: Sat Oct 16 20:47:38 2004
New Revision: 54948

Added:
   portals/pluto/branches/pluto-1.1/deploy/
   portals/pluto/branches/pluto-1.1/deploy/README
   portals/pluto/branches/pluto-1.1/deploy/deploy.iml
   portals/pluto/branches/pluto-1.1/deploy/maven.xml
   portals/pluto/branches/pluto-1.1/deploy/project.xml
   portals/pluto/branches/pluto-1.1/deploy/src/
   portals/pluto/branches/pluto-1.1/deploy/src/bin/
   portals/pluto/branches/pluto-1.1/deploy/src/bin/assemble.bat
   portals/pluto/branches/pluto-1.1/deploy/src/java/
   portals/pluto/branches/pluto-1.1/deploy/src/java/org/
   portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/
   portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/
   portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/deploy/
   portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/deploy/Assembler.java
   portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/deploy/Deployer.java
   portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/deploy/ant/
   portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/deploy/cli/
   portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/deploy/cli/AssemblerCLI.java
   portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/deploy/maven/
Log:
Adding the first stab at a deployer for Pluto 1.1 branch.

Added: portals/pluto/branches/pluto-1.1/deploy/README
==============================================================================
--- (empty file)
+++ portals/pluto/branches/pluto-1.1/deploy/README	Sat Oct 16 20:47:38 2004
@@ -0,0 +1,26 @@
+The purpose of the Pluto 1.1 deploy tool is to provide
+the following facilities for portlet development:
+
+1) Ant task with which wars may be created which
+   contain all the necessary resources needed to
+   deploy the webapp as a portlet application
+   within pluto.
+
+   usage: tbd
+
+
+2) Maven goal(s) with which wars may be created
+   which contain all of the necessary resources
+   needed to deploy the webapp as a portlet
+   application within pluto.
+
+   usage: maven pluto:assemble -- assembles web application (updates web.xml)
+          maven pluto:deploy  -- deploys to configured app server
+
+3) Command line utility with which wars may be
+   updated to contain all of the resources 
+   necessary to deploy the webapp as a portlet
+   application within pluto.
+
+   usage: assemble [OPTIONS] warfile|exploded
+          deploy [OPTIONS] warfile|exploded

Added: portals/pluto/branches/pluto-1.1/deploy/deploy.iml
==============================================================================
--- (empty file)
+++ portals/pluto/branches/pluto-1.1/deploy/deploy.iml	Sat Oct 16 20:47:38 2004
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module version="4" relativePaths="true" type="JAVA_MODULE">
+  <component name="ModuleRootManager" />
+  <component name="NewModuleRootManager">
+    <output url="file://$MODULE_DIR$/target/classes" />
+    <exclude-output />
+    <exclude-exploded />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/src/java" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="binding" />
+    <orderEntry type="library" name="commons-cli-1.0" level="application" />
+    <orderEntryProperties />
+  </component>
+</module>
+

Added: portals/pluto/branches/pluto-1.1/deploy/maven.xml
==============================================================================
--- (empty file)
+++ portals/pluto/branches/pluto-1.1/deploy/maven.xml	Sat Oct 16 20:47:38 2004
@@ -0,0 +1,51 @@
+<!-- 
+/*
+ * Copyright 2001-2004 The Apache Software Foundation.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ -->
+<project default="jar:jar"
+  xmlns:j="jelly:core"
+  xmlns:u="jelly:util"
+  xmlns:x="jelly:xml"
+  xmlns:ant="jelly:ant"
+  xmlns:maven="jelly:maven">
+
+  <goal name="executable:build" prereqs="jar:jar">
+    <j:set var="tag" value="${pom.artifactId}-${pom.currentVersion}" />
+    <j:set var="executableDir" value="${maven.build.dir}/executable/${tag}" />
+
+    <ant:mkdir dir="${executableDir}" />
+    <ant:mkdir dir="${executableDir}/bin" />
+    <ant:mkdir dir="${executableDir}/lib" />
+    
+    <ant:copy todir="${executableDir}/bin">
+      <ant:fileset dir="${basedir}/src/bin" />
+    </ant:copy>
+    
+    <j:forEach var="lib" items="${pom.artifacts}">
+      <j:set var="dep" value="${lib.dependency}" />
+      <j:if test="${dep.type=='jar'}">
+        <j:choose>
+            <ant:copy todir="${executableDir}/lib" file="${lib.path}" />
+        </j:choose>
+      </j:if>
+    </j:forEach>
+    <ant:copy todir="${executableDir}/lib" file="${maven.build.dir}/${maven.final.name}.jar" />
+
+    <ant:fixcrlf srcdir="${executableDir}" eol="crlf" includes="**/*.bat" />
+    <ant:fixcrlf srcdir="${executableDir}" eol="lf" includes="**/*.sh,**/assemble,**/deploy" />
+  </goal>
+</project>
+

Added: portals/pluto/branches/pluto-1.1/deploy/project.xml
==============================================================================
--- (empty file)
+++ portals/pluto/branches/pluto-1.1/deploy/project.xml	Sat Oct 16 20:47:38 2004
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+Copyright 2004 The Apache Software Foundation.
+Licensed  under the  Apache License,  Version 2.0  (the "License");
+you may not use  this file  except in  compliance with the License.
+You may obtain a copy of the License at 
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed  under the  License is distributed on an "AS IS" BASIS,
+WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
+implied.
+
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<project>
+<extend>${basedir}/../project.xml</extend>
+
+<name>Portlet Deployer</name>
+<id>pluto-deploy</id>
+<logo>/../../images/pluto.png</logo>
+
+<organization>
+  <logo>/../../images/apache-portals.gif</logo>
+</organization>
+
+<shortDescription>Deployment Utility for Pluto 1.1</shortDescription>
+
+<description>
+      Deployment tool used to build portlet application archives.
+</description>
+
+<dependencies>
+  <dependency>
+    <groupId>pluto</groupId>
+    <artifactId>pluto-binding</artifactId>
+    <version>1.1</version>
+  </dependency>
+
+  <dependency>
+    <id>commons-cli</id>
+    <version>1.0</version>
+  </dependency>
+
+  <dependency>
+    <id>commons-digester</id>
+    <version>1.6</version>
+  </dependency>
+
+  <dependency>
+    <id>commons-beanutils</id>
+    <version>1.7.0</version>
+  </dependency>
+
+  <dependency>
+    <id>commons-logging</id>
+    <version>1.0.4</version>
+  </dependency>
+
+</dependencies>
+
+</project>

Added: portals/pluto/branches/pluto-1.1/deploy/src/bin/assemble.bat
==============================================================================
--- (empty file)
+++ portals/pluto/branches/pluto-1.1/deploy/src/bin/assemble.bat	Sat Oct 16 20:47:38 2004
@@ -0,0 +1,27 @@
+@echo off
+
+rem
+rem  Copyright 2004 The Apache Software Foundation.
+rem  
+rem  Licensed under the Apache License, Version 2.0 (the "License");
+rem  you may not use this file except in compliance with the License.
+rem  You may obtain a copy of the License at
+rem  
+rem       http://www.apache.org/licenses/LICENSE-2.0
+rem  
+rem  Unless required by applicable law or agreed to in writing, software
+rem  distributed under the License is distributed on an "AS IS" BASIS,
+rem  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+rem  See the License for the specific language governing permissions and
+rem  limitations under the License.
+rem
+
+set JAVA_CMD=java
+set CLASSPATH=../lib/pluto-deploy-1.1.jar;
+set CLASSPATH=%CLASSPATH%../lib/pluto-binding-1.1.jar;
+set CLASSPATH=%CLASSPATH%../lib/commons-cli-1.0.jar;
+set CLASSPATH=%CLASSPATH%../lib/commons-digester-1.6.jar;
+set CLASSPATH=%CLASSPATH%../lib/commons-beanutils-1.7.0.jar;
+set CLASSPATH=%CLASSPATH%../lib/commons-logging-1.0.4.jar;
+
+java -classpath %CLASSPATH%  org.apache.pluto.deploy.cli.AssemblerCLI %1

Added: portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/deploy/Assembler.java
==============================================================================
--- (empty file)
+++ portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/deploy/Assembler.java	Sat Oct 16 20:47:38 2004
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pluto.deploy;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.FileOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ByteArrayInputStream;
+import java.util.jar.JarFile;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Enumeration;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.dom.DOMSource;
+
+import org.apache.pluto.binding.PortletDD;
+import org.apache.pluto.binding.PortletAppDD;
+import org.apache.pluto.binding.XMLBindingFactory;
+import org.apache.crimson.tree.TextNode;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+import sun.tools.jar.resources.jar;
+
+/**
+ * Used to assemble a web application into a portlet application.
+ *
+ * @author <a href="ddewolf@apache.org">David H. DeWolf</a>
+ * @version 1.0
+ * @since Oct 15, 2004
+ */
+public class Assembler {
+
+    private static final String PORTLET_XML = "WEB-INF/portlet.xml";
+
+    private static final String SERVLET_XML = "WEB-INF/web.xml";
+    
+    private static final String DISPATCH_SERVLET_CLASS =
+        "org.apache.pluto.core.PortletServlet";
+
+
+    public Assembler() {
+
+    }
+
+    public void assemble(File webapp, File destination)
+    throws IOException {
+
+        InputStream portletIn = null;
+        InputStream servletIn = null;
+        OutputStream out = null;
+        if(webapp.isDirectory()) {
+            File portletXML = new File(webapp, PORTLET_XML);
+            File servletXML = new File(webapp, SERVLET_XML);
+            portletIn = new FileInputStream(portletXML);
+            servletIn = new FileInputStream(servletXML);
+
+        }
+        else {
+            JarFile jar = new JarFile(webapp);
+            JarEntry portletXML = jar.getJarEntry(PORTLET_XML);
+            JarEntry servletXML = jar.getJarEntry(SERVLET_XML);
+
+            portletIn = jar.getInputStream(portletXML);
+            servletIn = jar.getInputStream(servletXML);
+        }
+
+        XMLBindingFactory factory =
+            XMLBindingFactory.createXMLBinding(XMLBindingFactory.READ_WRITE);
+
+        Document doc = parse(servletIn);
+
+        Collection servlets = new ArrayList();
+        Collection mappings = new ArrayList();
+
+        PortletAppDD portletAppDD = factory.getPortletAppDD(portletIn);
+        PortletDD[] portlets = portletAppDD.getPortlets();
+        for(int i=0;i<portlets.length;i++) {
+            String name = portlets[i].getPortletName();
+            Element servlet = doc.createElement("servlet");
+
+            Element servletName = doc.createElement("servlet-name");
+            servletName.appendChild(new TextNode(name));
+
+            Element servletClass = doc.createElement("servlet-class");
+            servletClass.appendChild(new TextNode(DISPATCH_SERVLET_CLASS));
+
+            Element initParam = doc.createElement("init-param");
+            Element paramName = doc.createElement("param-name");
+            paramName.appendChild(new TextNode("portlet-name"));
+
+            Element paramValue = doc.createElement("param-value");
+            paramValue.appendChild(new TextNode(name));
+
+            initParam.appendChild(paramName);
+            initParam.appendChild(paramValue);
+
+            Element load = doc.createElement("load-on-startup");
+            load.appendChild(new TextNode("1"));
+
+            servlet.appendChild(servletName);
+            servlet.appendChild(servletClass);
+            servlet.appendChild(initParam);
+            servlet.appendChild(load);
+
+            Element mapping = doc.createElement("servlet-mapping");
+            servletName = doc.createElement("servlet-name");
+            servletName.appendChild(new TextNode(name));
+            Element uri = doc.createElement("url-pattern");
+            uri.appendChild(new TextNode("/PlutoInvoker/"+name));
+            mapping.appendChild(servletName);
+            mapping.appendChild(uri);
+
+            servlets.add(servlet);
+            mappings.add(mapping);
+        }
+
+
+        Element webAppNode = (Element)doc.getDocumentElement();
+        NodeList nodes = webAppNode.getChildNodes();
+
+        // Find the first node that shouldn't be before the servlet
+        // and start appending.  This is kind of ugly, but the hack
+        // works for now!
+        for(int i=0;i<nodes.getLength();i++) {
+            Node node = nodes.item(i);
+            if(node.getNodeType() == Node.ELEMENT_NODE) {
+                if(!BEFORE_SERVLET_DEF.contains(node.getNodeName())) {
+                    Iterator it = servlets.iterator();
+                    while(it.hasNext()) {
+                        Node servlet = (Node)it.next();
+                        webAppNode.insertBefore(servlet, node);
+                        it.remove();
+                    }
+                }
+
+                if(!BEFORE_SERVLET_MAPPING_DEF.contains(node.getNodeName())) {
+                    Iterator it = mappings.iterator();
+                    while(it.hasNext()) {
+                        Node mapping = (Node)it.next();
+                        webAppNode.insertBefore(mapping, node);
+                        it.remove();
+                    }
+                }
+            }
+        }
+
+        // Now, in case there are not any nodes after the servlet def!
+        Iterator it = servlets.iterator();
+        while(it.hasNext()) {
+            webAppNode.appendChild((Node)it.next());
+        }
+
+        it = mappings.iterator();
+        while(it.hasNext()) {
+            webAppNode.appendChild((Node)it.next());
+        }
+
+        if(webapp.isDirectory()) {
+            out = new FileOutputStream(new File(destination, SERVLET_XML));
+        }
+        else {
+            JarFile jar = new JarFile(webapp);
+            out = new AssemblerJarOutputStream(jar, webapp);
+        }
+        save(doc, out);
+    }
+
+    private Document parse(InputStream in)
+    throws IOException {
+        Document doc = null;
+        try {
+            DocumentBuilderFactory fact = DocumentBuilderFactory.newInstance();
+            DocumentBuilder builder = fact.newDocumentBuilder();
+            doc = builder.parse(in);
+        } catch (ParserConfigurationException e) {
+            throw new IOException(e.getMessage());
+        } catch (SAXException e) {
+            throw new IOException(e.getMessage());
+        }
+        return doc;
+    }
+
+    private void save(Document doc, OutputStream out)
+    throws IOException {
+        try {
+            TransformerFactory fact = TransformerFactory.newInstance();
+            Transformer trans = fact.newTransformer();
+            trans.setOutputProperty(OutputKeys.INDENT, "yes");
+            trans.transform(new DOMSource(doc), new StreamResult(out));
+        } catch (TransformerConfigurationException e) {
+            throw new IOException(e.getMessage());
+        } catch (TransformerException e) {
+            throw new IOException(e.getMessage());
+        } finally {
+            out.flush();
+            out.close();
+        }
+    }
+
+    private static final Collection BEFORE_SERVLET_DEF = new ArrayList();
+
+    private static final Collection BEFORE_SERVLET_MAPPING_DEF = new ArrayList();
+
+    static {
+        BEFORE_SERVLET_DEF.add("icon");
+        BEFORE_SERVLET_DEF.add("display-name");
+        BEFORE_SERVLET_DEF.add("description");
+        BEFORE_SERVLET_DEF.add("distributable");
+        BEFORE_SERVLET_DEF.add("context-param");
+        BEFORE_SERVLET_DEF.add("filter");
+        BEFORE_SERVLET_DEF.add("filter-mapping");
+        BEFORE_SERVLET_DEF.add("listener");
+
+        BEFORE_SERVLET_MAPPING_DEF.addAll(BEFORE_SERVLET_DEF);
+        BEFORE_SERVLET_MAPPING_DEF.add("servlet");
+    }
+
+    /**
+     * OutputStream which captures the webapplication
+     * deployment descriptor and rewrites the jar file
+     * with it.
+     */
+    private static class AssemblerJarOutputStream
+        extends ByteArrayOutputStream {
+        JarFile jar;
+        File webapp;
+
+        AssemblerJarOutputStream(JarFile jar, File webapp) {
+            this.jar = jar;
+            this.webapp = webapp;
+        }
+
+        public void close() throws IOException {
+            super.flush();
+            super.close();
+            byte[] bits = toByteArray();
+
+            // we write it to a temp file and then copy it in case
+            // the destination is the same as the source: in which
+            // case we'll be unable to write it while we still have
+            // the jar open.
+            File file = new File(
+                webapp.getParent(), webapp.getName()+".tmp"
+            );
+
+            FileOutputStream fout = new FileOutputStream(file);
+            JarOutputStream out = new JarOutputStream(fout);
+
+            Enumeration enum = jar.entries();
+            while(enum.hasMoreElements()) {
+                JarEntry entry = (JarEntry)enum.nextElement();
+                entry = new JarEntry(entry.getName());
+                InputStream is = null;
+                if(SERVLET_XML.equals(entry.getName())) {
+                    is = new ByteArrayInputStream(bits);
+                }
+                else {
+                    is = jar.getInputStream(entry);
+                }
+                int bytesRead = -1;
+                byte[] buffer = new byte[256];
+                out.putNextEntry(new JarEntry(entry));
+                while ((bytesRead = is.read(buffer)) != -1) {
+                    out.write(buffer, 0, bytesRead);
+                }
+            }
+
+            out.flush();
+            out.close();
+
+            webapp.delete();
+            file.renameTo(webapp);
+            file.delete();
+        }
+    }
+}
+
+

Added: portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/deploy/Deployer.java
==============================================================================
--- (empty file)
+++ portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/deploy/Deployer.java	Sat Oct 16 20:47:38 2004
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pluto.deploy;
+
+/**
+ * @author <a href="ddewolf@apache.org">David H. DeWolf</a>
+ * @version 1.0
+ * @since Oct 15, 2004
+ */
+public class Deployer {
+
+    public Deployer() {
+
+    }
+
+}
+

Added: portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/deploy/cli/AssemblerCLI.java
==============================================================================
--- (empty file)
+++ portals/pluto/branches/pluto-1.1/deploy/src/java/org/apache/pluto/deploy/cli/AssemblerCLI.java	Sat Oct 16 20:47:38 2004
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pluto.deploy.cli;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.PosixParser;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.pluto.deploy.Assembler;
+
+/**
+ * Command Line Interface to the Pluto Assembler.
+ *
+ * @author <a href="ddewolf@apache.org">David H. DeWolf</a>
+ * @version 1.0
+ * @since Oct 15, 2004
+ */
+public class AssemblerCLI {
+
+    private Options options;
+    private String[] args;
+
+    public AssemblerCLI(String[] args) {
+        this.args = args;
+        options = new Options();
+        Option destination =
+            new Option("d" , "destination", true,
+                       "specify where the resulting webapp should be written ");
+        destination.setArgName("file");
+
+        Option debug =
+            new Option("debug", false, "print debug information.");
+        options.addOption(destination);
+        options.addOption(debug);
+    }
+
+    public void run() throws ParseException, IOException {
+        CommandLineParser parser = new PosixParser();
+        CommandLine line = parser.parse(options, args);
+
+        String[] args = line.getArgs();
+        if(args.length != 1) {
+            abort();
+            return;
+        }
+
+        String dest = line.getOptionValue("file");
+        if(dest == null) {
+            dest = args[0];
+        }
+
+        File source = new File(args[0]);
+        File result   = new File(dest);
+        result.getParentFile().mkdirs();
+
+        if(!source.exists()) {
+            System.out.println("File does not exist: "+source.getCanonicalPath());
+        }
+
+
+        System.out.println("-----------------------------------------------");
+        System.out.println("Assembling: "+source.getCanonicalPath());
+        System.out.println("        to: "+result.getCanonicalPath());
+
+        Assembler assembler = new Assembler();
+        assembler.assemble(new File(args[0]), new File(dest));
+
+        System.out.println("Complete!");
+    }
+
+    public void abort() {
+        HelpFormatter help = new HelpFormatter();
+        help.setArgName("webapp");
+        help.setWidth(60);
+        help.printHelp("assemble", options);
+    }
+
+    public static void main(String[] args)
+    throws ParseException, IOException{
+        new AssemblerCLI(args).run();
+    }
+}
+