AngularJS, Jersey, JSP and Java EE 6

The “topro” project (short for “topic proposer”, sorry I wasn’t very inspired on this) is a test that I made to play around with AngularJS. In the opposite to a lot of AngularJS examples on the net, the project implements the entire chain: AngualarJS on the browser side, REST to communicate with the application server, JAX-RS/Jersey as server-side REST implementation, JSP as templating system of the server-side front-end, Java EE 6 for server-side back-end. The project demonstrates many different things, some of them enumerated hereunder.

The choice of using JSP technology for templating may seem strange at first sight. First of all raises the question if we still need server-side templating while having client-side templating with AngularJS. Realistically, the answer is yes when it comes to more complex applications. A lot of things are easier (and smarter) be done on the server before actually delivering the pages: translations, transforming data, security role handling et al.

Concerning JSP, one has to admit that, despite of its age, it’s a great and proven technology. Pre-compiled into servlets, it’s fast compared to other Servlet-based technologies and the JSTL library provides most of what a happy programmer needs to perform efficient templating. Custom Tags come to rescue if this should not be enough. Obviously, Java code in the JSP pages has to be banished. If this discipline is not there, better use PHP then :)

For simplification, the project doesn’t use a database. Data is stored in a flat file de/serialised using the XStream library. You can pull the project and just run it (on Glassfish), without any installation.

The complete source code can be found here: https://github.com/imifos/sample-project-library/tree/master/topro-1

Some things discussed in this project:

*) How to use Jersey not only to implement REST based data resources, but also deliver (JSP) pages. This mechanism is not part of the Java EE 6 JAX-RS standard, but a Jersey specific implementation.

See web.xml and eu.lucubratory.topro1.front.res.page.MainPageResource

@Path("/res/pages")
@Produces(MediaType.TEXT_HTML)
@ManagedBean
public class MainPageResource {
   @GET
   @Path("main")
   public Viewable index(@Context HttpServletRequest request) {
      :
      return new Viewable("/main.jsp", null);
   }

*) How to configure Jersey in the web.xml file to differentiate REST from classic web server file accesses and how to define in which directory the JSP files are located. Since the WEB-INF directory is the only directory that cannot be accessed from the outside, this is the best place to store JSP files.

See web.xml

<filter>
   <filter-name>
      jersey
   </filter-name>
   <filter-class>
      com.sun.jersey.spi.container.servlet.ServletContainer
   </filter-class>
   <init-param>
      <!--  "Viewable" JSP root is placed in the /WEB-INF/jsp directory.
      This means that it's not accessible outside of the web app.
      There's no way to construct a URL that can retrieve these files. -->
      <param-name>
          com.sun.jersey.config.property.JSPTemplatesBasePath
      </param-name>
      <param-value>
          /WEB-INF/jsp
      </param-value>
   </init-param>
   <init-param>
      <!-- Declare what file type should be accessible thru Jersey without being interpreted as REST call.
      Escape img, js, css folders and *.jsp, *.html, *.xhtml from the Jersey filter -->
      <param-name>
          com.sun.jersey.config.property.WebPageContentRegex
      </param-name>
      <param-value>
          (/(img|js|css)/?.*)|(/.*.jsp)|(/WEB-INF/.*.jsp)|(/WEB-INF/.*.jspf)|(/.*.html)|
                                                                       (/favicon.ico)|(/robots.txt)
      </param-value>
   </init-param>
   <init-param>
      <param-name>
         com.sun.jersey.api.json.POJOMappingFeature
      </param-name>
      <param-value>true</param-value>
   </init-param>
   <init-param>
      <!-- Allow JAX-RS resources to use @RolesAllowed, @PermitAll and @DenyAll annotations (like EJBs) -->
      <param-name>
          com.sun.jersey.spi.container.ResourceFilters
      </param-name>
      <param-value>
          com.sun.jersey.api.container.filter.RolesAllowedResourceFilterFactory
      </param-value>
   </init-param>
</filter>

*) How to inject the org.codehaus.jackson library’s ObjectMapper and how to customise the serialiser. The ObjectMapper is documented as idempotent and thread safe – the perfect singletone.

See package eu.lucubratory.topro1.front.common.json

@Qualifier
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD,ElementType.PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface JsonMapper {
}

public class JsonMapperFactory implements Serializable {

    private static ObjectMapper customisedJsonMapper=null;

    public @Produces @JsonMapper ObjectMapper getJsonMapper(InjectionPoint ip) {

        if (customisedJsonMapper==null) {
           synchronized(JsonMapperFactory.class) {
              if (customisedJsonMapper==null) {
                  SimpleModule myModule = new SimpleModule("TOPRO1",new Version(1,0,0,null));
                  myModule.addSerializer(new ProposalTypeSerializer());
                  customisedJsonMapper=new ObjectMapper();
                  customisedJsonMapper.registerModule(myModule);
              }
           }
        }
        return customisedJsonMapper;
    }
:
}

public class ProposalTypeSerializer extends SerializerBase<ProposalTypes> {

     public ProposalTypeSerializer() {
        super(ProposalTypes.class, true);
     }

     @Override
     public void serialize(ProposalTypes value, JsonGenerator jgen, SerializerProvider provider)
                                                   throws IOException, JsonGenerationException {
         jgen.writeString(Translator.translate(value));
     }
}

*) How to define data REST resources in JAX-RS (Jersey)

See eu.lucubratory.topro1.front.res.topic.TopicsResource

@Path("/res/")
@ManagedBean
public class TopicsResource {

   @POST
   @Consumes(MediaType.APPLICATION_JSON)
   @Path("/topics")
   public void newTopic(@Context HttpServletRequest request, ProposalEntity proposal) {
      try {
         ...
      }
      catch (ValidationException ex) {
         ...
      }
      catch (SystemException ex) {
         throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ERROR);
      }
   }
:
}

*) How to use the ‘ServiceFacade’ and the ‘Service’ EJB patterns.

See package eu.lucubratory.topro1.domain.topic

@Stateless
@LocalBean
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class TopicFacade {

    @Inject TopicService topicService;

     public void newTopic(ProposalEntity topic) throws ValidationException, DatastoreFailureException {
         topicService.newTopic(topic);
     }
}

@Stateless
@LocalBean
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public class TopicService {

    public void newTopic(ProposalEntity topic) throws ValidationException, DatastoreFailureException {
       :
    }
}

Facades are thin transaction boundary beans and that always start a transaction which is then propagated to one or more services. The real power of this architecture becomes clear with more complex business operations distributed over multiple services.

Services are business logic EJBs that are executed inside of a transactions open by a Facade. Services handle business logic that is too broad to be placed directly in the concerned Entities. Services are never called by the front-end (this would trigger a transaction error), but always via a ServiceFacade.

On the client-side, we have…

*) How to use an application level controller (cf. ‘AppController’) to store global configurations. This avoids “polluting” the ‘$rootScope’ and allows to handle failed sub-controller pre-instantiation Promises. This top-level scope is inherited by all nested controller scopes, just like the ‘$rootScope’ is.

See main.jsp and controllers.js

<body ng-app="topro" ng-controller="AppController">
---
app.controller("AppController",function AppController($scope) {
    $scope.proposalTypes=initProposalTypes();
});

*) How to prepare data via JSP templating that is then passed into AngualrJS controllers.

See MainPageResource, main.jsp and controllers.js

public class MainPageResource {

   @Inject @RawJsonMapper ObjectMapper rawJsonMapper;

   @GET
   @Path("main")
   public Viewable index(@Context HttpServletRequest request) {

       List<ProposalType> proposalTypes=ProposalType.init();
       String jsonProposalTypes=null;

       try {
           jsonProposalTypes=rawJsonMapper.writeValueAsString(proposalTypes);
       }
       catch (IOException  ex) {
           throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
       }

       request.setAttribute("proposalTypes",jsonProposalTypes);

       return new Viewable("/main.jsp", null);
   }

---

main.jsp:
<body ng-app="topro" ng-controller="AppController">
   :
   <%-- EAGER DATA INITIALISATION --%>
   :
   <script type="text/javascript">
      function initProposalTypes() {
         return <c:out value="${proposalTypes}" escapeXml="false"/>
      }
   </script>
   :

---

app.controller("AppController",function AppController($scope) {
    $scope.proposalTypes=initProposalTypes();
});

*) How to define a Service/Factory, in this case to perform REST operations using the ngResources module of AngularJS. ‘ngResource’ is declared as a module dependency and injected by AngularJS.

See services.js

angular.module('topicsServices', ['ngResource']).
    factory('Topics',function($resource,AppContext){

       // Returns the topics rest resource, replacing POST by PUT on the update operation.
       // All other methods stay on the default behavior.
       // http://docs.angularjs.org/api/ngResource.$resource
       return $resource(AppContext+'/res/topics/:id', {}, { update: {method:'POST'} });
});

In this case, we declare the factory in an different module (i.e. ‘topicsServices’) than the main.jsp page module (i.e.’topro’) – this is optional. The factory() method of the module object registers a value factory, i.e. a Provider, under the name ‘topicsProvider’. Each time we inject the ‘Topics’ service, AngularJS looks for a ‘topicsProvider’ in its registry ($injector).

There is actually a difference between a Service and a Factory. A Factory represents an object that has a single method: get(). The result of this getter is injected. A Service on the other hand is an injectable constructor. It injects an entire singletone (!) object which can be used to share stuff, data, methods, behavior, event buses between scopes. Under the surface, Services use the Factory mechanism, which uses  the ‘$provide’ AngularJS service.

About dependency injection (DI), the question arises how to implement DI in a weakly-typed environment. Since one cannot rely on types to find what has to be injected (like in Java), AngularJS uses variable names instead. Injecting for example ‘$scope’ or ‘Topics’ in a controller makes AngularJS looking for a ‘$scopeProvider’ or ‘topicsProvider’ respectively ($injector). The $ sign is just part of the variable name and a naming convention to indicate that the information is coming from the AngulaJS framework. It’s the AngularJS namespace.

*) How to use a filter for data transformation (like translation):

See main.jsp and filters.js

   :
   <h2>Proposed Topics</h2>
   <div ng-repeat="topic in topics">
      <p style="font-size: 18px; font-style: oblique; font-weight:bold;">{{topic.title}}<p/>
      <p>{{topic.summary}}<p/>
      <p>{{topic.proposalDate | date:'yyyy-MM-dd'}}<p/>
      <p style="font-size: 9px; font-style: oblique; ">{{topic.comment}}</p>
      <p style="font-weight:bold;">{{topic.type | pronounce:proposalTypes}}</p>
   </div>
   :
---
app.filter('pronounce', function() {
    return function(id,idLabelArray) {
       for (var i in idLabelArray) {
         if (idLabelArray[i].id===id)
            return idLabelArray[i].label;
       }
       return 'no_label_for_'+id;
    }
});

It’s not possible (afaik) to inject the surrounding scope into a filter. By this, it’s not possible to take the translation reference data from the application scope, for example. This is rather a good design since filters should be kept independent of scopes. On the other hand, the ‘$rootScope’, which is the scope associated with the “ng-app” level element, can be injected.

For more complex operations that require scopes, it’s better to use services or scope methods directly.

Leave a Reply

Your email address will not be published. Required fields are marked *