Making Portlets Angular

This post is following on from our moves to separate out concerns when it comes to our portlet development.

Following on from my previous post on using JSON and ETag caching, where we focused on provide data for our portlet to render client-side, we then wanted to concentrate on making the UI render side more elegant. Typically to date we’d been using .JSP with Tags to render, and using jQuery to render the JSON to page. That ended up with fairly lengthy <script> and JSP files, which were frankly messy to look at.

Here’s a truncated example of the horror:

$( document ).ready(function() {
	    
	    var jsonurl = "/libraryonline/rest/summary";
	    
	    $.ajax({
	    	url: jsonurl,
	    	dataType: "text",
	    	
	    	success: function( data ){
	    		
	    		var json = $.parseJSON(data);
	    		
	    		$("#${n}libraryForm").attr("action",json.voyagerUrl);
	    		$("#${n}loginId").attr("value",json.barcode);
	    		$("#${n}lastName").attr("value",json.surname);
	    		
	    		$("#${n}loanTotal").html(json.totalLoans);
	    		$("#${n}requestTotal").html(json.totalRequests);
	    		$("#${n}fineTotal").html(json.totalFines);
	    		
	    		if(json.loanItems != null && json.loanItems.length > 0){
	    			/* populate loan items table*/
	    			
	    			var $itemTable = $("#${n}loanItemsTable");
	    			
	    			$.each(json.loanItems, function(key,entry){
	    				/* This is just getting silly */
	    				$itemTable.append("<tr><td>"+ entry.title +"</td><td>"+ entry.statusText +"</td><td>"+ entry.dueDateConverted +"</td></tr>");

	    			});
	    		}

I wanted to introduce some kind of templating for rendering the HTML, and declutter the JavaScript we’re adding into pages to do the render. There are many options here todo this, we settled on Angular.

Continue reading “Making Portlets Angular”

Unit testing AngularJS portlet with Maven and Jasmin

Recently, we have converted a few portlets to use AngularJS for the front end.  We have also used the Jasmine framework for unit testing. Since our portlets are already configured with Maven, we have added Jasmine support with jasmine-maven-plugin to the existing set up. This posts will walk you through the configuration required to run Jasmine tests with Maven.

Portlet directory layout

|—— Portlet
| |——– src
| | | ——– main
| | | | ——— webapp
| | | |————- js
                              app.js
| | | ——– test
| | |———— webapp
| | | |————- js
                              controllerSpec.js

Our angularJs files are contained in src/main/webapp/js directory. Jasmine spec files are placed in a test directory that reflects main.

Plugin configuration 

Based on the directory structure, we can configure jasmine-maven-plugin. we are using version 2.0 which requires Maven 3.1.x. This is an example plugin configuration for our portlet:

<build><plugins>
    …
    <plugin>
       <groupId>com.github.searls</groupId>
       <artifactId>jasmine-maven-plugin</artifactId>
       <version>2.0</version>
       <executions>
          <execution>
             <goals>
               <goal>test</goal>
             </goals>
          </execution>
       </executions>
 <!-- keep the configuration out of the execution so that the bdd goal has access to it -->
       <configuration>
           <jsSrcDir>src/main/webapp/js</jsSrcDir>
           <sourceIncludes>
               <include>app.js</include>
           </sourceIncludes>
           <jsTestSrcDir>src/test/webapp/js</jsTestSrcDir>
           <specIncludes>
               <include>*Spec.js</include>
           </specIncludes>
           <preloadSources>
               <source>webjars/angular.js</source>
               <source>webjars/angular-sanitize.js</source>
               <source>webjars/angular-mocks.js</source>
               <source>webjars/jquery.js</source>
           </preloadSources>
       </configuration>
 </plugin></plugins></build>
<dependencies>
 
<!-- jasmin maven plugin dependencies -->
 <dependency>
      <groupId>org.webjars</groupId>
      <artifactId>jquery</artifactId>
      <scope>test</scope>
 </dependency>
 <dependency>
      <groupId>org.webjars</groupId>
      <artifactId>angularjs</artifactId>
      <scope>test</scope>
 </dependency>
<!-- for headful tests
 <dependency>
      <groupId>org.seleniumhq.selenium</groupId>
      <artifactId>selenium-firefox-driver</artifactId>
      <scope>test</scope>
 </dependency>
 -->

 </dependencies>

<jsSrcDir>  points to diectory with your JavaScript, this will be your angular controllers, directives, filters, etc.

<sourceIncludes> specifies which files within jsSrcDir should be included and in which order (added sequentially as defined)

<jsTestSrcDir>  points to directory with your Jasmin specs

<specIncludes> specifies which spec files to include and in which order

<preloadSources> JavaScript files to be loaded before the application files and specs, added sequentially as defined; these should be all libraries that you need for your application e.g. angular files, jQuery etc. We can reference webjars files here as done in our example. Since we require them for the tests only, we reference them in the test scope.

Out of the box, jasmine-maven-plugin comes preconfigured with PhantomJS for headless tests. For headful tests, configure the plugin with a different driver, such as:

<webDriverClassName>org.openqa.selenium.firefox.FirefoxDriver</webDriverClassName>

Usage

Jasmine can be used for TDD (Test Driven Development). Run

mvn jasmine:bdd

This will fire off the browser at http://localhost:8234 and run the specs from the test directory whenever you refresh the page. This will give you an immediate feedback as you develop your angular controllers etc.

Running

mvn jasmine:test

will run the headless tests by default, unless configured otherwise.

Running

mvn clean test

will run all your tests in the portlet, including Jasmine headless tests provided there were no test failures before.

With this configuration we are now ready to write Jasmine specs for our portlets.

ETag and JSON data

Recently as part of an update project for our university portal MyEd (which runs on uPortal) there was an emphasis on moving our content to more client driven access to data. We wanted to separate out the data and presentation a bit more, and also cut down on the load and traffic which a big single server-side render would produce.

We wanted to use JSON as the data format as it is nice and lightweight, and easy to parse with existing Javascript libraries (like JQuery). We then wrote in static URLs into the uPortal portlets which would allow the currently authenticated user (and them alone) to access their own data.

Our portal is under a reasonably heavy concurrent load at any given time, so we wanted to explore caching of data to make sure we make any client side calls perform well under load.

Cache Headers versus ETag

Cache Headers are used to tell a browser to not re-request an object from the server until a certain time, typically by setting an expiry date. This avoids any traffic going to the server at all, which reduces load but can mean that changes to data are missed because the cache expiry date has not been reached.

ETagging is different, in that an ETag value is set in the header, for example:

ETag: "asb227873hva23456n"

When the browser re-requests data from the url it passes the ETag back to the server in an If-None-Match header, e.g:

If-None-Match: "asb227873hva23456n"

The server then uses the ETag to decide what to do, either to send an HTTP Status Code of 304 not modified (typically with a very short response), or refresh the data and return new information back to the client. This reduces the bandwidth required, but more importently allows the server to decide how and when to respond with fresh data.

In order to get the best performance, you would in most situations use both caching and ETag in order to limit high frequency client traffic to the server but also allow the server to mitigate load using the ETag. We found when using both that behaviour in our uPortal server alongside our load balancer led to unexpected results , so we opted to initially use ETagging only.

(As to why our load balancer was causing unexpected caching behaviour we’ll have to investigate later, and potentially write up another post in and of itself!)

Portlet modifications

So in the portlet itself (which is written in Java), we set the JSON data controller method to add in an ETag.

final String eTag = getETag(data);
final Date expiry = new Date(System.currentTimeMillis() + MAX_AGE_MILLIS);
        
session.setAttribute(SESSION_ATTR_CACHE_ETAG, eTag);
session.setAttribute(SESSION_ATTR_CACHE_EXPIRY, expiry);
response.setStatus(HttpServletResponse.SC_OK);
response.setHeader("Cache-Control", "must-revalidate");
response.setHeader("ETag", eTag);

Finally, we then added a check in the method for the ETag coming from the If-None-Match header:

final String ifNoneMatch = request.getHeader("If-None-Match");
 final String existingETag = (String)session.getAttribute(SESSION_ATTR_CACHE_ETAG);
 final Date existingExpiry = (Date)session.getAttribute(SESSION_ATTR_CACHE_EXPIRY);
 if (null != ifNoneMatch
 && null != existingETag
 && null != existingExpiry
 && ifNoneMatch.equals(existingETag)
 && System.currentTimeMillis() < existingExpiry.getTime())
 {
 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
 return null;
 }

The above code checks the passed in ETag, compares it with the one stored in the user session, and additionally compares it with an expiry tag, then responds with a NOT MODIFIED 304 if the tags match and the expiry hasn’t passed. The response is null which means it doesn’t have to query the underlying dataset to respond, and therefore the response time and the bandwidth used are dramatically reduced.

MyEd Updates 2014/5

One of the major projects over summer 2014 has been the update to MyEd. Behind the scenes we’ve moved from uPortal 3 to 4, although for most users the clearest changes are the excellent work done by Learning Teaching & Web on the new theme. The migration itself has taken months of effort, with many portlets (applications running within MyEd) essentially requiring to be completely rewritten for the new version. The configuration of the two systems are not directly compatible, and tools had to be developed to update and maintain configurations for over 100 channels (the small sectons seen by the user, such as “Learn” or “Staff Details”) across three different environments (development, test and live), testing each of these changes both in isolation and integrated into the complete system.

Many of these channels also depend on other applications (such as accommodation, event booking, Learn, etc.) which in some cases needed to be modified and those modifications tested. Extensive load testing was performed to ensure the systems would handle the very high load anticipated for any major university service at the start of term. Hopefully this helps to give an idea of the scale of the project.

So what next for MyEd? Mobile support was disabled in the current deployment, but a  project is currently underway to add support for mobile devices for a number of core parts of MyEd. I’m sure many will be pleased to know this is expected to include both campus maps and timetabling, with email, calendar, Learn and a number of other tools available at launch. Naturally both iPhone and Android platforms will be supported, with full details to follow.

Creating a skeleton Spring MVC Portlet

If you’re needing to create a new JSR-286 portlet, the Apereo Foundation (formerly JASIG), have provided a Maven Archetype for the creation of a skeleton portlet.

Essentially you can run the following command:

mvn archetype:generate -DarchetypeGroupId=org.jasig.portlet.archetype -DarchetypeArtifactId=jsr286-archetype

It will ask you for the group, artefact and package information, and will automatically create a skeleton Spring MVC Portlet for you.

See the using the uMobile Portlet Archetype article for full details.