1. Introduction

GServlet is an open source project inspired from the Groovlets, which aims to use the Groovy language and its provided modules to simplify Servlet API web development. Groovlets are Groovy scripts executed by a servlet. They are run on request, having the whole web context (request, response, etc.) bound to the evaluation context. They are much more suitable for smaller web applications. Compared to Java Servlets, coding in Groovy can be much simpler. It has a couple of implicit variables we can use, for example, request, response to access the HttpServletRequest, and HttpServletResponse objects. We have access to the HttpSession with the session variable. If we want to output data, we can use out, sout, and html.

Groovlet.groovy
if (!session) {
    session = request.getSession(true)
}
if (!session.counter) {
    session.counter = 1
}
html.html {
  head {
      title('Groovy Servlet')
  }
  body {
    p("Hello, ${request.remoteHost}: ${session.counter}! ${new Date()}")
  }
}
session.counter = session.counter + 1

The same philosophy has been followed in the design of this API while maintaining a class-based programming approach.

SessionCounterServlet.groovy
import org.gservlet.annotation.Servlet

@Servlet("/counter")
class SessionCounterServlet {

  void get() {
    if (!session.counter) {
      session.counter = 1
    }
    html.html {
      head {
        title('Groovy Servlet')
      }
      body {
        p("Hello, ${request.remoteHost}: ${session.counter}! ${new Date()}")
      }
    }
    session.counter = session.counter + 1
  }

}

If you have no prior experience with the Groovy language, there are several curated lists of online resources for learning it.

2. Main Features

  • Servlet 3.1+ Support

  • Groovy Scripting and Hot Reloading

  • JSON, XML, HTML and JDBC Support

3. Requirements

  • Java 8+

  • Java IDE (Eclipse, IntelliJ IDEA, NetBeans..)

  • Java EE 7+ compliant WebServer (Tomcat, Wildfly, Glassfish, Payara..)

4. Installation

To get started with GServlet, you may want to begin by creating your first project. This section shows you how to get up and running quickly. It is highly recommended to consume the API through a dependency management tool and the artifact can be found in Maven’s central repository. It is named gservlet-api and you just need to name a dependency on it in your project.

4.1. Maven

pom.xml
<dependency>
	<groupId>org.gservlet</groupId>
	<artifactId>gservlet-api</artifactId>
	<version>1.0.0</version>
</dependency>

4.2. Gradle

build.gradle
repositories {
    mavenCentral()
}

dependencies {
    compile("org.gservlet:gservlet-api:1.0.0")
}

5. Getting Started

Once your Java web server is installed and configured, you can put it to work. Five steps take you from writing your first Groovy servlet to running it. Using Maven, these steps are as follows:

Maven webapp project
  1. Create a Maven webapp project

  2. Create the groovy folder inside your webapp directory

  3. Write the servlet source code

  4. Run your Java web server

  5. Call your servlet from a web browser

Below are some examples that you can try out.

ProjectServlet.groovy
import org.gservlet.annotation.Servlet

@Servlet("/projects")
class ProjectServlet {

	List projects = []

	void init() {
	   projects << [id : 1, name : "Groovy", url : "https://groovy-lang.org"]
	   projects << [id : 2, name : "Spring", url : "https://spring.io"]
	   projects << [id : 3, name : "Maven",  url : "https://maven.apache.org"]
	}

	void get() {
	   json(projects)
	}

	void post() {
	   def project = request.body
	   projects << project
	   json(project)
	}

	void put() {
	   def project = request.body
	   int index = projects.findIndexOf { it.id == project.id }
	   projects[index] = project
	   json(project)
	}

	void delete() {
	   def project = request.body
	   int index = projects.findIndexOf { it.id == project.id }
	   json(projects.remove(index))
	}

}
CorsFilter.groovy
import org.gservlet.annotation.Filter

@Filter("/*")
class CorsFilter {

    void filter() {
      response.addHeader("Access-Control-Allow-Origin", "*")
      response.addHeader("Access-Control-Allow-Methods","GET, OPTIONS, HEAD, PUT, POST, DELETE")
      if (request.method == "OPTIONS") {
        response.status = response.SC_ACCEPTED
        return
      }
      next()
    }

}
ServletRequestListener.groovy
import org.gservlet.annotation.RequestListener

@RequestListener
class ServletRequestListener {

   void init() {
     println "request initialized"
   }

   void destroy() {
     println "request destroyed"
   }

}
Keep a note that the use of the default package is discouraged and for a hot reloading of your source code, set the GSERVLET_RELOAD environment variable to true in your IDE.

6. Creating Servlets

A servlet is a small Java program that runs within a Web server. The Servlet interface defines methods that all servlets must implement. To implement this interface, you can write a generic servlet that extends the GenericServlet class or an HTTP servlet which extends the HttpServlet class and overrides at least one method, usually one of these:

  • doGet, for HTTP GET requests

  • doPost, for HTTP POST requests

  • doPut, for HTTP PUT requests

  • doDelete, for HTTP DELETE requests

servlet diagram

Below is a Java class that extends the HttpServlet class:

HelloWordServlet.java
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("/index.html")
public class HelloWordServlet extends HttpServlet {

   public void doGet(HttpServletRequest request,HttpServletResponse response) throws IOException {
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("<html>");
      out.println("<body>");
      out.println("<p>Hello World!</p>");
      out.println("</body>");
      out.println("</html>");
   }

}

We are going to write its Groovy counterpart with the GServlet API so you can perceive the difference in terms of simplicity and clarity. The name of the HTTP request method handlers are shortened to get, post and so on. They take no arguments since the request and the response are now implicit variables.

HelloWordServlet.groovy
import org.gservlet.annotation.Servlet

@Servlet("/index.html")
class HelloWordServlet {

   void get() {
      out.println("<html>")
      out.println("<body>")
      out.println("<p>Hello World!</p>")
      out.println("</body>")
      out.println("</html>")
   }

}

By default the content type of the HttpServletResponse is set to text/html and the implicit out variable used to generate the HMTL content is nothing less than a reference to its PrintWriter object. We could use as well the implicit html variable which is an instance of a Groovy MarkupBuilder, to write a better version of this servlet.

HelloWordServlet.groovy
import org.gservlet.annotation.Servlet

@Servlet(value="/index.html", loadOnStartup = 1)
class HelloWordServlet {

  void get() {
     html.html {
       body {
         p("Hello World!")
       }
     }
  }

}

The generated HTML content looks like this:

Generated HTML
<!DOCTYPE html>
<html>
  <body>
    <p>Hello World!</p>
  </body>
</html>

6.1. Servlet annotation attributes

There are the same as those of the @WebServlet annotation.

Name Type Description

name

String

name of the servlet

value

String[]

URL patterns of the servlet

urlPatterns

String[]

URL patterns of the servlet

loadOnStartup

Integer

load-on-startup order of the servlet

initParams

InitParam[]

init parameters of the servlet

asyncSupported

boolean

Declares whether the servlet supports asynchronous operation mode

smallIcon

String

small icon of the servlet

largeIcon

String

large icon of the servlet

description

String

description of the servlet

displayName

String

display name of the servlet

6.2. Servlet implicit variables

The implicit variables made available to a Servlet are as follows:

Variable Description

logger

Logger object

config

ServletConfig object

request

HttpServletRequest object

response

HttpServletResponse object

session

HttpSession object

context

ServletContext object

sql

Sql object

out

PrintWriter object

html

MarkupBuilder object

xml

MarkupBuilder object

6.3. Servlet methods

For an exhaustive list of the supported methods, please read the Javadocs.

Method Description

void init()

handles the initialization process

void get()

handles the GET request

void post()

handles the POST request

void put()

handles the PUT request

void delete()

handles the DELETE request

void head()

handles the HEAD request

void options()

handles the OPTIONS request

void trace()

handles the TRACE request.

void forward(location)

Forwards the request to the provided location

void redirect(location)

Redirects the request to the provided location

void json(object)

Sends the response as JSON

void destroy()

invoked when taken out of the service

6.4. Servlet initialization parameters

Since Servlet 3, the @WebInitParam annotation is used to specify initialization parameters for a servlet programmatically, and it takes a required name and value. You can add a description but this is rather informative. In the initialization method init(), we can get our parameters using the getInitParameter() method of the ServletConfig object. In the GServlet API, the annotation has been shorten to @InitParam and you can get an initialization parameter through the implicit config variable using just its name or as described above.

UploadServlet.groovy
import org.gservlet.annotation.InitParam
import org.gservlet.annotation.Servlet

@Servlet( urlPatterns = "/upload",
initParams = [
	@InitParam(name = "uploadDirectory", value = "/images")
] )
class UploadServlet {

	void init() {
		println config.uploadDirectory
		println config.getInitParameter("uploadDirectory")
	}

}

The attributes of the @InitParam annotation are the same as those of the @WebInitParam annotation.

Name Type Description

name

String

name of the initialization parameter

value

String

value of the initialization parameter

6.5. Servlet Multipart configuration

Supporting file uploads is a very basic and common requirement for many web applications. Prior to Servlet 3.0, implementing file upload required the use of external libraries or complex input processing. Version 3.0 of the Java Servlet specification helps to provide a viable solution to the problem in a generic and portable way. The Servlet 3.0 specification supports file upload out of the box, so any web container that implements the specification can parse multipart requests and make mime attachments available through the HttpServletRequest object. A new annotation, @MultipartConfig, is used to indicate that the servlet on which it is declared expects requests to made using the multipart/form-data MIME type. Therefore, it can retrieve the Part components of a given multipart/form-data request by calling the getPart(String name) or getParts() method of the HttpServletRequest object.

UploadServlet.groovy
import org.gservlet.annotation.InitParam
import org.gservlet.annotation.Servlet
import javax.servlet.annotation.MultipartConfig

@Servlet( urlPatterns = "/upload",
initParams = [
	@InitParam(name = "uploadDirectory", value = "/images")
] )
@MultipartConfig( fileSizeThreshold = 1048576, maxFileSize = 5242880L, maxRequestSize = 26214400L )
class UploadServlet {

	String uploadPath

	void init() {
		uploadPath = context.getRealPath(config.uploadDirectory)
		File uploadDir = new File(uploadPath)
		if (!uploadDir.exists()) {
			uploadDir.mkdir()
		}
	}

	void get() {
		File uploadDir = new File(uploadPath)
		def files = []
		uploadDir.listFiles()?.each { file ->
			files << [name : file.name, length : file.length(), lastModified : file.lastModified()]
		}
		json(files);
	}

	void post() {
		request.getParts().each { part ->
			String file = uploadPath + File.separator + request.getFileName(part)
			part.write(file)
		}
		redirect(context.contextPath + "/upload");
	}


}

6.6. Servlet Security

The @ServletSecurity annotation is used to specify security constraints on a Java servlet. The annotations @HttpMethodConstraint and @HttpConstraint are used within that annotation to define the security constraints.

@ServletSecurity(
    httpMethodConstraints = <HttpMethodConstraint[]>,
    value = <HttpConstraint>
)

The httpMethodConstraints attribute specifies one or more constraints for some specific HTTP methods, whereas the value attribute specifies a constraint that applies for all other HTTP methods.

Encryption for all HTTP methods
import org.gservlet.annotation.Servlet
import javax.servlet.annotation.ServletSecurity
import javax.servlet.annotation.ServletSecurity.TransportGuarantee
import javax.servlet.annotation.HttpConstraint

@Servlet(value="/projects")
@ServletSecurity(@HttpConstraint(transportGuarantee = TransportGuarantee.CONFIDENTIAL))
class ProjectServlet {


}
Denying access to HTTP POST method
import org.gservlet.annotation.Servlet
import javax.servlet.annotation.ServletSecurity
import javax.servlet.annotation.HttpMethodConstraint
import javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic

@Servlet(value="/projects")
@ServletSecurity(httpMethodConstraints = @HttpMethodConstraint(value = "POST",
	emptyRoleSemantic = EmptyRoleSemantic.DENY))
class ProjectServlet {


}
Requiring that users must have the admin role
import org.gservlet.annotation.Servlet
import javax.servlet.annotation.ServletSecurity
import javax.servlet.annotation.HttpMethodConstraint

@Servlet(value="/projects")
@ServletSecurity(
 httpMethodConstraints = [
  @HttpMethodConstraint(value = "GET", rolesAllowed = "admin"),
  @HttpMethodConstraint(value = "POST", rolesAllowed = "admin"),
 ]
)
class ProjectServlet {


}

You can find more examples about how to use the @ServletSecurity annotation on the web.

7. Creating Filters

A filter is an object that performs filtering tasks on either the request to a resource (a servlet or static content), or on the response from a resource, or both. The Filter interface defines methods that all filters must implement. Filters perform filtering in the doFilter() method.

filter diagram

Below is a Java class that implements the Filter interface:

MyFilter.java
import javax.servlet.annotation.WebFilter;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.ServletException;
import java.io.IOException;

@WebFilter("/*")
public class MyFilter implements Filter {

    public void init(FilterConfig filterConfig) throws ServletException {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        chain.doFilter(request, response);
    }

    public void destroy() {
    }

}

Its Groovy version with the GServlet API looks like this:

MyFilter.groovy
import org.gservlet.annotation.Filter;

@Filter("/*")
class MyFilter {

    void init() {
    }

    void filter() {
        next()
    }

    void destroy() {
    }

}

7.1. Filter annotation attributes

There are the same as those of the @WebFilter annotation.

Name Type Description

filterName

String

name of the filter

value

String[]

URL patterns of the filter

urlPatterns

String[]

URL patterns of the filter

dispatcherTypes

DispatcherType[]

dispatcher types to which the filter applies

initParams

InitParam[]

init parameters of the filter

servletNames

String[]

names of the servlets to which the filter applies

asyncSupported

boolean

Declares whether the filter supports asynchronous operation mode

smallIcon

String

small icon of the filter

largeIcon

String

large icon of the filter

description

String

description of the filter

displayName

String

display name of the filter

7.2. Filter implicit variables

The implicit variables made available to a Filter are as follows:

Variable Description

logger

Logger object

config

FilterConfig object

request

HttpServletRequest object

response

HttpServletResponse object

chain

FilterChain object

session

HttpSession object

context

ServletContext object

sql

Sql object

out

PrintWriter object

html

MarkupBuilder object

xml

MarkupBuilder object

7.3. Filter methods

For an exhaustive list of the supported methods, please read the Javadocs.

Method Description

void init()

handles the initialization process

void filter()

handles the filtering tasks

void next()

Calls the next filter in the chain

void json(object)

Sends the response as JSON

void destroy()

invoked when taken out of the service

7.4. Filter initialization parameters

The @WebInitParam annotation is used to specify initialization parameters for a filter programmatically. In its initialization method init(), we can get our parameters using the getInitParameter() method of the FilterConfig object. Like for a servlet, the annotation has been shorten to @InitParam and you can get an initialization parameter through the implicit config variable using just its name or as described above.

LoggingFilter.groovy
import org.gservlet.annotation.InitParam
import org.gservlet.annotation.Filter

@Filter( value = "/*",
initParams = [
	@InitParam(name = "loggingDirectory", value = "/logs")
])
class LoggingFilter {

	void init() {
		println config.loggingDirectory
		println config.getInitParameter("loggingDirectory")
	}

}

8. Creating Listeners

During the lifetime of a typical Java EE web application, a number of events take place. The Servlet API provides a number of listener interfaces that we can implement to react to these events.

ServletContextListener

Interface for receiving notification events about ServletContext lifecycle changes.

ServletContextAttributeListener

Interface for receiving notification events about ServletContext attribute changes.

ServletRequestListener

Interface for receiving notification events about a ServletRequest coming into and going out of scope of a web application.

ServletRequestAttributeListener

Interface for receiving notification events about ServletRequest attribute changes.

HttpSessionListener

Interface for receiving notification events about HttpSession lifecycle changes.

HttpSessionAttributeListener

Interface for receiving notification events about HttpSession attribute changes.

HttpSessionBindingListener

Interface for receiving notification events when an object is bound to or unbound from a HttpSession.

HttpSessionActivationListener

Interface for receiving notification events when an HttpSession is being passivated and and activated.

HttpSessionIdListener

Interface for receiving notification events about HttpSession id changes.

8.1. ServletContextListener

This interface is for receiving notification events about ServletContext lifecycle changes. Implementations of this interface are invoked at their contextInitialized method in the order in which they have been declared, and at their contextDestroyed method in reverse order.

servletcontextlistener diagram

Below is a Java class that implements the ServletContextListener interface:

MyServletContextListener.java
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class MyServletContextListener implements ServletContextListener {

	public void contextInitialized(ServletContextEvent event) {
	   System.out.println("context started");
	}

	public void contextDestroyed(ServletContextEvent event) {
	   System.out.println("context destroyed");
	}

}

Its Groovy version with the GServlet API looks like this:

MyServletContextListener.groovy
import org.gservlet.annotation.ContextListener

@ContextListener
public class MyServletContextListener {

	void contextInitialized() {
	   println "context started"
	}

	void contextDestroyed() {
	   println "context destroyed"
	}

}

The implicit variables made available to a ServletContextListener are as follows:

Variable Description

logger

Logger object

context

ServletContext object

event

ServletContextEvent object

8.2. ServletContextAttributeListener

This interface is for receiving notification events about ServletContext attribute changes. The order in which implementations of this interface are invoked is unspecified.

servletcontextattributelistener diagram

Below is a Java class that implements the ServletContextAttributeListener interface:

MyServletContextAttributeListener.java
import javax.servlet.ServletContextAttributeEvent;
import javax.servlet.ServletContextAttributeListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class MyServletContextAttributeListener implements ServletContextAttributeListener {

    public void attributeAdded(ServletContextAttributeEvent event) {
        System.out.println("attr " + event.getName() + " added with value " + event.getValue());
    }

    public void attributeRemoved(ServletContextAttributeEvent event) {
        System.out.println("attr " + event.getName() + " removed with value " + event.getValue());
    }

    public void attributeReplaced(ServletContextAttributeEvent event) {
        System.out.println("attr " + event.getName() + " replaced with value " + event.getValue());
    }

}

Its Groovy version with the GServlet API looks like this:

MyServletContextAttributeListener.groovy
import org.gservlet.annotation.ContextAttributeListener

@ContextAttributeListener
public class MyServletContextAttributeListener {

    void attributeAdded() {
        println "attr $name added with value $value"
    }

    void attributeRemoved() {
        println "attr $name removed with value $value"
    }

    void attributeReplaced() {
        println "attr $name replaced with value $value"
    }

}

The implicit variables made available to a ServletContextAttributeListener are as follows:

Variable Description

logger

Logger object

context

ServletContext object

event

ServletContextAttributeEvent object

name

attribute name

value

attribute value

8.3. ServletRequestListener

This interface is for receiving notification events about requests coming into and going out of scope of a web application. A request is defined as coming into scope of a web application when it is about to enter the first servlet or filter of the web application, and as going out of scope as it exits the last servlet or the first filter in the chain. Implementations of this interface are invoked at their requestInitialized method in the order in which they have been declared, and at their requestDestroyed method in reverse order.

servletrequestlistener diagram

Below is a Java class that implements the ServletRequestListener interface:

MyServletRequestListener.java
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class MyServletRequestListener implements ServletRequestListener {

    public void requestInitialized(ServletRequestEvent event) {
        System.out.println("request initialized");
    }

    public void requestDestroyed(ServletRequestEvent event) {
        System.out.println("request destroyed");
    }

}

Its Groovy version with the GServlet API looks like this:

MyServletRequestListener.groovy
import org.gservlet.annotation.RequestListener

@RequestListener
public class MyServletRequestListener {

    void requestInitialized() {
        println "request initialized"
    }

    void requestDestroyed() {
        println "request destroyed"
    }

}

The implicit variables made available to a ServletRequestListener are as follows:

Variable Description

logger

Logger object

request

HttpServletRequest object

session

HttpSession object

context

ServletContext object

event

ServletRequestEvent object

8.4. ServletRequestAttributeListener

This interface is for receiving notification events about ServletRequest attribute changes. Notifications will be generated while the request is within the scope of the web application. A ServletRequest is defined as coming into scope of a web application when it is about to enter the first servlet or filter of the web application, and as going out of scope when it exits the last servlet or the first filter in the chain. The order in which implementations of this interface are invoked is unspecified.

servletrequestattributelistener diagram

Below is a Java class that implements the ServletRequestAttributeListener interface:

MyServletRequestAttributeListener.java
import javax.servlet.ServletRequestAttributeEvent;
import javax.servlet.ServletRequestAttributeListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class MyServletRequestAttributeListener implements ServletRequestAttributeListener {

    public void attributeAdded(ServletRequestAttributeEvent event) {
        System.out.println("attr " + event.getName() + " added with value " + event.getValue());
    }

    public void attributeRemoved(ServletRequestAttributeEvent event) {
        System.out.println("attr " + event.getName() + " removed with value " + event.getValue());
    }

    public void attributeReplaced(ServletRequestAttributeEvent event) {
        System.out.println("attr " + event.getName() + " replaced with value " + event.getValue());
    }

}

Its Groovy version with the GServlet API looks like this:

MyServletRequestAttributeListener.groovy
import org.gservlet.annotation.RequestAttributeListener

@RequestAttributeListener
public class MyServletRequestAttributeListener {

    void attributeAdded() {
        println "attr $name added with value $value"
    }

    void attributeRemoved() {
        println "attr $name removed with value $value"
    }

    void attributeReplaced() {
        println "attr $name replaced with value $value"
    }

}

The implicit variables made available to a ServletRequestAttributeListener are as follows:

Variable Description

logger

Logger object

request

HttpServletRequest object

session

HttpSession object

context

ServletContext object

event

ServletRequestAttributeEvent object

name

attribute name

value

attribute value

8.5. HttpSessionListener

This interface is for receiving notification events about HttpSession lifecycle changes. Implementations of this interface are invoked at their sessionCreated method in the order in which they have been declared, and at their sessionDestroyed method in reverse order.

httpsessionlistener diagram

Below is a Java class that implements the HttpSessionListener interface:

MyHttpSessionListener.java
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class MyHttpSessionListener implements HttpSessionListener {

	public void sessionCreated(HttpSessionEvent event) {
	  System.out.println("session created");
	}

	public void sessionDestroyed(HttpSessionEvent event) {
	  System.out.println("session destroyed");
	}

}

Its Groovy version with the GServlet API looks like this:

MyHttpSessionListener.groovy
import org.gservlet.annotation.SessionListener

@SessionListener
public class MyHttpSessionListener {

	void sessionCreated() {
	   println "session created"
	}

	void sessionDestroyed() {
	   println "session destroyed"
	}

}

The implicit variables made available to a HttpSessionListener are as follows:

Variable Description

logger

Logger object

session

HttpSession object

event

HttpSessionEvent object

8.6. HttpSessionAttributeListener

This interface is for receiving notification events about HttpSession attribute changes. The order in which implementations of this interface are invoked is unspecified.

httpsessionattributelistener diagram

Below is a Java class that implements the HttpSessionAttributeListener interface:

MyHttpSessionAttributeListener.java
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class MyHttpSessionAttributeListener implements HttpSessionAttributeListener {

    public void attributeAdded(HttpSessionBindingEvent event) {
        System.out.println("attr " + event.getName() + " added with value " + event.getValue());
    }

    public void attributeRemoved(HttpSessionBindingEvent event) {
        System.out.println("attr " + event.getName() + " removed with value " + event.getValue());
    }

    public void attributeReplaced(HttpSessionBindingEvent event) {
        System.out.println("attr " + event.getName() + " replaced with value " + event.getValue());
    }

}

Its Groovy version with the GServlet API looks like this:

MyHttpSessionAttributeListener.groovy
import org.gservlet.annotation.SessionAttributeListener

@SessionAttributeListener
public class MyHttpSessionAttributeListener {

    void attributeAdded() {
        println "attr $name added with value $value"
    }

    void attributeRemoved() {
        println "attr $name removed with value $value"
    }

    void attributeReplaced() {
        println "attr $name replaced with value $value"
    }

}

The implicit variables made available to a HttpSessionAttributeListener are as follows:

Variable Description

logger

Logger object

session

HttpSession object

event

HttpSessionBindingEvent object

name

attribute name

value

attribute value

8.7. HttpSessionBindingListener

This interface is for receiving notification events about when an object is bound to or unbound from a session. This may be as a result of a servlet programmer explicitly unbinding an attribute from a session, due to a session being invalidated, or due to a session timing out.

httpsessionbindinglistener diagram

Below is a Java class that implements the HttpSessionBindingListener interface:

MyHttpSessionBindingListener.java
import javax.servlet.http.HttpSessionBindingEvent;
import javax.servlet.http.HttpSessionBindingListener;


public class MyHttpSessionBindingListener implements HttpSessionBindingListener {

    public void valueBound(HttpSessionBindingEvent event) {
        System.out.println("attr " + event.getName() + " bounded with value " + event.getValue());
    }

    public void valueUnbound(HttpSessionBindingEvent event) {
        System.out.println("attr " + event.getName() + " unbounded with value " + event.getValue());
    }

}

Its Groovy version with the GServlet API looks like this:

MyHttpSessionBindingListener.groovy
import org.gservlet.annotation.SessionBindingListener

@SessionBindingListener
public class MyHttpSessionBindingListener {

    void valueBound() {
        println "attr $name bounded with value $value"
    }

    void valueUnbound() {
        println "attr $name unbounded with value $value"
    }

}

The implicit variables made available to a HttpSessionBindingListener are as follows:

Variable Description

logger

Logger object

session

HttpSession object

event

HttpSessionBindingEvent object

name

attribute name

value

attribute value

8.8. HttpSessionActivationListener

Objects that are bound to a session may listen to container events notifying them that sessions will be passivated and activated. A container that migrates session between VMs or persists sessions is required to notify all attributes bound to sessions implementing this interface.

httpsessionactivationlistener diagram

Below is a Java class that implements the HttpSessionActivationListener interface:

MyHttpSessionActivationListener.java
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionActivationListener;

public class MyHttpSessionActivationListener implements HttpSessionActivationListener {

    public void sessionDidActivate(HttpSessionEvent event) {
        System.out.println("session activated");
    }

    public void sessionWillPassivate(HttpSessionEvent event) {
        System.out.println("session passivated");
    }

}

Its Groovy version with the GServlet API looks like this:

MyHttpSessionActivationListener.groovy
import org.gservlet.annotation.SessionActivationListener

@SessionActivationListener
public class MyHttpSessionActivationListener {

    void sessionDidActivate() {
        println "session activated"
    }

    void sessionWillPassivate() {
        println "session passivated"
    }

}

The implicit variables made available to a HttpSessionActivationListener are as follows:

Variable Description

logger

Logger object

session

HttpSession object

event

HttpSessionEvent object

8.9. HttpSessionIdListener

This interface is for receiving notification events about HttpSession id changes. The order in which implementations of this interface are invoked is unspecified.

httpsessionidlistener diagram

Below is a Java class that implements the HttpSessionIdListener interface:

MyHttpSessionIdListener.java
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionIdListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class MyHttpSessionIdListener implements HttpSessionIdListener {

    public void sessionIdChanged(HttpSessionEvent event, String oldSessionId) {
        System.out.println("the session id was "+oldSessionId);
    }

}

Its Groovy version with the GServlet API looks like this:

MyHttpSessionIdListener.groovy
import org.servlet.annotation.SessionIdListener

@SessionIdListener
public class MyHttpSessionIdListener {

    void sessionIdChanged() {
        println "the session id was $oldSessionId"
    }

}

The implicit variables made available to a HttpSessionIdListener are as follows:

Variable Description

logger

Logger object

session

HttpSession object

event

HttpSessionEvent object

oldSessionId

old HttpSession Id

9. Handling Request Parameters

The request parameters are sent along with the request. You can send them as part of the URL or as part of the body of an HTTP request. The request.getParameter() method is used to get the value of a request parameter from the request and returns a string. We can also get an array of parameter values with the request.getParameterValues() method which returns an array of strings.

ServletParameters.java
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException;
import java.io.IOException;

@WebServlet("/parameters")
public class ServletParameters extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String param1 = request.getParameter("param1");
        String param2 = request.getParameter("param2");
        String[] paramArray = request.getParameterValues("paramArray");
    }

}

You can still use the approach above but handling request parameters with the GServlet API is a simple as this:

ServletParameters.groovy
import org.gservlet.annotation.Servlet

@Servlet("/parameters")
class ServletParameters {

   void get() {
     String param1 = request.param1
     String param2 = request.param2
     String[] paramArray = request.paramArray
   }

}

10. Managing Attributes

An attribute is an object that is used to share information in a web application. Attributes can be SET and GET from one of the following scopes : request, session, application.

ServletAttributes.java
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.io.IOException;

@WebServlet("/attributes")
public class ServletAttributes extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setAttribute("attribute1","Attribute 1");
        System.out.println(request.getAttribute("attribute1"));

        HttpSession session = request.getSession()
        session.setAttribute("attribute2","Attribute 2");
        System.out.println(session.getAttribute("attribute2"));

        ServletContext context = request.getServletContext();
        context.setAttribute("attribute3","Attribute 3");
        System.out.println(context.getAttribute("attribute3"));
    }

}

You can still use the approach above but managing attributes with the GServlet API is a simple as this:

ServletAttributes.groovy
import org.gservlet.annotation.Servlet

@Servlet("/attributes")
class ServletAttributes {

   void get() {
      request.attribute1 = "Attribute 1"
      println request.attribute1

      session.attribute2 = "Attribute 2"
      println session.attribute2

      context.attribute3 = "Attribute 3"
      context.attribute3
   }

}

11. Working With JSON

Groovy comes with integrated support for converting between Groovy objects and JSON. The classes dedicated to JSON serialisation and parsing are found in the groovy.json package. You can get an insight of how to use them in the Groovy documentation. In the GServlet API, we have simplified the process of parsing and producing JSON in your servlets and filters as below:

JSON Generation
import org.gservlet.annotation.Servlet

@Servlet("/projects")
class ProjectServlet {

   List projects = []

   void init() {
     projects << [id : 1, name : "Groovy", url : "https://groovy-lang.org"]
     projects << [id : 2, name : "Spring", url : "https://spring.io"]
     projects << [id : 3, name : "Maven",  url : "https://maven.apache.org"]
   }

   void get() {
     json(projects)
   }

   void post() {
     def project = request.body
     projects << project
     json(project)
   }

}

Whenever, the content type of the request is set to application/json, you can use its body property to get the payload as Groovy object. Your servlet or filter can use as well the built-in json() method to send JSON data as a response.

12. Working With HTML and XML

The most commonly used approach for creating HTML and XML content with Groovy is to use a builder. Two implicit variables, html and xml, which are bound to an instance of a MarkupBuilder have been made available to your servlets and filters for that purpose.

HTML Generation
import org.gservlet.annotation.Servlet

@Servlet("/index.html")
class HtmlServlet {

  void get() {
     html.html {
       body {
         p("Hello World!")
       }
     }
  }

}
XML Generation
import org.gservlet.annotation.Servlet

@Servlet("/books")
class XmlServlet {

  void get() {
	 xml.books {
		 book(id: "1", name : "Groovy in Action")
		 book(id: "2", name : "Spring in Action")
		 book(id: "3", name : "Maven in Action")
	 }
  }

}

13. Working With RDBMS

The groovy-sql module provides a higher-level abstraction over the current Java’s JDBC technology and it supports a wide variety of databases. To set up a database with the GServlet API, you have to create in the webapp directory, a file named gservlet.properties.

gservlet.properties
db.driver : com.mysql.cj.jdbc.Driver
db.url : jdbc:mysql://localhost:3306/gservlet_examples
db.user : root
db.password : changeit
db.minPoolSize : 5
db.maxPoolSize : 10

For each request, an Sql connection is automatically created from a data source and made available to your servlets and filters through the implicit sql variable as below:

ProjectServlet.groovy
import org.gservlet.annotation.Servlet

@Servlet("/projects")
class ProjectServlet {

   void post() {
     def params = [1, 'Groovy', 'https://groovy-lang.org']
     sql.execute 'insert into projects (id, name, url) values (?, ?, ?)', params
   }

}
ProjectFilter.groovy
import org.gservlet.annotation.Filter;

@Filter("/*")
class ProjectFilter {

   void filter() {
      sql.eachRow('select * from projects') { project ->
         println "${project.name.padRight(10)} ($project.url)"
      }
      next()
   }

}

After each request, the close() method of the Sql object is automatically invoked to bring it back to the connection pool. Below is a summary of the database configuration properties:

Name Required Description

db.driver

true

database driver class name

db.url

true

database url

db.user

true

database user

db.password

true

database password

db.minPoolSize

false

database minimum pool size. The default value is 1

db.maxPoolSize

false

database maximum pool size. The default value is 3

14. Unit Testing

Unit testing servlets is difficult, therefore it is recommended to move the main business logic in the servlet into a separate class which has no dependencies on the Servlet API. The Groovy programming language comes with great support for writing tests. In addition to the language features and test integration with state-of-the-art testing libraries and frameworks, the Groovy ecosystem has born a rich set of testing libraries and frameworks. In its testing guide, you can take at a closer look at JUnit integration, Spock for specifications, and Geb for functional tests.

15. Groovydocs Generation

Groovydoc was introduced in 2007 to provide for Groovy what Javadoc provides for Java. It is used to generate the API documentation for the Groovy and Java classes that compose your project.

ProjectServlet.groovy
import org.gservlet.annotation.Servlet

/**
 * This servlet handles the management of projects
 *
 * @author John Doe
 */
@Servlet(value="/projects")
class ProjectServlet {

	/**
	 * the projects list
	 */
	List projects = []

	/**
	 * the init method
	 */
	void init() {
		projects << [id : 1, name : "Groovy", url : "https://groovy-lang.org"]
		projects << [id : 2, name : "Spring", url : "https://spring.io"]
		projects << [id : 3, name : "Maven", url : "https://maven.apache.org"]
		projects << [id : 4, name : "JMeter", url : "https://jmeter.apache.org"]
	}

	/**
	 * handles the GET method
	 */
	void get() {
		json(projects)
	}

	/**
	 * handles the POST method
	 */
	void post() {
		def project = request.body
		projects << project
		json(project)
	}

}

15.1. Maven

GMavenPlus is a rewrite of GMaven, a plugin that allows you to integrate Groovy into your Maven projects. This is the basic configuration to generate Groovydocs with this plugin.

pom.xml
<project>

	<dependencies>
		<dependency>
			<groupId>org.codehaus.groovy</groupId>
			<artifactId>groovy-all</artifactId>
			<version>3.0.6</version>
			<type>pom</type>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.codehaus.gmavenplus</groupId>
				<artifactId>gmavenplus-plugin</artifactId>
				<version>1.11.0</version>
				<executions>
					<execution>
						<goals>
							<goal>groovydoc</goal>
						</goals>
					</execution>
				</executions>
				<configuration>
					<sources>
						<source>
							<directory>${project.basedir}/src/main/webapp/groovy</directory>
							<includes>
								<include>**/*.groovy</include>
							</includes>
						</source>
					</sources>
					<links>
					  <link>
					   <href>https://docs.oracle.com/javase/7/docs/api/</href>
					   <packages>java.,javax.,org.xml.</packages>
					  </link>
					  <link>
					   <href>http://docs.groovy-lang.org/latest/html/gapi/</href>
					   <packages>groovy.,org.codehaus.groovy.</packages>
					  </link>
					</links>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

Run mvn gplus:groovydoc and your documentation will be generated in the target/gapidocs directory.

15.2. Gradle

With Gradle, You can use the Groovydoc tool to generate your project documentation, and the version that is used, is the one from the Groovy dependency defined in the build script.

build.gradle
repositories {
    mavenCentral()
}

dependencies {
    compile "org.codehaus.groovy:groovy-all:3.0.6"
}

apply plugin: 'groovy'

sourceSets {
	main {
		groovy {
			srcDirs = ['src/main/webapp/groovy']
		}
	}
}

groovydoc {
  use = true
  groovyClasspath = configurations.compile
  source = sourceSets.main.groovy
  link 'https://docs.oracle.com/javase/7/docs/api/', 'java.'
  link 'http://docs.groovy-lang.org/latest/html/gapi/', 'groovy.', 'org.codehaus.groovy.'
}

Run gradle groovydoc and your documentation will be generated in the build/docs/groovydoc directory.

16. IDE Support

16.1. Eclipse

To augment the editor features such as content assist, the GServlet API ships with a DSL Descriptor (DSLD) to describe the editing semantics in a way that can be interpreted by the Groovy Development Tools (GDT) which provides Eclipse support for the Groovy programming language.

servlet.dsld
contribute(currentType(annos: annotatedBy(Servlet))) {
	property name : 'logger', type : java.util.Logger, provider : 'org.gservlet.AbstractServlet'
	property name : 'config', type : javax.servlet.ServletConfig, provider : 'org.gservlet.AbstractServlet'
	property name : 'request', type : javax.servlet.http.HttpServletRequest, provider : 'org.gservlet.AbstractServlet'
	property name : 'response', type : javax.servlet.http.HttpServletResponse, provider : 'org.gservlet.AbstractServlet'
	property name : 'session', type : javax.servlet.http.HttpSession, provider : 'org.gservlet.AbstractServlet'
	property name : 'context', type : javax.servlet.ServletContext, provider : 'org.gservlet.AbstractServlet'
	property name : 'sql', type : groovy.sql.Sql, provider : 'org.gservlet.AbstractServlet'
	property name : 'out', type : java.io.PrintWriter, provider : 'org.gservlet.AbstractServlet'
	property name : 'html', type : groovy.xml.MarkupBuilder, provider : 'org.gservlet.AbstractServlet'
}

contribute(currentType(annos: annotatedBy(Servlet))) {
	delegatesTo type : org.gservlet.AbstractServlet, except : [
		'service',
		'doGet',
		'doPost',
		'doHead',
		'doPut',
		'doTrace',
		'doOptions',
		'doDelete'
	]
}

contribute(currentType(annos: annotatedBy(Filter))) {
	property name : 'logger', type : java.util.Logger, provider : 'org.gservlet.AbstractFilter'
	property name : 'config', type : javax.servlet.FilterConfig, provider : 'org.gservlet.AbstractFilter'
	property name : 'request', type : javax.servlet.http.HttpServletRequest, provider : 'org.gservlet.AbstractFilter'
	property name : 'response', type : javax.servlet.http.HttpServletResponse, provider : 'org.gservlet.AbstractFilter'
	property name : 'chain', type : javax.servlet.FilterChain, provider : 'org.gservlet.AbstractFilter'
	property name : 'session', type : javax.servlet.http.HttpSession, provider : 'org.gservlet.AbstractFilter'
	property name : 'context', type : javax.servlet.ServletContext, provider : 'org.gservlet.AbstractFilter'
	property name : 'sql', type : groovy.sql.Sql, provider : 'org.gservlet.AbstractFilter'
	property name : 'out', type : java.io.PrintWriter, provider : 'org.gservlet.AbstractFilter'
	property name : 'html', type : groovy.xml.MarkupBuilder, provider : 'org.gservlet.AbstractFilter'
}

contribute(currentType(annos: annotatedBy(Filter))) {
	delegatesTo type : org.gservlet.AbstractFilter, except : ['init', 'doFilter']
}

Make sure your Eclipse project has the Groovy nature and that the Groovy DSL support is activated. To leverage the full power of the Groovy Development Tools (GDT), it is highly recommended to add the groovy folder as source folder, so you can use its wizards to create your artifacts.

Groovy DSL support
The use of the default package is discouraged and the example below illustrates a simple packaging practice to follow throughout your development for a cohesive structure of your project.
ProjectDao.groovy
package dao

class ProjectDao {

   List projects = []

   ProjectDao() {
     projects << [id : 1, name : "Groovy", url : "https://groovy-lang.org"]
     projects << [id : 2, name : "Spring", url : "https://spring.io"]
     projects << [id : 3, name : "Maven",  url : "https://maven.apache.org"]
   }

}
ProjectServlet.java
package servlet

import org.gservlet.annotation.Servlet
import dao.ProjectDao

@Servlet("/projects")
class ProjectServlet {

   ProjectDao projectDao

   void init() {
     projectDao = new ProjectDao()
   }

   void get() {
     json(projectDao.projects)
   }

}

16.2. IntelliJ IDEA

In the other hand, in IntelliJ IDEA, we can write GDSL files to have code completion on the injected methods/properties and closures. GroovyDSL is a scripting framework with a domain-specific language designed to define the behavior of end-user DSLs as script files which are executed by the IDE on the fly, bringing new reference resolution and code completion logic into the scope of a project.

servlet.gdsl
def classContext = context(scope: classScope())

contributor(classContext) {
  if (hasAnnotation('org.gservlet.annotation.Servlet')) {
    property name: 'logger', type: 'java.util.logging.Logger'
    property name: 'config', type: 'javax.servlet.ServletConfig'
    property name: 'request', type: 'javax.servlet.http.HttpServletRequest'
    property name: 'response',type: 'javax.servlet.http.HttpServletResponse'
    property name: 'session', type: 'javax.servlet.http.HttpSession'
    property name: 'context', type: 'javax.servlet.ServletContext'
    property name: 'sql', type: 'groovy.sql.Sql'
    property name: 'out', type: 'java.io.PrintWriter'
    property name: 'html', type: 'groovy.xml.MarkupBuilder'
    delegatesTo(findClass('org.gservlet.AbstractServlet'))
  }
}

contributor(classContext) {
  if (hasAnnotation('org.gservlet.annotation.Filter')) {
    property name: 'logger', type: 'java.util.logging.Logger'
    property name: 'config', type: 'javax.servlet.FilterConfig'
    property name: 'request', type: 'javax.servlet.http.HttpServletRequest'
    property name: 'response', type: 'javax.servlet.http.HttpServletResponse'
    property name: 'chain', type: 'javax.servlet.FilterChain'
    property name: 'session', type: 'javax.servlet.http.HttpSession'
    property name: 'context', type: 'javax.servlet.ServletContext'
    property name: 'sql', type: 'groovy.sql.Sql'
    property name: 'out', type: 'java.io.PrintWriter'
    property name: 'html', type: 'groovy.xml.MarkupBuilder'
    delegatesTo(findClass('org.gservlet.AbstractFilter'))
  }
}

17. Framework Support

17.1. Spring Boot

You just need this configuration below to leverage the GServlet API in your Spring Boot application.

SpringConfiguration.java
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.gservlet.GServletApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Bean;

@Configuration
public class SpringConfiguration implements ServletContextInitializer {

	private GServletApplication application;

	@Override
	public void onStartup(ServletContext context) throws ServletException {
		application = new GServletApplication(context);
		application.startOnSpringBoot();

	}

	@Bean(destroyMethod = "stop")
	public GServletApplication servletApplication() {
		return application;
	}

}

If you want to reuse the data source configured by Spring, this is how you must proceed:

SpringConfiguration.java
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.sql.DataSource;
import org.gservlet.GServletApplication;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfiguration implements ServletContextInitializer {

	private GServletApplication application;

	@Override
	public void onStartup(ServletContext context) throws ServletException {
		application = new GServletApplication(context);
		application.startOnSpringBoot();

	}

	@Bean(destroyMethod = "stop")
	public GServletApplication servletApplication(DataSource dataSource) {
		application.setDataSource(dataSource);
		return application;
	}

}

To enable Spring autowiring, you can use an implementation of the ScriptListener interface like this:

SpringConfiguration.java
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.sql.DataSource;
import org.gservlet.GServletApplication;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.support.SpringBeanAutowiringSupport;

@Configuration
public class SpringConfiguration implements ServletContextInitializer {

	private GServletApplication application;

	@Override
	public void onStartup(ServletContext context) throws ServletException {
		application = new GServletApplication(context);
		application.addScriptListener(bean -> {
			SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(bean, context);
		});
		application.startOnSpringBoot();
	}

	@Bean(destroyMethod = "stop")
	public GServletApplication servletApplication(DataSource dataSource) {
		application.setDataSource(dataSource);
		return application;
	}

}
Your Groovy scripts must be created in the src/main/resources/groovy folder if your application is packaged as a jar. There is a minor issue as the gservlet.properties file is required for the Spring Boot application to start successfully. It will be fixed in the next releases. Just create it empty in the src/main/resources folder if you have not the requirement to set up a database connection.

17.2. Other Java web frameworks

If the target framework supports the @WebListener annotation of the Servlet API, no configuration is required since we ship with a ServletContextListener which starts and stops the application. This statement is valid for frameworks like JSF, Apache Struts.

StartupListener.java
package org.gservlet;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;

@WebListener
public class StartupListener implements ServletContextListener {

	private GServletApplication application;

	@Override
	public void contextInitialized(ServletContextEvent event) {
		application = new GServletApplication(event.getServletContext());
		application.start();
	}

	@Override
	public void contextDestroyed(ServletContextEvent event) {
		application.stop();
	}

}

18. Code Examples

We have created several code examples on GitHub to help beginners to learn and gain expertise at GServlet. Checkout the appropriate branch for the version that you are using.

Copyright @2020. Free use of this software is granted under the terms of the Apache 2.0 License.

20. Authors

GServlet was created by Mamadou Lamine Ba.

21. Contributing

Contributions of any type or any scope, drive the project forward. There are lot of ways to contribute, not just code. We provide more information about how to get involved in our contribute page.

22. Reporting or Fixing Issues

You can search for existing issues in order to fix them or to raise a new one. We use the GitHub’s issue tracker to keep a track of all our current issues.