Shipping MVC 1.0 into Glassfish 5

The new Java EE specification MVC 1.0 (JSR 371) and it’s reference implementation Ozark brings action-based MVC for Java EE 8 platform.

MVC 1.0 is a good friend for me because I always thought why can’t we return a view from Jax-RS resources when I develop RESTful Web Services. Thus, Ozark implements this wish by using JAX-RS and CDI together.

MVC 1.0 RI (Ozark) works together with Tomcat and Glassfish seamlessly. If we want to run MVC 1.0 apps on Tomcat, we need to put the following dependencies into our web application.

  • MVC 1.0 Spec API
    • Includes spec’s interfaces, annotations eg.
  • Ozark RI API
    • Implements Spec API with CDI and Jax-RS
  • Weld
    • CDI reference implementation
  • Jersey
    • Jax-RS reference implementation

However, if we want to run MVC 1.0 apps on Glassfish, we don’t need to put CDI and Jax-RS dependencies into our application archive anymore because Glassfish contains CDI and Jax-RS modules out of the box. So, that is enough to put MVC 1.0 Spec API and Ozark RI into our web application to run it on Glassfish.

However, Glassfish 5 is the reference implementation for Java EE 8 (JSR 366), so MVC 1.0 should be a bundled module in Glassfish 5. This blog is about how to ship MVC 1.0 into Glassfish 5 as a module.

Glassfish uses OSGI for the module system and Apache Felix as an OSGI implementation. So, we should generate an OSGI bundle for MVC 1.0.

OSGI bundle is just a jar file that can bundle one or more packages from different jars together and some metadata in a MANIFEST.MF file that describes the module details. For example let’s look at an OSGI bundle that I built for MVC 1.0 before going into details;

javax.mvc Click to zoom

You can see the above javax.mvc.* package for MVC 1.0 Spec API and org.glassfish.ozark.* package for Ozark RI. Then, Let’s look into the MANIFEST.MF file;

Manifest-Version: 1.0
Bnd-LastModified: 1458465458223
Build-Jdk: 1.8.0_66
Built-By: usta
Bundle-ManifestVersion: 2
Bundle-Name: Ozark 1.0 MVC RI Bundle
Bundle-SymbolicName: org.glassfish.ozark.javax.mvc
Bundle-Version: 1.0.0
Created-By: Apache Maven Bundle Plugin
Export-Package: javax.mvc;uses:="javax.mvc.engine,javax.mvc.security,jav
 ax.ws.rs.core";version="1.0.0",javax.mvc.annotation;uses:="javax.enterp
 rise.context,javax.ws.rs";version="1.0.0",javax.mvc.binding;uses:="java
 x.validation";version="1.0.0",javax.mvc.engine;uses:="javax.mvc,javax.s
 ervlet.http,javax.ws.rs.container,javax.ws.rs.core";version="1.0.0",jav
 ax.mvc.event;uses:="javax.mvc.engine,javax.ws.rs.container,javax.ws.rs.
 core";version="1.0.0",javax.mvc.security;version="1.0.0",org.glassfish.
 ozark;uses:="javax.enterprise.context,javax.inject,javax.mvc,javax.mvc.
 security,javax.ws.rs.core";version="1.0.0",org.glassfish.ozark.binding;
 uses:="javax.enterprise.context,javax.mvc.binding,javax.validation,org.
 glassfish.jersey.server.spi";version="1.0.0",org.glassfish.ozark.cdi;us
 es:="javax.annotation,javax.enterprise.context,javax.enterprise.context
 .spi,javax.enterprise.event,javax.enterprise.inject.spi,javax.mvc.event
 ";version="1.0.0",org.glassfish.ozark.core;uses:="javax.annotation,java
 x.enterprise.context,javax.mvc,javax.mvc.annotation,javax.ws.rs,javax.w
 s.rs.container,javax.ws.rs.core,javax.ws.rs.ext";version="1.0.0",org.gl
 assfish.ozark.engine;uses:="javax.annotation,javax.enterprise.context,j
 avax.inject,javax.mvc,javax.mvc.engine,javax.servlet,javax.servlet.http
 ,javax.ws.rs.container,javax.ws.rs.core";version="1.0.0",org.glassfish.
 ozark.event;uses:="javax.enterprise.context,javax.mvc.engine,javax.mvc.
 event,javax.ws.rs.container,javax.ws.rs.core";version="1.0.0",org.glass
 fish.ozark.jersey;uses:="javax.annotation,javax.servlet.http,javax.ws.r
 s,javax.ws.rs.container,javax.ws.rs.core,org.glassfish.jersey.internal.
 spi,org.glassfish.jersey.message.internal,org.glassfish.jersey.server.m
 odel";version="1.0.0",org.glassfish.ozark.security;uses:="javax.annotat
 ion,javax.enterprise.context,javax.mvc.annotation,javax.mvc.security,ja
 vax.ws.rs,javax.ws.rs.container,javax.ws.rs.core,javax.ws.rs.ext";versi
 on="1.0.0",org.glassfish.ozark.servlet;uses:="javax.servlet,javax.servl
 et.annotation,javax.ws.rs";version="1.0.0",org.glassfish.ozark.util;use
 s:="javax.enterprise.context,javax.enterprise.inject.spi,javax.ws.rs.co
 re";version="1.0.0"
Import-Package: javax.annotation;version="[1.2,2)",javax.enterprise.cont
 ext,javax.enterprise.context.spi,javax.enterprise.event,javax.enterpris
 e.inject,javax.enterprise.inject.spi,javax.inject;version="[1.0,2)",jav
 ax.mvc,javax.mvc.annotation,javax.mvc.binding,javax.mvc.engine,javax.mv
 c.event,javax.mvc.security,javax.servlet,javax.servlet.annotation,javax
 .servlet.http,javax.validation;version="[1.1,2)",javax.ws.rs;version="[
 2.0,3)",javax.ws.rs.container;version="[2.0,3)",javax.ws.rs.core;versio
 n="[2.0,3)",javax.ws.rs.ext;version="[2.0,3)",org.glassfish.jersey.inte
 rnal.spi;version="[2.21,3)",org.glassfish.jersey.message.internal;versi
 on="[2.21,3)",org.glassfish.jersey.server.model;version="[2.21,3)",org.
 glassfish.jersey.server.spi;version="[2.21,3)",org.glassfish.ozark,org.
 glassfish.ozark.binding,org.glassfish.ozark.cdi,org.glassfish.ozark.cor
 e,org.glassfish.ozark.engine,org.glassfish.ozark.event,org.glassfish.oz
 ark.jersey,org.glassfish.ozark.security,org.glassfish.ozark.util
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.8))"
Tool: Bnd-3.0.0.201509101326

There are some properties for an OSGI bundle that describes the bundle. The values are easily understandable but you may not understand Export-Package and Import-Package properties if you have not experience with OSGI.

Export-Package
Declares that this bundle includes those listed packages.

Import-Package
Declares that this bundle requires those listed packages.

Let me show you how I created a Glassfish Module for MVC 1.0. You can follow up with the same steps for the new Java EE specs to move it into Glassfish.

Step 1: Get MVC 1.0 and Ozark RI source codes

MVC 1.0 Spec APIs and Ozark RI codes are located at java.net.

git clone git://java.net/mvc-spec~git

git clone git://java.net/ozark~sources

When I look at the different Java EE Specs, some of them use a single repository that have Spec API and RI together and some of them use separated repos like MVC 1.0 and Ozark RI. We actually don’t edit MVC Spec API, we will only edit Ozark sources.

Step 2: Package as OSGI bundle

The majority of the Java EE specs use Maven as a build system. The first step here is to change <packaging>jar</packaging> to <packaging>bundle</packaging> in Ozark sources’ pom.xml because we want to ship our artifacts as an OSGI bundle instead of a classic jar.

Step 3: Enable Maven Bundle Plugin

After we change packaging type to the bundle, we must also enable Apache Felix’s bundle plugin in the pom.xml file.

<plugins>
...
    <plugin>
        <groupId>org.apache.felix</groupId>
        <artifactId>maven-bundle-plugin</artifactId>
        <extensions>true</extensions>
        <configuration>
            <instructions>
                <Export-Package>
                    javax.mvc.*,org.glassfish.ozark.*   (1)
                </Export-Package>
            </instructions>
        </configuration>
    </plugin>
...
</plugins>
1 maven-bundle-plugin ships javax.mvc and org.glassfish.ozark packages and their sub-packages into the bundle. javax.mvc package is the root package for MVC 1.0 Spec API and org.glassfish.ozark package is for Ozark RI.

Normally the steps are finished here but we need to do one more step for MVC 1.0;

Ozark RI strongly uses CDI ,but Glassfish doesn’t automatically register CDI beans declared in its modules. So, We need to tell Glassfish to discover CDI beans declared in Ozark by CDI extension mechanism.

Step 4: Help Discovering Ozark’s CDI Beans

Ozark has already a CDI extension named OzarkCdiExtension under org.glassfish.ozark.cdi package. This extension currently registers a new RedirectScope for MVC 1.0. The extension is registered in services/javax.enterprise.inject.spi.Extension file.

We can use the same OzarkCdiExtension extension to complete our work. Arjan Tijms gave me a hint to register annotated types in a CDI extension. The following CDIUtils#addAnnotatedTypes method registers one or more annotated types to discover them by the CDI container.

// org.glassfish.ozark.util.CDIUtils.java

/**
 *
 * @param beforeBean The BeforeBeanDiscovery.
 * @param beanManager The BeanManager.
 * @param types annotated types to register
 */
public static void addAnnotatedTypes(BeforeBeanDiscovery bf, BeanManager bm, Class<?>... types) {
    for (Class<?> type : types) {
        bf.addAnnotatedType(bm.createAnnotatedType(type), null);
    }
}

After writing this helper method, we can register Ozark’s annotated types in OzarkCdiExtension like below.

// org.glassfish.ozark.cdi.OzarkCdiExtension.java

public class OzarkCdiExtension implements Extension {

...

/**
 * Registers given annotated types to be scanned during bean discovery
 *
 * @param beforeBean The BeforeBeanDiscovery
 * @param beanManager The BeanManager
 */
public void register(@Observes BeforeBeanDiscovery beforeBean, BeanManager beanManager) {
    CdiUtils.addAnnotatedTypes(beforeBean, beanManager,

            // .
            MvcContextImpl.class,

            // binding
            BindingResultImpl.class,
            BindingInterceptorImpl.class,

            // core
            Messages.class,
            ModelsImpl.class,
            ViewableWriter.class,
            ViewRequestFilter.class,
            ViewResponseFilter.class,

            // engine
            FaceletsViewEngine.class,
            JspViewEngine.class,
            ViewEngineFinder.class,

            // security
            CsrfImpl.class,
            CsrfProtectFilter.class,
            CsrfValidateInterceptor.class,
            EncodersImpl.class,

            // util
            CdiUtils.class

    );
}

...

}

So Let’s bundle Ozark sources after completing Step 4 with mvn clean install -DskipTests command. We will see the bundle jar in the /target directory. Now, the generated bundle can be moved to the Glassfish /modules directory like any bundles moved here before.

The changes that I’ve done for that work can be seen at https://github.com/rahmanusta/ozark/commit/10d4cf610fad1fc90817d473b01d01dab353f38f

The Glassfish module generated with the current MVC 1.0 and Ozark sources is located at https://github.com/rahmanusta/ozark/releases/tag/v1.0. You should manually copy/paste the bundle jar to the Glassfish $GF_HOME/glassfish/modules directory to run MVC 1.0 applications.

Let’s test the module with a simple MVC 1.0 application.

Step 1
Download the latest Glassfish 5 nightly build

Step 2
Extract the downloaded zip and copy javax.mvc-1.0.jar module to the $GF_HOME/glassfish/modules directory. Change directory to the /bin directory in terminal and run the following asadmin command to start Glassfish 5.

asadmin start-domain
Step 2
Clone the demo application or download it;

git clone https://github.com/rahmanusta/mvc-demo.git
Step 3
Build project with the given Maven command in the mvc-demo directory.

mvn clean install
Step 4
You will see the mvc.war file located at the mvc-demo/target directory. Run the following command to deploy demo MVC 1.0 app to the Glassfish 5.

asadmin deploy target/mvc.war

If you look into mvc.jar file with an archive application (Winrar, 7zip eg.) then you will not see any jar dependency inside it. Because, Glassfish 5 has all required modules anymore.

Step 5
Browse http://localhost:8080/mvc/app/person location and test the application.

mvc demo.png

 

Let’s follow the Java EE Guardians. I hope to see you again.

Bir Cevap Yazın