Tuesday, 5 January 2010

GWT client with Coldfusion CFC server

Here is an example project showing how to use a Google Web Toolkit generated JavaScript front end with server side logic implemented in Coldfusion.

The GWT RemoteServiceServlet can't be extended by Coldfusion components, so using GWT's RPC and serialization mechanisms isn't an option. For this example I will making use of a RESTful service using JSON.

Both the Java and CF code are contained in the same project and built using Maven.

The GWT entry point class below has a couple of text input boxes, a button to send a request and a text box to display some response text. The buttons click handler sends an http get request to a url backed by a Coldfusion CFC, parses the JSON formatted data returned, and updates the response text area.

Entry point class inputs:

GWTColdfusion.java

package org.adrianwalker.gwtcoldfusion.gwt.client;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

public final class GWTColdfusion implements EntryPoint {

 private static final String CFC = "/gwtcoldfusion/Message.cfc";
 private static final String METHOD = "getMessage";

 private Label nameLabel;
 private TextBox nameBox;
 private Label ageLabel;
 private TextBox ageBox;
 private Button button;
 private Label responseLabel;
 private TextArea responseArea;

 @Override
 public void onModuleLoad() {

   VerticalPanel basePanel = new VerticalPanel();

   nameLabel = new Label("Name:");
   nameBox = new TextBox();
   ageLabel = new Label("Age:");
   ageBox = new TextBox();
   responseLabel = new Label("Response:");
   responseArea = new TextArea();
   responseArea.setReadOnly(true);
   button = new Button("Click Me");

   button.addClickHandler(new ClickHandler() {

     @Override
     public void onClick(final ClickEvent event) {

       String url = CFC + "?method=" + METHOD + "&name=" + nameBox.getText() + "&age=" + ageBox.getText();

       RequestBuilder requestBuilder = new RequestBuilder(RequestBuilder.GET, url);
       requestBuilder.setCallback(new RequestCallback() {

         @Override
         public void onResponseReceived(final Request request, final Response response) {
           JSONObject jsonObject = (JSONObject) JSONParser.parse(response.getText());
           JSONString jsonString = (JSONString) jsonObject.get("MESSAGE");

           responseArea.setText(jsonString.stringValue());
         }

         @Override
         public void onError(final Request request, final Throwable t) {
           Window.alert("Error recieving response");
         }
       });
      
       try {
         requestBuilder.send();
       } catch (RequestException re) {
         Window.alert("Error sending request");
       }
     }
   });

   basePanel.add(nameLabel);
   basePanel.add(nameBox);
   basePanel.add(ageLabel);
   basePanel.add(ageBox);
   basePanel.add(responseLabel);
   basePanel.add(responseArea);
   basePanel.add(button);

   RootPanel.get().add(basePanel);
 }
}

The Coldfusion component accepts requests via http, constructs a response structure and returns it in JSON format.

Message.cfc

<cfcomponent output="false">
  
  <cfcontent type="application/json">
  
  <cffunction access="remote" returnformat="JSON" name="getMessage" >
    <cfargument required="true" type="string" name="name">
    <cfargument required="true" type="numeric" name="age">

    <cfset var response = {
      message = "Hello #ARGUMENTS.name#, you are #ARGUMENTS.age# years old"
    }>

    <cfreturn response>
  </cffunction>
</cfcomponent>

JSON response:

Response on interface:

The Maven project is available for download below.

Build the project using 'mvn clean war:explode', then copy the target/gwtcoldfusion directory to your Coldfusion server's wwwroot directory. To view the page point your brower at http://localhost:8500/gwtcoldfusion

Source Code

Thursday, 26 November 2009

GWT Rich Text Editor

Examples of GWT rich text editors are pretty rubbish, so here is a decent one.

It's based on the GWT Showcase Rich Text example, but with the stuff I didn't want taken out and a basic colour picker widget put in.

Also, I replaced the icons with some that I liked better from here.

Check it out:

Run the project with 'mvn clean install tomcat:run-war' and point your brower at http://localhost:8080/richTextEditor

Source Code

Friday, 4 September 2009

Coldfusion File Streaming & Stream Proxying

Hot on the heels of yesterdays post about Java Streaming Proxy Servlets comes even more streaming goodness with CF.

The first bit of code opens a file, captures the underlying servlet output stream and then streams out the contents of the file.

streamFile.cfm

<cfsilent>
  <cfif ReFind("[^a-zA-Z0-9_\-\.]", URL.filename)>
    <cfthrow message="not permitted">
  </cfif>

  <cfscript>
    filename = REQUEST.fileDirectory & "/" & URL.filename;
    inputFile = createObject("java", "java.io.File").init(filename);
    inputStream = createObject("java", "java.io.FileInputStream").init(inputFile);
    outputStream = getPageContext().getResponse().getResponse().getOutputStream();

    while(true) {
      b = inputStream.read();

      if(b EQ -1) {
        break;
      }

      outputStream.write(b);
    }
    outputStream.flush();

    inputStream.close();
    outputStream.close();   
  </cfscript>
</cfsilent>

Streaming the contents of a file to a browser is all good because you didn't have to load the whole thing in memory before sending it down the wire. But what if you want to stream a file to a browers that is held on another server, not accessible from the internet. In that case you'll want to stream the file through a proxy to the client.

streamProxy.cfm

<cfsilent>
  <cfheader name="Content-Disposition" value="attachment; filename=#URL.userFilename#">
  <cfcontent type="text/plain">

  <cfif ReFind("[^a-zA-Z0-9_\-\.]", URL.filename)>
    <cfthrow message="not permitted">
  <cfelseif ReFind("[^a-zA-Z0-9_\-\.]", URL.userFilename)>
    <cfthrow message="not permitted">
  </cfif>

  <cfscript>   
    proxy = createObject("java", "org.apache.commons.httpclient.HttpClient").init();

    if(len(CGI.QUERY_STRING) GT 0) {
      query = "?#CGI.QUERY_STRING#";
    } else {
      query = "";
    }

    uri = REQUEST.streamUrl & query;

    proxyMethod = createObject("java", "org.apache.commons.httpclient.methods.GetMethod").init(uri); 
    proxy.executeMethod(proxyMethod); 
    inputStream = proxyMethod.getResponseBodyAsStream(); 
    outputStream = getPageContext().getResponse().getResponse().getOutputStream();
  
    while(true) {
      b = inputStream.read();

      if(b EQ -1) {
        break;
      }

      outputStream.write(b);
    }

    outputStream.flush();

    inputStream.close();
    outputStream.close();
  </cfscript>
</cfsilent>

The proxy uses the Jakarta Commons HttpClient which comes bundled with ColdFusion to call the target stream, passing any URL query parameters supplied.

The response from target tream URL is streamed back to the client via the underlying servlet output stream, just like the file streaming code.

An example project is available for download, make sure to customise the REQUEST scope variables in the Application.cfm file before testing.

Source Code

Thursday, 3 September 2009

Streaming Http Proxy Servlet

You know when you just need a streaming http proxy servlet and you just can't find one? Well try this:

HttpProxyServlet.java

package org.adrianwalker.servlet.proxy;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;

public final class HttpProxyServlet extends HttpServlet {

  private URL url;
  private HttpClient proxy;

  @Override
  public void init(final ServletConfig config) throws ServletException {

    super.init(config);

    try {
      url = new URL(config.getInitParameter("url"));
    } catch (MalformedURLException me) {
      throw new ServletException("Proxy URL is invalid", me);
    }
    proxy = new HttpClient();
    proxy.getHostConfiguration().setHost(url.getHost());
  }

  @Override
  protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
          throws ServletException, IOException {

    Map<String, String[]> requestParameters = request.getParameterMap();

    StringBuilder query = new StringBuilder();
    for (String name : requestParameters.keySet()) {
      for (String value : requestParameters.get(name)) {

        if (query.length() == 0) {
          query.append("?");
        } else {
          query.append("&");
        }

        name = URLEncoder.encode(name, "UTF-8");
        value = URLEncoder.encode(value, "UTF-8");

        query.append(String.format("&%s=%s", name, value));
      }
    }

    String uri = String.format("%s%s", url.toString(), query.toString());
    GetMethod proxyMethod = new GetMethod(uri);
    
    proxy.executeMethod(proxyMethod);
    write(proxyMethod.getResponseBodyAsStream(), response.getOutputStream());
  }

  @Override
  protected void doPost(final HttpServletRequest request, final HttpServletResponse response)
          throws ServletException, IOException {

    Map<String, String[]> requestParameters = request.getParameterMap();

    String uri = url.toString();
    PostMethod proxyMethod = new PostMethod(uri);
    for (String name : requestParameters.keySet()) {
      for (String value : requestParameters.get(name)) {
        proxyMethod.addParameter(name, value);
      }
    }

    proxy.executeMethod(proxyMethod);
    write(proxyMethod.getResponseBodyAsStream(), response.getOutputStream());
  }

  private void write(final InputStream inputStream, final OutputStream outputStream) throws IOException {
    int b;
    while ((b = inputStream.read()) != -1) {
      outputStream.write(b);
    }

    outputStream.flush();
  }

  @Override
  public String getServletInfo() {
    return "Http Proxy Servlet";
  }
}

This class makes use of the Jakarta Commons HttpClient to forward requests to a configurable url and streams back the response.

A sample project using this servlet is available for download below. The servlet acts as a proxy for a google search, returning the search results page. The proxied url is configurable in the web applications web.xml file. The project builds a war and uses the jetty plugin so you can test the code.

Run the project with 'mvn clean install jetty:run-war' and point your brower at http://localhost:8080/http-proxy-servlet

Source Code

Friday, 28 August 2009

Maven GWT WAR JPA Jetty & Derby skeleton project.

Thats right, all that good stuff in one skeleton project.

Here is the pom.xml for the project:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>
  <groupId>org.adrianwalker.maven.skeleton.war.jpa.gwt</groupId>
  <artifactId>maven-war-gwt-jpa-derby-skeleton</artifactId>
  <packaging>war</packaging>
  <version>0.1.0</version>
  <name>Maven WAR-GWT-JPA-Derby Skeleton</name>

  <description>
    Skeleton project for creating GWT WAR running on Jetty, with a Derby
    database accessed through JPA.

    Usage: mvn clean install jetty:run-war
  </description>

  <url>http://www.adrianwalker.org</url>

  <organization>
    <name>adrianwalker.org</name>
    <url>http://www.adrianwalker.org</url>
  </organization>

  <developers>
    <developer>
      <name>Adrian Walker</name>
      <email>ady.walker@gmail.com</email>
      <organization>adrianwalker.org</organization>
      <organizationUrl>http://www.adrianwalker.org</organizationUrl>
    </developer>
  </developers>

  <repositories>
    <repository>
      <id>java.net</id>
      <url>http://download.java.net/maven/1</url>
      <layout>legacy</layout>
    </repository>
    <repository>
      <id>codehaus.org</id>
      <url>http://repository.codehaus.org</url>
    </repository>
  </repositories>

  <properties>
    <gwt.version>1.7.0</gwt.version>
  </properties>

  <build>
    <outputDirectory>war/WEB-INF/classes</outputDirectory>

    <resources>
      <resource>
        <directory>${basedir}/src/main/resources</directory>
        <includes>
          <include>**/*</include>
        </includes>
      </resource>
    </resources>

    <plugins>
      <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <configuration>
          <filesets>
            <fileset>
              <directory>war</directory>
              <includes>
                <include>**/*</include>
              </includes>
            </fileset>
          </filesets>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>gwt-maven-plugin</artifactId>
        <version>1.1</version>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.0.2</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
        <version>6.1.9</version>
      </plugin>
    </plugins>
  </build>
  
  <dependencies>
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-servlet</artifactId>
      <version>${gwt.version}</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-user</artifactId>
      <version>${gwt.version}</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>toplink.essentials</groupId>
      <artifactId>toplink-essentials</artifactId>
      <version>2.0-36</version>
    </dependency>
    <dependency>
      <groupId>org.apache.derby</groupId>
      <artifactId>derby</artifactId>
      <version>10.4.2.0</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>servlet-api</artifactId>
      <version>2.4</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <reporting>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-checkstyle-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>cobertura-maven-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>jxr-maven-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-pmd-plugin</artifactId>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>findbugs-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </reporting>
</project>

The major pain with this was getting a jndi accessible data source working with Jetty and JPA. Check out the persistence.xml and jetty-env.xml for the correct configuration:

jetty-env.xml

<?xml version="1.0"  encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">
<Configure class="org.mortbay.jetty.webapp.WebAppContext">
  <New id="message" class="org.mortbay.jetty.plus.naming.Resource">
    <Arg>jdbc/message</Arg>
    <Arg>
      <New class="org.apache.derby.jdbc.EmbeddedDataSource">
        <Set name="DatabaseName">target/database/message</Set>
        <Set name="user"></Set>
        <Set name="password"></Set>
        <Set name="createDatabase">create</Set>
      </New>
    </Arg>
  </New>
</Configure>

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
  <persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
    <provider>oracle.toplink.essentials.PersistenceProvider</provider>
    <non-jta-data-source>java:comp/env/jdbc/message</non-jta-data-source>
    <class>org.adrianwalker.maven.skeleton.jpa.entity.MessageEntity</class>
    <properties>
      <property name="toplink.ddl-generation" value="create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

Run the project with 'mvn clean install jetty:run-war' and point your brower at http://localhost:8080

Source Code

Thursday, 30 July 2009

Maven GWT WAR skeleton project.

Here is the second in a series of maven project skeletons. This project creates a WAR file containing a GWT default application.

This eclipse project is constructed to make use of the Maven and GWT plugins.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>
  <groupId>org.adrianwalker.maven.skeleton.war</groupId>
  <artifactId>maven-war-skeleton</artifactId>
  <packaging>war</packaging>
  <version>0.1.0</version>
  <name>Maven WAR Skeleton</name>
  <description>Skeleton project for creating GWT WARs</description>

  <url>http://www.adrianwalker.org</url>

  <organization>
    <name>adrianwalker.org</name>
    <url>http://www.adrianwalker.org</url>
  </organization>

  <developers>
    <developer>
      <name>Adrian Walker</name>
      <email>ady.walker@gmail.com</email>
      <organization>adrianwalker.org</organization>
      <organizationUrl>http://www.adrianwalker.org</organizationUrl>
    </developer>
  </developers>

  <properties>
    <gwt.version>1.7.0</gwt.version>
  </properties>

  <build>
    <outputDirectory>war/WEB-INF/classes</outputDirectory>

    <plugins>
      <plugin>
        <artifactId>maven-clean-plugin</artifactId>
        <configuration>
          <filesets>
            <fileset>
              <directory>war</directory>
              <includes>
                <include>**/*</include>
              </includes>
            </fileset>
          </filesets>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>gwt-maven-plugin</artifactId>
        <version>1.1</version>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins
        </groupId>
        <artifactId>maven-compiler-plugin
        </artifactId>
        <version>2.0.2</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-servlet</artifactId>
      <version>${gwt.version}</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>com.google.gwt</groupId>
      <artifactId>gwt-user</artifactId>
      <version>${gwt.version}</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <reporting>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-checkstyle-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>cobertura-maven-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>jxr-maven-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-pmd-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>findbugs-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </reporting>
</project>

Source Code

Monday, 27 July 2009

Maven executable JAR skeleton assembly project.

Maven project wizards are ok, but you still end up editing the POM a fair amount to get exactly what you need. It'd be useful just to have some skeleton projects with 90% of what you want ready to go.

Here is the first in a series of maven project skeletons. This project creates an executable JAR and a ZIP assembly to distribute it, along with its dependancies, config files and batch files etc.

The pom.xml includes various report generation plugins and references an assembly configuration, distribution.xml:

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.adrianwalker.maven.skeleton.jar</groupId>
  <artifactId>maven-jar-skeleton</artifactId>
  <packaging>jar</packaging>
  <version>0.1.0</version>
  <name>Maven JAR Skeleton</name>
  <description>Skeleton project for creating executable JARs</description>
  <url>http://www.adrianwalker.org</url>

  <organization>
    <name>adrianwalker.org</name>
    <url>http://www.adrianwalker.org</url>
  </organization>

  <developers>
    <developer>
      <name>Adrian Walker</name>
      <email>ady.walker@gmail.com</email>
      <organization>adrianwalker.org</organization>
      <organizationUrl>http://www.adrianwalker.org</organizationUrl>
    </developer>
  </developers>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>

      <plugin>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
          <archive>
            <manifest>
              <addClasspath>true</addClasspath>
              <mainClass>org.adrianwalker.maven.skeleton.jar.Main</mainClass>
            </manifest>
            <manifestEntries>
              <Class-Path>../conf/</Class-Path>
            </manifestEntries>
          </archive>
        </configuration>
      </plugin>

      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <configuration>
          <descriptors>
            <descriptor>src/main/assembly/distribution.xml</descriptor>
          </descriptors>
        </configuration>
        <executions>
          <execution>
            <id>make-assembly</id>
            <phase>package</phase>
            <goals>
              <goal>assembly</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>

    <resources>
      <resource>
        <directory>src/main/resources</directory>
        <excludes>
          <exclude>**/*</exclude>
        </excludes>
      </resource>
    </resources>
  </build>

  <reporting>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-checkstyle-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>cobertura-maven-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>jxr-maven-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-pmd-plugin</artifactId>
      </plugin>

      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>findbugs-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </reporting>

  <dependencies>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.5.6</version>
    </dependency>

    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>1.5.6</version>
    </dependency>

    <dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.14</version>
    </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

distribution.xml

<assembly>
  <id>distribution</id>
  <formats>
    <format>zip</format>
  </formats>

  <files>
    <file>
      <source>/src/main/resources/bin/run.bat</source>
      <outputDirectory>/bin</outputDirectory>
      <filtered>true</filtered>
    </file>
  </files>

  <fileSets>
    <fileSet>
      <directory>/src/main/resources/conf</directory>
      <outputDirectory>/conf</outputDirectory>
      <includes>
        <include>*.properties</include>
      </includes>
    </fileSet>
    <fileSet>
      <directory>/target</directory>
      <outputDirectory>/lib</outputDirectory>
      <includes>
        <include>*.jar</include>
      </includes>
    </fileSet>
  </fileSets>
  
  <dependencySets>
    <dependencySet>
      <outputDirectory>/lib</outputDirectory>
    </dependencySet>
  </dependencySets>
</assembly>

Source Code