5

I have been trying to make my Java application host a web page (an HTML page, not JSP) through Apache Tomcat embedded in the application. I am using Maven for the build system on NetBeans IDE 8.0.2. For some reason, Tomcat refuses to recognise the index.html page I have placed in the application despite multiple attempts and creating all sorts of folders like WEB-INF. But it still throws a 404 error at me.

Here is some of the relevant code as I have set up in my project (some code has been omitted but irrelevant to the situation):

1. MainApplication.java - fires up Tomcat

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.io.*;
import java.util.Optional;
import org.apache.catalina.startup.Tomcat;

public class MainApplication{

    public static final Optional<String> port = Optional.ofNullable(System.getenv("PORT"));

    public static void main(String[] args) throws Exception {
        String contextPath = "/";
        String appBase = ".";
        Tomcat tomcat = new Tomcat();
        tomcat.setPort(Integer.valueOf(port.orElse("8080")));
        tomcat.getHost().setAppBase(appBase);
        tomcat.addWebapp(contextPath, appBase);
        tomcat.start();
        tomcat.getServer().await();
    }
}

2. ShuttleServlet.java

@WebServlet(
            name = "ShuttleServlet", urlPatterns = {"/shuttle"}
    )

public class ShuttleServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // Code to interact with application to be added later
    } 

3. Directory structure

- src
|
 - main
 |
  - resources
  - webapp
  |
    * index.html
    * scripts.js

4. 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>com.example</groupId>
    <artifactId>TransportApp</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <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-logging-juli</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</groupId>
            <artifactId>tomcat-jasper</artifactId>
            <version>${tomcat.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jasper-el</artifactId>
            <version>${tomcat.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>tomcat-jsp-api</artifactId>
            <version>${tomcat.version}</version>
        </dependency>
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.5</version>
        </dependency>
    </dependencies>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
        <tomcat.version>7.0.57</tomcat.version>
    </properties>
    <build>
  <finalName>TransportApp</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>2.3.2</version>
          <inherited>true</inherited>
          <configuration>
              <source>1.8</source>
              <target>1.8</target>
          </configuration>          
      </plugin>      
      <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-assembly-plugin</artifactId>
          <configuration>
              <descriptorRefs>
                  <descriptorRef>jar-with-dependencies</descriptorRef>
              </descriptorRefs>
              <finalName>TransportApp-${project.version}</finalName>
              <archive>
                  <manifest>
                      <mainClass>com.example.TransportApp.MainApplication</mainClass>
                  </manifest>
              </archive>
          </configuration>
          <executions>
              <execution>
                  <phase>package</phase>
                  <goals>
                      <goal>single</goal>
                  </goals>
              </execution>
          </executions>
      </plugin>     
  </plugins>
</build>            
</project>

5. Tomcat console logs

Dec 04, 2016 3:09:24 AM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Dec 04, 2016 3:09:24 AM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Tomcat
Dec 04, 2016 3:09:24 AM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.57
Dec 04, 2016 3:09:24 AM org.apache.catalina.startup.ContextConfig getDefaultWebXmlFragment
INFO: No global web.xml found
Dec 04, 2016 3:09:25 AM org.apache.catalina.util.SessionIdGenerator createSecureRandom
INFO: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [170] milliseconds.
Dec 04, 2016 3:09:25 AM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]

All of these result in a 404 that has been persistent despite multiple attempts and now-deleted folders. I hope all of this helps in finding the culprit.

Casper Spruit
  • 944
  • 1
  • 13
  • 31
Mayukh Nair
  • 623
  • 1
  • 6
  • 26

5 Answers5

3

Root cause of the issue:

The issue is with your Launcher class. In the launcher class, below lines of code trying to look into the same directory for the web-app, which essentially is not correct.

 tomcat.addWebapp(contextPath, appBase);

Since appBase is set to ".", which means it will try to look into the same directory where your launcher class is.

Solution

Try to use below code, which is pretty straight forward to understand. You have to set the webApp path correctly to the tomcat context to recognize it when you hit that path at run-time.

    String webappDirLocation = "src/main/webapp/";
    Tomcat tomcat = new Tomcat();

    //The port that we should run on can be set into an environment variable
    //Look for that variable and default to 8080 if it isn't there.
    String webPort = System.getenv("PORT");
    if(webPort == null || webPort.isEmpty()) {
        webPort = "8080";
    }

    tomcat.setPort(Integer.valueOf(webPort));

    StandardContext ctx = (StandardContext) tomcat.addWebapp("/", new File(webappDirLocation).getAbsolutePath());
    System.out.println("configuring app with basedir: " + new File("./" + webappDirLocation).getAbsolutePath());

    // Declare an alternative location for your "WEB-INF/classes" dir
    // Servlet 3.0 annotation will work
    File additionWebInfClasses = new File("target/classes");
    WebResourceRoot resources = new StandardRoot(ctx);
    resources.addPreResources(new DirResourceSet(resources, "/WEB-     INF/classes", additionWebInfClasses.getAbsolutePath(), "/"));
    ctx.setResources(resources);

    tomcat.start();
    tomcat.getServer().await();
0

First maven is old, use Gradle instead ;)

Each process has a working directory, and when a File is relative it will be interpreted in relation to that. This is important to understand, because unless you are using this for a simple test, the directory structure may be different between development and deployment.

If you build your program as a jar file a relative URL like "./src/main/webapp" will most likely not work, because "src/main" would disappear and only "webapp" would be left (as when you build a WAR file).

Build tools like Gradle (and Maven) and your IDE has a "processResource" step after compilation, which copies resources into a location that will (typically) be part of the classpath. Since /main/resources/webapp will not be copied unless you are building a webapp, you will have issues running your code outside your IDE.

Personally I would recommend that you build a Spring boot app, which already have an embedded tomcat. In Spring Boot .html files are loaded from the classpath (main/resources/static), and because the resources are inside the classpath, they will have the same location in both development and deployed (because of resource processing).

For a spring boot web app you will need one dependency org.springframework.boot:spring-boot-starter-web:1.4.1.RELEASE So your build.gradle file would look like this (You can create a Gradle project in your IDE)

apply plugin: 'java'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile("org.springframework.boot:spring-boot-starter-web:1.4.1.RELEASE")
}

Your Java Main would look like this

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }
}

Put you .html file in /main/resources/static - DONE!! As you can probably guess there is a lot of magic inside Spring boot, and the reason you don't have to write any more code, is because the Spring Boot team has chosen very good default, but don't worry if you need to get more advanced later this is also possible. If you need serverside rendering you can add org.springframework.boot:spring-boot-starter-thymeleaf:{version}, and put your templates into /resources/templates and there you go.There is a ton of good tutorials out there.

Spring also has a much better abstraction on top of servlets called controllers, again lots of documentation.

Klaus Groenbaek
  • 4,820
  • 2
  • 15
  • 30
0

Try using SpringBoot!!! which has the embedded tomcat, use web and thymeleaf dependency, thymeleaf is the template engine which will recognize your webpage. try to create a spring boot project from this website http://start.spring.io/, which is a spring initializer used to create a maven project with spring boot. Add web and thymeleaf dependencies and generate a project. Import the project in your IDE as maven project, or use Spring tool suit

DemoApplication.java

 @Controller
 @SpringBootApplication
 public class DemoApplication {
     public static void main(String[] args) {
         SpringApplication.run(DemoApplication.class, args);
     }  
     @RequestMapping(value = "/homepage" , method = RequestMethod.GET  )
     public String sample()
     {
         return "home";

     }
}

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>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.2.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.7</java.version>
</properties>
<dependencies>
     <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
    </build>
    </project>

home.html

 <!DOCTYPE html>
 <html>
 <head>
 <meta charset="UTF-8"></meta>
 <title>HomePage</title>
 </head>
 <body>
 <h1>MY Spring Boot Home Page</h1>
 </body>
 </html>
Arsen Davtyan
  • 1,891
  • 8
  • 23
  • 40
0

I think you missing Hostname part as well as the building steps by maven.Try to run with maven commands and java -jar commands. Try with following see works or not.

 public static final Optional<String> PORT = Optional.ofNullable(System.getenv("PORT"));
public static final Optional<String> HOSTNAME = Optional.ofNullable(System.getenv("HOSTNAME"));

public static void main(String[] args) throws Exception {
    String contextPath = "/" ;
    String appBase = ".";
    Tomcat tomcat = new Tomcat();   
    tomcat.setPort(Integer.valueOf(PORT.orElse("8080") ));
    tomcat.setHostname(HOSTNAME.orElse("localhost"));
    tomcat.getHost().setAppBase(appBase);
    tomcat.addWebapp(contextPath, appBase);
    tomcat.start();
    tomcat.getServer().await();
}

Folow this steps : (you need to have maven locally installed) Open cmd,Go to source folder,where the pom file is.Do following commands

mvn compile

then press enter

mvn package.

then press enter

cd target then press enter

java -jar [your app name].jar (In target folder you will see a jar file, put its name here)

once you run from command you will able to brows by localhost in browser. Netbeans run gave me 404. i solved it this way.

you can check this link's Running the Web Application Part: http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/basic_app_embedded_tomcat/basic_app-tomcat-embedded.html

Ahmed Raaj
  • 508
  • 4
  • 16
0

I don't know if you have solved the problem described here. I hope this will help others. To embed Tomcat I used a plugin rather than dependencies: dowing so, one doesn't need a main class and the code to create a tomcat instance.
Here is what the POM must include:

<project ...>
  .....
  <dependencies>
    <!-- no need of any --->
  </dependencies>
  <build>
     <plugins>
        <plugin>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.3</version>
        </plugin>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>1.7</source>
                <target>1.7</target>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <configuration>                    
                <server>localhost</server>
                <path>/${project.build.finalName}</path>
            </configuration>
        </plugin>

    </plugins>
  </build>
</project>

Here is an example of web.xml

<web-app id="WebApp_ID" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">


   <display-name>My Test App</display-name>
   <description>A test app</description>

   <welcome-file-list>
      <welcome-file>index.html</welcome-file>
   </welcome-file-list>

   <session-config>
     <session-timeout>30</session-timeout>
   </session-config>
</web-app>

You don't need neither a main class nor the code inside (as in the question). We do of course need pages to show, so here is an example of the index.html (as stated in the web.xml):

<html>
   <body>
      <h2>Hello World!</h2>
   </body>
</html>

In eclipse: right-click, run as "run configuratuions", under "Maven Build" add a "New launch configuration", set the Base directory and enter under "Goals": tomcat7:run. ThaT's it! You shall get:

[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] --------------------------------------------------------------------
[INFO] Building rest-with-jersey Maven Webapp 0.0.1-SNAPSHOT
[INFO] --------------------------------------------------------------------
[INFO] 
[INFO] >>> tomcat7-maven-plugin:2.2:run (default-cli) > process-classes @ rest-with-jersey >>>
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ rest-with-jersey ---
[WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ rest-with-jersey ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] <<< tomcat7-maven-plugin:2.2:run (default-cli) < process-classes @ rest-with-jersey <<<
[INFO] 
[INFO] --- tomcat7-maven-plugin:2.2:run (default-cli) @ rest-with-jersey ---
[INFO] Running war on http://localhost:8080/rest-with-jersey
[INFO] Using existing Tomcat server configuration at /common/home/$$$/$$$$/lnx/workspace/rest-with-jersey/target/tomcat
[INFO] create webapp with contextPath: /rest-with-jersey
Sep 11, 2017 4:03:07 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-bio-8080"]
Sep 11, 2017 4:03:07 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Tomcat
Sep 11, 2017 4:03:07 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/7.0.47
Sep 11, 2017 4:03:09 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]

In a browser with http://localhost:8080/rest-with-jersey/ you get the " Hello World!". 
Meziane
  • 1,586
  • 1
  • 12
  • 22