The seed-web-core and its companion modules provide support for Web technologies, such as Servlets, WebSockets, embedded Web servers, serving static resources, etc…

Dependency

Web support requires the following dependency in your project:

<dependency>
    <groupId>org.seedstack.seed</groupId>
    <artifactId>seed-web-core</artifactId>
</dependency>
Show version
dependencies {
    compile("org.seedstack.seed:seed-web-core:3.3.0")
}

Running Web applications

With Undertow

By default, all SeedStack Web projects are running with the Undertow embedded Web server, using the following dependency:

<dependency>
    <groupId>org.seedstack.seed</groupId>
    <artifactId>seed-web-undertow</artifactId>
</dependency>
Show version
dependencies {
    compile("org.seedstack.seed:seed-web-undertow:3.3.0")
}

This dependency provides a launcher that will handle the Undertow startup and shutdown logic.

Undertow-based Web applications are started and stopped as described in this page.

In a container

You can choose to run your Web application in an external Servlet 3+ container. In that case, the seed-web-undertow module must be removed (if present) and the application must be packaged as a WAR.

See this guide to learn how to convert an Undertow-based project to WAR.

Servlets, filters and listeners

Servlets, filters and listeners can be detected and registered by SeedStack which makes them injectable and interceptable.

You can register a servlet by annotating your servlet class with @WebServlet:

@WebServlet("/myservlet/*")
public class MyServlet extends HttpServlet {
    ...
}

Similarly, you can register a filter by annotating your filter class with @WebFilter:

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

Also, you can register a listener by annotating your listener class with @WebListener

@WebListener
public class MyListener implements ServletContextListener {
    ...
}

Any class annotated with WebListener must implement one or more of the ServletContextListener, ServletContextAttributeListener, ServletRequestListener, ServletRequestAttributeListener, HttpSessionListener, HttpSessionAttributeListener or HttpSessionIdListener interfaces.

If you are running in a Web container also scanning those annotations, you need to disable the server detection to avoid getting an exception for duplicate registration.

WebSockets

Seed also integrates the Java API for WebSocket (JSR 356), allowing server and client endpoints to be injected. WebSocket support requires Java 7 and a compatible server to work.

Server endpoints

No specific configuration is required for server endpoint. Just declare a standard JSR 356 endpoint:

@ServerEndpoint(value = "/chat")
public class ChatEndpoint {

    @Logging
    private Logger logger;

    @Inject
    EchoService echoService;

    @OnOpen
    public void onOpen(Session session) {
        logger.info("Connected ... " + session.getId());
    }

    @OnMessage
    public void message(String message, 
                        Session client) throws IOException, EncodeException {
        for (Session peer : client.getOpenSessions()) {
            peer.getBasicRemote().sendText(echoService.echo(message));
        }
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        logger.info(String.format("Session %s close because of %s", 
            session.getId(), closeReason));
    }

    @OnError
    public void onError(Session session, Throwable t) {
        logger.error(t.getMessage, t);
    }

}

In this example, the endpoint receives a message and then broadcast it to all clients.

Client endpoints

Unlike server endpoints, client endpoints have to explicitly specify a SeedClientEndpointConfigurator in order to be managed by Seed.

@ClientEndpoint(configurator = SeedClientEndpointConfigurator.class)
public class ChatClientEndpoint1 {
    public static final String TEXT = "Client1 joins";
    public static CountDownLatch latch;
    public static String response;

    @OnOpen
    public void onOpen(Session session) {
        try {
            session.getBasicRemote().sendText(TEXT);
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

    @OnMessage
    public void processMessage(String message) {
        response = message;
        latch.countDown();
    }
}

Static resources

SeedStack provides static resource serving from the classpath and the document root with some benefits over the container default resource serving:

  • Automatic serving of pre-minified and/or pre-gzipped versions of resources,
  • On-the-fly gzipping of resources,
  • Cache friendly.

Behavior

The behavior is to serve resources located under the document root folder and, if not found, under the META-INF/resources classpath location on the /* path. For example, consider the following folder tree:

src/main/webapp
    index.html
    robots.txt

META-INF
    resources
        robots.txt
        lib
            jquery.js
            jquery.min.js
            jquery.min.js.gz

The default behavior is to serve index.html, robots.txt and jquery.js on the following paths:

/robots.txt
/index.html
/lib/jquery.js

The jquery.js file will be served as a minified and gzipped version (without the overhead of on-the-fly gzipping since a pre-gzipped version is already available).

Static resource serving is enabled by default. Resources from document root are always served in priority over classpath resources.

Configuration

web:
  static:
    # If true, static resource serving is enabled
    enabled: (boolean)
    
    # If true, minification support is enabled, serving *.min files instead of regular ones if possible
    minification: (boolean)
    
    # If true, gzip support is enabled, serving *.gz files instead of regular ones if possible
    gzip: (boolean)       
     
    # If true, resources are gzipped on the fly, unless an already gzipped version (*.gz) exists
    gzipOnTheFly: (boolean)
    
    # The size of the buffer used for send static resource data
    bufferSize: (int)

To dump the web.static configuration options:

mvn -q -Dargs="web.static" seedstack:config

MIME types

The following MIME types are automatically derived from extensions:

Mime typeExtensions
text/htmlhtml htm HTML HTM
text/plaintxt text TXT TEXT
text/javascriptjs JS
text/csscss less CSS LESS
image/gifgif GIF
image/jpegjpeg jpg jpe JPEG JPG JPE
image/pngpng PNG
image/vnd.microsoft.iconico ICO
application/pdfpdf PDF
application/jsonjson JSON
application/font-woffwoff WOFF
application/vnd.ms-fontobjecteot EOT
font/truetypettf TTF
font/opentypeotf OTF

Caching

Resource lookup mechanism try to find resources in the following order:

  1. Gzipped minified version,
  2. Gzipped version,
  3. Minified version,
  4. Normal version.

Once a resource is found, its metadata (but not the contents) is cached to avoid unnecessary lookup. This cache can be configured as below:

web:
    static:
        cache:
          # Maximum concurrent cache updates allowed
          concurrencyLevel: (int)
          
          # Maximum number of cache entries
          maxSize: (int)
          
          # Initial number of cache entries
          initialSize: (int)

To dump the web.static.cache configuration options:

mvn -q -Dargs="web.static.cache" seedstack:config

CORS

Cross-Origin Resource Sharing (CORS) is supported through a Java filter and can be enabled in any Seed application.

Seed integrates the CORS filter from dzhuvinov software. There is no need to install and configure the filter manually, it is automatically registered by Seed. All filter options can be specified through configuration properties.

Configuration

CORS can be enabled and configuration as below:

web:
    cors:
      # The servlet path mapping on which CORS will be active
      path: (String)
     
      # If true, Cross-Origin-Resource-Sharing (CORS) will be enabled
      enabled: (boolean)
      
      # Allows to specify custom properties to the CORS filter
      properties: 
        key: (String)

To dump the web.cors configuration options:

mvn -q -Dargs="web.cors" seedstack:config

CORS filters properties can be specified according to its documentation, but without the cors. prefix (e.g. tagRequests, supportedMethods, …).