Here, the “Front Controller” is Spring’s DispatcherServlet, and the “Controller” is a plain old Java object (POJO) with methods that can act as handlers mapped to URLs. The upshot is instead of having code tied to the HTTP protocol like traditional servlets, you can have a handler method that can be tested as any other Java class.
For example,
The annotation @Controller registers the HelloWorldController class as a controller, and the @RequestMapping(“/helloworld”) annotation tells the framework that the URL “/helloworld” will be handled by the helloWorld method, which simply adds the message “Hello World” to the model attributes using the name “message.”@Controller
public class HelloWorldController {
@RequestMapping("/helloWorld")
public String helloWorld(Model model) {
model.addAttribute("message", "Hello World!");
return "helloWorld";
}
}
The “View Template” is typically a JSP (Java Server Page). Spring MVC provides view resolvers that allow developers to provide a simple string (e.g., “helloWorld”) that will resolve to a JSP called helloWorld.jsp. You can set up this type of view resolver in the Spring context file, which, by default, is located at /WEB-INF/helloWorld-servlet.xml.
It will look something like this:
This resolver will take and string returned by the handler method, prefix it with “/WEB-INF/jsp” and add “.jsp” at the end. So, when our controller method returns “helloWorld,” Spring looks for the JSP at /WEB-INF/jsp/helloWorld.jsp and renders content in that JSP.<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config /><context:component-scan base-package= "com.helloworld.controller"/><bean /><bean id="jspViewResolver"class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="viewClass"value="org.springframework.web.servlet.view.JstlView" /><property name="prefix" value="/WEB-INF/jsp/" /><property name="suffix" value=".jsp" /><property name="order" value="1" /></bean></beans>
Spring also allows us to inject other components (such as services) into the Spring-managed beans, providing a powerful framework for wiring up all kinds of useful things.
Hello World Portlet
Let’s say we want to create a new portlet called Hello World. Portlets are structured as web apps with a WEB-INF directory, and in this directory, we will have the web.xml file (just like in a servlet environment) as well as a portlet.xml file. Liferay also has some additional files (liferay-display.xml, liferay-plugin-package.properties, and liferay-portlet.xml).
Add Spring Dispatcher
First, we need to register Spring’s DispatcherPortlet in our portlet.xml. Like their ServletDispatcher, Spring provides a dispatcher for portlets as well. When creating a new Liferay portlet project, the Liferay IDE will use the portlet class com.liferay.util.bridges.mvc.MVCPortlet. We want to change this mapping to use Spring’s dispatcher portlet instead. So, inside portlet.xml:We provide the location for the Spring config file (/WEB-INF/spring/hello-world-portlet.xml).<portlet><portlet-name>hello-world</portlet-name><display-name>Hello World</display-name><portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class><init-param><name>contextConfigLocation</name><value>/WEB-INF/spring/hello-world-portlet.xml</value></init-param>…</portlet>
Note: by default, the framework looks for a config file called /WEB-INF/{portlet name with hyphens removed}-portlet.xml, but we can specify another location and/or file name using the init-param contextConfigLocation as shown above. If we hadn’t specified the location, the default location for our portlet named hello-world would be /WEB-INF/helloworld-portlet.xml.
Add Spring Bridge between Portlet Request/Response and Servlet Request/Response
Next, we need to register Spring’s ViewRendererServlet in our new portlet’s web.xml:
Now, any portlet request that comes to the URL /WEB-INF/servlet/view is handled by the ViewRendererServlet. This servlet is responsible for converting portlet requests and responses to their servlet counterparts in order to reuse all the view technologies available to Spring MVC.
<servlet><servlet-name>view-servlet</servlet-name><servlet-class>org.springframework.web.servlet.ViewRendererServlet</servlet-class><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>view-servlet</servlet-name><url-pattern>/WEB-INF/servlet/view</url-pattern></servlet-mapping>
Create Controller Class
Now we can create our controller in much the same way we have done in the servlet world. There are a couple differences, however. First, the controller needs to be mapped to @RequestMapping(“VIEW”). The value “VIEW” is defined in the ViewRendererServlet, so this is going to be the same for any controller you need to create:If you are following along in an IDE like Eclipse, you may see some problems with the annotations. Our portlet doesn’t have the Spring classes it needs in order to know what the annotations @Controller and @RequestMapping are.@Controller@RequestMapping("VIEW") //always map to “VIEW”public class MyHelloWorldController { //class name can be anything you want.//put handler methods in here}
Liferay includes the Spring framework, but each portlet has its own classpath dependencies, so we need to add the Spring code to our portlet. Dependencies like this are managed in the liferay-plugin-package.properties file. Add all the Spring modules you need:
Now, all the Spring classes are available in our portlet.name=Hello Worldmodule-group-id=liferaymodule-incremental-version=1tags=short-description=change-log=page-url=http://www.liferay.comauthor=Liferay, Inc.licenses=LGPLportal-dependency-jars=\spring-asm.jar,\spring-beans.jar,\spring-context-support.jar,\spring-context.jar,\spring-core.jar,\spring-expression.jar,\spring-web-portlet.jar,\spring-web-servlet.jar,\spring-web.jar,\jstl-api.jar,\jstl-impl.jarliferay-versions=6.1.1
Handler Mapping
The handler mappings for Spring MVC portlets are similar to their servlet cousins, but there are some important differences. First, the portlet spec has more than one kind of request/response pairs. Accordingly, there are three @XxxMapping annotations: @ActionMapping, @RenderMapping, and @ResourceMapping.Adding the appropriate annotation to our handler methods lets us have access to the correct portlet request/response pairs, if needed. As with Spring MVC in the servlet world, the request and response objects can be passed in as method parameters, but this is optional. Each annotation corresponds to a portlet request type:
- @ActionMapping javax.portlet.ActionRequest and ActionResponse
- @RenderMapping javax.portlet.RenderRequest and RenderResponse
- @ResourceMapping javax.portlet.ResourceRequest and ResourceResponse
In our Hello World example, we could use the same method as the servlet version, but we would put the @RenderMapping annotation on the method:
@Controller@RequestMapping(“VIEW”)public class HelloWorldController {@RenderMapping()public String helloWorld(Model model) {model.addAttribute("message", "Hello World!");return "helloWorld";}}
AJAX and JSON Views
The Resource action allows the portlet to serve up resources without the usual re-rendering of portlets that the portal container requires for other actions. We can use this to support Ajax requests for JSON data.First, Spring MVC uses Jackson to marshal and unmarshal JSON objects. In the servlet world, annotations are available to handle all the JSON stuff. In the portlet world, however, we need to be a little more specific in our code. Let’s say we want a resource called “Thing” which has two properties: thing1 and thing2. Our Java object looks like this:
In our controller, let’s add a method for getting a “Thing” resource whenever a resource called “thingResource” is requested:
public class Thing {private String thing1;private String thing2;//Getters / Setters, etc.}
@ResourceMapping("thingResource")public View getThing(){Thing thing = new Thing();thing.setThing1("I am thing 1");thing.setThing2("thing 2");MappingJacksonJsonView view = new MappingJacksonJsonView();view.addStaticAttribute("thingObject", thing);return view;}
There are a couple things to note here. Spring provides an abstraction called View that can be returned directly, as opposed to using a view resolver. In this case, we want to use MappingJacksonJsonView. This view will transform our Java object into JSON. Of course, Liferay provides its own JSON creation utilities, but if you come from the Spring MVC world, Jackson is likely already familiar, and it works very well with the MVC framework.
We just create the view and add our “thing” object as a static attribute called “thingObject.”
Now, we can call our named resource (“thingResource”) using via Ajax by using some JavaScript like this:
Liferay allows you to reference your portlet URLs by using a naming convention like {portlet name without hyphens}_WAR_{portlet name without hyphens}portlet. In our case, this means helloworld_WAR_helloworldportlet. We set the resource ID as “thingResource,” which maps to the @ResourceMapping value in our controller. Once the Ajax call returns with the object, you can retrieve the named “thingObject” as described above.
AUI().use('liferay-portlet-url', 'aui-base', 'aui-io', function( A ) {var portletURL = new Liferay.PortletURL( 'RESOURCE_PHASE' );
portletURL.setPortletId( "helloworld_WAR_helloworldportlet" );portletURL.setResourceId("thingResource");portletURL.setCopyCurrentRenderParameters(true);A.io.request( portletURL.toString(), {dataType: 'json',
on: {success: function(event, id, obj) {
var instance = this,
data = instance.get('responseData'),thing = data['thingObject'];log.console('thing1 = ' + thing.thing1 + ' and thing2 = ' + thing.thing2);}}});});
Adding Jackson to Liferay
Unfortunately, Liferay does NOT include the Jackson mapping jars, so we need to add them. You need both the jackson-core-asl and Jackson-mapper-asl jars. These can be found in the Maven repository (http://mvnrepository.com/artifact/org.codehaus.jackson/jackson-core-asl and http://mvnrepository.com/artifact/org.codehaus.jackson/jackson-mapper-asl). Download the version you want and add the jars to the ROOT webapps library by placing them in tomcat/webapps/ROOT/WEB-INF/lib.Now that the jars are in the ROOT web app, they can be referenced in our Hello World project’s liferay-plugin-package.properties:
And that’s it. Spring MVC and all the goodies the Spring framework offers are available to our new portlet.portal-dependency-jars=\jackson-core-asl-1.4.2.jar,\jackson-mapper-asl-1.4.2.jar,\spring-asm.jar,\spring-beans.jar,\spring-context-support.jar,\spring-context.jar,\spring-core.jar,\spring-expression.jar,\spring-web-portlet.jar,\spring-web-servlet.jar,\spring-web.jar,\jstl-api.jar,\ jstl-impl.jarliferay-versions=6.1.1
Curtsy: Steve Clement
No comments:
Post a Comment