javatomcatdeploymentweb-applicationswar

Running a Java application deployed as WAR on Tomcat server


I have a very simple Java application, all it does is write a file with the current date as the title and, a single line in the file which is also the current date. I just made this as a test:

package webfilewriter;

import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 *
 * @author bhemrik
 */
public class WebFileWriter {

    public static final String FILE_PATH;
    
    static {
        FILE_PATH = "resources\\";
    }

    public static void main(String[] args) throws IOException {
        LocalDateTime now = LocalDateTime.now();
        String nowString = timestampToString(now);
        writeToFile(nowString);
    }

    private static String timestampToString(LocalDateTime now) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(
                "yyyy-M-d__HH-mm-ss");
        return now.format(formatter);
    }

    private static void writeToFile(String nowString) throws IOException {
        FileWriter writer = new FileWriter(FILE_PATH + nowString);
        writer.append(nowString);
        writer.close();
    }
    
}

I have built this simple application as a WAR file, and deployed it on my Tomcat server. N

ow obviously this has no user interface to it. No Servlets HTML pages or anything of that sort, the entry point should be the main() method, regardless the fact that it is deployed on a tomcat server.

The question is how do I tell Tomcat to run it? I don't want to implement a context listener or anything really, I dont even need it to be runable from a browser, I just want to be able to run it over the internet somehow, no ssh-ing into the server.

Isn't tomcat supposed to be providing me with this functionality? What am I missing? How can I make this work?


Solution

  • No user-interface, no users

    now obviously this has no user interface to it

    You need a user-interface. The very definition of the term is the mechanism by which the user can interact with a piece of software. See Wikipedia.

    Having no user-interface means you have no users.

    No Servlets HTML pages or anything of that sort

    Apache Tomcat has two purposes:

    If you have no content and no Servlets, then you have no need for Tomcat.

    See Introduction to Java Servlets at Baeldung.com.

    I have a very simple Java application

    You have a simple console app, which uses a command-line interface. When the class name is invoked on the command-line using the java tool, your main method is executed by the JVM.

    No need for a WAR in this case; use JAR.

    If you want a user to access a console app remotely from another computer, then they must utilize a remote-access technology such as ssh.

    The question is how do I tell tomcat to run it?

    You write a Web app rather than a console app. You write your code that abide by the Jakarta Servlet specification rather than writing a main method.

    I don't want to implement a context listener

    A context listener is part of Jakarta Servlet technology. A context listener runs before your Web app’s first request is processed, and after the last response is sent.

    A context listener is optional in a Web app. Use one when when you need a hook into your Web app’s lifecycle. For example when you need to set up and later tear down resources that will be used as you process requests.

    If you have no Servlet, then you have no need of a context listener.

    What am i missing?

    A user-interface is what you are missing.

    How can I make this work?

    You need to write code for your choice of:

    You commented:

    running an application with the main method as the enrty point from a remote computer over the internet is completly impossible

    Yes, correct. Not without a secure connection like Secure Shell (SSH) Protocol.

    What I'm trying to do is really so simple, I cannot fathom that it should be this hard.

    Think about it. Do you really want any random person on the Internet to run software on your computer without authorization?

    What you are asking for is exactly what operating systems, server software, and web browsers are all trying to make impossible by default.

    You commented:

    why it is so hard to simply interpret an http request as a call for an application to execute

    That is exactly what a Jakarta Servlet does: Interpret a request coming in over HTTP, then execute some code, and finally return a response.

    So get busy and write your servlet.

    Example Servlet

    Write your file-writing class separately. Something like the following.

    Tip: Learn about NIO.2 in Java for simplified handling of files & folders.

    By the way, avoid static methods generally. They are not object-oriented.

    package work.basil.ex.filewriter;
    
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.time.OffsetDateTime;
    import java.time.ZoneId;
    import java.time.temporal.ChronoUnit;
    
    public class TimestampFileWriter
    {
        private static final Path folder = Path.of ( "/Users/basil_dot_work/" );
    
        public Path writeToFile ( )
        {
            ZoneId z = ZoneId.of ( "Africa/Tunis" ); // Or use JVM’s current default time zone:  ZoneId.systemDefault()
            OffsetDateTime now = OffsetDateTime.now ( z ).truncatedTo ( ChronoUnit.SECONDS );
            String fileName =
                    now
                            .toString ( )
                            .replace ( "T" , "_" )
                            .replace ( ":" , "_" )  // COLON is a delimiter in legacy macOS file system.
                            .concat ( ".txt" );  // Example: 2024-10-19_18_50_11+01_00.txt
            //System.out.println ( "fileName = " + fileName );
            Path path = folder.resolve ( fileName );
            String content = "At the tone the time will be: " + now;
            try
            {
                return Files.writeString ( path , content , StandardCharsets.UTF_8 );
            }
            catch ( IOException e )
            {
                throw new RuntimeException ( e );
            }
        }
    }
    

    Verify that class by writing a test or a main method.

    public static void main ( String[] args )
    {
        TimestampFileWriter app = new TimestampFileWriter ( );
        Path path = app.writeToFile ( );
        System.out.println ( "path = " + path );
    }
    

    path = /Users/basil_dot_work/2024-10-19_18_57_27+01_00.txt

    Write a servlet that makes use of that TimestampFileWriter class.

    package work.basil.ex.filewriter;
    
    import jakarta.servlet.http.*;
    import jakarta.servlet.annotation.*;
    
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.nio.file.Path;
    
    @WebServlet ( name = "work.basil.ex.filewriter.FileWriterServlet", value = "/write" )
    public class FileWriterServlet extends HttpServlet
    {
        @Override
        protected void doGet ( HttpServletRequest request , HttpServletResponse response )
        {
            response.setContentType ( "text/plain;charset=UTF-8" ); // We are returning plain text rather than HTML. For HTML use "text/html".
    
            // Logic
            TimestampFileWriter app = new TimestampFileWriter ( );
            Path path =  app.writeToFile ( );
    
            // Report to user.
            try
            {
                PrintWriter out = response.getWriter ( );
                out.println ( "File written. Path = " + path.toString () );
            }
            catch ( IOException e )
            {
                throw new RuntimeException ( e );
            }
        }
    }
    

    The url is: http://localhost:8080/FileWriterServlet/write.

    The Maven POM file:

    <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>work.basil.example</groupId>
        <artifactId>ExServlet</artifactId>
        <version>1.0-SNAPSHOT</version>
        <name>ExServlet</name>
        <packaging>war</packaging>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.compiler.release>23</maven.compiler.release>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>jakarta.servlet</groupId>
                <artifactId>jakarta.servlet-api</artifactId>
                <version>6.1.0</version>
                <scope>provided</scope>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
            <dependency>
                <groupId>org.junit.jupiter</groupId>
                <artifactId>junit-jupiter</artifactId>
                <version>5.11.2</version>
                <scope>test</scope>
            </dependency>
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.4.0</version>
                </plugin>
            </plugins>
        </build>
    </project>
    

    Resulting in web browser:

    screenshot of result in browser

    But now you have a different security problem. Any malicious user can call your URL repeatedly to fill up your storage device with these created files.


    By the way, in real work I would consider using date-time values “in UTC”, meaning an offset of zero hours-minutes-seconds from the temporal meridian of UTC.