Using EXTJS with Liferay

General Blogs March 6, 2014 By Vitor Silva Staff

As promised this blog post is a continuation of the previous one about RESTFul services (check the related blog)

On this blog post I will explain how to integrate an external javascript library - EXTJS with Liferay, combined with our Permission System.

I am going to use the same RESTFul APIs developed previously to provide the CRUD operations and use EXTJS to invoke these APIs via AJAX in order to build the UI.

Before starting to do this you should ask yourself:

 - Why do I want to use a third party library like EXTJS

 - Do I really need this library

 - Do I really really need this library

More than often the answer can be "no". Have in mind Liferay is build using AlloyUI and you have available the components of YUI so as a rule of thumb I would say, if possible, use the frameworks available.

This is for several reasons: 

 - We support AUI and have created all the components to meet our requirements

 - Using AUI will make your portlets look exactly the same so you gain consistency (think about if you want for example to have your portlets available in the marketplace or not)

 - AUI is integrated with our Liferay Themes engine

 - Your team of developers will need to understand AUI (at least how to use it) as it is everywhere in our source code, without knowing it you will be in trouble if you need to extend our code so you need to learn it anyways

But in some cases this might not be enough for you, so you need to look at other options.

Common frameworks of choice for the UI are JQuery, JSF, Vaading, GWT, etc

I am going to use EXTJS because I don't see a lot of examples elsewhere and it is a really good framework.

Take a look at the framework here

It has great support for dashboards and it is in general really powerful, so it might be a good choice for you.

Lets just have in mind a couple of things about javascript with portlets:

 - Sandbox your code - remember that you might have other portlets in a page so your code should not interfere with others

 - Namespace your functions - same reason, by using the portlet namespace for your functions and divs you are keeping your javascript code safe.

 - Put your javascript where it needs to be: in a js file if possible

Before proceeding I need to make a statement that I am not a javascript expert. There are a ton of liferay employees that can probably write the javascript code better than me, my point here is just to prove the concepts..

So you start by downloading the community version of ExtJS and put the files in a js folder inside my project.

The next thing I need to do is import the Ext files required to use them in my portlets. I will do that on my liferay-portlet.xml

ExtJs has several versions of the main js file you can import into your project. We are using the debug with comments version, so it is really easy to debug the extjs files and you can also read the comments.

In production do not use this version because it is a big js file, you have the trimmed version without comments you can import.

My project structure will consist on one init.jsp (using Liferay pattern of having a shared jsp with the imports), one main page called defaultView.jsp and two additional jsp one for the RESTFul example and another with mock data showing a complex dashboard running inside the portlet

for each of the two jsp I have created a javascript file where all the EXT code is.

On my main jsp I have two buttons that redirect to the two jsps depending on what you want to see.

So for that I created a couple of renderURL and assigned them with two aui buttons. This way if you click on RESTFul Data button you will se an EXT grid that is using the RESTFul APIs described in my last post, and if you click on Sample Form Dashboard you will see a complex dashboard using sample data.

I now need to tell Spring MVC to redirect me to the appropriate page depending on the renderURL selected

My next step is to develop the code for the Sample Form Dashboard javascript file. This code is just copied from one of the EXTJS examples online. The only difference is that I pass the divId as a parameter (the div that EXTJS should use to render the content). 

The javascript code is really extensive so you can find the js file here

See how easy it is to combine several different types of ext components into one screen, and quickly produce extremely complex UI

I then developed my FormDashboard.jsp to go along with it

 

Notice how I combine the use of jstl variables with javascript and jsp, passing my portlet namespace into the javascript function. 

In my init.jsp I defined the variables I need. And I don't use scriptlets because I have them and I think it polutes the code ( strange for a Liferay guy but true)

If you click on Sample Form Dashboard this is what you will see:

If you click on a company EXT updates the graphs to show the company value. This is a really awesome dashboard all in javascript!

So after this I created the RESTFulData.js code. I chose a Grid (similar to a data table) to present all of the existing Samples, allowing the users to Delete, Update and Create samples as well (see code here).

I start by defining a model, basically my JSON object HelloSample that you have seen in my previous post

EXT has great support for rest calls, by just defining a proxy and integrating it inside a Grid you are half done.

Also i use my config variable to obtain the permissions the user has. After defining my json store I just need to define my grid panel.

Notice how the columns are easy to define. Finally I add dockedItems to my grid panel to allow Add and Delete functions

 

Afterwards I just need to import the js file in my RESTFulData.jsp

Never mind about the permissions we are going to cover that next, but notice how I import the external JS file and namespace my js variables. The function itself is not namespaced so in the real world I would create an ext component instead of a function. The component could receive every configuration as parameters this way I would have sandboxed my code. But this is still safe, I use my portlet name as part of the function identifier so there is no issue about having two functions on the same page with the same name. If we add this portlet twice we simple use the same javascript function twice to create two grids but render to different ids so it will work anyways.

For this implementation I decided to create a config variable where I will add all of my runtime variables I will need in javascript (like portletUrls, permissions, etc) and pass that object into my javascript function

Now I need to tell EXT to disable or hide the buttons for CRUD operations the user does not have access to and so for that I will start by defining the liferay permissions I require. So in my source folder I created a folder called resource-actions and a file inside called default.xml

These are the permissions active for this portlet:

I then load the permissions to jstl variables on my jsp, in order to know what a user can or cannot do and pass these permissions to my config javascript var that goes straight into my JS function.

In my ext code I disable the buttons and operations a user is not entitled to do

Create:

Update:

Delete:

Remember you need to add the permissionChecking also to your service files, this way if a user does not have access to delete Samples he cannot invoke the REST service as well.

And here it is:

The grid:

If you have permissions you are able to add entries by pressing the add button

Notice the nice inline editing of EXT grid

If you select one row you are able to delete the entry (if you have permissions)

If you double click on a row you are able to change the content on the columns 

So as you can see it is not that hard to integrate an external third party JS framework, even a really complicated one like EXTJS.

I hope you liked it

 

 

 

Using RESTFul services with Liferay

General Blogs February 24, 2014 By Vitor Silva Staff

I will start a series of blog posts describing on how to use third party frameworks integrated with Liferay, based on my past experience as a core engineer, team leader, solution architect and consultant on large enterprise projects. I was often faced with challenges on how to integrate Liferay with frameworks such as Spring, while wanting to take advantage of our Liferay Service Builder for obvious reasons.

On this blog post I will describe step by step how to create RESTFul services using Liferay, Spring MVC, Liferay Service Builder and Maven, and how to leverage Spring framework, integrating that with our Permission System, and testing it all with SOAP UI. You can find the source code here

Some of you might ask, why RESTFul when we could use JSON WS out of the box?  My standard answer is that you should use as many out of the box features as possible

But in large projects using standard Liferay MVC with Remote Services might not be efficient or sometimes in projects you are faced with customer requirements regarding their current architecture.

Or sometimes choosing RESTFul services might be simply an architecture decision because in fact it provides a clean, smart, friendly and optimized solution. RESTFul services are described using a WADL file so all of your frontend can be looked at as one big standard API, and in this case using JSON.

For the ones that have never worked with RESTFul services take a look at the wikipedia article

You can also know more about Spring RESTFul Web Services 

The basic concept that I will illustrate is how to perform an http GET, PUT, POST, UPDATE, DELETE for CRUD operations, and how to return the appropriate Http Status Codes, by performing exception handling

An experienced developer will want to take advantage of both Liferay and Spring and sometimes you are not sure on how to do it at all. Most Liferay examples tend to cover our standard architecture but as you know, you can develop portlets in pretty much you can think of and nowadays you can even plug the Application Display Templates framework to rewrite your UI using Freemarker or Velocity.

So in my sample project I will produce RESTFul services that will be consumed by custom portlets and other third party systems. These RESTFul services will interact with custom Liferay Services to provide CRUD operations .

I have decided to use Spring MVC as the Web Framework for this project for several reasons:

- Liferay uses Spring internally (in a real project a team of Liferay developers would need to know Spring to understand how Liferay works)

-Spring has great support for Portlets

-Spring has great support for RESTFul Web Services 

Before starting to build the solution we need to understand some things:

 - Liferay is using Spring internally and for all of the custom services using Service Builder (one needs to be extra careful with Spring versions)

 - Spring is a complex framework and in this project we will use Spring MVC for portlets (to produce a custom portlet) plus Spring MVC for servlets (to create a RESTFul API that can be consumed by the sample portlet and third party systems)

 - Although we are using MVC, by using Service Builder we are using AOP, IOC, etc so a lot of additional Spring modules

 - We need to keep in mind what Liferay provides out of the box and what Spring provides out of the box, and make decisions because there is some overlapping between features but what we want is to leverage Liferay and use Spring when needed, not override Liferay features with Spring ones (example Liferay Permission System vs Spring Security), we would be better off looking at both approaches and decide on how to to integrate both or choose just the Liferay bit.

Setting up the project:

1 - Maven

If you have already used or Liferay Developer Studio v 2.0 you can see how easy it is to create a Maven Project

You can read more about maven support from our guide

https://www.liferay.com/documentation/liferay-portal/6.2/development/-/ai/developing-plugins-using-maven-liferay-portal-6-2-dev-guide-02-en

There are also tons of blog posts you can read about maven in general and applied to Liferay

You can of course find the pom.xml together with the source code at my GitHub. Notice the Spring MVC dependencies and Jackson dependencies. Jackson is used as a framework that transforms objects into JSON format and vice-versa.

I have one active profile dev-local, but you can have several ones for different environments

This is extremely useful for deployments

If I want to deploy my maven project I simply execute the command:

mvn compile package liferay:deploy  -P dev-local

I have told before that we are using Spring MVC but which version should you use?

As a rule of thumb I would use the same Spring Version that Liferay is using

For 6.2 unfortunately it is still 3.0.7 which is a bit old but it provides all the support we  need for this project. Lets keep in mind we will be using Liferay Service Builder that is amazing, with all of the Spring files for transaction management, cache support, cluster awareness, indexing,  inline permissions,  soap and json APIs,etc is so positive that for me clearly it is worthy of having an older version in my project (its already version 3!) and having compatibility with Liferay generated source code. If you want to use a newer version you will face problems using our Service Builder also keep in mind possible classloader issues because we use Spring to do all of our heavy lifting and classloading magic.

2 - Spring MVC Configuration

We now need to setup Spring MVC for our Portlet and for our servlet that will serve RESTFul services

Portlet configuration is simple we just need to use the DispatcherPortlet and  define a contextConfigLocation that points to a valid Spring files.

Notice that the Spring Beans and controllers are all in the Portlet Web Application Context

We still need to configure the Web xml to add servlet support for spring plus the servlet view that will allow the portlet to serve a jsp when the renderRequest is performed (we will se about that later)

The Spring beans injected by this PortalDelegateServlet are loaded by the global Web Application Context. So after portlet.xml and web.xml configuration we now need to create the spring files for both the Portlet and the Servlet.

We will use annotations for the controllers so the application context xml is really simple


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:p="http://www.springframework.org/schema/p"

    xmlns:context="http://www.springframework.org/schema/context"

    xsi:schemaLocation="

        http://www.springframework.org/schema/beans

        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

        http://www.springframework.org/schema/context

        http://www.springframework.org/schema/context/spring-context-3.0.xsd">


<bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>

    <bean class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
  
<context:component-scan base-package="com.liferay.samples.portlet"/>    

<bean id="viewResolver"

     class="org.springframework.web.servlet.view.InternalResourceViewResolver" >

        <property name="prefix">

            <value>/WEB-INF/jsp/</value>

        </property>

        <property name="suffix">

            <value>.jsp</value>

        </property>

    </bean>

</beans>
 
The application context for the RESTFull servlet is a bit more complicated because of some details regarding content negotiation spring interceptors and custom jackson object mappers we need but it I am going to describe this beans when they are necessary.
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:mvc="http://www.springframework.org/schema/mvc" 

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

        http://www.springframework.org/schema/context 

        http://www.springframework.org/schema/context/spring-context-3.0.xsd

        http://www.springframework.org/schema/mvc

        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">


<context:component-scan base-package="com.liferay.samples.servlet" />


<mvc:annotation-driven />

<mvc:interceptors>

<mvc:interceptor >

<mvc:mapping path="/helloSample"/>

<mvc:mapping path="/helloSample/sampleId/*"/>

<bean class="com.liferay.samples.servlet.interceptor.SecureRequestHandlerInterceptor"></bean>

</mvc:interceptor>

</mvc:interceptors>



<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">

  <property name="mediaTypes">

    <map>

      <entry key="html" value="text/html"/>

      <entry key="json" value="application/json"/>

    </map>

  </property>

  <property name="viewResolvers">

    <list>

      <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver">

        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>

        <property name="prefix" value="/WEB-INF/jsp/"/>

        <property name="suffix" value=".jsp"/>

      </bean>

    </list>

  </property>

  <property name="defaultViews">

    <list>

      <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">

        <property name="prefixJson" value="true"/>

      </bean>

    </list>

  </property>

</bean>



    <bean

        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">

        <property name="messageConverters">

            <list>

                <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">

                    <property name="objectMapper" ref="jacksonObjectMapper" />

                </bean>

            </list>

        </property>

    </bean>


    <bean id="jacksonObjectMapper" class="com.liferay.samples.rest.json.LiferayObjectMapper" />

    

</beans>
 
3 - Using Liferay Service Builder to generate out CRUD operations

Create a service.xml inside the WEB-INF folder

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.2.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_2_0.dtd">

<service-builder package-path="com.liferay.samples.services">

<author>Liferay</author>

<namespace>SAMPLES</namespace>

<entity name="HelloSample" local-service="true" remote-service="true" >

<column name="sampleId" type="long" primary="true" />

<column name="sampleName" type="String" />

<column name="sampleAddress" type="String" />

</entity>

</service-builder>
We are going to generate Local services and Remote services, the remote services also provide Remote API's via SOAP and JSON

You can build the service using the UI button or execute the maven liferay:build-service goal

You can read more about Service Builder at:

https://www.liferay.com/documentation/liferay-portal/6.2/development/-/ai/what-is-service-builder-liferay-portal-6-2-dev-guide-04-en

Using Service Builder has several advantages for you:

- Generates all the spring files for IOC and AOP

- All of your custom services will respect the same patterns and look exactly the same as Liferay's.

- By using the same patterns you guarantee that you will be able to use all of the Liferay features in your portlet (Asset framework, Indexing, Permissions, Workflow, etc)

- It is really complicated to develop the same cache management, cluster awareness, performant architecture that the service builder gives you for free. Hibernate is there so you still have all the features you are used to. 

- Service Builder is going to evolve naturally as our platform evolves too, once you migrate to newer versions, new features will be available out of the box for your services (for example the Recicle Bin feature for 6.2) 

- You will save time and money.

 

4 - Portlet Controller

Because our controller will only be invoked when a renderPhase is invoked it is incredibly simple,  with just one method, returning a string. This string will be resolved into a view (jsp) using the configuration defined in the portlet application context.

package com.liferay.samples.portlet;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("VIEW")
public class SampleSpringMVCRESTFulPortlet {

@RequestMapping
public String initRESTFulApp(){
return "defaultView";
}

}

Define @Controller to tell spring to map this class as a controller

Define @RequestMapping("VIEW") to tell spring this is a Portlet controller in VIEW mode

Create a method and use the @RequestMapping to serve this method by default when the render phase is invoked.

5 - RESTFul  Controller 

This controller is serving all of the REST calls. There are a lot of things you need to consider when creating a RESTFul controller. The first thing is you need to know the annotations used in spring to map requests and also the annotations for http status and annotations for exception handling.

So these are the Http requests implemented in the controller

GET /helloSample -> returns all samples as json and http status 200 OK

GET /helloSample/sampleId/{sampleId} -> a id is passed as a parameter and returns one entity with http status 200 OK or if the entity does not exist returns the http status 404 NOT FOUND

POST /helloSample -> creates an entity (the entity is passed as json format ) and returns a 201 Created or  a 400 Bad Request if the creation fails

PUT /helloSample/sampleId/{sampleId} ->id passed as parameter plus entity passed as json format and returns 204 No Content or 400 Bad Request if the update fails

DELETE /helloSample/sampleId/{sampleId} -> id passed as parameter returns a 204 No Content or 404 Not Found if the entity id does not exist OR 400 Bad Request if the sampleID is empty

Note that the RESTFul "Specification" does not force you to return always the same codes in all cases, sometimes you can use PUT or POST to create or update entities. Here is the Controller implementation:

(...)

@Controller

public class SampleRESTFullController {

@RequestMapping(value = "/helloSample", method=RequestMethod.GET)
@ResponseStatus(HttpStatus.OK)
public @ResponseBody List<HelloSample> helloSample() throws SystemException {

try {

return HelloSampleServiceUtil.getAllHelloSamples();

} catch (SystemException e) {

throw e;

}

}

@RequestMapping(value = "/helloSample/sampleId/{sampleId}", method=RequestMethod.GET)
public @ResponseBody HelloSample helloSample(@PathVariable("sampleId") Long sampleId, HttpServletRequest request) throws NoSuchHelloSampleException, SystemException {

try {

return HelloSampleServiceUtil.getHelloSample(sampleId);

} catch (PortalException e) {

throw (NoSuchHelloSampleException) e;

} catch (SystemException e) {

throw e;

}
}

@RequestMapping(value = "/helloSample", method=RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
public @ResponseBody HelloSample createSample(@RequestBody HelloSampleImpl helloSample) throws NoSuchHelloSampleException, SystemException {

try {

HelloSample createdObject = HelloSampleServiceUtil.addHelloSample(helloSample);

return createdObject;

}  catch (SystemException e) {

throw e;

}

}

@RequestMapping(value = "/helloSample/sampleId/{sampleId}",method = RequestMethod.PUT)

@ResponseStatus(HttpStatus.NO_CONTENT)

void update(@PathVariable("sampleId") long sampleId,@RequestBody HelloSampleImpl helloSample) throws SystemException {

try {

helloSample.setSampleId(sampleId);

HelloSampleServiceUtil.updateHelloSample(helloSample);

} catch (SystemException e) {

throw e;

}

}


@RequestMapping(value = "/helloSample/sampleId/{sampleId}",method = RequestMethod.DELETE)
@ResponseStatus(HttpStatus.NO_CONTENT)
void delete(@PathVariable("sampleId") long sampleId) throws SystemException, NoSuchHelloSampleException {

try {

HelloSampleServiceUtil.deleteHelloSample(sampleId);

} catch (SystemException e) {

throw e;

} catch (PortalException e) {

throw (NoSuchHelloSampleException) e;

}

}

(...)

Notice in Spring code how simply you can return a list of objects just by adding the @ResponseBody

public @ResponseBody List<HelloSample> helloSample()

To obtain a variable passed as a query parameter you simply add a variable as a parameter and annotate it as a @PathVariable, Spring will do all the casting for you. (@PathVariable("sampleId") Long sampleId)

If you need to have the servlet request just add that to the method as a parameter otherwise don't.

Even more cool is the ability of Spring to create complex object based on a json variable, by simply adding 

@RequestBody HelloSampleImpl helloSample as a method parameter.

6.1 - Using the generated Entity as Model object

We will be tempted to use the generated entity as model object returned by the controller

It does not work out of the box

Luckily I can tell you how to do it.

The first thing is that Spring cannot use the generated interface so we need to pass the Implementation as a parameter

The second thing is that the implementation is not serializable

The third thing is  there are properties internally in the generated entities that we a) don't need to transform into JSON and b) cannot transform into JSON

6.1.1 - Serialization

So in order to use the entity implementation one will need to use the Jackson annotation @JsonIgnoreProperties and add that to the implementation to filter out all of the fields that are not necessary.

@JsonIgnoreProperties({ "expandoBridge", "expandoBridgeAttributes","cachedModel","escapedModel","modelAttributes","modelClass","modelClassName","new","primaryKey","primaryKeyObj" })

public class HelloSampleImpl extends HelloSampleBaseImpl implements Serializable{

(...)

}
6.1.2 - Deserialization
When there is a request sent to the server the JSON object is going to be transformed into the HelloSampleImpl using a number of different techniques by Spring. The Deserialization process will
We need to filter the fields we do not with to deserialize, and tell Jackson to ignore that altogether.
This is done by creating our own Jackson ObjectMapper where we will inject MixIn annotations in order to properly reimplement the canDeserialize method. 
 
package com.liferay.samples.rest.json;

import com.liferay.portal.model.BaseModel;
import com.liferay.portal.service.ServiceContext;
import com.liferay.portlet.expando.model.ExpandoBridge;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.DeserializerProvider;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.SerializerProvider;
import org.codehaus.jackson.type.JavaType;


/***************************************************************************

 * <b>Description:</b> An ObjectMapper to add mixIn annotations

 * This will make all the service builder entities valid for jackson 

 * deserialization

 * <b>Created:</b>20 Feb 2014 17:48:09  @author Vitor Silva 

 **************************************************************************/

public class LiferayObjectMapper extends ObjectMapper {


public LiferayObjectMapper() {

super();

}


public LiferayObjectMapper(JsonFactory jf, SerializerProvider sp,

DeserializerProvider dp, SerializationConfig sconfig,

DeserializationConfig dconfig) {

super(jf, sp, dp, sconfig, dconfig);

}

public LiferayObjectMapper(JsonFactory jf, SerializerProvider sp,

DeserializerProvider dp) {

super(jf, sp, dp);

}

public LiferayObjectMapper(JsonFactory jf) {
super(jf);
}

    
@Override

public boolean canDeserialize(JavaType type) {
DeserializationConfig desConfig = copyDeserializationConfig();
addMixInAnnotations(desConfig, type);
return _deserializerProvider.hasValueDeserializerFor(desConfig, type);
}


/***************************************************************************

* <b>Description:</b> Adds mix in annotations to filter out

* entity internal fields like expando that prevent deserialization

* 

* <b>Created:</b>20 Feb 2014 16:57:31  @author Vitor Silva 

* @param desConfig 

**************************************************************************/

protected void addMixInAnnotations(DeserializationConfig desConfig, JavaType type) {

desConfig.addMixInAnnotations(type.getClass(), IgnoreExpandoAttributesMixIn.class);


}


abstract class IgnoreExpandoAttributesMixIn

{


@JsonIgnore public abstract void setExpandoBridgeAttributes(ServiceContext serviceContext);

@JsonIgnore public abstract void setExpandoBridgeAttributes(BaseModel<?> baseModel);

@JsonIgnore public abstract void setExpandoBridgeAttributes(ExpandoBridge expandoBridge);

}



}
The MixIn annotations are added generically to all entities so it will work for all service builder entities from now on.
 
After that one needs to add the custom Object Mapper to Spring's Web Application Context configuration
 
(...)
 <bean

        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">

        <property name="messageConverters">

            <list>

                <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">

                    <property name="objectMapper" ref="jacksonObjectMapper" />

                </bean>

            </list>

        </property>

    </bean>


    <bean id="jacksonObjectMapper" class="com.liferay.samples.rest.json.LiferayObjectMapper" />

    (...)
 
6.2 - Exception Handling

By adding a @ResponseStatus(HttpStatus.OK) to a method we are basically stating that the method returns OK usually but if there is an exception we need to return a different Http Status and we can do that easily in Spring by creating exception handling methods that will catch the exceptions thrown out of original methods and return different Http Status codes

There is a generated exception by Service Builder called NoSuchHelloSampleException and we can add a spring ResponseStatus as annotation, meaning that if that exception is thrown Spring returns the Http code automatically as response of the REST call

(...)

/**
 * @author Liferay
 */

@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="No such HelloSample")

public class NoSuchHelloSampleException extends NoSuchModelException {

(...)
We can manipulate exceptions thrown from the service layer in the Controller class by adding annotated methods for exception handling
 
(...)
@ResponseStatus(HttpStatus.CONFLICT)  
@ExceptionHandler(SystemException.class)
public void handleConflict(SystemException se) {

    if(se.getCause() instanceof ORMException){

     throw (ORMException) se.getCause();

    }

}


@ResponseStatus(HttpStatus.BAD_REQUEST) 
@ExceptionHandler(ORMException.class)
public void handleBadRequest(SystemException se) {  

}

(...)
 
7 - Integrating Permission System
In order to integrate Liferay Permission System (for external access) we need to provide the ability to authenticate a user and initialize the permission system. This is a bit tricky we need to understand a couple of principles. How can we authenticate a user in Liferay in a web service call? What is the best way to do it? Should we use Spring security?
I based myself on SecureFilter implementation, that is used (or was in 6.1) to authenticate remote servlet requests. It knows how to initialize the necessary thread locals and what to add to the http session etc
So I will use most of the SecureFilter's code. Now it is just a matter to decide where to add it
To be fair I cannot say where is definitely the best place to use it (My gut feeling is Spring Security AccessDecisionVoter but it is not clear). I have decided not to integrate Spring Security. I have decided to create a HandlerInterceptor to intercept the REST requests and validate the users. In Spring framework you can add interceptors to pretty much anywhere, specially servlet requests
I then mapped some URLs to that interceptor using Spring
 
<mvc:interceptors>
       <mvc:interceptor >
           <mvc:mapping path="/helloSample"/>
           <mvc:mapping path="/helloSample/sampleId/*"/>
           <bean class="com.liferay.samples.servlet.interceptor.SecureRequestHandlerInterceptor"></bean>
       </mvc:interceptor>
</mvc:interceptors>
And the handler interceptor code (not fully tested):
(...)
@Override

public boolean preHandle(HttpServletRequest request,

HttpServletResponse response, Object handler) throws Exception {

if(METHODS_TO_CHECK.contains(request.getMethod().toUpperCase())){

if (_log.isDebugEnabled()) {

if (_httpsRequired) {

_log.debug("https is required");

}

else {

_log.debug("https is not required");

}

}


if (_httpsRequired && !request.isSecure()) {

if (_log.isDebugEnabled()) {

String completeURL = HttpUtil.getCompleteURL(request);


_log.debug("Securing " + completeURL);

}


StringBundler redirectURL = new StringBundler(5);


redirectURL.append(Http.HTTPS_WITH_SLASH);

redirectURL.append(request.getServerName());

redirectURL.append(request.getServletPath());


String queryString = request.getQueryString();


if (Validator.isNotNull(queryString)) {

redirectURL.append(StringPool.QUESTION);

redirectURL.append(request.getQueryString());

}


if (_log.isDebugEnabled()) {

_log.debug("Redirect to " + redirectURL);

}


response.sendRedirect(redirectURL.toString());

return false;

}

else {

if (_log.isDebugEnabled()) {

String completeURL = HttpUtil.getCompleteURL(request);

_log.debug("Not securing " + completeURL);

}


User user = PortalUtil.getUser(request);

if ((user != null) && !user.isDefaultUser()) {

request = setCredentials(

request, request.getSession(), user.getUserId(), null);

return true;

}

else {

if (_digestAuthEnabled) {

return digestAuth(request, response)!=null?true:false;

}

else if (_basicAuthEnabled) {

return basicAuth(request, response)!=null?true:false;

}
return false;
}

}
}

return true;
}
(...)
 
 
8 - Testing the RESTFul Services with SOAPUI
It is extremely easy to test the developed RESTFul services using SOAPUI. SOAPUI has great support to generate the web services calls, test cases and WADL file (similar to WSDL but for REST services)
I started by creating a REST call to retrieve all the samples
After starting SOAP UI you just need to do File->New->REST Project and add that http get request
 
After that I just added the other http requests like the PUT, POST and DELETE
Notice that for de DELETE, PUT and GET of a single entity you need to pass an entity ID so I added those services as children

This SOAPUI project is inside the project on github

8.1 - GET

Here is an example of a GET execution

and the Http Status returned:

plus the JSON list returned

8.2 - POST

And the HTTP 201 response:

8.3 - PUT

If I try to update a non existent entity I will get an Http NOT FOUND

 

8.4 - DELETE

The delete is roughly the same as the PUT

it returns 204 if successful or 404 if the sampleId does not exist or 400 if the entity is not well formed

8.5 - WADL generation

A nice feature of SOAPUI is the ability to generate a Web Application Description Language file that will serve as a contract that can be consumed by third parties. The interesting thing with REST is that you can just model a controller in a way that it will comply with REST specification, thus allowing you to generate a WADL of your controller layer 

<?xml version="1.0" encoding="UTF-8"?>
<application xmlns="http://wadl.dev.java.net/2009/02">
  <doc xml:lang="en" title="RESTFul"/>
  <resources base="http://localhost:8080">
    <resource path="sample-springmvc-restfull-portlet/services/helloSample" id="HelloSample">
      <doc xml:lang="en" title="HelloSample"/>
      <param name="sampleId" default="12502" type="xs:string" required="false" style="template" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
      <resource path="sampleId/{sampleId}" id="{sampleId}">
        <doc xml:lang="en" title="{sampleId}"/>
        <param name="sampleId" default="{sampleId}" type="xs:string" required="false" style="template" xmlns:xs="http://www.w3.org/2001/XMLSchema"/>
        <method name="GET" id="GET_SAMPLE_BY_ID">
          <doc xml:lang="en" title="GET_SAMPLE_BY_ID"/>
          <request/>
          <response status="200">
            <representation mediaType="application/json;charset=UTF-8" element="ns:Response" xmlns:ns="http://localhost/sample-springmvc-restfull-portlet/services/helloSample/sampleId/12502"/>
          </response>
        </method>
        <method name="PUT" id="UPDATE_SAMPLE_BY_ID">
          <doc xml:lang="en" title="UPDATE_SAMPLE_BY_ID"/>
         <request>
            <representation mediaType="application/json"/>
          </request>
          <response status="204">
            <representation mediaType="" element="data"/>
          </response>
          <response status="">
            <representation mediaType="application/json"/>
          </response>
        </method>
        <method name="DELETE" id="DELETE_SAMPLE_BY_ID">
          <doc xml:lang="en" title="DELETE_SAMPLE_BY_ID"/>
          <request/>
          <response status="405">
            <representation mediaType="text/html;charset=utf-8" element="html"/>
          </response>
          <response status="204">
            <representation mediaType="" element="data"/>
          </response>
        </method>
      </resource>
      <method name="GET" id="GET_ALL_SAMPLES">
        <doc xml:lang="en" title="GET_ALL_SAMPLES"/>
        <request/>
        <response status="200">
          <representation mediaType="application/json;charset=UTF-8"/>
        </response>
      </method>
      <method name="POST" id="CREATE_SAMPLE">
        <doc xml:lang="en" title="CREATE_SAMPLE"/>
        <request>
          <representation mediaType="application/json"/>
        </request>
        <response status="405 415">
          <representation mediaType="text/html;charset=utf-8"/>
        </response>
        <response status="">
          <representation mediaType="application/json"/>
        </response>
        <response status="201">
          <representation mediaType="application/json"/>
        </response>
      </method>
    </resource>
  </resources>
</application>

On the next blog post I will continue developing this application by integrating Permissions and an external Javascript Library that we have not seen much in examples: EXTJS

We will perform AJAX calls to RESTFul Web Services in order to produce the UI client side, combining a Thin Server Architecture with a Service Oriented Architecture

 

 

Showing 2 results.