16 February 2011

HIPPOs RESTful JAX-RS Component Support and Spring Android

The new Hippo CMS 7.5 release brings some quite interesting features. The most interesting new feature for me was support for RESTful components within the Hippo Site Toolkit (HST-2 v2.20.01). Being able to expose data in a RESTful manner opens up a whole new set of possibilities for external application developers.

As you might have read in my previous post, I'm building a sample application to get acquainted with the Android platform. My previous post was mainly focussed on layouts and ListViews, but this time I will be focussing on information retrieval from an external REST service. That's why I've used the default REST service that comes with the online Hippo GoGreen demo as my source of information.  The GoGreen REST service exposes a list of 'top products' with additional information about the products that can be used nicely for this demo project, but first let's start at the beginning.

Getting started with RESTful HST-2 components

From what I've seen in the documentation and in the GoGreen source code, there are two different methods of exposing data with the RESTful components.
  1. The data can be exposed based on the primary JCR NodeType of a resource inside the Hippo repository. The HST-2 sitemap will determine the URLs of the items based on the relative path of the items inside the repository. This approach can be done with the JaxrsRestContentPipeline.
  2. A sitemap item (or mount) can be configured as a JaxrsRestPlainPipeline. By doing so, the HST will try to match the request within a Jax-RS based resource provider component that handles all the (relative) URL matching from there on.   
    In this example I will use the JaxrsRestPlainPipeline approach, which is also used by the Hippo GoGreen demo to create the 'top products' resource. The response output of a REST pipeline can be in all kinds of different formats. For this example we will use JSON, but you can also use XML instead.

    Configuration


    The first step in the proces of setting up our own REST service is to create an HST mount. The configuration for our mount has to look something similar to :

    <sv:node sv:name="restapi">
      <sv:property sv:name="jcr:primaryType" sv:type="Name">
        <sv:value>hst:mount</sv:value>
      </sv:property>
      <sv:property sv:name="hst:alias" sv:type="String">
        <sv:value>restapi</sv:value>
      </sv:property>
      <sv:property sv:name="hst:authenticated" sv:type="Boolean">
        <sv:value>false</sv:value>
      </sv:property>
      <sv:property sv:name="hst:isSite" sv:type="Boolean">
        <sv:value>false</sv:value>
      </sv:property>
      <sv:property sv:name="hst:mountpoint" sv:type="String">
        <sv:value>/hst:hst/hst:sites/rest-live</sv:value>
      </sv:property>
      <sv:property sv:name="hst:mountsite" sv:type="String">
        <sv:value>site</sv:value>
      </sv:property>
      <sv:property sv:name="hst:namedpipeline" sv:type="String">
        <sv:value>JaxrsRestContentPipeline</sv:value>
      </sv:property>
      <sv:property sv:name="hst:roles" sv:type="String">
        <sv:value>everybody</sv:value>
      </sv:property>
      <sv:property sv:name="hst:showport" sv:type="Boolean">
        <sv:value>true</sv:value>
      </sv:property>
      <sv:property sv:name="hst:subjectbasedsession" sv:type="Boolean">
        <sv:value>true</sv:value>
      </sv:property>
      <sv:property sv:name="hst:types" sv:type="String">
        <sv:value>rest</sv:value>
      </sv:property>
    </sv:node>
    

    As you can see there is lot to configure for a mount, but I don not want to go into much detail. The next step is to setup an HST sitemap for this mount. In the configuration above, our mount uses a default namedpipeline of type  JaxrsRestContentPipeline , since we want to use a  JaxrsRestPlainPipeline, we can override the type of pipeline by specifying the hst:namedpipeline property on an HST sitemap item for this mount, for example for the sitemap item called 'topproducts'.

    <sv:node sv:name="topproducts">
      <sv:property sv:name="jcr:primaryType" sv:type="Name">
        <sv:value>hst:sitemapitem</sv:value>
      </sv:property>
      <sv:property sv:name="hst:namedpipeline" sv:type="String">
        <sv:value>JaxrsRestPlainPipeline</sv:value>
      </sv:property>
    </sv:node>
    

    Spring Configuration

    Now after we stored the HST-2 configuration in the repository, the next step is to register our new component as a plain resource provider in our website Spring configuration. We can do this by creating a file called custom-jaxrs-resources.xml in the src/main/resources/META-INF/hst-assembly/overrides/ folder of our Hippo site project with the following content.

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" 
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
      
      <import resource="classpath:/org/hippoecm/hst/site/optional/jaxrs/SpringComponentManager-rest-jackson.xml" />
      <import resource="classpath:/org/hippoecm/hst/site/optional/jaxrs/SpringComponentManager-rest-plain-pipeline.xml" />
      <import resource="classpath:/org/hippoecm/hst/site/optional/jaxrs/SpringComponentManager-rest-content-pipeline.xml" />
      
      <!-- Custom JAX-RS REST Plain Resource Providers to be overriden. -->
      <bean id="customRestPlainResourceProviders" class="org.springframework.beans.factory.config.ListFactoryBean">
        <property name="sourceList">
          <list>
            <bean class="org.apache.cxf.jaxrs.lifecycle.SingletonResourceProvider">
              <constructor-arg>
                <bean class="com.onehippo.gogreen.jaxrs.services.TopProductsResource" />
              </constructor-arg>
            </bean>
          </list>
        </property>
      </bean>
          
    </beans>
    

    With this configuration in place the HST-2 has knowledge of our custom resource and the TopProductsResource can start creating the response.

    Now let's take a look at our TopProductsResource.

    @Path("/topproducts/") 
    public class TopProductsResource extends AbstractResource {
      @GET
      @Path("/topproducts/")
      public List<ProductLinkRepresentation> getProductResources(@Context HttpServletRequest servletRequest, @Context HttpServletResponse servletResponse, @Context UriInfo uriInfo,
                @QueryParam("sortby") @DefaultValue("hippogogreen:rating") String sortBy, 
                @QueryParam("sortdir") @DefaultValue("descending") String sortDirection,
                @QueryParam("max") @DefaultValue("10") String maxParam) {
            
          List<ProductLinkRepresentation> productRepList = new ArrayList<ProductLinkRepresentation>();
          HstRequestContext requestContext = getRequestContext(servletRequest);
            
          try {
              Node mountContentNode = getNodeFromMount(requestContext);
              HstQueryResult result = getHstQueryResult(sortBy, sortDirection, maxParam, requestContext, mountContentNode);
              HippoBeanIterator iterator = result.getHippoBeans();
    
              while (iterator.hasNext()) {
                  Product productBean = (Product) iterator.nextHippoBean();
                    
                  if (productBean != null) {
                    ProductLinkRepresentation productRep = new ProductLinkRepresentation(requestContext).represent(productBean);
                    productRepList.add(productRep);
                  }
              }
          } catch (Exception e) {
            log.warn("Failed to retrieve top products. {}", e);        
            throw new WebApplicationException(e);
          }
            
          return productRepList;
      }
    }
    

    The TopProductsResource has a @Path("/topproducts/") annotation set on the class level. This is what's making the request to '/topproducts' being handled by this specific resource. As you can see the only other thing the resource does is perform the query from the getProductResources() method. Take a look at the full source code for more details on the TopProductsResource class.

    Response output


    Now that we've setup the configuration and put the component in place, let's take a look at our actual response. You can see what the response of the TopProductsResource is if you go to the following URL:

    http://www.demo.onehippo.com/restapi/topproducts?_type=json

    Note: the URL might not be available at the time you try it, because the GoGreen demo is restarted every 30 minutes with a fresh set of content. If the URL does not work try again in 5 minutes.

    Since we specified the response type as JSON, the actual response should look something like what is shown below. For readability I've removed some properties, but I guess you get the idea.

    [
      {
        productLink: "http://www.demo.onehippo.com/restapi/products/food/2010/07/organic-cotton-reusable-lunch-bag./"
        price: 34
        rating: 5
        smallThumbnail: "http://www.demo.onehippo.com/binaries/smallthumbnail/content/gallery/products/2010/06/organic-lunch-bag.jpg"
        localizedName: "Organic Cotton Reusable Lunch Bag"
        primaryNodeTypeName: "hippogogreen:product"
      },
      {
        productLink: "http://www.demo.onehippo.com/restapi/products/food/2010/07/birch-wood-compostable-cutlery./"
        price: 5
        rating: 4.25
        smallThumbnail: "http://www.demo.onehippo.com/binaries/smallthumbnail/content/gallery/products/2010/07/wooden-cutlery.png"
        localizedName: "Birch Wood Compostable Cutlery"
        primaryNodeTypeName: "hippogogreen:product"
      }
    ]
    

    As you can see the response is quite simple and contains an array of product items with their properties.
    If you want to know more about RESTful Component support there is a nice page on the HST-2 wiki. Now let's move on with the Android part of this post.

    Spring Android

    Android version 2.2 has native support for handling JSON. I tried that, but I recently discovered Spring Android. Spring Android is quite new and gives you an easy to use REST client. The reason I chose to use Spring Android is that it takes less code to handle requests then by doing it the native Android way with the default HttpClient. Now when we combining Spring Android with Jackson it makes working with JSON really easy. All you have to do is create a mapping class, so that Jackson knows how to map the response array.

    To be able to work with the JSON response we will need the following three libraries in our Android project.
    • spring-android-rest-template-1.0.0.M2.jar
    • jackson-core-asl-1.7.1.jar
    • jackson-mapper-asl-1.7.1.jar

    Using Spring Android


    For my Android application I've created a service class called ProductService.

    public class ProductService {
      private static final String RESTAPI_BASE_URI = "http://www.demo.onehippo.com/restapi";
      private static final String RESTAPI_RESPONSE_TYPE = "_type=json";
    
      public static ArrayList<Product> getAllProductsFromHippo() {
        ArrayList<Product> products = new ArrayList<Product>();
        RestTemplate restTemplate = new RestTemplate();
        
        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        //add the Jackson mapper for easy mapping of JSON to POJO's
        messageConverters.add(new MappingJacksonHttpMessageConverter());
    
        String url = RESTAPI_BASE_URI + "/topproducts./?" + RESTAPI_RESPONSE_TYPE;
    
        Product[] productsFromHippo = restTemplate.getForObject(url, Product[].class);
        products.addAll(Arrays.asList(productsFromHippo));
        return products;
      }
    }
    

    As you can see the getAllProductsFromHippo method uses the Spring Android RestTemplate in combination with the MappingJacksonHttpMessageConverter to map the JSON response to an array of Product classes. Let's have a closer look at a Product class.

    package org.onehippo.gogreen.android.data;
    
    import org.codehaus.jackson.annotate.JsonIgnoreProperties;
    import org.codehaus.jackson.annotate.JsonProperty;
    
    @JsonIgnoreProperties(ignoreUnknown = true)
    public class Product {
    
      @JsonProperty
      private String localizedName;
    
      public String getLocalizedName() {
          return localizedName;
      }
    
      public void setLocalizedName(final String localizedName) {
          this.localizedName = localizedName;
      }
    }
    

    The Product class is quite simple. It only contains the localized name (for now). To make sure the mapping succeeds, I've also added the annotation JsonIgnoreProperties , so that it will ignore unknown properties during the mapping phase.
    Now if we provide the list of Product items to the Android ArrayAdapter, which is used by our ListView we will see all the items in the list returned by the HST-2 REST service.

    Resources used

    The following resources were used to create this post:

    09 February 2011

    Working with Android Layouts and ListViews

    I've been the owner of an Android phone for about 5 months now. The thought of creating an application for the Android platform has appealed to me ever since. That's why I recently started with Android development as a learning project for the next couple of weeks. In this post I will start sharing my experience with developing Android applications.

    Getting started

    The basic thing, while starting out with a new technology, is getting to know the fundamentals. There are some great introduction and advanced videos by Google on how to develop applications for the Android platform. The use of proper tooling can also help out a lot on this part. Both Eclipse and IntelliJ has great support for developing Android application since IntelliJ 10 (and it's free to use).

    For this project I'm trying to create a native Android client based on the Hippo GoGreen mobile website. If you take a look at the mobile site, there are two main entry points for browsing the site: Products and Events. I started out with Events, where I wanted to create a list of event items and show the event with a nice calendar item on the left next to the title of the event. I wanted the end result to look something like:


    In Android you can create a screen/page by creating an Activity. Adding a ListView to an Activity is a matter of configuration. With Android you can define the layout of your View either by defining a piece of XML, or by writing the code in Java. For this example I use the XML notation.

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <ListView
            android:id="@android:id/list"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent" />
        <TextView
            android:id="@android:id/empty"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:text="@string/empty_events"
            android:gravity="center"
            android:textAppearance="?android:attr/textAppearanceMedium" />
    </FrameLayout>
    

    As you can see the layout contains a ListView and a TextView. You can probably guess what the ListView is for, but I've added the TextView to show a message with 'No events' to the end user if no events are found.
    Adding some text to a list item in a ListView is quite simple and there are some good examples available in the Android tutorials. When I got to the point where I needed to add the dynamic calendar, I really had to start digging into layout options. Android has several kinds of default layouts available.

    Hovering the text over an image might not be that hard for an experienced Android developer (and there might be other ways than how I solved it), but when you're first introduced to the Android layout system it might be a bit confusing sometimes. My main goal was to show the day of the month and an abbreviation of the month dynamically on top of the calendar image and this is how I did it.

    Customizing the list item view


    To be able to show a customized list item View we first need to create a snippet of XML that represents the layout of our list item.

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/list_item_event"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        style="@style/ListItem">
    
        <org.onehippo.gogreen.android.ui.view.SimpleCalendarView
            android:id="@+id/calendar_today"
            android:layout_height="fill_parent"
            android:layout_width="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_alignParentBottom="true"
            android:gravity="center_horizontal|top"
            />
    
        <TextView
            android:gravity="left"
            android:id="@+id/event_title"
            android:layout_alignParentRight="true"
            android:layout_alignParentBottom="true"
            android:layout_alignWithParentIfMissing="true"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@+id/calendar_today"
            style="@style/EventTitle" />
    </RelativeLayout>
    

    As you can see in the above snippet, I'm using a RelativeLayout for my list item. The nice thing about the RelativeLayout is that you can define the relative position of the TextView versus the SimpleCalendarView. In my case the text is to the right of the calendar item. In the above layout configuration the list item contains two elements: the calendar (which is our custom View component) and a TextView, which will contain the title.

    Creating the calendar view


    For the dynamic calendar I've created a custom View which will position my 'inner' views in such a way that both TextViews containing the day and month position nicely on top of the ImageView. Now let's take a look at some code.

    public class SimpleCalendarView extends FrameLayout {
    
        private ImageView calendarImageView = null;
        private TextView calendarMonthTextView = null;
        private TextView calendarDayTextView = null;
    
        public SimpleCalendarView(Context context) {
            super(context);
        }
    
        public SimpleCalendarView(Context context, AttributeSet attributeSet) {
            super(context, attributeSet);
    
            setUpImageView(context);
            setUpDayView(context);
            setUpMonthView(context);
    
            /* Add child views to this object. */
            addView(calendarImageView);
            addView(calendarMonthTextView);
            addView(calendarDayTextView);
        }
    
        private void setUpImageView(final Context context) {
            calendarImageView = new ImageView(context);
            calendarImageView.setImageResource(R.drawable.bg_calendar);
            calendarImageView.setScaleType(ImageView.ScaleType.FIT_XY);
        }
    
        private void setUpMonthView(final Context context) {
            calendarMonthTextView = new TextView(context);
            calendarMonthTextView.setTextSize(7);
            calendarMonthTextView.setTypeface(Typeface.DEFAULT_BOLD);
            calendarMonthTextView.setPadding(0, 4, 0, 0);
            calendarMonthTextView.setTextColor(Color.WHITE);
            calendarMonthTextView.setGravity(Gravity.CENTER_HORIZONTAL);
        }
    
        private void setUpDayView(final Context context) {
            calendarDayTextView = new TextView(context);
            calendarDayTextView.setTextSize(10);
            calendarDayTextView.setTypeface(Typeface.DEFAULT_BOLD);
            calendarDayTextView.setPadding(0, 13, 0, 0);
            calendarDayTextView.setTextColor(Color.WHITE);
            calendarDayTextView.setGravity(Gravity.CENTER_HORIZONTAL);
        }
     
        public void setDayOfMonth(final int day) {
            this.calendarDayTextView.setText(Integer.toString(day));
        }
    
        public void setMonth(final String month) {
            this.calendarMonthTextView.setText(month);
        }
    }
    

    If you look at the above code you can see that adding the ImageView and TextViews is quite straight-forward. Because the SimpleCalendarView extends a FrameLayout, all inner views are positioned to the top left of the View by default. By setting the gravity of both TextViews to CENTER_HORIZONTAL the text is positioned in the middle of the image. Now by setting some top padding, the day and month are put into place.
    So after all it wasn't that hard to do. You just have to get to know the possibilities of the different Layouts. For my own convenience I added two extra methods to the SimpleCalendarView, so that I can easily set the month and day for each event without having to call the individual TextViews.

    What's next?

    This has been an interesting lesson in working with layouts, views and lists. My next post will probably be about how to retrieve data from a remote server and use that data to feed the list.

    Resources used

    The following resources were used while trying to create this view: