Thursday, May 24, 2012

Vaadin applications and tabbed browsing


For Magnolia CMS 5 we're developing a new user interface using Vaadin and plenty of custom GWT (Google Web Toolkit) on the client side. One of the challenges that we've had to solve is support for tabbed browsing. Vaadin's core concept is that there's state on the server that represents the state of the UI and there's also state on the client that represents what's being displayed.

Given a running Vaadin application in one browser tab, what happens if the user copies the application's URL and opens a new tab with the same URL?

The new tab will use the same server side state and go from there causing the first tab to go out of sync. The problem is that there's now two clients using the same server side state. And here lies the problem:

How can we make sure each tab has its own state on the server when the server has no way of finding out which tab is sending it a request?

There's a fix suggested by the Vaadin folks that uses multiple windows within a Vaadin application where each tab is supposed to get its own window. In this context a window is a server side component representing a whole browser page. However, this has a number of flaws. The first obvious problem is that a Vaadin application is synchronized and processes requests one by one, so if there's a lengthy operation in one window then all windows are stalled. The second problem is how it uses the URL. When you open the application in a first tab the URL is the same as without the fix. It uses the default window. When you copy and paste its URL to a new tab Vaadin will automatically create a new window on the server and add its generated window name to the URL. So far so good, two tabs in the browser each using a different window component on the server side. But when the user copies the URL from the second tab, the one that includes a window name, there's nothing done to create a new window. As a consequence the newly created third starts using the same window causing the second tab to go out of sync.

Another possible solution could use the HTTP referer header, then the server could look at that and see which application the client is using. An id for the application would then be part of the URL. But the referer header is optional and there are browser plugins and proxy servers that removes it for privacy reasons so we don't want to depend on it being there.

The solution I came up with takes advantage of the fact that there's a property on the javascript window object called name that survives a page reload. As far as I now this is the only state that is kept when you navigate in a browser or reloads the page. We can use this to keep track of which application we're using. That is, the id of the server side state this browser tab is connected to. But the server still has no idea when it's serving a request.

Vaadin is a single page web application where the first request sends a bootstrap page that loads javascript which then drives the application by issuing ajax requests to the server. The same thing happens on reload or opening a bookmark. By adding a javascript snippet to this page that checks the window.name property we can direct the ajax calls towards a specific application on the server. In the bootstrap page we embed an application id suggested by the server. The client then decides if it wants to use it or if it wants to use an id it has placed in window.name.

Here's a simplified example of how it works:

public class MultipleBrowserWindowsApplicationServlet extends ApplicationServlet {

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        if (requestIsNotAnAjaxCall(request)) {

            // Generate a new application id that we'll suggest the client can use
            String applicationId = generateNewApplicationId();

            // Serve the bootstrap page with the suggested application id
            writeBootstrapPage(request, response, applicationId);
        } else {
            super.service(request, response);
        }
    }
}

The bootstrap page includes this script that does the trick:

<script type="text/javascript">
//<![CDATA[
  if (!window.name) {
    window.name = <application id suggested by the server>;
  }
  vaadin.vaadinConfigurations["ctxpathmagnoliavelvet-268765284"]["appUri"] += "/" + window.name;
//]]>
</script>

And voila, the client keeps track of which state on the server it's connected to and directs its ajax calls to it. This way it keeps using the same application on the server after a page reload or opening a bookmark and the user is free to copy and paste the URL in a new tab.

To finalize the solution there were a few things I had to solve.

The most problematic was that Vaadin creates and starts an application on the server before it sends the bootstrap page. This is problematic because it creates applications that are never actually used. It took some experimentation to get this solved properly. Fortunately Vaadin already supports creating and starting the application on the first ajax call if it hasn't already been done, so that part just worked.

Another problem was the 'restartApplication' parameter that is used to force the creation of a new application on the server. If the client always prefers the id it has in window.name that makes this parameter useless. To solve it I extended the bootstrap page a bit so it can force the client to use the suggested application id when necessary.

In summary, having state both on the server and on the client is a challenge when it comes to tabbed browsing. This solution works because it's a single page web application that's entirely driven by ajax after that first request.

The source code is available, posted on pastie.org for prettier formatting =) To use it change the servlet class in web.xml. Because the theme and the caption (the title of the page) is set in the bootstrap page and the application is started after the bootstrap page has been served those are set using init parameters to the servlet. Use a snippet like this:

  <servlet>
    <servlet-name>MyApplication</servlet-name>
    <servlet-class>info.magnolia.ui.vaadin.integration.servlet.MultipleBrowserWindowsApplicationServlet</servlet-class>
    <init-param>
      <param-name>theme</param-name>
      <param-value>myTheme</param-value>
    </init-param>
    <init-param>
      <param-name>caption</param-name>
      <param-value>My Application</param-value>
    </init-param>
  </servlet>

Wednesday, December 28, 2011

CMIS support for Magnolia CMS


This autumn I've had the opportunity to work on implementing CMIS support in Magnolia CMS. CMIS stands for Content Management Interoperability Services and is a specification administered by OASIS. The goal is to improve interoperability between content management systems and it achieves this by defining a model representing the content in a repository and a set of services for accessing it. The services come in two flavors, SOAP Web Services and REST-style services using ATOM-format.

The first version of Magnolia to have CMIS support will be 4.5. The scope of the first release is to expose the DMS in Magnolia over CMIS. This means that you will be able to download, manipulate and store content in Magnolia and access Magnolia content in other CMIS enabled systems.

The implementation is based on OpenCMIS. An open source framework for implementing both server and client -side implementations. It's one of the libraries developed by Apache Chemistry. Of special interest to us is the server binding to JCR. OpenCMIS implements the services which delegates to a binding. We have extended the JCR binding to match the repository layout we use in Magnolia and integrated the OpenCMIS authentication mechanism to use our security model.

In the process we encountered a few things we wanted to improve to make OpenCMIS adaptable to our repository layout. We developed this and contributed patches that were accepted by the active and focused team working on OpenCMIS. As a result our implementation is quite minimal and mainly consists of mapping the CMIS model onto our JCR layout. The value of OpenCMIS is impressive, an entire stack with both SOAP and REST that only needs to know how to interact with the specifics of a JCR layout.

The source code for the Magnolia CMIS Module is publicly available as always.

Tuesday, November 22, 2011

Blossom 1.2.3 & Blossom Data Extension 1.0 Released

Today I have released version 1.2.3 of Blossom and Blossom Data Extension 1.0, an extension for using Blossom style dialogs with the Magnolia Data Module. It introduces a new annotation @DataTypeDialogFactory which is used for creating dialogs for data module types. It has the same feature set as @DialogFactory and is used in the same way.

In this release the feature set TabBuilder is expanded adding support for the multi select control, the include control and for adding any control by type. It's now also possible to get the dialog and the DialogCreationContext from the TabBuilder.

This is an example of how a dialog used for the type 'category':
@DataTypeDialogFactory("category")
public class CategoryDialog {

    @TabFactory("Settings")
    public void settingsTab(TabBuilder tab) {
        tab.addEdit("name", "Name", "");
        tab.addEdit("maxResults", "Max results", "");
    }
}

To use the Data Extension you need to add it to your pom.xml
<dependency>
  <groupId>info.magnolia</groupId>
  <artifactId>magnolia-module-blossom-extension-data</artifactId>
  <version>1.0</version>
</dependency>

Add this line to your blossom-servlet.xml or equivalent.

<bean class="info.magnolia.module.blossom.extension.data.DataDialogExporter" />

And if you're using classpath scanning you'll also want to add an include-filter for the new annotation.

<context:component-scan base-package="package-containing-dialogs" use-default-filters="false">
  ...
  <context:include-filter type="annotation" expression="info.magnolia.module.blossom.extension.data.DataTypeDialogFactory"/>
</context:component-scan>

For more details see the Blossom Data Extension wiki page.

Monday, November 7, 2011

Webinar - How US Navy used Blossom to integrate Spring Applications with Magnolia CMS

I have some exciting news to share!

As many Blossom users are already aware, Campbell Ewald used Magnolia CMS and Spring Framework for building navy.com. On November 15th there will be a webinar on how they did it and how Blossom made their app integrations straightforward. It will be hosted by Campbell Ewald's Matt Dertinger, Magnolia's Sean McMains and myself.

The webinar is for Spring developers of all levels and you will learn how to develop content rich websites with Spring. It will take you through setting up your project and with examples show how Blossom solved real use cases for the navy. Matt will also show how they used it with RESTEasy and took advantage of Bean Validation (JSR-303).

Register now, I've been told it's starting to fill up already.

See you there!

UPDATE
The webinar recording is now online.

Sunday, May 8, 2011

Twitter and Spring Social in Magnolia

Tweets on 'summer' embedded in a page.
Integrations with social networks are everywhere. All the big social sites offer javascript widgets that you can easily drop into your pages and display things like recent tweets, facebook likes and so on. Though recently there's been some voices raised saying that these widgets sometimes don't blend nicely with the design of the rest of site. I've seen this especially on cms blogs reviewing high profile sites as they launch or relaunch with new designs.

So, last weekend I decided to find out how much work it would take to display a Twitter search using Spring Social with Blossom, the Spring integration for Magnolia CMS. Magnolia calls reusable components that you can snap into a page paragraphs. I wanted a paragraph that an author could add to any page to show relevant tweets about its topic. The author supplies the search query and the maximum number of tweets to display.On the right is the end result.

Twitter provides a RESTful API that can return JSON or ATOM. There's a lot of functionality in there and most of it requires OAuth authentication. For this example we'll do a simple search which doesn't require authentication. Spring Social offers an OAuth implementation that is compatible with Twitter and seems to be minimal effort to use. A twitter search using the API is done with a request like http://search.twitter.com/search.json?q=summer. Spring Social uses RestTemplate and parses the response using Jackson to return plain java objects representing the tweets.

Since Blossom is an extension of Spring Web MVC that lets Spring controllers be embedded into the content of a page the twitter search paragraph is implemented as a controller. The interface provided to the author to enable filling in the search query and the maximum number of tweets is provided by Magnolia using dialogs. By adding methods in our controller and annotating them Magnolia will call them when the dialog is to be displayed and for validation when its submitted. The view for displaying the tweets is a JSP.

In the source below you'll see that Spring Social makes things really easy since it will request the Twitter API and return the tweets with just one line of code.

@Controller
@Paragraph("Twitter")
public class TwitterParagraph {

  @RequestMapping("/twitter")
  public String twitter(ModelMap model, Content content) {

    String query = content.getNodeData("query").getString();
    int maxResults = Integer.parseInt(content.getNodeData("maxResults").getString());

    TwitterTemplate twitterTemplate = new TwitterTemplate();
    SearchResults searchResults = twitterTemplate.searchOperations().search(query, 1, maxResults);

    List<Tweet> tweets = searchResults.getTweets();
    model.put("query", query);
    model.put("tweets", tweets.subList(0, Math.min(tweets.size(), maxResults)));

    return "twitter";
  }

  @TabFactory("Settings")
  public void settingsTab(TabBuilder tab) {
    tab.addEdit("query", "Query", "Search phrase on Twitter");
    tab.addEdit("maxResults", "Max results", "Number of tweets to display at most");
  }

  @TabValidator("Settings")
  public void validateSettings(DialogTab tab) {
    if (!StringUtils.hasText(tab.getSub("query").getValue()))
      AlertUtil.setMessage("Query must not be empty");
    if (!NumberUtil.isPositiveInteger(tab.getSub("maxResults").getValue()))
      AlertUtil.setMessage("Max results must be a positive number");
  }
}

If you haven't used Magnolia or Blossom before this screenshot might be helpful to see what the dialog presented to the author looks like. The methods annotated with @TabFactory and @TabValidator are used to provide the user interface and validate the input.



And here's the source for the view.

<h2>Twitter</h2>
<div>Recently about <a href="http://twitter.com/search/${query}">${query}</a></div>
<ul class="tweets">
<c:forEach items="${tweets}" var="tweet">
    <li>
        <div>${tweet.text}</div>
        <div class="signature">
            <img src="${tweet.profileImageUrl}"/>
            <a href="https://twitter.com/${tweet.fromUser}">${tweet.fromUser}</a>
            <div class="date"><fmt:formatDate value="${tweet.createdAt}" pattern="d MMM HH:mm"/></div>
            <div style="clear:both;"></div>
        </div>
    </li>
</c:forEach>
</ul>

So how much work was it? I think I spent less than an hour setting it up and writing the java code, followed by a couple of hours on the view and css to get it decent enough to show in a blog post ;-)

To try it out you need to add their milestone repository since Spring Social has still to reach its 1.0 release. Here's the maven snippets you'll need.

<repository>
  <id>org.springframework.maven.milestone</id>
  <name>Spring Maven Milestone Repository</name>
  <url>http://maven.springframework.org/milestone</url>
  <snapshots>
    <enabled>false</enabled>
  </snapshots>
</repository>

<dependency>
  <groupId>org.springframework.social</groupId>
  <artifactId>spring-social-core</artifactId>
  <version>1.0.0.M3</version>
</dependency>
<dependency>
  <groupId>org.springframework.social</groupId>
  <artifactId>spring-social-twitter</artifactId>
  <version>1.0.0.M3</version>
</dependency> 

Monday, March 21, 2011

Spring Web MVC with Content

The idea that sparked me writing the Blossom Module for Magnolia CMS was to bring the CMS and especially the content into Spring Web MVC not the other way around. Controllers should be the building blocks when composing pages. The latest version 1.2.2 brings a small but significant piece to the puzzle. By adding a custom WebArgumentResolver you can add content objects to the argument list in annotated handler methods.

This is an example of a paragraph that displays a contact form where the author that created the page specified which office will be receiving the filled in form. The office is fetched from a database and passed to the view where details such as its address are displayed.

@RequestMapping("/contactForm")
public String contactForm(ModelMap model, Content page, Content paragraph) {
    model.put("office", officeDao.getOfficeById(paragraph.getNodeData("office").getString()));
    return "contactForm";
}

To enable it you need to add the BlossomWebArgumentResolver to your AnnotationMethodHandlerAdapter.

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <property name="customArgumentResolver">
    <bean class="info.magnolia.module.blossom.web.BlossomWebArgumentResolver" />
  </property>
</bean>

BlossomWebArgumentResolver provides support for a few more arguments that is handy to have handed directly to your handler method. They are: AggregationState, WebContext, Context, User and MgnlUser.


Thursday, December 23, 2010

Blossom 1.2 released

Magnolia Blossom 1.2 has just been released.

This release brings a bunch of new features and fixes two important bugs. A new sample is also available, its a webapp project that requires nothing more than a plain maven installation to start up.

The biggest new feature is spring integration in content2bean bringing dependency injection and AOP. This can  be used directly from your spring xml files to load beans from the repository. When changes are made in the repository the bean will be transparently reloaded. This allows for easy reconfiguration of a running application.

The new annotation @InitSaveHandler allows you to customize the savehandler to use for saving a dialog.

There's also a few additions that make writing controllers even easier. For instance you can get the Content object passed directly to your handler method and you can return redirects by returning the uuid prefixed with the name of the repository. As an example; return "website:" + content.getUUID(); would return a redirect to a page.

The sample is available in our subversion repository. Check out the sources and run mvn jetty:run-war to start it up. Consult the reference documentation for more details.

Cheers and merry X-mas!