You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@batchee.apache.org by rm...@apache.org on 2014/01/05 21:12:00 UTC

git commit: BATCHEE-12 adding bar mvn goal and support in cli

Updated Branches:
  refs/heads/master 0dd9d2c4d -> 012278dd7


BATCHEE-12 adding bar mvn goal and support in cli


Project: http://git-wip-us.apache.org/repos/asf/incubator-batchee/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-batchee/commit/012278dd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-batchee/tree/012278dd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-batchee/diff/012278dd

Branch: refs/heads/master
Commit: 012278dd7fdcc82b79f8dad6e9ffae6e73b41e4d
Parents: 0dd9d2c
Author: Romain Manni-Bucau <rm...@apache.org>
Authored: Sun Jan 5 21:11:47 2014 +0100
Committer: Romain Manni-Bucau <rm...@apache.org>
Committed: Sun Jan 5 21:11:47 2014 +0100

----------------------------------------------------------------------
 tools/cli/pom.xml                               |  50 ++++-
 .../classloader/ChildFirstURLClassLoader.java   | 114 +++++++++++
 .../batchee/cli/command/JobOperatorCommand.java |  96 ++++++++--
 .../java/org/apache/batchee/cli/zip/Zips.java   |  92 +++++++++
 tools/maven-plugin/pom.xml                      | 138 +++++++------
 .../org/apache/batchee/tools/maven/BarMojo.java | 192 +++++++++++++++++++
 .../resources/META-INF/plexus/components.xml    |  45 +++++
 7 files changed, 652 insertions(+), 75 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/012278dd/tools/cli/pom.xml
----------------------------------------------------------------------
diff --git a/tools/cli/pom.xml b/tools/cli/pom.xml
index 098d20d..4f1f592 100644
--- a/tools/cli/pom.xml
+++ b/tools/cli/pom.xml
@@ -27,7 +27,7 @@
   </parent>
 
   <artifactId>batchee-cli</artifactId>
-  <name>BatchEE :: CLI</name>
+  <name>BatchEE :: Tools :: CLI</name>
 
   <dependencies>
     <dependency>
@@ -54,12 +54,23 @@
       <groupId>io.airlift</groupId>
       <artifactId>airline</artifactId>
       <version>0.6</version>
+      <exclusions>
+        <exclusion>
+          <groupId>javax.inject</groupId>
+          <artifactId>javax.inject</artifactId>
+        </exclusion>
+      </exclusions>
     </dependency>
     <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
       <version>3.1</version>
     </dependency>
+    <dependency>
+      <groupId>commons-io</groupId>
+      <artifactId>commons-io</artifactId>
+      <version>2.4</version>
+    </dependency>
 
     <!-- for lifecycles -->
     <dependency>
@@ -92,4 +103,41 @@
       <version>1.4.0</version>
     </dependency>
   </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <version>2.2</version>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+            <configuration>
+              <createDependencyReducedPom>false</createDependencyReducedPom>
+              <artifactSet>
+                <excludes>
+                  <exclude>org.springframework:*</exclude>
+                  <exclude>org.apache.geronimo.specs:geronimo-ejb_3.1_spec</exclude>
+                  <exclude>org.apache.deltaspike.cdictrl:deltaspike-cdictrl-api</exclude>
+                  <exclude>org.testng:*</exclude>
+                  <exclude>org.junit:*</exclude>
+                  <exclude>org.hamcrest:*</exclude>
+                  <exclude>com.github.stefanbirkner:*</exclude>
+                </excludes>
+              </artifactSet>
+              <transformers>
+                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                  <mainClass>org.apache.batchee.cli.BatchEECLI</mainClass>
+                </transformer>
+              </transformers>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+    </plugins>
+  </build>
 </project>

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/012278dd/tools/cli/src/main/java/org/apache/batchee/cli/classloader/ChildFirstURLClassLoader.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/classloader/ChildFirstURLClassLoader.java b/tools/cli/src/main/java/org/apache/batchee/cli/classloader/ChildFirstURLClassLoader.java
new file mode 100644
index 0000000..c69d65f
--- /dev/null
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/classloader/ChildFirstURLClassLoader.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.batchee.cli.classloader;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+public class ChildFirstURLClassLoader  extends URLClassLoader {
+    private final ClassLoader system;
+
+    public ChildFirstURLClassLoader(final URL[] urls, final ClassLoader parent) {
+        super(urls, parent);
+        system = ClassLoader.getSystemClassLoader();
+    }
+
+    @Override
+    public Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
+        // already loaded?
+        Class<?> clazz = findLoadedClass(name);
+        if (clazz != null) {
+            if (resolve) {
+                resolveClass(clazz);
+            }
+            return clazz;
+        }
+
+        // JSE classes
+        try {
+            clazz = system.loadClass(name);
+            if (clazz != null) {
+                if (resolve) {
+                    resolveClass(clazz);
+                }
+                return clazz;
+            }
+        } catch (ClassNotFoundException ignored) {
+            // no-op
+        }
+
+        final boolean ok = shouldSkip(name);
+        clazz = loadInternal(name, resolve);
+        if (clazz != null) {
+            return clazz;
+        }
+
+        // finally delegate
+        clazz = loadFromParent(name, resolve);
+        if (clazz != null) {
+            return clazz;
+        }
+
+        if (!ok) {
+            clazz = loadInternal(name, resolve);
+            if (clazz != null) {
+                return clazz;
+            }
+        }
+
+        throw new ClassNotFoundException(name);
+    }
+
+    // extension point for next releases but here to don't need to re-implement the login in loadClass
+    private boolean shouldSkip(final String name) {
+        return false;
+    }
+
+    private Class<?> loadFromParent(final String name, final boolean resolve) {
+        ClassLoader parent = getParent();
+        if (parent == null) {
+            parent = system;
+        }
+        try {
+            final Class<?> clazz = Class.forName(name, false, parent);
+            if (clazz != null) {
+                if (resolve) {
+                    resolveClass(clazz);
+                }
+                return clazz;
+            }
+        } catch (ClassNotFoundException ignored) {
+            // no-op
+        }
+        return null;
+    }
+
+    private Class<?> loadInternal(final String name, final boolean resolve) {
+        try {
+            final Class<?> clazz = findClass(name);
+            if (clazz != null) {
+                if (resolve) {
+                    resolveClass(clazz);
+                }
+                return clazz;
+            }
+        } catch (final ClassNotFoundException ignored) {
+            // no-op
+        }
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/012278dd/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java b/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java
index 3d4e621..b910732 100644
--- a/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/command/JobOperatorCommand.java
@@ -17,21 +17,24 @@
 package org.apache.batchee.cli.command;
 
 import io.airlift.command.Option;
+import org.apache.batchee.cli.classloader.ChildFirstURLClassLoader;
 import org.apache.batchee.cli.lifecycle.Lifecycle;
+import org.apache.batchee.cli.zip.Zips;
 import org.apache.batchee.container.exception.BatchContainerRuntimeException;
 import org.apache.batchee.jaxrs.client.BatchEEJAXRSClientFactory;
 import org.apache.batchee.jaxrs.client.ClientConfiguration;
 import org.apache.batchee.jaxrs.client.ClientSecurity;
 import org.apache.batchee.jaxrs.client.ClientSslConfiguration;
+import org.apache.commons.io.FileUtils;
 
 import javax.batch.operations.JobOperator;
 import javax.batch.runtime.BatchRuntime;
 import java.io.File;
 import java.io.FileFilter;
 import java.io.FilenameFilter;
+import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
-import java.net.URLClassLoader;
 import java.util.Collection;
 import java.util.LinkedList;
 
@@ -46,6 +49,8 @@ import static java.lang.Thread.currentThread;
  *       by folders to be able to run them contextual using this command.
 */
 public abstract class JobOperatorCommand implements Runnable {
+    // Remote config
+
     @Option(name = "-url", description = "when using JAXRS the batchee resource url")
     private String baseUrl = null;
 
@@ -88,12 +93,20 @@ public abstract class JobOperatorCommand implements Runnable {
     @Option(name = "-trustManagerProvider", description = "when using JAXRS the trustManagerProvider")
     private String trustManagerProvider = null;
 
+    // local config
+
     @Option(name = "-lifecycle", description = "the lifecycle class to use")
     private String lifecycle = null;
 
     @Option(name = "-libs", description = "folder containing additional libraries, the folder is added too to the loader")
     private String libs = null;
 
+    @Option(name = "-archive", description = "a bar archive")
+    private String archive = null;
+
+    @Option(name = "-work", description = "work directory (default to java.io.tmp)")
+    private String work = System.getProperty("batchee.home", System.getProperty("java.io.tmpdir")) + "/batchee";
+
     @Option(name = "-shared-libs", description = "folder containing shared libraries, the folder is added too to the loader")
     private String sharedLibs = null;
 
@@ -187,24 +200,73 @@ public abstract class JobOperatorCommand implements Runnable {
         }
     }
 
+    // TODO: import URLClassLoaderFirst instead of URLClassLoader
     private ClassLoader createLoader(final ClassLoader parent) throws MalformedURLException {
-        if (libs == null) {
-            return parent;
-        }
+        final Collection<URL> urls = new LinkedList<URL>();
 
-        final File folder = new File(libs);
-        if (!folder.exists()) {
-            return parent;
+        if (libs != null) {
+            final File folder = new File(libs);
+            if (folder.exists()) {
+                addFolder(folder, urls);
+            }
         }
 
         // we add libs/*.jar and libs/xxx/*.jar to be able to sort libs but only one level to keep it simple
-        final Collection<URL> urls = new LinkedList<URL>();
-        addFolder(folder, urls);
-        if (sharedLibs != null) { // add it later to let specific libs be taken before
-            addFolder(new File(sharedLibs), urls);
+        if (archive != null) {
+            final File bar = new File(archive);
+            final File exploded;
+            if (bar.exists()) {
+                if (bar.isFile()) { // bar to unzip
+                    exploded = new File(work, bar.getName());
+                } else if (bar.isDirectory()) { // already unpacked
+                    exploded = bar;
+                } else {
+                    throw new IllegalArgumentException("unsupported archive type for: '" + archive + "'");
+                }
+
+                final File timestamp = new File(exploded, "timestamp.txt");
+
+                long ts = Long.MIN_VALUE;
+                if (exploded.exists()) {
+                    if (timestamp.exists()) {
+                        try {
+                            ts = Long.parseLong(FileUtils.readFileToString(timestamp).trim());
+                        } catch (final IOException e) {
+                            ts = Long.MIN_VALUE;
+                        }
+                    }
+                }
+
+                if (ts == Long.MIN_VALUE || ts < bar.lastModified()) {
+                    explode(bar, exploded, timestamp, bar.lastModified());
+                }
+
+                // bar archives are split accross 3 folders
+                addFolder(new File(exploded, "batch/jobs"), urls);
+                addFolder(new File(exploded, "batch/classes"), urls);
+                addFolder(new File(exploded, "libs"), urls);
+            } else {
+                throw new IllegalArgumentException("'" + archive + "' doesn't exist");
+            }
         }
 
-        return new URLClassLoader(urls.toArray(new URL[urls.size()]), parent);
+        final ClassLoader sharedClassLoader = createSharedClassLoader(parent);
+        if (libs == null && archive == null) {
+            return sharedClassLoader;
+        }
+        return new ChildFirstURLClassLoader(urls.toArray(new URL[urls.size()]), sharedClassLoader);
+    }
+
+    private ClassLoader createSharedClassLoader(final ClassLoader parent) throws MalformedURLException {
+        final ClassLoader usedParent;
+        if (sharedLibs != null) { // add it later to let specific libs be taken before
+            final Collection<URL> sharedUrls = new LinkedList<URL>();
+            addFolder(new File(sharedLibs), sharedUrls);
+            usedParent = new ChildFirstURLClassLoader(sharedUrls.toArray(new URL[sharedUrls.size()]), parent);
+        } else {
+            usedParent = parent;
+        }
+        return usedParent;
     }
 
     private void addFolder(File folder, Collection<URL> urls) throws MalformedURLException {
@@ -232,6 +294,16 @@ public abstract class JobOperatorCommand implements Runnable {
         urls.add(folder.toURI().toURL());
     }
 
+    private static void explode(final File source, final File target, final File timestampFile, final long time) {
+        try {
+            FileUtils.deleteDirectory(target);
+            Zips.unzip(source, target);
+            FileUtils.write(timestampFile, Long.toString(time));
+        } catch (final IOException e) {
+            // no-op
+        }
+    }
+
     private static class JarFilter implements FilenameFilter {
         public static final FilenameFilter INSTANCE = new JarFilter();
 

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/012278dd/tools/cli/src/main/java/org/apache/batchee/cli/zip/Zips.java
----------------------------------------------------------------------
diff --git a/tools/cli/src/main/java/org/apache/batchee/cli/zip/Zips.java b/tools/cli/src/main/java/org/apache/batchee/cli/zip/Zips.java
new file mode 100644
index 0000000..d3d1acb
--- /dev/null
+++ b/tools/cli/src/main/java/org/apache/batchee/cli/zip/Zips.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.batchee.cli.zip;
+
+import org.apache.batchee.container.exception.BatchContainerRuntimeException;
+import org.apache.commons.io.IOUtils;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+public class Zips {
+    private static final int FILE_BUFFER_SIZE = 32768;
+    private static final int COPY_BUFFER_SIZE = 1024;
+
+    public static void unzip(final File zipFile, final File destination) throws IOException {
+        ZipInputStream in = null;
+        try {
+            mkdir(destination);
+
+            in = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile), FILE_BUFFER_SIZE));
+
+            ZipEntry entry;
+            while ((entry = in.getNextEntry()) != null) {
+                final String path = entry.getName();
+                final File file = new File(destination, path);
+
+                if (entry.isDirectory()) {
+                    continue;
+                }
+
+                mkdir(file.getParentFile());
+
+                final OutputStream out = new BufferedOutputStream(new FileOutputStream(file), FILE_BUFFER_SIZE);
+                try {
+                    copy(in, out);
+                } finally {
+                    IOUtils.closeQuietly(out);
+                }
+
+                final long lastModified = entry.getTime();
+                if (lastModified > 0) {
+                    file.setLastModified(lastModified);
+                }
+
+            }
+        } catch (final IOException e) {
+            throw new IOException("Unable to unzip " + zipFile.getAbsolutePath(), e);
+        } finally {
+            IOUtils.closeQuietly(in);
+        }
+    }
+
+    private static void copy(final ZipInputStream from, final OutputStream to) throws IOException {
+        final byte[] buffer = new byte[COPY_BUFFER_SIZE];
+        int length;
+        while ((length = from.read(buffer)) != -1) {
+            to.write(buffer, 0, length);
+        }
+        to.flush();
+    }
+
+    private static void mkdir(final File file) {
+        if (!file.exists() && !file.mkdirs()) {
+            throw new BatchContainerRuntimeException("Can't create " + file.getPath());
+        }
+    }
+
+    private Zips() {
+        // no-op
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/012278dd/tools/maven-plugin/pom.xml
----------------------------------------------------------------------
diff --git a/tools/maven-plugin/pom.xml b/tools/maven-plugin/pom.xml
index e32c12b..ebe4eb8 100644
--- a/tools/maven-plugin/pom.xml
+++ b/tools/maven-plugin/pom.xml
@@ -18,72 +18,86 @@
 <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/xsd/maven-4.0.0.xsd">
-    <modelVersion>4.0.0</modelVersion>
+  <modelVersion>4.0.0</modelVersion>
 
-    <parent>
-        <artifactId>batchee-tools</artifactId>
-        <groupId>org.apache.batchee</groupId>
-        <version>0.1-incubating-SNAPSHOT</version>
-    </parent>
+  <parent>
+    <artifactId>batchee-tools</artifactId>
+    <groupId>org.apache.batchee</groupId>
+    <version>0.1-incubating-SNAPSHOT</version>
+  </parent>
 
-    <artifactId>batchee-maven-plugin</artifactId>
-    <name>BatchEE :: Tools :: Maven Plugin</name>
-    <packaging>maven-plugin</packaging>
+  <artifactId>batchee-maven-plugin</artifactId>
+  <name>BatchEE :: Tools :: Maven Plugin</name>
+  <packaging>maven-plugin</packaging>
 
-    <dependencies>
-        <dependency>
-            <groupId>org.apache.geronimo.specs</groupId>
-            <artifactId>geronimo-jbatch_1.0_spec</artifactId>
-            <scope>compile</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.batchee</groupId>
-            <artifactId>batchee-jbatch</artifactId>
-            <version>${project.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.batchee</groupId>
-            <artifactId>batchee-jaxrs-client</artifactId>
-            <version>${project.version}</version>
-        </dependency>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-jbatch_1.0_spec</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.batchee</groupId>
+      <artifactId>batchee-jbatch</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.batchee</groupId>
+      <artifactId>batchee-jaxrs-client</artifactId>
+      <version>${project.version}</version>
+    </dependency>
 
-        <dependency>
-            <groupId>org.apache.maven</groupId>
-            <artifactId>maven-plugin-api</artifactId>
-            <version>3.0.5</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.maven.plugin-tools</groupId>
-            <artifactId>maven-plugin-annotations</artifactId>
-            <version>3.2</version>
-        </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-plugin-api</artifactId>
+      <version>${maven.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-core</artifactId>
+      <version>${maven.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-archiver</artifactId>
+      <version>2.5</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.maven.plugin-tools</groupId>
+      <artifactId>maven-plugin-annotations</artifactId>
+      <version>3.2</version>
+    </dependency>
 
-        <dependency>
-            <groupId>org.testng</groupId>
-            <artifactId>testng</artifactId>
-        </dependency>
-    </dependencies>
+    <dependency>
+      <groupId>org.testng</groupId>
+      <artifactId>testng</artifactId>
+    </dependency>
+  </dependencies>
 
-    <build>
-        <plugins>
-            <plugin>
-                <groupId>org.apache.maven.plugins</groupId>
-                <artifactId>maven-plugin-plugin</artifactId>
-                <version>3.2</version>
-                <executions>
-                    <execution>
-                        <id>mojo-descriptor</id>
-                        <goals>
-                            <goal>descriptor</goal>
-                            <goal>helpmojo</goal>
-                        </goals>
-                    </execution>
-                </executions>
-                <configuration>
-                    <goalPrefix>batchee</goalPrefix>
-                    <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
-                </configuration>
-            </plugin>
-        </plugins>
-    </build>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-plugin-plugin</artifactId>
+        <version>3.2</version>
+        <executions>
+          <execution>
+            <id>mojo-descriptor</id>
+            <goals>
+              <goal>descriptor</goal>
+              <goal>helpmojo</goal>
+            </goals>
+          </execution>
+        </executions>
+        <configuration>
+          <goalPrefix>batchee</goalPrefix>
+          <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+
+  <properties>
+    <maven.version>3.0.5</maven.version>
+  </properties>
 </project>

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/012278dd/tools/maven-plugin/src/main/java/org/apache/batchee/tools/maven/BarMojo.java
----------------------------------------------------------------------
diff --git a/tools/maven-plugin/src/main/java/org/apache/batchee/tools/maven/BarMojo.java b/tools/maven-plugin/src/main/java/org/apache/batchee/tools/maven/BarMojo.java
new file mode 100644
index 0000000..080b4f3
--- /dev/null
+++ b/tools/maven-plugin/src/main/java/org/apache/batchee/tools/maven/BarMojo.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.batchee.tools.maven;
+
+import org.apache.maven.ProjectDependenciesResolver;
+import org.apache.maven.archiver.MavenArchiveConfiguration;
+import org.apache.maven.archiver.MavenArchiver;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.MavenProjectHelper;
+import org.codehaus.plexus.archiver.Archiver;
+import org.codehaus.plexus.archiver.jar.JarArchiver;
+import org.codehaus.plexus.archiver.util.DefaultFileSet;
+import org.codehaus.plexus.components.io.fileselectors.FileInfo;
+import org.codehaus.plexus.components.io.fileselectors.FileSelector;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import static java.util.Arrays.asList;
+
+/**
+ * create a bar archive using packaging bar and this plugin:
+ * <packaging>bar</packaging>
+ * <p/>
+ * <plugin>
+ * <groupId>org.apache.batchee</groupId>
+ * <artifactId>batchee-maven-plugin</artifactId>
+ * <version>0.1-incubating-SNAPSHOT</version>
+ * <extensions>true</extensions>
+ * </plugin>
+ */
+@Mojo(name = "bar", requiresDependencyResolution = ResolutionScope.COMPILE_PLUS_RUNTIME, defaultPhase = LifecyclePhase.PACKAGE)
+public class BarMojo extends AbstractMojo {
+    private static final String JOB_XML_PATH = "META-INF/batch-jobs/";
+
+    @Component(role = Archiver.class, hint = "jar")
+    private JarArchiver jarArchiver;
+
+    @Component
+    private MavenProjectHelper helper;
+
+    @Parameter(readonly = true, defaultValue = "${project}")
+    private MavenProject project;
+
+    @Parameter(defaultValue = "${localRepository}")
+    protected ArtifactRepository localRepo;
+
+    @Parameter(defaultValue = "${project.remoteArtifactRepositories}")
+    protected List<ArtifactRepository> remoteRepos;
+
+    @Component(role = ProjectDependenciesResolver.class)
+    protected ProjectDependenciesResolver resolver;
+
+    @Parameter(defaultValue = "${session}", required = true)
+    protected MavenSession session;
+
+    @Parameter(property = "batchee.finalName", defaultValue = "${project.build.finalName}", required = true)
+    private String finalName;
+
+    @Parameter(defaultValue = "${project.build.directory}", required = true)
+    private File outputDirectory;
+
+    @Parameter(property = "batchee.classifier")
+    private String classifier;
+
+    @Parameter(property = "batchee.classes", defaultValue = "${project.build.directory}/classes")
+    private File classes;
+
+    @Override
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        if (classifier == null && !"bar".equals(project.getPackaging())) {
+            getLog().info("Packaging type is not 'bar' so ignoring bar creation");
+            return;
+        }
+
+        final File bar = createBar();
+
+        if (classifier != null) {
+            helper.attachArtifact(project, "bar", classifier, bar);
+        } else {
+            project.getArtifact().setFile(bar);
+        }
+    }
+
+    private File createBar() throws MojoExecutionException {
+        final File target = new File(outputDirectory, finalName + (classifier != null ? classifier : "") + ".bar");
+
+        final MavenArchiver archiver = new MavenArchiver();
+        archiver.setOutputFile(target);
+        archiver.setArchiver(jarArchiver);
+
+        // lib/*
+        try {
+            for (final Artifact dependency : resolver.resolve(project, asList("compile", "runtime", "system"), session)) {
+                if ("provided".equals(dependency.getScope())) { // not sure why compile triggers provided using resolver
+                    continue;
+                }
+                jarArchiver.addFile(dependency.getFile(), "libs/" + artifactPath(dependency));
+            }
+        } catch (final Exception e) {
+            throw new MojoExecutionException(e.getMessage(), e);
+        }
+
+        // classes
+        if (classes.isDirectory()) {
+            final FileSelector jobSelector = new FileSelector() {
+                @Override
+                public boolean isSelected(final FileInfo fileInfo) throws IOException {
+                    final String name = fileInfo.getName();
+                    return name.replace('\\', '/').startsWith(JOB_XML_PATH) && name.endsWith(".xml");
+                }
+            };
+
+            final DefaultFileSet fileSet = new DefaultFileSet();
+            fileSet.setDirectory(classes);
+            fileSet.setIncludingEmptyDirectories(false);
+
+            fileSet.setPrefix("batch/classes/");
+            fileSet.setFileSelectors(new FileSelector[]{
+                    new FileSelector() {
+                        @Override
+                        public boolean isSelected(final FileInfo fileInfo) throws IOException {
+                            return !jobSelector.isSelected(fileInfo);
+                        }
+                    }
+            });
+            jarArchiver.addFileSet(fileSet);
+
+            fileSet.setPrefix("batch/jobs/");
+            fileSet.setFileSelectors(new FileSelector[]{ jobSelector });
+            jarArchiver.addFileSet(fileSet);
+        }
+
+        try {
+            archiver.createArchive(session, project, new MavenArchiveConfiguration());
+        } catch (final Exception e) {
+            throw new MojoExecutionException(e.getMessage(), e);
+        }
+
+        return target;
+    }
+
+    // artifactId-[classifier-]version.type
+    private static String artifactPath(final Artifact artifact) {
+        final StringBuilder sb = new StringBuilder();
+
+        sb.append(artifact.getArtifactId());
+        sb.append("-");
+
+        if (artifact.hasClassifier()) {
+            sb.append(artifact.getClassifier());
+            sb.append("-");
+        }
+
+        if (artifact.getBaseVersion() != null) {
+            sb.append(artifact.getBaseVersion());
+        } else if (artifact.getVersion() != null) {
+            sb.append(artifact.getVersion());
+        } else {
+            sb.append(artifact.getVersionRange().toString());
+        }
+
+        sb.append('.').append(artifact.getType());
+        return sb.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-batchee/blob/012278dd/tools/maven-plugin/src/main/resources/META-INF/plexus/components.xml
----------------------------------------------------------------------
diff --git a/tools/maven-plugin/src/main/resources/META-INF/plexus/components.xml b/tools/maven-plugin/src/main/resources/META-INF/plexus/components.xml
new file mode 100644
index 0000000..cd7eb7e
--- /dev/null
+++ b/tools/maven-plugin/src/main/resources/META-INF/plexus/components.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You 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.
+-->
+<component-set>
+  <components>
+    <component>
+      <role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role>
+      <role-hint>bar</role-hint>
+      <implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation>
+      <configuration>
+        <phases>
+          <process-resources>org.apache.maven.plugins:maven-resources-plugin:resources</process-resources>
+          <compile>org.apache.maven.plugins:maven-compiler-plugin:compile</compile>
+          <package>org.apache.batchee:batchee-maven-plugin:bar</package>
+          <install>org.apache.maven.plugins:maven-install-plugin:install</install>
+          <deploy>org.apache.maven.plugins:maven-deploy-plugin:deploy</deploy>
+        </phases>
+      </configuration>
+    </component>
+    <component>
+      <role>org.apache.maven.artifact.handler.ArtifactHandler</role>
+      <role-hint>bar</role-hint>
+      <implementation>org.apache.maven.artifact.handler.DefaultArtifactHandler</implementation>
+      <configuration>
+        <extension>bar</extension>
+        <type>bar</type>
+        <packaging>bar</packaging>
+      </configuration>
+    </component>
+  </components>
+</component-set>