my assignment for the internship is to query some API. I have to do it by not using any application frameworks, namely Spring or Spring Boot.
Two semesters ago I had Servlet programming as a course. But I forgot big part of it.
One of the requirements is to be able to start the app from cmd. So I have decide to create simple Maven project from Eclipse (File -> New -> Maven Project). Also, I added in embedded Tomcat as a dependency so the app can be started from cmd just by using Maven commands.
I have this in my 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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.company</groupId> <artifactId>AssignmentAppWeb</artifactId> <version>0.0.1-SNAPSHOT</version> <name>AssignmentApp</name> <description>Assignment App</description> <properties> <tomcat.version>8.0.48</tomcat.version> </properties> <dependencies> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-core</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <version>${tomcat.version}</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-logging-juli</artifactId> <version>${tomcat.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>appassembler-maven-plugin</artifactId> <version>2.0.0</version> <configuration> <assembleDirectory>target</assembleDirectory> <programs> <program> <mainClass>p.Main</mainClass> </program> </programs> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>assemble</goal> </goals> </execution> </executions> </plugin> <plugin> <!-- Build an executable JAR --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <version>3.1.0</version> <configuration> <archive> <manifest> <mainClass>p.Main</mainClass> </manifest> </archive> </configuration> </plugin> </plugins> </build> </project>
This is in my main class:
package p; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.catalina.Context; import org.apache.catalina.LifecycleException; import org.apache.catalina.startup.Tomcat; public class Main { public static void main(String[] args) throws LifecycleException { Tomcat tomcat = new Tomcat(); tomcat.setBaseDir("temp"); tomcat.setPort(8080); String contextPath = "/"; String docBase = new File(".").getAbsolutePath(); Context context = tomcat.addContext(contextPath, docBase); HttpServlet servlet = new HttpServlet() { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter writer = resp.getWriter(); writer.println("<html><title>Welcome</title><body>"); writer.println("<h1>Have a Great Day!</h1>"); writer.println("</body></html>"); } }; String servletName = "Servlet1"; String urlPattern = "/go"; tomcat.addServlet(contextPath, servletName, servlet); context.addServletMappingDecoded(urlPattern, servletName); tomcat.start(); tomcat.getServer().await(); } }
Here is the general project structure:
If I cd
into target
, after doing mvn clean instal
, and then java -jar AssignmentAppWeb-0.0.1-SNAPSHOT.jar
, I get this error:
C:UsersMiljanDesktopFevoWS1AssignmentAppWebtarget>java -jar AssignmentAppWeb-0.0.1-SNAPSHOT.jar Error: A JNI error has occurred, please check your installation and try again Exception in thread "main" java.lang.NoClassDefFoundError: javax/servlet/Servlet at java.lang.Class.getDeclaredMethods0(Native Method) at java.lang.Class.privateGetDeclaredMethods(Class.java:2701) at java.lang.Class.privateGetMethodRecursive(Class.java:3048) at java.lang.Class.getMethod0(Class.java:3018) at java.lang.Class.getMethod(Class.java:1784) at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:650) at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:632) Caused by: java.lang.ClassNotFoundException: javax.servlet.Servlet at java.net.URLClassLoader.findClass(URLClassLoader.java:382) at java.lang.ClassLoader.loadClass(ClassLoader.java:418) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355) at java.lang.ClassLoader.loadClass(ClassLoader.java:351) ... 7 more
What am I doing wrong? I just need to add couple of controller via Servlets and that’s it. Not sure whether registering servlets via web.xml
is more correct way rather then through code like in Main
class. So, how to start embedded Tomact?
Advertisement
Answer
The cause of the problem in this question is that to run this program, Java needs all the runtime dependencies defined by Maven. In most projects these are too many and too complex to specify by hand, mainly IMO because of the transitive dependencies. Maven, as our dependency manager, provides tools to assist. Here are a few that I know of:
Simplest case, the Maven Exec plugin. If you do not mind running your program through a Maven project. For this case:
mvn exec:java -Dexec.mainClass=p.Main
The Maven Assembly Plugin. The description from the site is to the point: “enables developers to combine project output into a single distributable archive that also contains dependencies, modules, site documentation, and other files“. This is not as simple, but still quite straightforward. It is configured by a file called the assembly descriptor that defines precisely what to include in the final assembly.
The Maven Dependency Plugin with
dependency:copy
ordependency:copy-dependencies
will copy the dependency jars into some folder in your file system. It can include transitive dependencies of course and apply simple transformations such as stripping the version number from the jar file. From there you can either include them in your classpath by hand, or let a script do it for you.The Maven Shade Plugin goes one step forward by re-packaging all your dependencies and application code into a single jar, optionally renaming some of them.