IntelliJ IDEA Ultimate Notes

Russell Bateman
May 2018

Nota bene: This really has turned more into notes on how to use IDEA Ultimate to write web applications.


Web-programming tutorial links

Apache/Maven WAR plug-in

What do you do to cause Maven to build a WAR file? Add this to pom.xml:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-war-plugin</artifactId>
      <version>3.2.0</version>
      <configuration>
        <webResources>
          <resource>
            <!-- this is relative to the pom.xml directory -->
            <directory>web</directory>
          </resource>
        </webResources>
      </configuration>
    </plugin>
  </plugins>
</build>

The <directory> specified above assumes the following structure in your project:

~/dev/hello-restlet $ tree
.
├── hello-restlet.iml
├── pom.xml
├── src
│   └── ...
└── web
    └── WEB-INF
        └── web.xml

IntelliJ IDEA Tomcat tutorial

I downloaded IntelliJ IDEA Ultimate (here at work too) and then did Install and Configure Tomcat IntelliJ IDEA which is a year old, has no sound and is full of misleading mousing. Here I am reducing that experience to a set of steps. It did work, however, and that puts me hugely up over the experience I had with Eclipse WTP back in 2008 when I really started in earnest trying to learn to do web programming with Eclipse.

  1. Close (all) IDEA projects to see Welcome to IntelliJ IDEA splash page.
  2. Choose Create New Project.
  3. Choose Maven (don't select any archetype).
  4. Click Next.
  5. GroupId: com.etretatlogiciels.tomcat
  6. ArtifactId: tomcat-demo
  7. Click Next.
  8. Click Finish.
  9. (IntelliJ IDEA launches with pom.xml in Editor pane.
  10. Launch a browser to download Tomcat 9:
    1. Download from Binary Distributions the first tarball.
    2. Explode this tarball.
    3. Copy it somewhere; I copied to ~/dev
  11. Choose Edit Configurations.
  12. Click the green + and look for Tomcat Server. You may have to click on "36 items more (irrelevant)."
  13. Choose Local (Tomcat Server); you'll see a dialog for Unnamed Tomcat Server. Configure the Application Server for the Tomcat 9 downloaded by navigating to and selecting its root subdirectory (something like ~/dev/apache-tomcat-9.0.7).
  14. Change Unnamed to something; I chose to use tomcat-demo.
  15. Click Deployment, then the green + sign, then External Source..., then choose the project root subdirectory and click OK. See this in the Deploy at the server startup list of the dialog. Click Apply.
  16. Click OK to dismiss the Run/Debug Configurations dialog.
  17. Expand the project root in the Project pane.

  18. Right-click the project root and choose New → HTML File. Name it index and click OK. (This puts index.html into the project root which is not where you want it, but the right place is the object of a different tutorial.)
  19. Edit index.html and add this text to the <body>:
    Hello viewer Please Subscribe
    Thanks For Waching...
    
  20. Click the green in the Run pane at the bottom right. You'll see a new window come up in your browser at localhost:8080:

  21. Add various proper HTML elements around the text, then click the green to restart Tomcat and see the changes:

This is working, so I'm going to have to spring the $150 for this IDE now.


Hello-world restlet in IntelliJ IDEA and Maven with Jersey and Tomcat

These are practical and up-to-date instructions based on Sam Jesso's 2014 Starting out with Jersey & Apache Tomcat using IntelliJ. Here are the steps, illustrations and elaborations I followed:

  1. Ensure IntelliJ IDEA Ultimate Edition (we're using latest version as of May 2018) for this tutorial. It is in theory possible to debug a web application using the Community Edition, but it's extremely unlikely to be able to develop one without Ultimate.

  2. Prepare exploded Tomcat, Jersey and JAX-RS downloads in your filesystem. Though you may put them on other paths, for the purposes of this tutorial, I'm assuming:
    1. ~/dev/apache-tomcat-9.0.7 (Tomcat, from the first tarball under Binary Distributions here.) Since you're doing this on your development host, there's little reason to install it. You're going to give IDEA the path to where you exploded Tomcat.

    2. (The Jersey dependency is satisfied by the JAX-RS download below and by a Maven pom.xml dependency later.)

    3. ~/dev/jaxrs-ri (JAX-RS—from jaxrs-ri-2.25.1.zip downloaded from the Jersey JAX-RS 2.1 RI bundle here.

  3. Installing Maven: If you're a serious developer, you should have Maven installed on your development host. This way, you can (or should be able to) build any project without IDEA's help. However, IDEA contains Maven and here we'll just use that one.

  4. Close other IDEA projects and do Create New Project:

  5. The new project must be configured a number of ways. Depending on whether you've already used IntelliJ IDEA before, some may already be set up.
    1. Project SDK: —you should have your own copy of a JDK separate from the JRE in use by your host's operating system. Whatever the case, you may need to read Selecting the JDK version the IDE will run under.

    2. Java EE version: —you should try to use the latest version available. Mine's one version later than that used by the original tutorial.

    3. Application Server —we're using Tomcat here. If you've already used Tomcat, this will be set. If not, click New and choose it. This won't be an option if you haven't set IDEA up with the Tomcat plug-in, however, by default this should already be established.

    4. Click to select Java Enterprise and check RESTful Web Service:

    5. For Use library:, the original tutorial step is impossible to satisfy here: there is no option to postpone setting up the library. So, do this:
      1. Click Create....

      2. Navigate to where you dropped the JAX-RS download into your filesystem. Find javax.ws.rs-api-2.1.jar in the api subdirectory under that location. This is the library that's going to give the Jersey/ReST annotations for the Java restlet you'll write. Here's the library you should have seen and chosen:

      3. Click OK, then Next.

    6. Configure the project's name and where it will live in your development host's filesystem. Mine is ~/dev/hello-rest.

    7. Click Finish; the IntelliJ IDEA IDE workbench will launch.

  6. At this point, we must add framework support to the project.
    1. Right-click your project's root. It's in bold at the top of the content region of the Project pane in the workbench.

    2. Choose Add Framework Support....

    3. First, scroll down in the left pane that appears in the new dialog to find Maven. Click Maven and then OK. The dialog will disappear to leave you with pom.xml up in the Editor pane.

    4. Right-click the project root again and choose Add Framework Support....

    5. Click Web Application, it should be near the top. Don't click anything underneath it (hierarchically).

    6. Click OK. You're back in the IDE workbench.

  7. Because we have Maven, so that we can build this project not only in the IDE, but also from the command line, we need to tell Maven to bring in Jersey. Change or add the highlighted lines below to pom.xml. (Don't scrape these changes from here or you'll include invisible, illegal characters that will cause mysterious errors in 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.jersey</groupId>
      <artifactId>hello-rest</artifactId>
      <version>1.0-SNAPSHOT</version>
    
      <dependencies>
        <dependency>
          <groupId>com.sun.jersey</groupId>
          <artifactId>jersey-bundle</artifactId>
          <version>1.19.1</version>
        </dependency>
      </dependencies>
    
    </project>
    
    Reimport Maven changes by clicking Import Changes if it appears in the bottom right of your IDE.

  8. Add Java ReST code to the project.
    1. First, create a new package path (which corresponds to the groupId you added to pom.xml. Expand out the directory hierarchy under src, then right-click java and choose New → Package:

    2. In the space provided for a new package name, type: com.example.jersey and click OK.

    3. Next, right-click the new package you just created and choose New → Java Class. Name the class HelloWorld and press Enter or click OK.

    4. Make the class look like this (again, don't scrape this or you'll get bad characters*):
      package com.example.jersey;
      
      import javax.ws.rs.GET;
      import javax.ws.rs.Path;
      import javax.ws.rs.Produces;
      import javax.ws.rs.core.MediaType;
      
      @Path( "/hello" )
      public class HelloWorld
      {
        @GET
        @Produces( MediaType.TEXT_PLAIN )
        public String getMessage()
        {
          return "Hello world!";
        }
      }
      
  9. Next, we set up Tomcat. This step is fairly crucial because if not done just right, you'll get HTTP status code 404 Not Found errors.

    Do File → Settings... → Build, Execution, Deployment → Application Servers. Click the green +, select Tomcat Server, then, in the Tomcat Server dialog, navigate to, select and click OK on the root of the Apache Tomcat you downloaded. In my case, this is the path ~/dev/apache-tomcat-9.0.7.

    You should see two important JARs under Libraries when you've done this:

    Close the Settings dialog by clicking the OK button.

  10. Now, create a Run/Debug Configuration for Tomcat by doing Run → Edit Configurations.... Click the green +, select Tomcat Server → Local and fill out the Server tab as shown below. (Note that the browser command line is more complete than that of the original tutorial which means it will work first time.)

    It is imperative that you pay close attention to the URL under Open browser below: unlike how Eclipse deploys and works, IDEA must have the URL to this degree of specificity in order to work. You will see a new tab appear in your browser with this URL and little else. If you do not have a complete URL, depending on the options of your servlet, you likely will get HTTP 404 and nothing will work.

    Then, click the Deployment tab, the green + and choose Artifact.... You should get hello-rest:war exploded in the list of what to Deploy at the server startup with an application context of   /.
    Click OK. (See note at end of this section on Run Configuration.)

  11. Now we tackle the servlet configuration via web.xml. The web-application support in IDEA has already created this file for you on the path hello-restlet/web/WEB-INF. Double-click to open it. Copy and paste the same thing that's in the original tutorial; make it look like this; the highlighted lines are the ones added beyond what IDEA already included:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                                 http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
      <servlet>
        <servlet-name>Example API</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
    
        <init-param>
          <param-name>com.sun.jersey.config.property.packages</param-name>
          <param-value>com.example.jersey</param-value>
        </init-param>
    
        <init-param>
          <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
          <param-value>true</param-value>
        </init-param>
      </servlet>
    
      <servlet-mapping>
        <servlet-name>Example API</servlet-name>
        <url-pattern>/*</url-pattern>
      </servlet-mapping>
    
    </web-app>
    
    There's a lot going on in this file that I'm not going to explain. I will, however, explain that
    • <servlet-name> in both <servlet> and <servlet-mapping> must be identical in order to link the two, i.e.: make the servlet mapping configuration belong to the defined servlet.
    • The package (or comma-delimited packages—because we might have more Java code on multiple package paths) com.sun.jersey.config.property.packages is a variable read by Jersey such that it will look through the Java classes on this path (these paths) to include in the running servlet. If you refactor your Jersey code (HelloWorld.java) away from com.example.jersey, no one will show up at the party Tomcat throws when you launch it and nothing will happen.

  12. As the original tutorial points out, the last thing to do is to tell Tomcat about your configuration file. This is an obscure, but very crucial step. Go to File → Project Structure, then click on Artifacts. You should see this:

    If you click on the ? next to Available Elements, you'll get a helping prompt that says, "Double-click on element to put into default location" and some other comments. This is what you want to do: double-click JAX-RS and the two JARs listed. This action works a little differently in IntelliJ IDEA Ultimate 2018 than it did under the version targeted by the original tutorial. Click OK to dismiss the Project Structure dialog.

  13. That's it. You need only launch the restlet now. There are several ways to do this. One is Run → Run 'Tomcat 9.0.7'. You'll see a new Run pane come up at the bottom of the IDE workbench, fill with logging statements from Tomcat and then, after a brief delay, your default browser will gain a new tab that looks something like this:

    It's the plain-text response from your restlet code in HellWorld.java which is answering the URL supplied by your browser's HTTP GET command http://localhost:8080/hello.

* Optionally, you can scrape these, paste them into a simple editor like vim, gvim or gedit, then copy them from there into your new class.

Beware of JDKs later than Java 8

As I revisit this tutorial, I discover that it no longer works because, when launched, it yields (in the browser), this error:

java.lang. TypeNotPresentException: Type javax.xml.bind.JAXBContext Exception

This is because I'm trying to use JDK11 which no longer supports Java EE and, in particular, has removed support for java.xml.bind. So, I tried adding such support to pom.xml (in addition already to Jersey):

<dependency>
  <groupId>com.sun.jersey</groupId>
  <artifactId>jersey-bundle</artifactId>
  <version>1.19.1</version>
</dependency>
<dependency>
  <groupId>com.sun.xml.bind</groupId>
  <artifactId>jaxb-core</artifactId>
  <version>2.3.0.1</version>
</dependency>
<dependency>
  <groupId>javax.xml.bind</groupId>
  <artifactId>jaxb-api</artifactId>
  <version>2.3.1</version>
</dependency>
<dependency>
  <groupId>com.sun.xml.bind</groupId>
  <artifactId>jaxb-impl</artifactId>
  <version>2.3.1</version>
</dependency>

This did not work or solve the issue. By Java 6, Java had built-in support for XML-based web services because they were so popular, but eventually, such decisions made Java bigger and bigger until a new trend reversed this. In Java 9 and 10, this support was deprecated, then Java 11 eliminated it altogether. The solution became to add JAXB as a dependency to the project, however, this doesn't always work as is the case above.

The solution is to find an old, JDK8 to develop with until this is sorted out by someone. (And take the dependencies above out of pom.xml.) I don't know how to solve it yet, so this is my solution.

Whither next?

Next up should be a tutorial on how to endow the restlet above with an index.jsp (that's meaningful and not the template one that appeared above as the result of adding frameworks) that does something.

Note on Run Configuration

If developing on two hosts, or if collaborating with others, you will not be maintaining .idea/workspace.xml under version control (for all sorts of reasons). Therefore, the step on creating a run/debug configuration will have to be done in every IDEA instance you run. .idea/workspace.xml is where this is kept because, in this case, it's a file-system dependent operation (i.e.: where your copy of Tomcat lives).


Trouble-shooting the outcome of the Jersey tutorial

Misère ! I came back months later and my Jersey example no longer worked in IntelliJ IDEA. Then, I redid the hello-world tutorial and the one before that, then I played around with it and it started to work, but what did I do to make it work?

GET http://localhost:7070/mdht-servlet

The MDHT restlet is up.

        Manifest-Version: 1.0
  Implementation-Version: 1.0.0-SNAPSHOT
Implementation-Vendor-Id: com.windofkeltia.mdht
              Build-Time: 2019-06-13T20:24:30Z
              Created-By: IntelliJ IDEA
               Build-JDK: 1.8.0_211
   Specification-Version: 1.0.0-SNAPSHOT
Copyright © 2018-2019 by Acme Solutions and Etretat Logiciels, LLC.
Proprietary and confidential. All rights reserved.


POST http://localhost:7070/mdht-servlet

<?xml version="1.0" encoding="UTF-8"?>
<ClinicalDocument
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns="urn:hl7-org:v3" xsi:schemaLocation="urn:hl7-org:v3 infrastructure/cda/CDA_SDTC.xsd">
  <realmCode code="US"/>
  <typeId root="2.16.840.1.113883.1.3" extension="POCD_HD000040"/>
  <templateId root="2.16.840.1.113883.10.20.22.1.2" extension="2015-08-01"/>
  <id root="3fe5991c-3624-40b8-9b4c-72d4db8fb5ff"/>
  <code code="34133-9" codeSystem="2.16.840.1.113883.6.1" codeSystemName="LOINC" displayName="Summarization of Episode Note"/>
  <title>Continuity of Care Document</title>
  <effectiveTime value="20190613203413+0000"/>
  <confidentialityCode code="N" codeSystem="2.16.840.1.113883.5.25"/>
  <languageCode code="en-US"/>
  <setId root="2.16.840.1.113883.19.5.99999.19" extension="sTT988"/>
  <versionNumber value="1"/>
  ...
          <entry>
            <observation classCode="OBS" moodCode="EVN">
              <templateId root="2.16.840.1.113883.10.20.22.4.38" extension="2015-08-01"/>
              <templateId root="2.16.840.1.113883.10.20.22.4.38"/>
              <id root="497170e3-8bba-4184-9114-44fe9995baf8"/>
              <code code="228272008" codeSystem="2.16.840.1.113883.6.96" codeSystemName="SNOWMED CT" displayName="Health-related behavior">
                <translation code="29762-2" codeSystem="2.16.840.1.113883.6.1" codeSystemName="LOINC" displayName="Social History"/>
              </code>
              <text>
                <reference value="#SOCIAL-HISTORY-612243438"/>
              </text>
              <statusCode code="completed"/>
              <effectiveTime value="20190613203413+0000"/>
            </observation>
          </entry>
        </section>
      </component>
    </structuredBody>
  </component>
</ClinicalDocument>

I'm not certain what I've done to make this work. Here are all the details of my configuration.

com.windofkeltia.servlet.MdhtServlet.java:

The comment about the URL has to do with IDEA working and Tomcat working identically. The point is that, when Tomcat deploys, the WAR name itself (mdht-servlet.war) is already the root and part of the path. To add something (to the @Path annotation below) will only lengthen the URL again.

/**
 * The point of entry into this servlet. The URL at this point is already
 * http://localhost:8080/mdht-servlet; don't add to it.
 * @author Russell Bateman
 * @since May 2018
 */
@Path( "" )
public class MdhtRestlet
{
  ...
web/WEB-INF/web.xml:

The servlet name can't be willy-nilly, but must match.

  <servlet>
    <servlet-name>mdht-servlet</servlet-name>
    <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
  ...
  <servlet-mapping>
    <servlet-name>mdht-servlet</servlet-name>
    <url-pattern>/*</url-pattern>
  </servlet-mapping>
</web-app>

IntelliJ IDEA configuration

Project Settings
Project name:           mdht-servlet
Project SDK:            1.8             (problem with Java thereafter)
Project language level: 8 - Lambdas, type annotations, etc.
Run/Debug Configurations:
Tomcat Server
Name:      mdht-servlet
URL:       http://localhost:7070/mdht-servlet
HTTP port: 7070
JMX port:  1099

Deployment tab
Deploy at the server startup
    mdht-servlet:war exploded
Application context: /mdht-servlet

Maven build configuration

pom.xml:

What's notable: the JAXB dependencies, in-project location of the parent of web.xml.

  ...
  <artifactId>mdht-servlet</artifactId>
  ...
    <dependency>
      <groupId>com.sun.jersey</groupId>
      <artifactId>jersey-bundle</artifactId>
      <version>${jersey-bundle.version}</version>
    </dependency>
    <dependency>
      <groupId>javax.activation</groupId>
      <artifactId>activation</artifactId>
      <version>1.1.1</version>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>${servlet-api.version}</version>
      <scope>provided</scope>   <!-- (provided by Tomcat) -->
    </dependency>
    <dependency>
      <groupId>com.sun.xml.bind</groupId>
      <artifactId>jaxb-core</artifactId>
      <version>2.3.0.1</version>
    </dependency>
    <dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
      <version>2.3.1</version>
    </dependency>
    <dependency>
      <groupId>com.sun.xml.bind</groupId>
      <artifactId>jaxb-impl</artifactId>
      <version>2.3.1</version>
    </dependency>
    ...
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>${maven-war-plugin.version}</version>
        <configuration>
          <webResources>
            <resource>
              <!-- this is relative to the pom.xml directory -->
              <directory>web</directory>
            </resource>
          </webResources>

          <!-- Magic for maintaining a build timestamp in MANIFEST.MF: -->
          <archive>
            <manifest>
              <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
              <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
            <manifestEntries>
              <Build-Time>${maven.build.timestamp}</Build-Time>
            </manifestEntries>
          </archive>

        </configuration>
      </plugin>
      ...

It seems that Java has been lightened because folk have complained it's got too big in recent years. For example, JAXB (XML-binding in web applications) once seemed important to everyone and certain libraries were included by default. This was deprecated in Java 9 and 10, then, in Java 11, JEE was thrown out altogether. The problem is that bloggers are slow to catch up on how now to build something JEE (like mdht-servlet). So, I have had to a) include 3 JAXB JARs and b) return the project build to using Java 8. I don't yet know how to build it using Java 11, but I don't care too much right now.

Oh, why does it make a difference that mdht-servlet run in the IDE? Because it's much easier to debug that way than to have to deploy it to a Tomcat instance and debug it remotely.

Here is a list of things I looked at, changed and that may have a bearing on the success. Note that not all were done specifically to solve the 404; some were done to stop (JAXB) crashes or make the thing launch at all.


JSP example using IntelliJ IDEA and Tomcat

This is an interpretation of Part 1.1: Java EE Webapplication with servlet and JSP page.

  1. Java project in IntelliJ IDEA that runs on a Tomcat server and serves up web/index.jsp. This is all it takes.

    web/index.jsp:
    <%--
      Created by IntelliJ IDEA.
      User: russ
      Date: 5/4/18
      Time: 10:25 AM
      To change this template use File | Settings | File Templates.
    
      Note: because this file is fetched implicitly because
      under the web subdirectory, even without being named
      in a welcome-file-list element.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" %>
    <html>
    <head>
    <title>Web App Tutorial Page</title>
    </head>
    <body>
    <h1> Hello World </h1>
    <p>
    Body text. This is my first webapp JSP page.
    </p>
    </body>
    </html>
    
    web/WEB-INF/web.xml:
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                        http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
    
      (empty)
    
    </web-app>
    
    Output:

  2. Demonstration modifying index.jsp.
    web/index.jsp:
    <%@ page import="java.util.Date" %>
    <%--
      Created by IntelliJ IDEA.
      User: russ
      Date: 5/4/18
      Time: 10:25 AM
      To change this template use File | Settings | File Templates.
    
      Note: because this file is fetched implicitly because
      under the web subdirectory, even without being named
      in a welcome-file-list element.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" %>
    <html>
    <head>
    <title>Web App Tutorial Page</title>
    </head>
    <body>
    <h1> Hello World </h1>
    <p>
    Body text. This is my first webapp JSP page.
    </p>
    <%
      Date date = new Date();
      out.print( "<h3>" + date.toString() + "</h3>" );
    %>
    
    </body>
    </html>
    
    Output:

  3. Create a form on a JSP page.
    web/login.jsp:
    <%--
      Created by IntelliJ IDEA.
      User: russ
      Date: 5/4/18
      Time: 10:25 AM
      To change this template use File | Settings | File Templates.
    
      Note: because this file is fetched implicitly because under the web subdirectory,
      even without being named in a welcome-file-list element.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" %>
    <html>
    <head>
    <title>Login Page</title>
    </head>
    <body>
    <h1> Welcome, please log in: </h1>
    <form action="/login" method="POST">
            Name:     <input type="text"     name="username" width="30" /><br />
            Password: <input type="password" name="password" width="10" /><br />
                      <input type="submit"   value="login" />
    </form>
    </body>
    </html>
    
    Output:

    This goes nowhere since we have to create a servlet to recieve the form's log-in data.

  4. Create a servlet:
    1. Create the servlet:
      1. Right-click src folder and create a new package, webapp.
      2. Right-click the new package and choose New... → Servlet.
      3. name it Login.
      4. Package webapp.
      5. Class webapp.login.
      6. Allow to create annotated class.
      Login.java:
      package webapp;
      
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;
      
      @WebServlet( name = "Login" )
      public class Login extends HttpServlet
      {
        protected void doPost( HttpServletRequest request, HttpServletResponse response )
            throws ServletException, IOException
        {
        }
      
        protected void doGet( HttpServletRequest request, HttpServletResponse response )
            throws ServletException, IOException
        {
        }
      }
      
    2. Configure URL via web/WEB-INF/web.xml.
      web/WEB-INF/web.xml:
      <?xml version="1.0" encoding="UTF-8"?>
      <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                          http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
               version="4.0">
      
        
          Login
          webapp.Login
        
      
        
          Login
          /login
        
      
      </web-app>
      
      Input, do the form and press Enter (servlet method doPost() is called, you'll get a blank page:

      Or, use this input and press Enter (servlet method doGet() is called. Again, you'll see a blank page...

      The output is the same from either way because the servlet isn't doing anything with the data yet.

    3. Receive request parameters from URL GET.
      src/webapp/Login.java:
        protected void doGet( HttpServletRequest request, HttpServletResponse response )
            throws ServletException, IOException
        {
          String      username = request.getParameter( "username" );
          String      password = request.getParameter( "password" );
          PrintWriter out = response.getWriter();
          out.println( "Username: " + username );
          out.println( "Password: " + password );
        }
      
      Output corrected using coded servlet method (GET) above:

    4. Receive request parameters via POSTd form.
      src/webapp/Login.java:
        protected void doPost( HttpServletRequest request, HttpServletResponse response )
            throws ServletException, IOException
        {
          String      username = request.getParameter( "username" );
          String      password = request.getParameter( "password" );
          PrintWriter out = response.getWriter();
          out.println( "From POST'd form:" );
          out.println( "Username: " + username );
          out.println( "Password: " + password );
        }
      
      Output using coded servlet method (POST) above (after filling out form and pressing return/clicking Log in—I changed the text in the button to better English):

    5. Send values (reinject) to front end (JSP page). Right-click on web folder and choose New... → JSP/JSPX. Name it welcome. First, however, I want to correct the project structure to make it canonical by fleshing out the src subdirectory the way it should be and adding a test subdirectory under it. I don't have tests right now in this tutorial, but in a real world scenario, I would. (I rebuilt and ran the servlet—it still works.)

      web/welcome.jsp:
      <%--
        Created by IntelliJ IDEA.
        User: russ
        Date: 5/7/18
        Time: 3:09 PM
        To change this template use File | Settings | File Templates.
      --%>
      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
        <title> Welcome </title>
      </head>
      <body>
      <h1> Welcome </h1>
      <p>
        Dear: ${username}, your password is ${password}.
      	<!-- (${} is called "expression language") -->
      </p>
      </body>
      </html>
      
      Next, because I'm using expression language, I need to forward the values of username and password to welcome.jsp in order to be able to use them, see highlighted lines. Meanwhile, let's also do some clean-up of the code in this class:
      src/main/java/webapp/Login.java:
      package webapp;
      
      import javax.servlet.ServletException;
      import javax.servlet.annotation.WebServlet;
      import javax.servlet.http.HttpServlet;
      import javax.servlet.http.HttpServletRequest;
      import javax.servlet.http.HttpServletResponse;
      import java.io.IOException;
      import java.io.PrintWriter;
      
      @WebServlet( name = "Login" )
      public class Login extends HttpServlet
      {
        private static final String QPARM_USERNAME = "username";
        private static final String QPARM_PASSWORD = "password";
        private static final String JSP_USERNAME   = "username";
        private static final String JSP_PASSWORD   = "password";
      
        protected void doPost( HttpServletRequest request, HttpServletResponse response )
            throws ServletException, IOException
        {
          request.setAttribute( JSP_USERNAME, request.getParameter( QPARM_USERNAME ) );
          request.setAttribute( JSP_PASSWORD, request.getParameter( QPARM_PASSWORD ) );
          request.getRequestDispatcher( "/welcome.jsp" ).forward( request, response );
        }
      
        protected void doGet( HttpServletRequest request, HttpServletResponse response )
            throws IOException
        {
          String      username = request.getParameter( QPARM_USERNAME );
          String      password = request.getParameter( QPARM_PASSWORD );
          PrintWriter out      = response.getWriter();
          out.println( "Username: " + username );
          out.println( "Password: " + password  );
        }
      }
      
      Output after filling out form:

  5. Validate log-in form and send result. To do this, we'll need to add a POJO (User) and revamp Login and login.jsp:
    src/main/pojo/User.java:
    package pojo;
    
    public class User
    {
      private String username;
      private String password;
    
      public User( final String username, final String password )
      {
        this.username = username;
        this.password = password;
      }
    
      public boolean isValidUsername() { return username.equals( "russ" ); }
      public boolean isValidPassword() { return password.equals( "snagglepuss" ); }
    }
    
    src/main/webapp/Login.java:
      private static final String QPARM_USERNAME   = "username";
      private static final String QPARM_PASSWORD   = "password";
      private static final String JSP_USERNAME     = "username";
      private static final String JSP_PASSWORD     = "password";
      private static final String INVALID_USERNAME = "username is unknown";
      private static final String INVALID_PASSWORD = "password was wrong";
    
      protected void doPost( HttpServletRequest request, HttpServletResponse response )
          throws ServletException, IOException
      {
        String username = request.getParameter( QPARM_USERNAME );
        String password = request.getParameter( QPARM_PASSWORD );
        User   user     = new User( username, password );
        String errors   = "";
    
        if( !user.isValidUsername() )
          errors += "   " + INVALID_USERNAME + " (" + username + ")<br />";
        else if( !user.isValidPassword() )
          errors += "   " + INVALID_PASSWORD + " (" + password + ")<br />";
    
        if( errors.length() < 1 )
        {
          request.setAttribute( JSP_USERNAME, request.getParameter( QPARM_USERNAME ) );
          request.setAttribute( JSP_PASSWORD, request.getParameter( QPARM_PASSWORD ) );
          request.getRequestDispatcher( "/welcome.jsp" ).forward( request, response );
        }
        else
        {
          request.setAttribute( "error_message", "Invalid login:<br />" + errors + "Please try again." );
          request.getRequestDispatcher( "/login.jsp" ).forward( request, response );
        }
      }
    
    web/login.jsp:
    <%--
      Created by IntelliJ IDEA.
      User: russ
      Date: 5/4/18
      Time: 10:25 AM
      To change this template use File | Settings | File Templates.
    
      Note: because this file is fetched implicitly because under the web subdirectory,
      even without being named in a welcome-file-list element.
    --%>
    <%@ page contentType="text/html;charset=UTF-8" %>
    <html>
    <head>
    <title>Login Page</title>
    </head>
    <body>
    <h1> Welcome, please log in: </h1>
    <form action="/login" method="POST">
            Name:     <input type="text"     name="username" width="30" /><br />
            Password: <input type="password" name="password" width="10" /><br />
                      <input type="submit"   value="Log in" />
    </form>
    
    <p>
      <span style="color: red"> ${error_message} </span>
    </p>
    </body>
    </html>
    

Project Structure Artifacts and their path

(Please note that this problem and its solution came about three years later than the write-ups above, so the accompanying illustration is unrelated to any seen earlier on this page.)

When the Tomcat output says it can't find the WAR, to wit:


[2021-05-05 05:54:14,833] Artifact jersey:war exploded: Error during artifact deployment. See server log for details.
[2021-05-05 05:54:14,834] Artifact jersey:war exploded: com.intellij.javaee.oss.admin.jmx.JmxAdminException: \
    com.intellij.execution.ExecutionException: out/artifacts/jersey_war_exploded not found for the web module.

Originally, the Output directory: setting for Project Structure → Artifacts was "out/artifacts/jersey_war_exploded" based on Project Structure → Project → Project compiler output: defaulting to "out" (which I don't like and tend to ignore).

The solution, given to me by JetBrains support, is to place a full path in Project Structure → Artifacts → Output directory: when you create that:

Now, I don't like that path, as I say, so I'm going to use "/home/russ/dev/jersey/target/jersey_war_exploded" instead.

russ@tirion ~/dev/jersey $ find . -name jersey_war_exploded
./target/jersey_war_exploded

Next, the name of the field to configure is Output directory:. Consequently, I'm not going to give the path all the way down to the artifact name, which IntelliJ IDEA seems to ignore. So, my final path is "/home/russ/dev/jersey/target" and, in the end, I get a successful deployment:


...
06-May-2021 06:55:15.713 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
06-May-2021 06:55:15.726 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [32] milliseconds
Connected to server
[2021-05-06 06:55:15,970] Artifact jersey:war exploded: Artifact is being deployed, please wait...
[2021-05-06 06:55:16,333] Artifact jersey:war exploded: Artifact is deployed successfully
[2021-05-06 06:55:16,334] Artifact jersey:war exploded: Deploy took 364 milliseconds
06-May-2021 06:55:25.714 INFO [Catalina-utility-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web app...
06-May-2021 06:55:25.732 INFO [Catalina-utility-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web...
...

IntelliJ IDEA changing project type

What if you've got a project that you did not originally create with adequate resources and framework support?

If you erase the .idea subdirectory, then create a new project in IDEA giving it the name of the project you wish to change, you can accomplish the same thing without creating a whole new project only to copy all your code and other resources into it.* For example:

  1. Ensure that you do not have IDEA running on the project you wish to change.
  2. Remove the .idea subdirectory from the project you wish to change.
  3. Click File → Close Project and then click Create New Project
  4. Click Java Enterprise.
  5. Click Web Application in Additional Libraries and Frameworks.
  6. Click RESTful Web Service in Additional Libraries and Frameworks.
  7. Click Next.
  8. Click Next.
  9. Make Project name: the same as your existing project; ensure that Project location: follows suit and that the other settings in More Settings are good.
  10. Click Finish.

* which is certainly a simpler if more tedious option.


The "web descriptor" for a servlet/application

This means, specifically, web.xml on path src/main/resources/webapp/WEB-INF. You can put it elsewhere (but why?). Here's what it looks like in Project Structure → Modules:

You get this by right-clicking your project name at the top of the Project pane on the left in IDEA, then choose Add Framework Support..., click on and check JAX RESTful Web Services, then OK. This add the Web line in the center pane under your project name in the Project Structure → Modules dialog.

At very least, seemingly, the web descriptor looks something like this:

<?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"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                             http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0">
	<display-name>jersey</display-name>

	<servlet>
		<servlet-name>jersey</servlet-name>
		<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>jersey</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>
</web-app>

Function of Deploy applications configured in Tomcat instance in Run/Debug Configuration

If checked, you will see this:


...
Connected to server
[2021-05-06 11:21:30,669] Artifact jersey:war exploded: Artifact is being deployed, please wait...
[2021-05-06 11:21:31,055] Artifact jersey:war exploded: Artifact is deployed successfully
[2021-05-06 11:21:31,055] Artifact jersey:war exploded: Deploy took 386 milliseconds
06-May-2021 11:21:40.570 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/home/russ/dev/apache-tomcat-10.0.5/webapps/host-manager]
06-May-2021 11:21:40.588 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/home/russ/dev/apache-tomcat-10.0.5/webapps/host-manager] has finished in [17] ms
06-May-2021 11:21:40.588 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/home/russ/dev/apache-tomcat-10.0.5/webapps/docs]
06-May-2021 11:21:40.595 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/home/russ/dev/apache-tomcat-10.0.5/webapps/docs] has finished in [6] ms
06-May-2021 11:21:40.595 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/home/russ/dev/apache-tomcat-10.0.5/webapps/examples]
06-May-2021 11:21:40.672 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/home/russ/dev/apache-tomcat-10.0.5/webapps/examples] has finished in [77] ms
06-May-2021 11:21:40.672 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/home/russ/dev/apache-tomcat-10.0.5/webapps/jersey_war]
06-May-2021 11:21:40.823 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/home/russ/dev/apache-tomcat-10.0.5/webapps/jersey_war] has finished in [151] ms
06-May-2021 11:21:40.823 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/home/russ/dev/apache-tomcat-10.0.5/webapps/manager]
06-May-2021 11:21:40.836 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/home/russ/dev/apache-tomcat-10.0.5/webapps/manager] has finished in [13] ms

...which is a lot of stuff you didn't want anyway probably. Uncheck it to see only one application deployed, yours:


...
Connected to server
[2021-05-06 11:18:13,647] Artifact jersey:war exploded: Artifact is being deployed, please wait...
[2021-05-06 11:18:14,009] Artifact jersey:war exploded: Artifact is deployed successfully
[2021-05-06 11:18:14,009] Artifact jersey:war exploded: Deploy took 362 milliseconds
06-May-2021 11:18:23.540 INFO [Catalina-utility-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/home/russ/dev/apache-tomcat-10.0.5/webapps/manager]
06-May-2021 11:18:23.556 INFO [Catalina-utility-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/home/russ/dev/apache-tomcat-10.0.5/webapps/manager] has finished in [16] ms

...which is probably all you want.


Tomcat "complains" of no TLDs in JARs

TLD means "tagged library descriptor." Seeing the following show up when Tomcat launches doesn't mean there's an error. It's just information.

org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. \
    Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. \
    Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.

To avoid seeing this, add the following line to src/main/resources/logging.properties:

org.apache.jasper.servlet.TldScanner.level = SEVERE

Create this file a) if it doesn't exist and even if b) you use log-back as your logger. In my projects, this is often the only line in logging.properties. Any attempt in logback.xml to fix this failed for me.

However, there is a cost to this. If you tolerate the error/warning, then you get to see this including the deployed services package path and the classes found therein:


11-May-2021 15:00:25.462 INFO [main] org.apache.catalina.startup.Catalina.load Server initialization in [287] milliseconds
11-May-2021 15:00:25.476 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
11-May-2021 15:00:25.476 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache Tomcat/9.0.45]
11-May-2021 15:00:25.481 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8088"]
11-May-2021 15:00:25.486 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [23] milliseconds
Connected to server
[2021-05-11 03:00:25,688] Artifact simple-webapp:war: Artifact is being deployed, please wait...
11-May-2021 15:00:25.954 INFO [RMI TCP Connection(2)-127.0.0.1] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs ...
11-May-2021 15:00:25.965 INFO [RMI TCP Connection(2)-127.0.0.1] com.sun.jersey.api.core.PackagesResourceConfig.init Scanning for root resource and \
    provider classes in the packages:
  com.madhawa.services
11-May-2021 15:00:25.976 INFO [RMI TCP Connection(2)-127.0.0.1] com.sun.jersey.api.core.ScanningResourceConfig.logClasses Root resource classes found:
  class com.madhawa.services.GoodbyeService
  class com.madhawa.services.HelloService
11-May-2021 15:00:25.976 INFO [RMI TCP Connection(2)-127.0.0.1] com.sun.jersey.api.core.ScanningResourceConfig.init No provider classes found.
11-May-2021 15:00:26.020 INFO [RMI TCP Connection(2)-127.0.0.1] com.sun.jersey.server.impl.application.WebApplicationImpl._initiate Initiating \
    'Jersey: 1.19.4 05/24/2017 03:20 PM'
[2021-05-11 03:00:26,266] Artifact simple-webapp:war: Artifact is deployed successfully
[2021-05-11 03:00:26,266] Artifact simple-webapp:war: Deploy took 578 milliseconds
11-May-2021 15:00:35.482 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/home...
11-May-2021 15:00:35.496 INFO [Catalina-utility-2] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/...

Blocking the error/warning, you see this shorter, cleaner output, but no deployed services package path or classnames:


11-May-2021 14:58:19.905 INFO [main] org.apache.catalina.startup.Catalina.load Server initialization in [272] milliseconds
11-May-2021 14:58:19.919 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
11-May-2021 14:58:19.920 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache Tomcat/9.0.45]
11-May-2021 14:58:19.925 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8088"]
11-May-2021 14:58:19.936 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [30] milliseconds
Connected to server
[2021-05-11 02:58:20,131] Artifact simple-webapp:war: Artifact is being deployed, please wait...
[2021-05-11 02:58:20,727] Artifact simple-webapp:war: Artifact is deployed successfully
[2021-05-11 02:58:20,727] Artifact simple-webapp:war: Deploy took 596 milliseconds
11-May-2021 14:58:29.926 INFO [Catalina-utility-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/home...
11-May-2021 14:58:29.942 INFO [Catalina-utility-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/...

That never absent HTTP 404 status...

Friday, 7 May 2021

I tried this tutorial, How to Create [a] Java Web Project in IntelliJ IDEA, but could not get it working—failing to HTTP status 404 as is often the case. Nothing I did could get it to work.

I discovered that my problem was using Tomcat 10!

It turns out that IntelliJ IDEA doesn't yet work with version 10, but only 9. JetBrains Support know of this now and predict future fixes as they are able to get to it.

Why did I try to use Tomcat 10? I had just finished building my new development host, tirion. As I'm usually lazy and beholden to older versions of software I have just always used (Tomcat 6, 7, etc.), I often decide to turn over a new leaf when I build new, so I went for Tomcat 10.

Some links that might be helpful...


Maintain code-style (Java)...

Import code style

Imports code-style settings such as are created below (in Export current code style) as an XML file that's easily transported. In the illustration to the right, that's russ.xml.

  1. Bring up File → Settings.
  2. Click Show scheme actions (the gear icon).
  3. Choose Import Scheme....
  4. Navigate to and select the desired scheme, something like scheme.xml.
  5. Click OK.

Export current code style

This will create using the current code style Scheme name an XML file containing code-style settings in a directory on your filesystem.

  1. Bring up File → Settings.
  2. Click Show scheme actions (the gear icon).
  3. Choose Export → IntelliJ IDEA code style XML.
  4. Navigate to and select the subdirectory where it should land.
  5. Click OK.