0

I have a embedded jetty java application, which starts itself up and serving requests at the mentioned routes. While testing it is working good. Now that I want to deploy this java application via war file it is giving me issues.

  1. While running java -jar server--1.0-SNAPSHOT.war : It is giving me error as Error: Could not find or load main class com.server.core.App

  2. This one will come after the 1st issue is fixed, how to include all the dependencies into the war file.

Here is my pom.xml https://gist.github.com/shadow-fox/24ec2c7d40f4b0e6aae5

<?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>com.server</groupId>
    <artifactId>server</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-server</artifactId>
            <version>${jetty-version}</version>
        </dependency>

        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-servlet</artifactId>
            <version>${jetty-version}</version>
        </dependency>

        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-util</artifactId>
            <version>${jetty-version}</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-servlet-core</artifactId>
            <version>${jersey-version}</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-server</artifactId>
            <version>${jersey-version}</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.containers</groupId>
            <artifactId>jersey-container-jetty-http</artifactId>
            <version>${jersey-version}</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-moxy</artifactId>
            <version>${jersey-version}</version>
        </dependency>

        <dependency>
            <groupId>org.glassfish.jersey.core</groupId>
            <artifactId>jersey-client</artifactId>
            <version>${jersey-version}</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>${jackson-version}</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson-version}</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>${jackson-version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j-version}</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit-version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <jdk-version>1.8</jdk-version>
        <jetty-version>9.3.3.v20150827</jetty-version>
        <jersey-version>2.21</jersey-version>
        <junit-version>4.12</junit-version>
        <jackson-version>2.6.1</jackson-version>
        <log4j-version>2.3</log4j-version>
        <mvn-compiler>3.3</mvn-compiler>
        <mvn-war>2.6</mvn-war>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${mvn-compiler}</version>
                <configuration>
                    <source>${jdk-version}</source>
                    <target>${jdk-version}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>${mvn-war}</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>com.server.core.App</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Where I might have messed up: The classpathPrefix not sure what to set here. My classes goes to target dir as default (Using IDEA Intellij) . The mainClass does exist on the same path.

App.java

package com.server.core;

import com.server.core.filters.RequestFilter;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.servlet.ServletContainer;

import java.util.EnumSet;

import javax.servlet.DispatcherType;

public class App {

    private static final Logger logger = LogManager.getLogger(App.class);

    public static void main(String[] args) throws Exception {

        Server server = new Server();
        ServerConnector connector = new ServerConnector(server);
        connector.setPort(8080);
        server.addConnector(connector);

        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
        context.setContextPath("/");
        context.addFilter(RequestFilter.class, "/*", EnumSet.of(DispatcherType.INCLUDE,
                DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR,
                DispatcherType.FORWARD));
        server.setHandler(context);

        ServletHolder servletHolder = new ServletHolder(ServletContainer.class);
        servletHolder.setInitOrder(0);
        servletHolder.setInitParameter("jersey.config.server.provider.packages", "com/server/core/endpoints");
        context.addServlet(servletHolder, "/*");

        try {
            server.start();
            logger.debug("Server started");
            logger.debug(server);
            server.join();
        } catch (Throwable t) {
            logger.error(System.err);
        } finally {
            server.destroy();
        }
    }
}

There are lot of questions similar to this but couldn't find for this use-case.

Pranaya Behera
  • 545
  • 1
  • 9
  • 24
  • Normaly a war is a web-archive and has no main class. You should change to `jar` – Jens Sep 21 '15 at 07:49
  • If I do that then it is giving me ```Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/jetty/server/Connector``` . I would like to do that in war file. I have not gone through what is the difference between these two, but if you could provide some that would be helpful. – Pranaya Behera Sep 21 '15 at 07:53
  • read about the maven shade plugin https://maven.apache.org/plugins/maven-shade-plugin/ – Jens Sep 21 '15 at 07:57

2 Answers2

0

Update:: This is obsolete now. As I have went ahead with what @joakim-erdfelt suggested. As mentioned by @Jens in the comments I have changed the packaging from war to jar and using maven-shaded-plugin I am able to run the application without any issues. This is the modified version of the pom.xml

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-shade-plugin</artifactId>
  <version>${mvn-shade}</version>
  <executions>
      <execution>
          <phase>package</phase>
          <goals>
              <goal>shade</goal>
          </goals>
          <configuration>
              <filters>
                  <filter>
                      <artifact>*:*</artifact>
                      <excludes>
                          <exclude>META-INF/*.SF</exclude>
                          <exclude>META-INF/*.DSA</exclude>
                          <exclude>META-INF/*.RSA</exclude>
                      </excludes>
                  </filter>
              </filters>
              <transformers>
                  <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                      <mainClass>com.igp.core.App</mainClass>
                  </transformer>
              </transformers>
          </configuration>
      </execution>
  </executions>
</plugin>

Note that I have removed maven-jar-plugin from the plugin list as well.

Pranaya Behera
  • 545
  • 1
  • 9
  • 24
0

A self executing WAR with Jetty is possible, however its a bit tricky to setup, as the various maven plugins tend to undo the efforts of a Self Executing WAR.

The Jetty Project maintains such an example project.

https://github.com/jetty-project/embedded-jetty-live-war

The key is how its assembled.

The project has 4 main parts:

  • /thewebapp/ - this is the WAR file, the webapp, as it exists in its native format, with normal maven war and a produced artifact that is just a WAR file that isn't (yet) self-executing.
  • /theserver/ - this is the Embedded Jetty Server jetty.livewar.ServerMain.main(String args[]) which you customize to initialize your Jetty server and its WebApp. This project also is the place where you customize for things like JDBC servers libraries, JNDI, logging, etc. This project produces a uber-jar with all of the dependencies needed to run the server. Special care is taken with the maven-shade-plugin to merge META-INF/services/ files.
  • /server-bootstrap/ - this contains 2 small classes that sets up a LiveWarClassLoader from the content in the live WAR and then runs jetty.livewar.ServerMain.main(String args[]) from this new ClassLoader. This project also contains the live META-INF/MANIFEST.MF that the live WAR will need/use
  • /livewar-assembly/ - this is the project that ties together the above 3 projects into a Live/Executable WAR file. The artifacts from from the above 3 projects are unpacked by the maven-assembly-plugin and put into place where they will be most functional (and safe). For example, the server classes from /theserver/ are placed in /WEB-INF/jetty-server/ to make them inaccessible from Web Clients accessing the WAR file.

Note: there are 3 files present in your new assembled WAR file that you should be aware of, as these files can be downloaded by a Web Client as static content if you use this setup.

/jetty/bootstrap/JettyBootstrap.class
/jetty/bootstrap/LiveWarClassLoader.class
/META-INF/MANIFEST.MF

The example project is setup in such a way that information present in these bootstrap files should not reveal private or sensitive information about your Server or its operations. Merely that the Webapp can be started as a Live/Executable WAR file.

It might seem strange to have 4 modules result in 1 executable artifact, but you have to understand you are dealing with 3 things to set this environment up.

  1. The WebApp itself (with all of its classes and jars) - this is /thewebapp/
  2. The Classes representing the Server Main, and its requirements. (not to be co-mingled with the WebApp classes) - this is /theserver/
  3. The Main-Class executable that binds the above 2 together intelligently (so as to not create a security issue for yourself) - this is /server-bootstrap/

The final module, /livewar-assembly/ just ties together the 3 parts into a unified whole.

I know it might be tempting to use maven-shade-plugin for it all, but you will not be able to accomplish all of the following requirements (you can accomplish some of them, and then attempt to fix for the others, but ultimately break the others, playing an endless game of wack-a-mole) ...

  1. Placing the Server jars in a place where they cannot be accessed via the Live WebApp.
  2. Merging Server side (and ONLY the server side) META-INF/services intelligently.
  3. Adding the BootStrap classes to the root of the WAR.
  4. Using a custom META-INF/MANIFEST.MF meant for the bootstrap (without also getting a merged copy of the META-INF/MANIFEST.MF files from all of the Server dependencies)
Joakim Erdfelt
  • 46,896
  • 7
  • 86
  • 136
  • I have added one answer in terms of jar file using maven-shaded-plugin, could you please comment on whether it is good enough. And the problem is that now it is creating every dependencies to the classpath hence the size increases significantly. – Pranaya Behera Sep 21 '15 at 11:45
  • I have looked at the repository before as well but couldnt make out why there are 4 modules set up for one executable file to be produced. – Pranaya Behera Sep 21 '15 at 11:45
  • Added commentary about it. The `maven-shade-plugin` is insufficient to accomplish the end goal, its just not designed to handle this kind of nuanced merging, etc ... – Joakim Erdfelt Sep 21 '15 at 14:00
  • Got it. Let me assemble in that way. Will post after doing so. – Pranaya Behera Sep 21 '15 at 14:39
  • Same way I can jar as well ? – Pranaya Behera Sep 21 '15 at 14:48