Getting started with Java Servlets

There is a reason why we have not examined a web deployment context file up until now. With Java Servlet 3.1, you do not have to define a context file, because developers can choose to write Servlets using the annotations, and web container can work out how to build the metadata for the Servlet at the deployment time.

A simple Servlet

It is time to examine a sample Java Servlet and we will introduce the annotations. The code for SimpleServlet is as follows:

package je7hb.servlets.simple;

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;
import java.util.Date;

@WebServlet("/simple")
public class SimpleServlet extends HttpServlet {
  
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  throws ServletException, IOException {
    resp.setContentType("text/plain");
    PrintWriter pwriter = resp.getWriter();
    pwriter.printf("This is the class `%s'
"+ "The date time is %s
", this.getClass().getName(), new Date());
    }
  }

The preceding code is perhaps the easiest Servlet that you can develop and deploy to an application server or web container.

The SimpleServlet subclasses the abstract base class HttpServlet, and chooses to override the doGet() method, which handles the HTTP GET protocol request. The Servlet class is also annotated with @javax.servlet.annotation.WebServlet. This is one of many new annotations since Servlet 3.0 and the Java EE 7 specifications.

The doGet() method takes two parameters, namely HttpServletRequest and HttpServletResponse. With the response object, Servlets sets the content type of the response to text/plain. This should only be set once per response for a given request. The method retrieves a PrintWriter instance for the response. The getWriter() method is one of the many convenient methods in HttpServletReponse. Servlet generates the output of the Servlet's full class name, and the current data and time.

By the way the HttpServletResponse interface also has a getOutputStream() method that returns java.io.OutputStream. This is useful for streaming the output applications. Let me warn you to decide on using either OutputStream or Writer. Once the output has been pushed to the HTTP response then the Servlet framework prevents the application from changing the content type mid-flow.

Annotating a class with @WebServlet, unfortunately, is not a free-for-all. A Java class annotated with @WebServlet must be a subclass of HttpServlet. The annotation serves to purely define a Servlet component in a web application. In particular, a Servlet can have a unique name and can respond to a set of URL patterns. In the SimpleServlet code, we are declaring Servlet with the name as simple.

In order to access this Servlet on a web container, for example, we would specify a URL like the following URL:

http://<host>[:<port>]/<web-app>/simple

Where host is the hostname of the server, port is the port number, and web-app specifies the name of the web application.

A table of the attribute definitions for the @WebServlet annotation is as follows:

Attribute

Type

Description

Default Value

name

String

Defines the name of Servlet.

Empty

value

String []

Defines the URL patterns of Servlet.

None

urlPatterns

String []

Defines the URL patterns of Servlet.

 

loadOnStartup

Int

Specifies the load-on-startup order of Servlet.

-1

initParams

WebInitParam []

The initial parameters of Servlet.

None

asyncSupported

Boolean

Specifies if Servlet supports asynchronous operations and processing or not.

See also the ServletRequest.startAsync() methods.

false

displayName

String

The display name of Servlet

Empty string

description

String

The description for a Servlet

Empty string

smallIcon

String

Specifies a path to a small icon

None

largeIcon

String

Specifies a path to a large icon

None

The URL path mapping

According to the preceding information, we could have annotated Servlet with specific URL patterns and given it internally, a different name. Some additional usages of the @WebServlet annotations with the same Servlet are as follows:

@WebServlet(urlPatterns = {"/simple"}, name = "simple")
public class SimpleServlet extends HttpServlet {/*... */}
// For example, http://localhost:8080/mywebapp/simple

This annotation declares the exact same Servlet configuration as the previous example code. SimpleServlet will be invoked by web container with any URL pattern matching /simple. The URL patterns are exact and therefore, the preceding pattern does not match the URL /simple/1, which presumably would be useless in a RESTful application. If you want to match anything after the forward slash (/) then you must use something like the following code:

@WebServlet(urlPatterns = {"/simple/*"}, name = "simple")
public class SimpleServlet extends HttpServlet {/*... */}
// For example, http://localhost:8080/mywebapp/simple/1
// For example, http://localhost:8080/mywebapp/simple/glasgow

What if we wanted to configure Servlet to respond to certain names? We can achieve this requirement with two URL patterns like the following:

@WebServlet(urlPatterns = {"/sarah","/paul"}, name = "simple")
public class SimpleServlet extends HttpServlet {/*... */}
// For example, http://localhost:8080/mywebapp/sarah// or http://localhost:8080/mywebapp/paul

Web container only invokes SimpleServlet when the incoming URL request matches the pattern. The forward slash characters (/) at the beginning of the patterns are significant. They serve to demarcate the URL paths.

See the Miscellaneous features section of this chapter, for further information on the URL patterns.

Now we have a Java Servlet, so what do we need to install it onto a web container? The answer is that we have to assemble it as a compiled Java class into a WAR file, a web application archive. So let's use Gradle to do it, and we are going to deploy it onto a GlassFish application server, but in this chapter, we make use of building a so-called container-less web application. This is just a fancy name for running an embedded web container or application server from the standard entry into a Java application, the JVM invokes main(String args[]).

The Gradle build project

The Gradle build for the SimpleServlet example is as follows:

apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'maven'
apply plugin: 'eclipse'
apply plugin: 'idea'

group = 'com.javaeehandbook.book1'
archivesBaseName = 'ch06-servlets-basic'
version = '1.0'

repositories {
  mavenCentral()
  maven {
    url 'https://maven.java.net/content/groups/promoted'
    }
  maven {
    url 'http://repository.jboss.org/nexus/content/groups/public'
    }
  }

dependencies {
  providedCompile 'org.glassfish.main.extras: glassfish-embedded-all: 4.0.1-b01'
  compile 'org.glassfish.main.extras: glassfish-embedded-all: 4.0.1-b01'
  compile 'javax: javaee-api: 7.0'
  
  testCompile 'junit: junit: 4.11'
  }

war {
  // webXml = file("src/main/webapp/WEB-INF/web.xml")
  }

// Override Gradle defaults - a force an exploded JAR view
sourceSets {
  main {
    output.resourcesDir = 'build/classes/main'
    output.classesDir   = 'build/classes/main'
    }
  test {
    output.resourcesDir = 'build/classes/test'
    output.classesDir   = 'build/classes/test'
    }
  }

task(run, dependsOn: 'classes', type: JavaExec) {
  description = 'Runs the main application'
  main = 'je7hb.common.webcontainer.embedded.glassfish.EmbeddedRunner'
  classpath = sourceSets.main.runtimeClasspath
  args 'Mary', 'Peter', 'Jane'
  standardInput = System.in
  }

In the build file, the most crucial dependency is glassfish-embedded-all; it has to be the very first dependency, otherwise the execution of the embedded runner fails with a ValidationException exception. The exception happens because the GlassFish application serve, uses the Hibernate Validator as the default Bean Validation component. Luckily, Gradle does pay attention to the order of the dependencies, because the embedded runner works from the command line and also from an IDE.

The build file adds some additional repositories, the Maven GlassFish Promoted and the JBoss Public sites.

Because we have a Servlet in the application, Gradle applies the War plugin, which generates a WAR file for ourselves in the folder build/libs. The extra configuration of the project dependencies named as providedCompile ensures that the embedded glassfish dependency is not included into the WAR file.

Finally, in the Gradle task named run, that is a type of the Java execution task, we override the standard input to System.in, because we want the embedded runner, which we will see shortly, to respond to the console input.

Let us move on to the GlassFish embedded runner.

The containerless Java web application

In order to go and demonstrate the container-less application, the following example uses the GlassFish embedded API, at the time of writing. Since GlassFish is the reference implementation to Java EE 7, we should see how to invoke it, start the server, stop the server, and most importantly deploy a web application to the server.

It is possible to go container-less with other servers, such as Apache Tomcat, Jetty, and Caucho Resin. The book does not cover those approaches, however, and there is sure to documentation online that explains how to configure those containers for Java EE 7 and Servlets 3.1.

GlassFish has well documented embedded document online since Version 3.1.2 and this is the information we have used in the code. Fortunately, the API is still compatible with GlassFish 4.0.1 at the time of writing, which means we can use it.

The code for EmbeddedRunner is as follows:

package je7hb.common.webcontainer.embedded.glassfish;

import org.glassfish.embeddable.*;
import java.io.File;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicBoolean;

public class EmbeddedRunner {
  private int port;
  private AtomicBoolean initialized = new AtomicBoolean();
  private GlassFish glassfish;
  
  public EmbeddedRunner(int port) {
    this.port = port;
    }
  
  public EmbeddedRunner init() throws Exception{
    if (initialized.get()) {
      throw new RuntimeException("runner was already initialized");
      }
    
    BootstrapProperties bootstrapProperties = new BootstrapProperties();
    GlassFishRuntime glassfishRuntime = GlassFishRuntime.bootstrap(bootstrapProperties);
    
    GlassFishProperties glassfishProperties = new GlassFishProperties();
    glassfishProperties.setPort("http-listener", port);
    glassfish = glassfishRuntime.newGlassFish(glassfishProperties);
    initialized.set(true);
    return this;
    }
  
  private void check() {
    if (!initialized.get()) {
      throw new RuntimeException("runner was not initialised");
      }
    }
  
  public EmbeddedRunner start() throws Exception {
    check();
    glassfish.start();
    return this;
    }
  
  public EmbeddedRunner stop() throws Exception {
    check();
    glassfish.stop();
    return this;
    }
  
  public EmbeddedRunner deployWithRename(String war, String newContext ) throws Exception {
    Deployer deployer = glassfish.getDeployer();
    deployer.deploy(new File(war), "--name="+newContext, "--contextroot = "+newContext, "--force=true");
    return this;
    }
  
  public static void main(String args[]) throws Exception {
    EmbeddedRunner runner = new EmbeddedRunner(8080).init().start();
    runner.deployWithRename("build/libs/ch06-servlets-basic-1.0.war", "mywebapp");
    Thread.sleep(1000);
    System.out.printf("**** Press the ENTER key to stop "+"the server ****");
    Scanner sc = new Scanner(System.in);
    while(!sc.nextLine().equals(""));
    runner.stop();
    }
  }

As you can see in the program, EmbeddedRunner looks like a barrel and stock Java application. In the main() program, we instantiate the object with a port number 8080, initialize the runner, and start the server. The program then proceeds to deploy a WAR file from the build, it delays for one second, and then waits for the user to type ENTER. As soon as the user does it, the program stops the server, and exits normally.

The GlassFish server is launched by initializing an instance with two types of properties, namely bootstrap and standard. These properties help developers to configure special GlassFish-only features. The bootstrap properties can be used to configure an installation root, if the server or launch space already has a version of GlassFish installed. For this example, we do not use this feature. The standard properties can configure the instance root, the configuration file locally of a particular domain area, and also services and port numbers.

In EmbeddedRunner, we configure only the HTTP listener for the embedded server, which is passed into the init() method as 8080. Having created a GlassfishRuntime instance, the runner invokes a new Glassfish object instance, which represents the application server.

The methods start() and stop(), start and stop the application server respectively.

The deployWithRename() method is a convenient method that deploys the simple Servlet WAR file to the server, and it does a unique rename of the web context from the clumsy ch06-servlets-basic-1.0, which by default is the base name of the WAR file without the suffix. In this case, the web application context is renamed mywebapp.

Now that we have a Java Servlet, a Gradle build file, and an embedded runner application, we can invoke it, and run the program. Here is how, with the following Gradle commands:

$ Gradle clean
$ Gradle war
$ Gradle run

Enter in your favorite web browser URL http://localhost:8080/mywebapp/simple. The output on the page should be something like the following:

This is the class `je7hb.servlets.simple.SimpleServlet'
The date time is Sat Feb 02 16:45:52 GMT 2013

To complete this section, here is a different version of the same Servlet, but this time we configure the initialization parameters through the annotations. This Servlet is called SimpleServletWithInitParams.

package je7hb.servlets.simple;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
// Additional imports from before are omitted

@WebServlet(name = "servletWithInitParams", urlPatterns = {"/initparams"}, initParams = {@WebInitParam(name = "source", value = "East Croydon"), @WebInitParam(name = "target", value = "London Bridge"), @WebInitParam(name = "time", value = "11:57:00")})
public class SimpleServletWithInitParams extends HttpServlet {
  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  throws ServletException, IOException {
    resp.setContentType("text/plain");
    PrintWriter pwriter = resp.getWriter();
    pwriter.printf("This is the class `%s'
The date "+ "time is %s
", this.getClass().getName(), new Date());
    for ( String name: Collections.list(this.getServletConfig().getInitParameterNames())) {
      pwriter.printf("init parameter: %s = %s
", name, getServletConfig().getInitParameter(name));
      }
    }
  }

The extra bit of code in this example retrieves java.util.Enumeration of the Servlet initialization parameters. In the Servlet method, we call the utility static list() method of java.util.Collections to turn the Enumeration into java.util.Iterator<String>. (This is an artifact from Java's history when the Servlet API, which was created in 1999, existed long before the release of Java SE 5, generic types, and annotations in 2005!) We then iterate through the parameters and dump their values to the Servlet's response, the output buffer, using the PrintWriter instance.

When running the embedded runner, if you invoke this Servlet with the quasi URL http://localhost:8080/mywebapp/initparams, you should see the output like the following:

This is the class `je7hb.servlets.simple.SimpleServletWithInitParams'
The date time is Mon Feb 04 20:04:58 GMT 2013
init parameter: time = 11:57:00
init parameter: source = East Croydon
init parameter: target = London Bridge

The @WebInitParam annotation accepts three attributes: name, value, and description. The name attribute specifies the Servlet's initialization parameter name, value is the associated value, and description is self-explanatory.

Congratulations, this is your first Java Servlet! Best of all, you did not have to specify a web XML deployment context, better known as a web.xml file.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.14.142.194