Using batik-rasterizer.jar from version 1.7 of batik, I was wondering how to call the jar correctly.
java -jar batik-rasterizer-1.7.jar -m image/png -q 0.8 $1
Which gave me:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/batik/i18n/LocalizableSupport
Therefore, I was looking for the correct MainClass and ClassPath to use. I'll post a script I came up with as an answer in a minute ...
This would be the maven repackage approach:
create a maven project based on the pom.xml below
add the Java class Rasterizer
add the JUnit test TestRasterizer
add the sample File readandblue.svg
run mvn install
You should get a "shaded" jar file target/com.bitplan.rasterizer-0.0.1.jar
which already has the java.awt.headless call included:
batik-rasterizer.jar - convert svg to png
You could also use the pom.xml "standalone" and simply change the main class in there.
Rasterize.java wrapper class
package com.bitplan.rasterizer;
import org.apache.batik.apps.rasterizer.Main;
/**
* Rasterizer main class
* https://stackoverflow.com/questions/13112967/how-to-call-batik-rasterizer-jar
* #author wf
*
*/
public class Rasterizer {
/**
* wrapper entry point for batik rasterizer
* #param args
*/
public static void main(String[] args) {
System.setProperty("java.awt.headless", "true");
// see
// http://www.docjar.org/html/api/org/apache/batik/apps/rasterizer/Main.java.html
Main main=new Main(args);
main.execute();
}
}
TestRasterizer.java JUnit testcase
package com.bitplan.rasterizer;
import static org.junit.Assert.*;
import java.io.File;
import org.junit.Test;
/**
* test the rasterizer
* #author wf
*
*/
public class TestRasterizer {
#Test
public void testRasterizer() {
File testFile=new File("src/test/data/redandblue.svg");
File pngFile=new File("src/test/data/redandblue.png");
if (pngFile.exists())
pngFile.delete();
assertTrue(testFile.exists());
String [] args={"-scriptSecurityOff","-m","image/png","-q","0.8",testFile.getPath()};
com.bitplan.rasterizer.Rasterizer.main(args);
assertTrue(pngFile.exists());
}
}
sample svg file: readandblue.svg
<svg xmlns="http://www.w3.org/2000/svg"
width="467" height="462">
<rect x="80" y="60" width="250" height="250" rx="20"
style="fill:#ff0000; stroke:#000000;stroke-width:2px;" />
<rect x="140" y="120" width="250" height="250" rx="40"
style="fill:#0000ff; stroke:#000000; stroke-width:2px;
fill-opacity:0.7;" />
</svg>
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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bitplan.java</groupId>
<version>0.0.1</version>
<artifactId>com.bitplan.rasterizer</artifactId>
<name>com.bitplan.rasterizer</name>
<description>Provide rasterizer access</description>
<properties>
<junit3.version>3.8.1</junit3.version>
<junit4.version>4.11</junit4.version>
</properties>
<dependencies>
<!-- batik rasterizer -->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-rasterizer</artifactId>
<version>1.7</version>
</dependency>
<!-- https://issues.apache.org/bugzilla/show_bug.cgi?id=44682 -->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-codec</artifactId>
<version>1.7</version>
</dependency>
<!-- Runtime Junit access -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit4.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<!-- <mainClass>org.apache.batik.apps.rasterizer.Main</mainClass> -->
<mainClass>com.bitplan.rasterizer.Rasterizer</mainClass> -->
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
This script gets all necessary jars from the maven repository into a classpath variable. Probably there is a better way e.g. repacking using maven ...
#!/bin/bash
#set -x
batikdir=$HOME/.m2/repository/org/apache/xmlgraphics/batik-rasterizer/1.7/
batik=batik-rasterizer-1.7.jar
main=org.apache.batik.apps.rasterizer.Main
jarlist=/tmp/jarlist$$.txt
cp=$batikdir/$batik
find $HOME/.m2/repository -type f -name *.jar > $jarlist
for j in batik-ext-1.7.jar batik-dom-1.7.jar batik-css-1.7.jar batik-svg-dom-1.7.jar batik-gvt-1.7.jar batik-parser-1.7.jar batik-script-1.7.jar batik-bridge-1.7.jar batik-anim-1.7.jar batik-transcoder-1.7.jar batik-awt-util-1.7.jar batik-codec-1.7.jar batik-util-1.7.jar batik-xml-1.7.jar xerces-2.5.0.jar xalan-2.6.0.jar xml-apis-1.3.04.jar xml-apis-ext-1.3.04.jar fop-0.94.jar batik-js.jar
do
#echo $j
p=`grep $j $jarlist`
cp="$cp:$p"
done
#echo $cp
# http://tech.groups.yahoo.com/group/svg-developers/message/47939
java -cp $cp $main -scriptSecurityOff -m image/png -q 0.8 $1
rm -f $jarlist
to get the libraries i created a dummy maven project with the following pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!--
install maven 2 to use this pom file
run e.g. mvn dependency:go-offline to download all libraries
-->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.bitplan.svg</groupId>
<artifactId>com.bitplan.svg</artifactId>
<version>0.0.1</version>
<name>Dummy Project for SVG </name>
<packaging>pom</packaging>
<dependencies>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-rasterizer</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-codec</artifactId>
<version>1.7</version>
</dependency>
</dependencies>
<!-- configure jar plugin to build test-jar http://maven.apache.org/guides/mini/guide-attached-tests.html -->
<build>
<extensions>
<extension>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-ssh</artifactId>
<version>1.0</version>
</extension>
</extensions>
</build>
</project>
Related
tl;dr: Why does this work locally but not when I deploy to my live App Engine project?
I'm trying to create a barebones servlet-based web app using the Java 11 version of App Engine. I'm updating a few projects from Java 8 to Java 11 following this guide. I'm also using this guide and this example. My goal is to use Jetty to run a very simple web app that serves a single static HTML file and a single servlet file in App Engine.
My web app works fine when I run locally:
mvn clean install
mvn exec:java -Dexec.args="target/app-engine-hello-world-1.war"
When I run these commands, both my index.html and my servlet URL work fine.
But when I deploy to my live site:
mvn package appengine:deploy
...the command succeeds, but when I navigate to my live URL, I get this error for both the HTML file and the servlet URL: "Error: Server Error. The server encountered an error and could not complete your request. Please try again in 30 seconds." If I look in the logs in the Cloud console, I see this error:
Error: Could not find or load main class io.happycoding.Main
Caused by: java.lang.ClassNotFoundException: io.happycoding.Main
Something is off with my setup, but I don't see anything obviously wrong.
Here are the files in my project:
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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.happycoding</groupId>
<artifactId>app-engine-hello-world</artifactId>
<version>1</version>
<packaging>war</packaging>
<properties>
<!-- App Engine currently supports Java 11 -->
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.4.31.v20200723</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>9.4.31.v20200723</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>9.4.31.v20200723</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId>
<version>9.4.31.v20200723</version>
<type>jar</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>io.happycoding.Main</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>2.2.0</version>
<configuration>
<projectId>happy-coding-gcloud</projectId>
<version>1</version>
</configuration>
</plugin>
</plugins>
</build>
</project>
src/main/appengine/app.yaml
runtime: java11
entrypoint: 'java -cp "*" io.happycoding.Main app-engine-hello-world-1.war'
src/main/java/io/happycoding/Main.java
package io.happycoding;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.webapp.Configuration.ClassList;
import org.eclipse.jetty.webapp.WebAppContext;
import io.happycoding.servlets.HelloWorldServlet;
/** Simple Jetty Main that can execute a WAR file when passed as an argument. */
public class Main {
public static void main(String[] args) throws Exception {
if (args.length != 1) {
System.err.println("Usage: need a relative path to the war file to execute");
System.exit(1);
}
System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StrErrLog");
System.setProperty("org.eclipse.jetty.LEVEL", "INFO");
Server server = new Server(8080);
WebAppContext webapp = new WebAppContext();
webapp.setContextPath("/");
webapp.setWar(args[0]);
ClassList classlist = ClassList.setServerDefault(server);
// Enable Annotation Scanning.
classlist.addBefore(
"org.eclipse.jetty.webapp.JettyWebXmlConfiguration",
"org.eclipse.jetty.annotations.AnnotationConfiguration");
server.setHandler(webapp);
server.join();
}
}
src/main/webapp/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Google Cloud Hello World</title>
</head>
<body>
<h1>Google Cloud Hello World</h1>
<p>This is a sample HTML file. Click here to see content served from a servlet.</p>
<p>Learn more at HappyCoding.io.</p>
</body>
</html>
src/main/java/io/happycoding/servlets/HelloWorldServlet.java
package io.happycoding.servlets;
import java.io.IOException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
#WebServlet("/hello")
public class HelloWorldServlet extends HttpServlet {
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html;");
response.getOutputStream().println("<h1>Hello world!</h1>");
}
}
I'm guessing something is off with how I'm setting the classpath of the live site, but I don't see anything obviously wrong.
With the packaging property in pom.xml set to war, I get a .war file with these contents:
index.html
META-INF/MANIFEST.MF
META-INF/maven/io.happycoding/app-engine-hello-world/pom.properties
META-INF/maven/io.happycoding/app-engine-hello-world/pom.xml
WEB-INF/classes/io/happycoding/Main.class
WEB-INF/classes/io/happycoding/servlets/HelloWorldServlet.class
WEB-INF/classes/lib/asm-7.3.1.jar
WEB-INF/classes/lib/asm-analysis-7.3.1.jar
WEB-INF/classes/lib/asm-commons-7.3.1.jar
WEB-INF/classes/lib/asm-tree-7.3.1.jar
WEB-INF/classes/lib/javax.annotation-api-1.3.jar
WEB-INF/classes/lib/javax.servlet-api-4.0.1.jar
WEB-INF/classes/lib/jetty-annotations-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-http-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-io-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-jndi-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-plus-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-security-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-server-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-servlet-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-util-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-webapp-9.4.31.v20200723.jar
WEB-INF/classes/lib/jetty-xml-9.4.31.v20200723.jar
If I change the packaging property in pom.xml to jar, then I get a .jar file with these contents:
io/happycoding/Main.class
io/happycoding/servlets/HelloWorldServlet.class
META-INF/MANIFEST.MF
META-INF/maven/io.happycoding/app-engine-hello-world/pom.properties
META-INF/maven/io.happycoding/app-engine-hello-world/pom.xml
And I get this error in the logs for the live site instead:
Error: Unable to initialize main class io.happycoding.Main
Caused by: java.lang.NoClassDefFoundError: org/eclipse/jetty/server/Handler
That feels like progress, but then I also get 404 errors in my live server, so I feel pretty stuck.
What do I need to change about my above setup to make it work both locally and on my live server?
Edit: I can see the following files in the App Engine debugger:
I tried adding this to my pom.xml file:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.2</version>
<executions>
<execution>
<id>copy</id>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>
${project.build.directory}/appengine-staging
</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
Then I see these file in the App Engine debugger:
But I still get the same error.
I believe that the problem is caused by my Main class being inside a .war file which has no effect on the classpath, which is why it can't be found.
How do I package my project up so it works locally and on my live server?
I think your problem is that you are including the Main class in the war itself, and App Engine is unable to find it.
As you can see in the GCP migration guide, the Main class is defined in an external dependency named simple-jetty-main.
With the execution of the maven-dependency-plugin this dependency is copied to the appengine-staging directory, making it accessible from the Java classpath.
This is the reason why the Main class can be found in the example proposed in the guide when executing the command from the app.yaml entrypoint:
entrypoint: 'java -cp "*" com.example.appengine.demo.jettymain.Main helloworld.war'
Therefore, the solution will be to include your Main class in another library, independent from the war file that you need to deploy.
Maybe you can create a library - as Google does with simple-jetty-main - that can reuse in your GCP projects for this task.
Just for testing, in order to confirm this point, you can use the simple-jetty-main library itself (you can clone the required code from https://github.com/GoogleCloudPlatform/java-docs-samples/tree/master/appengine-java11/appengine-simple-jetty-main). Install it, include the dependency in your pom.xml, include also the maven-dependency-plugin, and define your entrypoint as follows:
entrypoint: 'java -cp "*" com.example.appengine.demo.jettymain.Main app-engine-hello-world-1.war'
For your comments, you will prefer not to have the separation between the Main class and the rest of the code.
To meet that requirement we must first change the Main class so that Jetty can serve HelloWorldSevlet and the static content. The code is actually very similar to the one you provided. Please excuse the simplicity of the setup, it is based on web.xml file; if necessary, further development can be done to deal with annotations or whatever is deemed appropriate:
package io.happycoding;
import java.net.URL;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
public class Main {
public static final String WEBAPP_RESOURCES_LOCATION = "META-INF/resources";
public static void main(String[] args) throws Exception {
System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.StrErrLog");
System.setProperty("org.eclipse.jetty.LEVEL", "INFO");
Server server = new Server(8080);
URL webAppDir = Thread.currentThread().getContextClassLoader().getResource(WEBAPP_RESOURCES_LOCATION);
if (webAppDir == null) {
throw new RuntimeException(String.format("Unable to find %s directory into the JAR file", WEBAPP_RESOURCES_LOCATION));
}
WebAppContext webAppContext = new WebAppContext();
webAppContext.setContextPath("/");
webAppContext.setDescriptor(WEBAPP_RESOURCES_LOCATION + "/WEB-INF/web.xml");
webAppContext.setResourceBase(webAppDir.toURI().toString());
webAppContext.setParentLoaderPriority(true);
server.setHandler(webAppContext);
server.start();
server.join();
}
}
The static resources can be loaded from a directory of your choice (it will be parameterized in the pom.xml).
For instance, I have created the src/main/webapp folder to store the static content.
In this folder, you also need to define - in this case, due to the way we setup Jetty - a WEB-INF directory with this web.xml file inside:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<servlet>
<servlet-name>HelloWorldServlet</servlet-name>
<servlet-class>io.happycoding.servlets.HelloWorldServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloWorldServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
This is a tree of my source code setup:
The pom.xml file is very similar to the one you provided. I only included the maven-resources-plugin to copy the web app static content to the jar file, and the maven-shade-plugin to generate an UberJar:
<?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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.happycoding</groupId>
<artifactId>app-engine-hello-world</artifactId>
<version>1</version>
<packaging>jar</packaging>
<properties>
<!-- App Engine currently supports Java 11 -->
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<failOnMissingWebXml>false</failOnMissingWebXml>
<!-- Directory where static content resides -->
<webapp.dir>./src/main/webapp</webapp.dir>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>9.4.31.v20200723</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-webapp</artifactId>
<version>9.4.31.v20200723</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-util</artifactId>
<version>9.4.31.v20200723</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-annotations</artifactId>
<version>9.4.31.v20200723</version>
<type>jar</type>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>io.happycoding.Main</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<executions>
<execution>
<id>copy-web-resources</id>
<phase>compile</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/classes/META-INF/resources</outputDirectory>
<resources>
<resource>
<directory>${webapp.dir}</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>io.happycoding.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>appengine-maven-plugin</artifactId>
<version>2.2.0</version>
<configuration>
<projectId>happy-coding-gcloud</projectId>
<version>1</version>
</configuration>
</plugin>
</plugins>
</build>
</project>
With this setup, you can run the application locally by executing the following command:
mvn exec:java
You can also run the program locally right from the java tool:
java -jar appengine-deploy-sample-1.jar
Sorry, I cannot test the setup in GCP but I think, that according to the migration guide, you can try to deploy the application without indicating the entrypoint in your app.yaml.
If it does not work, you can try to run the app by configuring an entrypoint similar to the following:
entrypoint: 'java -jar appengine-deploy-sample-1.jar'
Or maybe:
entrypoint: 'java -cp "*" -jar appengine-deploy-sample-1.jar'
This might be a weird question but I am curious about this:
I want to create a Java EE Web Project, that I can package into a JAR file (<packaging>jar</packaging> instead of <packaging>war</packaging>).
In other words, I want to include the Java EE web server inside a JAR (built by maven).
Inside the WebServer I want to use Servlets like I can use them when I package it to a WAR file but without requireing the devices that execute the JAR to have a Web server installed where they can deploy my JAR.
I want something like an executeable JAR that contains the server and runs it without the need to install something else.
Is there a (ideally light-weight) server that works within a JAR file or any other possibility to create a JAR file like this?
If you want to use vanilla Java EE, you can use an Embedded Jetty Server or Embedded Tomcat Server:
Here is an example with Embedded Tomcat and Maven:
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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.company</groupId>
<artifactId>app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>12</maven.compiler.source>
<maven.compiler.target>12</maven.compiler.target>
<servlet.version>3.1.0</servlet.version>
<jsf.version>2.2.19</jsf.version>
<tomcat.version>9.0.21</tomcat.version>
</properties>
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>${jsf.version}</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>${jsf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>${tomcat.version}</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-el</artifactId>
<version>${tomcat.version}</version>
</dependency>
</dependencies>
<build>
<finalName>app</finalName>
<resources>
<resource>
<directory>src/main/webapp</directory>
<targetPath>META-INF/resources</targetPath>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>12</source>
<target>12</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<finalName>app-${project.version}</finalName>
<archive>
<manifest>
<mainClass>org.company.app.Application</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
mainClass:
package org.company.app;
import org.apache.catalina.WebResourceRoot;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.startup.Tomcat;
import org.apache.catalina.webresources.DirResourceSet;
import org.apache.catalina.webresources.StandardRoot;
import java.io.File;
public class Application {
public static void main(final String[] args) throws Exception {
final String webappPath = new File("src/main/webapp").getAbsolutePath();
final Tomcat tomcat = new Tomcat();
final StandardContext ctx = (StandardContext) tomcat.addWebapp("/", webappPath);
System.out.println(ctx
);
// Declare an alternative location for your "WEB-INF/classes" dir
// Servlet 3.0 annotation will work
final String targetClassesPath = new File("target/classes").getAbsolutePath();
final WebResourceRoot resources = new StandardRoot(ctx);
resources.addPreResources(new DirResourceSet(//
resources, "/WEB-INF/classes", //
targetClassesPath, "/"));
ctx.setResources(resources);
tomcat.start();
tomcat.getServer().await();
}
}
and the rest as usual java ee development
I'm using Maven and Vert.x to create an application.
Everything is working fine while I run it in my IDE (IntelliJ) but I can't make it work using command line.
I have a Launcher class that deploy some verticles but the problem is the same with all my verticles.
So far here is what I've tried:
vertx run Launcher.java
vertx run com.packagename.Launcher.java
// user-content-service-0.1.jar create via: mvn clean package
run com.packagename.Launcher.java -cp target\user-content-service-0.1.jar
As error I get this:
.../path/Launcher.java:8: error: cannot find symbol
private static final Logger logger = logManager.getLogger(Launcher.class);
symbol: variable LogManager
location: class com.packagename.Launcher
java.lang.RuntimeException: Compilation failed
...
The issue seems to come from the fact that the compiler is not able to find dependancies
Here is what my pom.xml looks like:
<?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>com.packagename</groupId>
<artifactId>user-content-service</artifactId>
<packaging>jar</packaging>
<version>0.1</version>
<name>Project - user-content-service</name>
<url>http://maven.apache.org</url>
<properties>
<vertx.version>[3.5.0,3.6)</vertx.version>
<java.version>1.8</java.version>
<maven-compiler-plugin.version>3.3</maven-compiler-plugin.version>
<log4j.version>[2.10.0,2.11)</log4j.version>
<junit.version>4.12</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${log4j.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-unit</artifactId>
<version>${vertx.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-dynamodb</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-config</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web-client</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>guru.nidi.raml</groupId>
<artifactId>raml-tester</artifactId>
<version>0.9.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
And that's my Launcher.java file:
package com.packagename;
import io.vertx.core.AbstractVerticle; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger;
public class Launcher extends AbstractVerticle {
private static final Logger logger = LogManager.getLogger(Launcher.class);
#Override
public void start() {
vertx.deployVerticle("com.packagename.DynamoDBVerticle", logRes -> {
if (logRes.succeeded()) {
vertx.deployVerticle("com.packagename.UploaderVerticle", uploaderRes -> {
if (uploaderRes.succeeded()) {
vertx.deployVerticle("com.packagename.ServerVerticle", serverRes -> {
if (!serverRes.succeeded()) {
logger.error("Could not start server");
}
});
}
else {
logger.error("Could not start uploader");
}
});
}
else {
logger.error("Could not start Dynamo");
}
});
} }
Any clue what could I be doing wrong here ?
Thanks !
You are having a classpath issue; because you package your jarfile with maven-jar in the package phase, you are only getting that in the build directory; if you want to run on the command-line with java -jar, you should create a fat-jar (aka uber-jar, a jar file specifically packaged in order for it to contain your classes and all dependencies declared in the pom file).
You can add the maven-shade-plugin to do that in the package phase, ie:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<Main-Class>io.vertx.core.Launcher</Main-Class>
<Main-Verticle>com.packagename.Launcher</Main-Verticle>
</manifestEntries>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/services/io.vertx.core.spi.VerticleFactory</resource>
</transformer>
</transformers>
<artifactSet/>
<outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar</outputFile>
</configuration>
</execution>
</executions>
</plugin>
Note: you have a class name clash between the vert.x Launcher (used as Main-Class, in the fat-jar), and your own Launcher (which is actually a Verticle): I'd suggest to rename it.
After maven package, you will be then able to do:
java -jar target/user-content-service-0.1-fat.jar
Check in the vert.x samples for more information.
I am creating a simple helloworld hadoop project. I really do not know what to include to get around this error. It seems the hadoop libraries need some resource I am not including.
I have tried adding the following argument to the run configurations.. But it is not helping the issue..
-Djavax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl
Here is my code:
/**
* Writes a static string to a file using the Hadoop Libraries
*/
public class WriteToFile {
public static void main(String[] args) {
//String to print to file
final String HELLOWORLD = "Hello World! This is Chris writing to the file.";
try {
//Instantiating the configuration
Configuration conf = new Configuration();
//Creating the file system
FileSystem fs = FileSystem.get(conf);
//Instantiating the path
Path path = new Path("/user/c4511/homework1.txt");
//Checking for the existence of the file
if(fs.exists(path)){
//delete if it already exists
fs.delete(path, true);
}
//Creating an output stream
FSDataOutputStream fsdos = fs.create(path);
//Writing helloworld static string to the file
fsdos.writeUTF(HELLOWORLD);
//Closing all connection
fsdos.close();
fs.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
What is causing this issue?
And here is the error I am getting
Nov 17, 2014 9:30:30 AM org.apache.hadoop.conf.Configuration loadResource
SEVERE: error parsing conf file: javax.xml.parsers.ParserConfigurationException: Feature 'http://apache.org/xml/features/xinclude' is not recognized.
Exception in thread "main" java.lang.RuntimeException: javax.xml.parsers.ParserConfigurationException: Feature 'http://apache.org/xml/features/xinclude' is not recognized.
at org.apache.hadoop.conf.Configuration.loadResource(Configuration.java:1833)
at org.apache.hadoop.conf.Configuration.loadResources(Configuration.java:1689)
at org.apache.hadoop.conf.Configuration.getProps(Configuration.java:1635)
at org.apache.hadoop.conf.Configuration.get(Configuration.java:790)
at org.apache.hadoop.fs.FileSystem.getDefaultUri(FileSystem.java:166)
at org.apache.hadoop.fs.FileSystem.get(FileSystem.java:158)
at WriteToFile.main(WriteToFile.java:24)
Caused by: javax.xml.parsers.ParserConfigurationException: Feature 'http://apache.org/xml/features/xinclude' is not recognized.
at org.apache.xerces.jaxp.DocumentBuilderFactoryImpl.newDocumentBuilder(Unknown Source)
at org.apache.hadoop.conf.Configuration.loadResource(Configuration.java:1720)
... 6 more
I had the same Exception in my project when I moved the project from 2.5.1 to 2.6.0. I had to solve it using the maven pom file, when I added xerces:* to the shaded jar file.
<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>
<groupId>emc.lab.hadoop</groupId>
<artifactId>DartAnalytics</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>DartAnalytics</name>
<description>Examples for usage of Dart simulated data</description>
<properties>
<main.class>OffsetRTMain</main.class>
<hadoop.version>2.6.0</hadoop.version>
<minimize.jar>true</minimize.jar>
</properties>
<!-- <repositories> <repository> <id>mvn.twitter</id> <url>http://maven.twttr.com</url>
</repository> </repositories> -->
<build>
<plugins>
<plugin>
<!-- The shade plugin allows us to compile the dependencies into the
jar file -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
<configuration>
<!-- minimize the jar removes all files that are not addressed in the
file. but the filters include stuff we must include -->
<minimizeJar>${minimize.jar}</minimizeJar>
<filters>
<filter>
<artifact>com.hadoop.gplcompression:hadoop-lzo</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<!-- This solves the hadoop 2.6.0 problem with ClassNotFound of "org.apache.xerces.jaxp.DocumentBuilderFactoryImpl" -->
<artifact>xerces:*</artifact>
<includes>
<include>**</include>
</includes>
</filter>
<filter>
<artifact>org.apache.hadoop:*</artifact>
<excludes>
<exclude>**</exclude>
</excludes>
</filter>
</filters>
<finalName>uber-${project.artifactId}-${project.version}</finalName>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>${main.class}</mainClass>
</transformer>
</transformers>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<!-- you can add this to the local repo by running mvn install:install-file
-Dfile=libs/hadoop-lzo-0.4.20-SNAPSHOT.jar -DgroupId=com.hadoop.gplcompression
-DartifactId=hadoop-lzo -Dversion=0.4.20 -Dpackaging=jar from the main project
directory -->
<!-- Another option is to build from outside the EMC network and get access
to the twitter maven repository by changing the version to a version in the
repository and un-commenting the repository addition -->
<dependency>
<groupId>com.hadoop.gplcompression</groupId>
<artifactId>hadoop-lzo</artifactId>
<version>0.4.20</version>
</dependency>
<dependency>
<groupId>net.sf.trove4j</groupId>
<artifactId>trove4j</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>${hadoop.version}</version>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>2.5.0</version>
</dependency>
<dependency>
<groupId>com.twitter.elephantbird</groupId>
<artifactId>elephant-bird-core</artifactId>
<version>4.5</version>
</dependency>
<!-- <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId>
<version>18.0</version> </dependency> -->
</dependencies>
Does anyone know of any web applications that are built with Maven to an RPM? The RPM Maven Plugin provides the functionality to build to an RPM, but it's documentation is lacking.
Specifically, I'm looking for an example that would include multiple modules, i.e. Chapter 8. A Multi-module Project, from the "Maven by Example" series.
An example with only a single module would be:
<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>com.mycompany.app</groupId>
<artifactId>my-webapp</artifactId>
<packaging>war</packaging>
<version>1.0-SOME-SNAPSHOT</version>
<name>my-webapp</name>
<url>http://maven.apache.org</url>
<properties>
<rpm.install.basedir>/opt/tomcat6</rpm.install.basedir>
<rpm.install.webapps>${rpm.install.basedir}/webapps</rpm.install.webapps>
<rpm.install.config>${rpm.install.basedir}/lib</rpm.install.config>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp.jstl</groupId>
<artifactId>jstl-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jstl-impl</artifactId>
<version>1.2</version>
</dependency>
<!-- Jackson JSON Processor -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.8.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rpm-maven-plugin</artifactId>
<version>2.1-alpha-1</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>attached-rpm</goal>
</goals>
</execution>
</executions>
<configuration>
<copyright>My Company</copyright>
<distribution>My Distribution</distribution>
<group>Applications/Internet</group>
<packager>${user.name}</packager>
<changelogFile>CHANGELOG</changelogFile>
<defaultDirmode>500</defaultDirmode>
<defaultFilemode>400</defaultFilemode>
<defaultUsername>tomcat6</defaultUsername>
<defaultGroupname>tomcat6</defaultGroupname>
<requires>
<require>apache-tomcat >= 6.0.20-2</require>
</requires>
<mappings>
<!-- webapps deployment -->
<mapping>
<directory>${rpm.install.webapps}/${project.artifactId}</directory>
<sources>
<source>
<location>target/${project.artifactId}-${project.version}</location>
</source>
</sources>
</mapping>
<!-- configuration files -->
<mapping>
<directory>${rpm.install.config}</directory>
<configuration>true</configuration>
<sources>
<source>
<location>src/main/resources/my-webapp.jdbc.properties.sample</location>
</source>
<source>
<location>src/main/resources/my-webapp.runtime.properties</location>
<destination>my-webapp.runtime.properties.sample</destination>
</source>
</sources>
</mapping>
<!-- (Optional) Create other necessary directory structure -->
<mapping>
<directory>${rpm.install.basedir}/my-webapp-workspace</directory>
<filemode>750</filemode>
<username>tomcatuser</username>
<groupname>tomcatuser</groupname>
</mapping>
</mappings>
<!-- (Optional) -->
<preinstallScriptlet>
<scriptFile>src/main/scripts/rpm/pre-install.sh</scriptFile>
</preinstallScriptlet>
<!-- (Optional) -->
<postinstallScriptlet>
<script>echo "WARNING: Restart tomcat to ensure changes take effect."</script>
</postinstallScriptlet>
</configuration>
</plugin>
</plugins>
</build>
</project>
Thanks for any/all help!
So, it turns out the example that I gave in asking my question was quite wrong, which probably contributed to why I didn't get the complete answer I was looking for. What I was really looking for was an RPM to install to a server, that depended on a servlet container (i.e. Tomcat) and would install the included Web Applications (webapps) to Tomcat's webapps directory.
As such, here is the proper answer:
<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>com.mycompany.app</groupId>
<artifactId>rpm-with-webapp</artifactId>
<packaging>pom</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>${project.artifactId}</name>
<url>http://maven.apache.org</url>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>application-master-pom</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<rpm.install.basedir>/srv/apache-tomcat-6.0.33</rpm.install.basedir>
<rpm.install.webapps>${rpm.install.basedir}/webapps</rpm.install.webapps>
<rpm.install.config>${rpm.install.basedir}/lib</rpm.install.config>
</properties>
<build>
<extensions>
<extension>
<groupId>org.apache.maven.wagon</groupId>
<artifactId>wagon-webdav</artifactId>
<version>1.0-beta-2</version>
</extension>
</extensions>
</build>
<profiles>
<profile>
<id>build-rpm</id>
<activation>
<property>
<name>build-rpm</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>rpm-maven-plugin</artifactId>
<version>2.1-alpha-1</version>
<extensions>true</extensions>
<executions>
<execution>
<goals>
<goal>attached-rpm</goal>
</goals>
<phase>verify</phase>
</execution>
</executions>
<configuration>
<classifier>${rpm.classifier}</classifier>
<copyright>My Company</copyright>
<distribution>My Distribution</distribution>
<group>Applications/Internet</group>
<packager>${user.name}</packager>
<changelogFile>CHANGELOG</changelogFile>
<defaultDirmode>500</defaultDirmode>
<defaultFilemode>400</defaultFilemode>
<defaultUsername>tomcatuser</defaultUsername>
<defaultGroupname>tomcatuser</defaultGroupname>
<requires>
<require>apache-tomcat >= 6.0.20-2</require>
</requires>
<mappings>
<!-- web app 1 (module #1) -->
<mapping>
<directory>${rpm.install.webapps}/myWebApp1</directory>
<sources>
<source>
<location>../path-to/myWebApp1/target/myWebApp1</location>
</source>
</sources>
</mapping>
<!-- web app 2 (module #2) -->
<mapping>
<directory>${rpm.install.webapps}/myWebApp2</directory>
<sources>
<source>
<location>../path-to/myWebApp2/target/unified-browser-widget</location>
</source>
</sources>
</mapping>
<!-- web app 3 (module #3) -->
<mapping>
<directory>${rpm.install.webapps}/myWebApp3</directory>
<sources>
<source>
<location>../path-to/myWebApp3/target/report-services</location>
</source>
</sources>
</mapping>
</mappings>
<postinstallScriptlet>
<script>echo "WARNING: You may need to restart tomcat to ensure changes take effect."</script>
</postinstallScriptlet>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
The thing to note about this is that I was looking for a "multi-module" project, but really what I meant was packaging multiple related Web Applications into a single RPM. So, the proper configuration of this Maven build tells the RPM installer that Apache Tomcat is required and installs the webapps to the proper folder within Tomcat.
I would suggest making the rpm a separate project (even if part of a multi-module) and have it declare dependencies on the war(s) or additional artifacts provided by other projects.
As far as I know Red Hat is working hard to deliver their Java projects (POM) in Fedora (RPM). Good example could be JBoss Application Server 7 which is being packaged for Fedora right now. Its a big bunch of POM files that are being translated from POMs to RPMs using various techniques and RPM macros. Those RPMs are somehow Maven-compatible. I don't know many details, but feel free to ask on lists/irc channels.
But my message is - generally it is not possible to use a "translator" that would read a set of pom files and produced set of rpm files. There are snags, you have to package everything one by one by hand.
If your target is not to do it the clean way, you could distribute your app in an one big RPM. Its pretty easy to create binary-only rpm. But this is not the way how open-source is delivered in linux distributions.