I built a sampleYoutube Demo Portlet using Simple Youtube FLex application
This portlet is very simple, all that it does is forward control to a JSP which includes Flex object from Simple Youtube FLex application and thent he
Simple Youtube Flex application
I wanted to build a simple Flex application for working with Youtube API, most of the blogs that i found on the internet talk about how to use as3-youtube-data-api for building that type of application, so i tried to build Flex application using that library but could not get it working, so i decided to build a simple demo application that makes use of Youtube API without any library
My Sample application lets you search for video first and then when you double click on a video, it will display that video below the search result for display
This the source code of my sample Youtube Flex application
In my Sample application i have a
The response is a JSON feed cricket video. I am decoding the feed and displaying it in the spreadsheet format using this code
Every row in the spread sheet has
When you click on any of the video it will build a URL like
My Sample application lets you search for video first and then when you double click on a video, it will display that video below the search result for display
This the source code of my sample Youtube Flex application
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
backgroundColor="0x000000" viewSourceURL="srcview/index.html" >
<mx:Script>
<![CDATA[
import com.adobe.serialization.json.JSON;
import mx.collections.ArrayCollection;
import mx.collections.XMLListCollection;
import mx.controls.dataGridClasses.DataGridColumn;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
[Bindable]
protected var dp:ArrayCollection;
protected function onResult(event:ResultEvent):void{
var feed:Object = JSON.decode(event.result as String).feed;
dp = new ArrayCollection(feed.entry);
}
protected function onFault(event:FaultEvent):void{
trace("Fault: " + event.fault.faultString);
}
protected function dgLabelFunction(row:Object,column:DataGridColumn):String{
if (column.dataField=="title")
return row.title.$t;
else if (column.dataField=="content")
return row.content.$t;
else return row.published.$t;
}
protected function getURL():String{
trace("Entering getURL()");
var videoUrlStr:String = dg.selectedItem.link[0].href;
var startingIndex:int = videoUrlStr.indexOf("?v=") +3;
var endingIndex = videoUrlStr.indexOf("&feature");
var tokenLength:int = endingIndex - startingIndex;
var v:String = videoUrlStr.substr(startingIndex, tokenLength);
var videoUrl = "http://www.youtube.com/v/" + v;
return videoUrl;
}
private function displayYouTube(videoUrl:String):void{
trace("Changing value of URL " + videoUrl);
swfloader.source = videoUrl;
}
]]>
</mx:Script>
<mx:HTTPService id="youTubeService" url="http://gdata.youtube.com/feeds/api/videos"
resultFormat="text" result="onResult(event)" fault="onFault(event)">
<mx:request>
<q>{searchTxt.text}</q>
<alt>json</alt>
</mx:request>
</mx:HTTPService>
<mx:HBox top="10" left="10">
<mx:Label text="Search YouTube:" color="0xFFFFFF"/>
<mx:TextInput id="searchTxt" enter="youTubeService.send()"/>
<mx:Button label="Go!" click="youTubeService.send()"/>
<mx:Label id="msg" text="Double click result to open video" visible="false"/>
</mx:HBox>
<mx:HBox width="100%" top="40" left="10" color="#000000">
<mx:DataGrid id="dg" width="620" height="220" doubleClickEnabled="true"
dataProvider="{dp}" doubleClick="displayYouTube(getURL())">
<mx:columns>
<mx:DataGridColumn dataField="title" labelFunction="dgLabelFunction"
headerText="Title" width="150"/>
<mx:DataGridColumn dataField="content" labelFunction="dgLabelFunction"
headerText="Content" width="200" />
<mx:DataGridColumn dataField="published" labelFunction="dgLabelFunction"
headerText="Published" width="150"/>
</mx:columns>
</mx:DataGrid>
</mx:HBox>
<mx:SWFLoader x="10" y="268" source="" id="swfloader"
autoLoad="true" scaleContent="true"/>
</mx:Application>
In my Sample application i have a
mx:HTTPService
pointing to http://gdata.youtube.com/feeds/api/videos
, when you enter some text in the search box and click on GO it makes a HTTP request to the Http Service by making HTTP GET call to http://gdata.youtube.com/feeds/api/videos?alt=json&q=cricket
, in this value of q is cricket because thats my search criteria.The response is a JSON feed cricket video. I am decoding the feed and displaying it in the spreadsheet format using this code
protected function onResult(event:ResultEvent):void{
var feed:Object = JSON.decode(event.result as String).feed;
dp = new ArrayCollection(feed.entry);
}
Every row in the spread sheet has
displayYouTube()
has event listener for double click, when you double click on any row in the spread sheet i am building a URL to that video and assiging it as value of source for SWFLoader that is part of the mxml.
protected function getURL():String{
trace("Entering getURL()");
var videoUrlStr:String = dg.selectedItem.link[0].href;
var startingIndex:int = videoUrlStr.indexOf("?v=") +3;
var endingIndex = videoUrlStr.indexOf("&feature");
var tokenLength:int = endingIndex - startingIndex;
var v:String = videoUrlStr.substr(startingIndex, tokenLength);
var videoUrl = "http://www.youtube.com/v/" + v;
return videoUrl;
}
private function displayYouTube(videoUrl:String):void{
trace("Changing value of URL " + videoUrl);
swfloader.source = videoUrl;
}
When you click on any of the video it will build a URL like
http://www.youtube.com/v/q9ew_nITQWY
and set it as value of SWFLoader
source attribute.
Flex Flickr Demo Portlet
I built a sample portlet using Simple Flickr Flex Application to demonstrate how to use Flex. You can download this portlet from here, it will work in any JSR 168 or JSR 286 compliant portlet container
Its a very simple portlet all that it does is forward control to a JSP that includes Flicker Flex object and then the actual business logic is inside Flex
Its a very simple portlet all that it does is forward control to a JSP that includes Flicker Flex object and then the actual business logic is inside Flex
Simple Flickr Flex application
I wanted to build a simple Flex application that would get photos from the Flickr Service, but most of the tutorials that i found on the web they build a very complicated full fledged application, so i built this simple application, which gets the recently added photos from Flex and displays them in simple layout. This demo application covers basic pattern for using Flickr service from Flex and once you get basics you should be able to modify it for adding other calls.
I am using As3FlickrLib library for building my code,
In order to use Flickr Service you will need a Flickr API key, you can get it from here , once you have it, you can replace the
Before you start using FlickrService you will have to authenticate with the service, you can do that by using following code
Create object of
After you get response for the frob, you can start making the actual calls to get data from service, in my case i wanted to keep this application very simple so i am making call to get all the recent additions to flickr and displaying it using this code
As you can see after getting authentication i am attaching event listener for
And response is XML feed with information about recently added photos like this
I am iterating through the feed and creating URLs to the photos.
Note: If you want to call any other method of FlickrService you can find the corresponding Event name and method name and use them to call the service
I am using As3FlickrLib library for building my code,
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955"
minHeight="600" creationComplete="init()">
<fx:Script>
<![CDATA[
import adobe.utils.CustomActions;
import com.adobe.webapis.flickr.AuthPerm;
import com.adobe.webapis.flickr.FlickrService;
import com.adobe.webapis.flickr.PagedPhotoList;
import com.adobe.webapis.flickr.Photo;
import com.adobe.webapis.flickr.events.FlickrResultEvent;
import mx.collections.ArrayCollection;
import mx.collections.ArrayList;
import mx.logging.Log;
import mx.logging.LogEventLevel;
import mx.logging.targets.TraceTarget;
public var flickr:FlickrService = new FlickrService("ReplaceWithYourFlickerKey");
private function initLogging():void {
var logTarget:TraceTarget = new TraceTarget();
logTarget.level = LogEventLevel.ALL;
Log.addTarget(logTarget);
}
public function init():void{
initLogging();
trace("After enabling logging");
flickr.secret=""ReplaceWithYourFlickerSecret"";
flickr.addEventListener (FlickrResultEvent.AUTH_GET_FROB, onGetFrob);
flickr.auth.getFrob();
}
public function onGetFrob (event : FlickrResultEvent) : void{
if (event.success){
var frob : String = event.data.frob as String;
var authURL : String = flickr.getLoginURL (frob, AuthPerm.READ);
flickr.addEventListener(FlickrResultEvent.PHOTOS_GET_RECENT,showRecent);
flickr.photos.getRecent();
}
}
public function showRecent(e:Object):void{
trace("Inside showPhotoDetail "+ e)
var photoList:PagedPhotoList = e.data.photos;
trace("Paged Photo List "+ photoList);
var photos:ArrayList = new ArrayList();
for( var i = 1 ; i < 40 ; i++){
var photo:Photo = photoList.photos[i];
var photoUrl:String ='http://static.flickr.com/' +
photo.server+ '/' + photo.id + '_' + photo.secret + '_t.jpg';
trace(photoUrl);
photos.addItem(photoUrl);
}
imageDisplay.dataProvider = photos;
}
]]>
</fx:Script>
<fx:Declarations>
<!-- Place non-visual elements (e.g., services, value objects) here -->
</fx:Declarations>
<s:Panel x="0" y="0" width="600" height="500" title="Flickr Demo">
<s:List id="imageDisplay" width="530"
height="400" x="0" y="0">
<s:layout>
<s:TileLayout requestedColumnCount="5" requestedRowCount="8"
horizontalGap="2" verticalGap="2"/>
</s:layout>
<s:itemRenderer>
<fx:Component>
<mx:Image source="{data}"
height="100" width="100" />
</fx:Component>
</s:itemRenderer>
</s:List>
</s:Panel>
</s:Application>
In order to use Flickr Service you will need a Flickr API key, you can get it from here , once you have it, you can replace the
ReplaceWithYourFlickerKey
in sample code with your key and ReplaceWithYourFlickerSecret
in sample code with your secret.Before you start using FlickrService you will have to authenticate with the service, you can do that by using following code
public var flickr:FlickrService = new FlickrService("ReplaceWithYourFlickerKey");
flickr.secret="ReplaceWithYourFlickerSecret";
flickr.addEventListener (FlickrResultEvent.AUTH_GET_FROB, onGetFrob);
flickr.auth.getFrob();
Create object of
FlickrService
by passing your developer key, then set the secret property equal to value of secret you got from Flickr, Once that is done add a event listener for FlickrResultEvent.AUTH_GET_FROB
and call the flickr.auth.getFrob()
, this will result in Authentication call being made to Flickr Service, with your key and secret and the response will return a frob value like this
Request
http://api.flickr.com/services/rest/?api_key=yourkey&method=flickr.auth.getFrob&api_sig=secretinencodedformat&
Response
<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
<frob>72157623957288592-f1571eccb125f756-29961</frob>
</rsp>
After you get response for the frob, you can start making the actual calls to get data from service, in my case i wanted to keep this application very simple so i am making call to get all the recent additions to flickr and displaying it using this code
public function onGetFrob (event : FlickrResultEvent) : void{
if (event.success){
var frob : String = event.data.frob as String;
var authURL : String = flickr.getLoginURL (frob, AuthPerm.READ);
flickr.addEventListener(FlickrResultEvent.PHOTOS_GET_RECENT,showRecent);
flickr.photos.getRecent();
}
}
public function showRecent(e:Object):void{
trace("Inside showPhotoDetail "+ e)
var photoList:PagedPhotoList = e.data.photos;
trace("Paged Photo List "+ photoList);
var photos:ArrayList = new ArrayList();
for( var i = 1 ; i < 40 ; i++){
var photo:Photo = photoList.photos[i];
var photoUrl:String ='http://static.flickr.com/' +
photo.server+ '/' + photo.id + '_' + photo.secret + '_t.jpg';
trace(photoUrl);
photos.addItem(photoUrl);
}
imageDisplay.dataProvider = photos;
}
As you can see after getting authentication i am attaching event listener for
FlickrResultEvent.PHOTOS_GET_RECENT
event and then making call to get flickr.photos.getRecent()
recent photos, This results in following HTTP request to flickr service
http://api.flickr.com/services/rest/?api_key=apikey&method=flickr.photos.getRecent&extras=&per_page=100&page=1&
And response is XML feed with information about recently added photos like this
<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
<photos page="1" pages="10" perpage="100" total="1000">
<photo id="4563046917" owner="19673208@N08" secret="2e8188f6e7" server="4052"
farm="5" title="DSCN0379" ispublic="1" isfriend="0" isfamily="0" />
<photo id="4563047015" owner="8867627@N07" secret="16e4c986a6" server="3375"
farm="4" title="red blossom" ispublic="1" isfriend="0" isfamily="0" />
I am iterating through the feed and creating URLs to the photos.
Note: If you want to call any other method of FlickrService you can find the corresponding Event name and method name and use them to call the service
BlazeDS Contact Portlet
I wanted to learn how to i can use BlazeDS technology in portlet so i built a sample Contact Management application, This application has
You can download the sample code for this article from here
The BlazeDS application is nothing but a Servlet that you can add to your portlet application along with set of jars and you should be able to use it for communicating to Flex
BlazeDSContactPortlet
, which is used for returning a JSP that includes Flex from doView()
method but once the Flex application is loaded it will directly communicate to MessageBrokerServlet
inside the portlet, which is disadvantage of using BlazeDS in portlet application, you wont have portlet context in any other callsYou can download the sample code for this article from here
The BlazeDS application is nothing but a Servlet that you can add to your portlet application along with set of jars and you should be able to use it for communicating to Flex
- First download the blazeds.war and copy all the jars from its WEB-INF/lib to your WEB-INF/lib folder
- Then add
HttpFlexSession
listener andMessageBrokerServlet
servlet to your web.xml like this
<listener>
<listener-class>flex.messaging.HttpFlexSession</listener-class>
</listener>
<servlet>
<servlet-name>MessageBrokerServlet</servlet-name>
<servlet-class>flex.messaging.MessageBrokerServlet</servlet-class>
<init-param>
<param-name>services.configuration.file</param-name>
<param-value>/WEB-INF/flex/services-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>MessageBrokerServlet</servlet-name>
<url-pattern>/messagebroker/*</url-pattern>
</servlet-mapping> - The copy flex folder from the blazeds.war to your WEB-INF folder and change services-config.xml file so that value of my-amf channel points to the
MessageBrokerServlet
in your environment.
<channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel">
<endpoint url="http://localhost:10040/wpcert/PA_BlazeDSContact/messagebroker/amf"
class="flex.messaging.endpoints.AMFEndpoint"/>
</channel-definition>
The value of url will change based on your environment, in my case base URL of the server is http://localhost:10040 and the web application that has theMessageBrokerServlet
is available at http://localhost:10040/wpcert/PA_BlazeDSContact, so i add/messagebroker/amf
to it and that becomes value ofmy-amf
channel url - I am more interested in using BlazeDS to make calls to Java Objects in my portlet application from my Flex code, so first i declare the Java Class that should be invoked from my Flex class by adding this code to my remoting-config.xml file like this
<destination id="contactdao">
<properties>
<source>com.webspherenotes.flex.dao.ContactDAOImpl</source>
</properties>
</destination>
Now i can call methods ofcom.webspherenotes.flex.dao.ContactDAOImpl
Java Class directly from my Flex code - Now inside my flex code i can add this code to call methods of the
ContactDAOImpl
[Bindable]
public var contactList:ArrayCollection;
public var contactDAORO:RemoteObject;
public function getContactList():void {
contactDAORO = new RemoteObject();
contactDAORO.destination = "contactdao";
contactDAORO.endpoint ="http://localhost:10040/wpcert/PA_BlazeDSContact/messagebroker/amf"
contactDAORO.addEventListener("result",getContactListHandler);
contactDAORO.addEventListener("fault",faultHandler);
contactDAORO.getContactList();
}
public function faultHandler (event:FaultEvent):void {
Alert.show(event.fault.faultString, 'Error');
}
private function getContactListHandler(event:ResultEvent):void{
contactList = event.result as ArrayCollection;
dgEmployees.dataProvider = contactList;
}
<mx:DataGrid x="10" y="174" width="460" enabled="true" editable="false"
id="dgEmployees">
<mx:columns>
<mx:DataGridColumn headerText="First Name" dataField="firstName"/>
<mx:DataGridColumn headerText="Last Name" dataField="lastName"/>
<mx:DataGridColumn headerText="Email" dataField="email"/>
</mx:columns>
</mx:DataGrid>
My Flex code hasdgEmployees
asDataGrid
, that displays list of contacts in spread sheet format.
I am callinggetContactList()
, flex method on the application load, this method is creating object ofRemoteObject
and pointing to URL whereMessageBrokerServlet
in my application is listening, then i am setting value ofcontactDAORO.destination
tocontactdao
, when i do thatMessageBrokerServlet
, will take value of destination and find out the java class from that is mapped to contactdao by reading remoting-config.xml, in our case it is pointing tocom.webspherenotes.flex.dao.ContactDAOImpl
, then i am settinggetContactListHandler
, as flex method which should be used to handle result of this call andfaultHandler
method for handling error cases in this flex call.
ThecontactDAORO.getContactList()
means i want to call thegetContactList()
method ofContactDAOImpl
class, this method returns ArrayList of Contact objects, on the flex side i am converting thatArrayList
toArrayCollection
and i have a Contact flex class that is mapped to the Contact java class. Inside thegetContactList()
method i am setting the list returned by Java code as dataprovider for the datagrid - This is how my Action Script Contact class looks like
package com.webspherenotes.flex
{
[Bindable]
[RemoteClass(alias="com.webspherenotes.flex.dao.Contact")]
public class Contact
{
private var _firstName:String;
private var _lastName:String;
private var _email:String;
public function Contact()
{
}
public function get email():String
{
return _email;
}
public function set email(value:String):void
{
_email = value;
}
public function get lastName():String
{
return _lastName;
}
public function set lastName(value:String):void
{
_lastName = value;
}
public function get firstName():String
{
return _firstName;
}
public function set firstName(value:String):void
{
_firstName = value;
}
}
}
What is blazeDS
BlazeDS is a open source project, that provides some infrastructure that can be used to simplify development of J2EE backend and Flex for front end type of application
BlazeDS functionality can be divided into following three key services:
If you want to build J2EE on server side and Flex for front end type of application, then you can either use JSON, XML or Flex for data exchange but using BlazeDS provides following advantages
BlazeDS functionality can be divided into following three key services:
- The Remoting Service allows your Flex application to directly invoke methods of Java objects deployed in your application server.
- The Message Service provides a publish/subscribe infrastructure that allows your Flex application to publish messages and subscribe to a messaging destination, enabling the development of real-time data push and collaborative applications.
- The Proxy Service allows your Flex application to make cross-domain service requests in a secure and controlled manner. In other words, it allows your Flex application to access a service available on a different domain than the domain from where the application was downloaded (without having to deploy a crossdomain.xml policy file on the target domain).
If you want to build J2EE on server side and Flex for front end type of application, then you can either use JSON, XML or Flex for data exchange but using BlazeDS provides following advantages
- It takes care of converting Java object into Action Script object required by Flex and other way round.
- It is more efficient then either JSON or XML for communicating between J2EE and Flex application
HelloWorld Spring Portlet MVC Framework 3.0.1
Spring Portlet MVC Framework 3.0.1 has support for developing Portlet Specification 2.0 compliant portlets.
I want to learn about how to use Spring for developing Portlet Specification 2.0 compliant so i built this HelloWorld portlet. This portlet is very simple, it has one Controller for handling all View mode related requests ant that controller sets a string in request object and forwards control to JSP for displaying markup, but i am using it as starting point. You can download the sample code from here and follow these steps to create your own portlet
I want to learn about how to use Spring for developing Portlet Specification 2.0 compliant so i built this HelloWorld portlet. This portlet is very simple, it has one Controller for handling all View mode related requests ant that controller sets a string in request object and forwards control to JSP for displaying markup, but i am using it as starting point. You can download the sample code from here and follow these steps to create your own portlet
- First download the Spring Framework 3.0.2 from the Spring Framework Site
- Create a portlet and copy following jars in the WEB-INF/lib folder of your portlet, you can download the sample portlet from here and copy these jars from it
- aopalliance-1.0.jar
- commons-logging-1.1.1.jar
spring-aop-3.0.2.jar - spring-asm-3.0.2.RELEASE.jar
- spring-beans-3.0.2.RELEASE.jar
- spring-context-3.0.2.RELEASE.jar
- spring-context-support-3.0.2.RELEASE.jar
- spring-core-3.0.2.RELEASE.jar
- spring-expression-3.0.2.RELEASE.jar
- spring-web-3.0.2.RELEASE.jar
- spring-webmvc-3.0.2.RELEASE.jar
- spring-webmvc-portlet-3.0.2.RELEASE.jar
Next step is to addorg.springframework.web.portlet.DispatcherPortlet
portlet in your portlet.xml like this. The DispatcherPorltet is Controller portlet and responsible for handling every portlet request
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
<portlet>
<portlet-name>HelloSpring31Portlet</portlet-name>
<display-name>Hello Spring 31 Portlet</display-name>
<portlet-class>org.springframework.web.portlet.DispatcherPortlet
</portlet-class>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<portlet-info>
<title>Hello Spring 31 Portlet</title>
<short-title>Hello Spring 31 Portlet</short-title>
<keywords>Hello Spring 31 Portlet</keywords>
</portlet-info>
</portlet>
</portlet-app>- Now change your web.xml and add
org.springframework.web.servlet.ViewRendererServlet
to it like this, this step would make sure that you can use the Spring View layer in your portlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>HelloSpring31Portlet</display-name>
<servlet>
<servlet-name>ViewRendererServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.ViewRendererServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ViewRendererServlet</servlet-name>
<url-pattern>/WEB-INF/servlet/view</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
</web-app> - The next step is to create a Spring configuration file in the WEB-INF directory, the name of the file should be HelloSpring31Portlet-portlet.xml, basic idea is name of the configuration file should
<portletname>-portlet.xml
, where portletname is defined in portlet.xml,
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="helloSpring31Action"
class="com.webspherenotes.mvc.spring.action.HelloSpring31Action" />
<bean id="portletModeHandlerMapping"
class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
<property name="order" value="1" />
<property name="portletModeMap">
<map>
<entry key="view">
<ref bean="helloSpring31Action" />
</entry>
</map>
</property>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
This file has following 3 bean definitions- com.webspherenotes.mvc.spring.action.HelloSpring31Action This is Controller or Action class that i am going to develop next
- org.springframework.web.portlet.handler.PortletModeHandlerMapping This bean is used for mapping controller class to the mode, It allows you to map only one Controller or Action class for every mode, in our sample code i am mapping helloSpring31Action to the VIEW mode, that means whenever this portlet gets called in VIEW mode it will pass control to helloSpring31Action for generating response
- org.springframework.web.servlet.view.InternalResourceViewResolver: This bean is used for resolving a resource, In our case i am saying that i am going to use JSPs for view i.e. genearting markup and all my JSPs are placed inside WEB-INF/jsp folder, so i am setting value of prefix attribute to WEB-INF/jsp the value of suffix attribute is .jsp that means the extension for all my JSP file. Now inside my controller class when i forward control to
ModelAndView("hello")
, it will take that value and forward control to /WEB-INF/jsp/hello.jsp for generating markup
- Next create applicationContext.xml file in your WEB-INF folder, it will be valid spring configuration file like this but wont have any bean definition. Every Spring Web MVC application will look for it and will fail if its not able to find this file. You can use it to define application level beans
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
</bean>
</beans> - Next create
HelloSpring31Action
class like this
package com.webspherenotes.mvc.spring.action;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import org.springframework.web.portlet.ModelAndView;
import org.springframework.web.portlet.mvc.AbstractController;
public class HelloSpring31Action extends AbstractController{
protected ModelAndView handleRenderRequestInternal(RenderRequest request,
RenderResponse response) throws Exception {
System.out.println("Entering HelloSpring31Action.handleRenderRequestInternal()");
ModelAndView modelAndView = new ModelAndView("hello");
modelAndView.addObject("userName", "Sunil");
System.out.println("Exiting HelloSpring31Action.handleRenderRequestInternal()");
return modelAndView;
}
}
As you can see theHelloSpring31Action
class extendsorg.springframework.web.portlet.mvc.AbstractController class and implements handleRenderRequestInternal
method, inside this method i am not executing any business logic instead i am creating object ofModelAndView
, settinguserName
attribute to it and then forwarding control to hello.jsp. Inside the hello.jsp theuserName
object would be available inrenderRequest
- Last step is to create hello.jsp inside /WEB-INF/jsp folder like this
&tl;%@page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" session="false"%≷
<%@taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%≷
<portlet:defineObjects /≷
<h1≷Hello From Hello Spring 2.0.8 &tl;%=renderRequest.getAttribute("userName") %≷ < /h1≷
The hello.jsp is very simple only thing that it does is read value ofuserName
attribute set byHelloSpring31Action
fromrenderRequest
and display it to user,
If your using the Apache Maven as build tool then you can add following dependencies for your project and it will take care of downloading and packaging appropriate jars
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc-portlet</artifactId>
<version>${spring.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
Public Render Parameter
The JSR 286 specification introduces concept of Public Render paramter which are same as the render paramter before with a differnece that they can be shared across portlets. Your portlet code for setting render parameter in the URL and reading it remains same, only difference is you declare a render parameter in the portlet.xml and then they are shared across different portlets
I built two sample portlet one is WeatherPortlet and other is MapPortlet, they do nothing but set zip as render parameter and read zip parameter and display it in view mode, this is how my WeatherPorltet looks like
The MapPortlet is same as that of the WeatherPortlet. Now the next part is in the portlet.xml i declare zip as public render parameter and now if i set zip parameter in map it gets available to weather and other way round
In WebSphere once you set zip render parameter it remains in the URL until you logout and login again, if you add the Weather and Map portlet on different pages then zip code set on one page is available on other
I built two sample portlet one is WeatherPortlet and other is MapPortlet, they do nothing but set zip as render parameter and read zip parameter and display it in view mode, this is how my WeatherPorltet looks like
public class WeatherPortlet extends GenericPortlet{
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering WeatherPortlet.doView()");
response.setContentType("text/html");
response.getWriter().println("Value of zip render parameter " +
request.getParameter("zip"));
PortletURL sfoWeather = response.createRenderURL();
sfoWeather.setParameter("zip", "94101");
response.getWriter().println("<a href='"+ sfoWeather.toString()+"'>SFO</a>");
PortletURL puneWeather = response.createRenderURL();
puneWeather.setParameter("zip", "411007");
response.getWriter().println("<a href='"+
puneWeather.toString()+"'>Pune</a>");
System.out.println("Exiting WeatherPortlet.doView()");
}
}
The MapPortlet is same as that of the WeatherPortlet. Now the next part is in the portlet.xml i declare zip as public render parameter and now if i set zip parameter in map it gets available to weather and other way round
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
<portlet>
<portlet-name>WeatherPortlet</portlet-name>
<display-name>Weather Portlet</display-name>
<portlet-class>com.webspherenotes.jsr286.WeatherPortlet</portlet-class>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<portlet-info>
<title>Weather Portlet</title>
<short-title>Weather Portlet</short-title>
<keywords>WeatherPortlet</keywords>
</portlet-info>
<supported-public-render-parameter>zip</supported-public-render-parameter>
</portlet>
<portlet>
<portlet-name>MapPortlet</portlet-name>
<display-name>MapPortlet Portlet</display-name>
<portlet-class>com.webspherenotes.jsr286.MapPortlet</portlet-class>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<portlet-info>
<title>MapPortlet Portlet</title>
<short-title>MapPortlet Portlet</short-title>
<keywords>MapPortletPortlet</keywords>
</portlet-info>
<supported-public-render-parameter>zip</supported-public-render-parameter>
</portlet>
<public-render-parameter>
<identifier>zip</identifier>
<qname xmlns:x="http://webspherenotes.com/params">x:zip</qname>
</public-render-parameter>
</portlet-app>
In WebSphere once you set zip render parameter it remains in the URL until you logout and login again, if you add the Weather and Map portlet on different pages then zip code set on one page is available on other
Converting Action Script object to XML
The Flex and XML Portlet is sample of how you can send XML String from Java Portlet to Flex and Flex displays it in
I changed the Sample code so that now i am converting the data submitted by user in Flex application into XML first then submitting it using HTTP POST method. I am using As3xml library for converting Action Script object into XML.
In order to get this sample working first i had to define Flex Contact class then change the addContact() method like this
First i did create a
I am submitting a form to the actionURL with this XML as value of
Then on the server side i had to create a method which can parse the XML string into Java Object, i am using Xstream for that
DataGrid
format. In that blog i covered how to submit data entered by user in Flex application, using HTTP POST method.I changed the Sample code so that now i am converting the data submitted by user in Flex application into XML first then submitting it using HTTP POST method. I am using As3xml library for converting Action Script object into XML.
In order to get this sample working first i had to define Flex Contact class then change the addContact() method like this
public function addContact():void{
trace("Entering useHttpService");
service = new HTTPService();
service.url = nextActionURL;
service.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
service.method = "POST";
var myContact:Contact = getContact();
var myContactXML:XML= Asx3mer.instance.toXML(myContact);
var params:Object = { myContactStr: myContactXML };
service.send(params);
getContactList();
trace("Entering useHttpService");
}
public function getContact():Contact{
var myContact:Contact =new Contact();
myContact.firstName = txtFirstName.text;
myContact.lastName = txtLastName.text;
myContact.email = txtEmail.text;
return myContact;
}
First i did create a
getContact()
method which reads values entered by user in Flex application and returns a Contact
object based on it. Then converting Contact Flex object into XML object is pretty simple all i had to do was call Asx3mer.instance.toXML()
. What i liked most is about working with XML in Flex is how easy it is pretty print XML, all i had to do was print the XML object and it prints a XML like this
<com.webspherenotes.flex.Contact>
<firstName>Sachin</firstName>
<lastName>Tendulkar</lastName>
<email>sachin@mi.com</email>
</com.webspherenotes.flex.Contact>
I am submitting a form to the actionURL with this XML as value of
myContactStr
Then on the server side i had to create a method which can parse the XML string into Java Object, i am using Xstream for that
public static Contact convertXMLToJava(String xmlStr){
XStream xstream = new XStream(new DomDriver());
xstream.setMode(XStream.NO_REFERENCES);
xstream.alias("com.webspherenotes.flex.Contact", Contact.class);
System.out.println("setting values of contact object");
Contact contact= (Contact)xstream.fromXML(xmlStr);
return contact;
}
Flex and XML Portlet
Before couple of days i did create a Flex and JSON portlet, that portlet is a simple portlet which sends list of contacts to flex in JSON format and then flex is displaying that data in the Grid format then i changed it so that Flex UI converts the data submitted by user in the JSON string format and submit it to portlet, the portlet is responsible for parsing JSON back to Java Object and inserting it into database.
Today i made some changes in the portlet so that portlet reads list of contacts from database, converts it into XML and sends it to Flex object and then Flex is displaying it like this using
The server side code for this portlet is almost same as that of Flex and JSON portlet, with difference that i am using Xstream for converting Contact Java object into XML like this
The client side flex code is almost same, only thing that i had to change is the method that handles response of resourceURL and converts it into dataprovider for the DataGrid like this
The XML object which is part of default package in Flex takes XML string as input and converts it into flex object. This is the response returned by portlet
After converting this object into Flex you can access elements as properties of flex object Ex. you can access values of actionURL element using
In order to display contacts in
Today i made some changes in the portlet so that portlet reads list of contacts from database, converts it into XML and sends it to Flex object and then Flex is displaying it like this using
DataGrid
. YOu can also insert new record in Contact table, when you do that i am passing values submitted by user separately as POST parametersThe server side code for this portlet is almost same as that of Flex and JSON portlet, with difference that i am using Xstream for converting Contact Java object into XML like this
public static String convertJavaToXML(ContactDTO contactDto){
XStream xstream = new XStream(new DomDriver());
xstream.setMode(XStream.NO_REFERENCES);
xstream.alias("contactDTO",ContactDTO.class);
xstream.alias("contact", Contact.class);
String xmlString = xstream.toXML(contactDto);
return xmlString ;
}
The client side flex code is almost same, only thing that i had to change is the method that handles response of resourceURL and converts it into dataprovider for the DataGrid like this
public function httpResult(event:ResultEvent):void {
trace("Entering httpResult");
var result:Object = event.result;
trace(" Response in string" + event.result.toString());
var xmlData:Object =new XML(event.result.toString());
nextActionURL = xmlData.actionURL;
var xmlListColl:XMLListCollection = new XMLListCollection(xmlData.contactList.children());
trace("After creating xmlListCollection " + xmlListColl);
dgEmployees.dataProvider = xmlListColl;
trace("Entering httpResult");
}
The XML object which is part of default package in Flex takes XML string as input and converts it into flex object. This is the response returned by portlet
<contactDTO>
<actionURL>/pluto/portal/Sunils Test Page/__acXMLContactPortlet0x2XMLContactPortlet!-318001599%7C0/__pmXMLContactPortlet0x2XMLContactPortlet!-318001599%7C0_view/__wsXMLContactPortlet0x2XMLContactPortlet!-318001599%7C0_normal?</actionURL>
<contactList>
<contact>
<firstName>Jiya</firstName>
<lastName>Patil</lastName>
<email>jiya@gmail.com</email>
</contact>
</contactDTO>
After converting this object into Flex you can access elements as properties of flex object Ex. you can access values of actionURL element using
xmlData.actionURL
.In order to display contacts in
DataGrid
first i am creating a XMLListCollection
object like this XMLListCollection(xmlData.contactList.children())
then setting it as value of dataProvider
for the DataGrid
Displaying UTF-8 Characters in resource response
The portlet specification says that when resource request is made the portal only acts as proxy and as a result you have more control on the HTTP request, response, you can set additional headers do things that you cannot in the
I do have this sample portlet which displays some Chinese characters in both
When i looked at the HTTP headers of response, it seems that when portal returns response it automatically sets character encoding to
This is how the response looks like after setting character encoding manually, as you can see it adds the information to contentType header
render
method, this issue also has some disadvantages, For example if you want to return UTF-8 characters in the doView()
method you dont have to do any thing special but if you want to return same UTF-8
characters through serveResource
method then you will have to call PortletResponse.setCharacterEncoding("UTF-8")
I do have this sample portlet which displays some Chinese characters in both
doView()
and serveResource()
method the characters are displayed right in the view mode markup but it displays funny characters while displaying response of the resource URLWhen i looked at the HTTP headers of response, it seems that when portal returns response it automatically sets character encoding to
UTF-8
irrespective of if your setting UTF-8
characters or not but when you return markup from serveResource
, character encoding is not set by portal, so i had to change my code and set the character encoding manually and now it works
public class ServeResourceUTF8Portlet extends GenericPortlet{
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType("text/html");
response.getWriter().println("æ ·æœ¬å—符");
response.getWriter().println("
");
getPortletContext().getRequestDispatcher("/view.jsp").include(request, response);
}
public void serveResource(ResourceRequest request, ResourceResponse response)
throws PortletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
response.getWriter().println("æ ·æœ¬å—符");
}
}
This is how the response looks like after setting character encoding manually, as you can see it adds the information to contentType header
Effect of setCacheability on resource URL
With the
I tried building a sample URLCacheabilityPortlet to see how the URL Cacheability works but it seems that it does not work as described in the portlet specification either in the Apache Pluto or WebSphere Pluto, but i could see that portal server is generating different URLs for
When i did add two instances of this portlet on one websphere portal page this is the output that i got
setCacheability
method on the ResourceURL
the portlet can indicate that it only needs parts of the overall state via the cache level parameter and thus the portal application can create URLs that result in an increased likelihood of a subsequent browser access being served from a browser/web cache. With the getCachability
method on the ResourceURL
the portlet can retrieve the current cache level.- FULL – The resource URL does not need to contain the current state of the page or the current render parameters, portlet mode, or window state of the portlet. Thus the portlet should not access the portlet mode, window state, or render parameters in the
serveResource
call.URLs of the typeFULL
have the highest cacheability in the browser as they do not depend on any state of the portlet or page. - PORTLET – The
serveResource
call triggered by a PORTLET resource URL does have access to the portlet state consisting of the render parameters, portlet mode and window state.URLs of the type PORTLET are cacheable on the portlet level in the browser and can be served from the browser cache for as long as the state of this portlet does not change - PAGE – The resource URL may contain artifacts that require knowledge of the state of the complete page, like PortletURLs, or resource URLs of type PAGE. The markup returned by such a resource URL may contain any portlet URL. Resource URLs of the type PAGE are only cacheable on the page level and can only be served from the browser cache as long as no state on the page changes.
I tried building a sample URLCacheabilityPortlet to see how the URL Cacheability works but it seems that it does not work as described in the portlet specification either in the Apache Pluto or WebSphere Pluto, but i could see that portal server is generating different URLs for
serveResource
depending on the Cacheability level
public class URLCacheabilityPortlet extends GenericPortlet{
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering URLCacheabilityPortlet.doView()");
response.setContentType("text/html");
getPortletContext().getRequestDispatcher("/view.jsp").include(request, response);
System.out.println("Exiting URLCacheabilityPortlet.doView()");
}
public void serveResource(ResourceRequest request, ResourceResponse response)
throws PortletException, IOException {
System.out.println("Entering URLCacheabilityPortlet.serveResource()");
response.setContentType("text/html");
ResourceURL fullCachableURL = response.createResourceURL();
fullCachableURL.setCacheability(ResourceURL.FULL);
fullCachableURL.setProperty(ResourceURL.SHARED, "TestQName");
request.setAttribute("fullCachableUrl", fullCachableURL.toString());
ResourceURL pageCachableURL = response.createResourceURL();
pageCachableURL.setCacheability(ResourceURL.PAGE);
request.setAttribute("pageCachableURL", pageCachableURL.toString());
ResourceURL portletCacheableURL = response.createResourceURL();
portletCacheableURL.setCacheability(ResourceURL.PORTLET);
request.setAttribute("portletCacheableURL", portletCacheableURL.toString());
getPortletContext().getRequestDispatcher("/index.jsp").include(request, response);
System.out.println("Entering URLCacheabilityPortlet.serveResource()");
}
}
When i did add two instances of this portlet on one websphere portal page this is the output that i got
Serving Resources notes
The portlet can create resource URLs pointing back to itself via the
Resource URLs should be provided with the current portlet mode, window state, and render parameters that the portlet can access via the
ResourceURLs cannot change the current portlet mode, window state or render parameters. Parameters set on a resource URL are not render parameters but parameters for serving this resource and will last only for the current serveResource request. If a parameter is set that has the same name as a render parameter that this resource URL contains, the render parameter values must be the last entries in the parameter value array
createResourceURL
method on the PortletResponse
. When an end user invokes such a resource URL the portlet container must call the serveResource
method of the portlet or return a valid cached result for this resource URLResource URLs should be provided with the current portlet mode, window state, and render parameters that the portlet can access via the
ResourceRequest
with getPortletMode, getWindowState,
or one of the getParameter
methods. ResourceURLs cannot change the current portlet mode, window state or render parameters. Parameters set on a resource URL are not render parameters but parameters for serving this resource and will last only for the current serveResource request. If a parameter is set that has the same name as a render parameter that this resource URL contains, the render parameter values must be the last entries in the parameter value array
Setting arbitrary HTTP headers during serveResource
As per portlet specification "Given that the portal/portlet container does not render any additional markup for a
A portlet can access the headers of the HTTP client request through the
So i tried building a sample portlet to see if i can set
The
When i tried this portlet in the WebSphere Portal i could see that the additional headers that i am setting are getting passed back in the response like this
As you can see the response has
serveResource
response it is important for the portlet to be able to access the incoming request headers and to be able to set new headers for the response.A portlet can access the headers of the HTTP client request through the
getProperty
or getProperties
call, like all portlet requests. A portlet can set HTTP headers for the response via the setProperty
or addProperty
call in the PortletResponse
. To be successfully transmitted back to the client, headers must be set before the response is committed. Headers set after the response is committed will be ignored by the portlet container"So i tried building a sample portlet to see if i can set
public class ServeResourceHttpRequestPortlet extends GenericPortlet{
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ServeResourceHttpRequestPortlet.doView()");
response.setContentType("text/html");
getPortletContext().getRequestDispatcher("/index.jsp").include(request, response);
System.out.println("Exiting ServeResourceHttpRequestPortlet.doView()");
}
public void serveResource(ResourceRequest request, ResourceResponse response)
throws PortletException, IOException {
System.out.println("Entering ServeResourceHttpRequestPortlet.serveResource()");
response.setProperty(ResourceResponse.EXPIRATION_CACHE, "600");
response.setProperty("Expires", "1000")
response.setContentType("text/html");
System.out.println("Request method is " + request.getMethod());
response.getWriter().println("HTTP Method used is " + request.getMethod());
System.out.println("Exiting ServeResourceHttpRequestPortlet.serveResource()");
}
}
The
doView()
method is passing control to the index.jsp which is making HTTP GET call to the resource URL.When i tried this portlet in the WebSphere Portal i could see that the additional headers that i am setting are getting passed back in the response like this
As you can see the response has
Expires
and cache-control
header that i set in the serveResource
method
Making POST and DELETE calls to resource URL
As per portlet specification "For use cases that require state changes the
The
As you can see the
In the
As you can see the index.jsp has 4 buttons which try GET, POST, PUT and DELETE call to the resource URL. As you can see the
serveResource
call should be issued via an HTTP
method POST
or PUT
or DELETE
." I tried creating a ServeResourceHttpRequestPortlet
to check if i can make HTTP PUT
or HTTP DELETE
call to portlet and i tried it on both WebSphere Portal and Apache Pluto and i got 405: HTTP method DELETE is not not supported by this URL error.
The
ServeResourceHttpRequestPortlet
is like this
public class ServeResourceHttpRequestPortlet extends GenericPortlet{
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ServeResourceHttpRequestPortlet.doView()");
response.setContentType("text/html");
getPortletContext().getRequestDispatcher("/index.jsp").include(request, response);
System.out.println("Exiting ServeResourceHttpRequestPortlet.doView()");
}
public void serveResource(ResourceRequest request, ResourceResponse response)
throws PortletException, IOException {
System.out.println("Entering ServeResourceHttpRequestPortlet.serveResource()");
response.setContentType("text/html");
System.out.println("Request method is " + request.getMethod());
response.getWriter().println("HTTP Method used is " + request.getMethod());
System.out.println("Exiting ServeResourceHttpRequestPortlet.serveResource()");
}
}
As you can see the
serveResource()
method simply returns name of the HTTP method used for making this request. In the
doView()
, method i am passing control to index.jsp which is like this
<%@page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1" session="false"%>
<%@taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>
<style type="text/css">
@import src="http://o.aolcdn.com/dojo/1.3.2/dijit/themes/tundra/tundra.css";
@import src=""http://o.aolcdn.com/dojo/1.3.3/dojo/resources/dojo.css";
</style>
<script type="text/javascript" src="http://o.aolcdn.com/dojo/1.3.2/dojo/dojo.xd.js"
djConfig="parseOnLoad:true">
</script>
<portlet:defineObjects />
<script type="text/javascript">
dojo.require("dijit.form.Button");
dojo.require("dojo.parser");
</script>
<table>
<tr>
<td>
<button dojoType="dijit.form.Button" id="get">GET
<script type="dojo/method" event="onClick">
dojo.xhrGet({
url: "<portlet:resourceURL/>",
load: function(data, ioargs){
dojo.byId("resourceResponse").innerHTML = data;
},
error: function(error,ioargs){
alert(error);
}
});
</script>
</button>
</td>
<td>
<button dojoType="dijit.form.Button" id="post">POST
<script type="dojo/method" event="onClick">
dojo.xhrPost({
url: "<portlet:resourceURL/>",
load: function(data, ioargs){
dojo.byId("resourceResponse").innerHTML = data;
},
error: function(error,ioargs){
alert(error);
}
});
</script>
</button>
</td>
<td>
<button dojoType="dijit.form.Button" id="delete">DELETE
<script type="dojo/method" event="onClick">
dojo.xhrDelete({
url: "<portlet:resourceURL/>",
load: function(data, ioargs){
dojo.byId("resourceResponse").innerHTML = data;
},
error: function(error,ioargs){
alert(error);
}
});
</script>
</button>
</td>
<td>
<button dojoType="dijit.form.Button" id="put">PUT
<script type="dojo/method" event="onClick">
dojo.xhrPut({
url: "<portlet:resourceURL/>",
load: function(data, ioargs){
dojo.byId("resourceResponse").innerHTML = data;
},
error: function(error,ioargs){
alert(error);
}
});
</script>
</button>
</td>
</tr>
</table>
<div id="resourceResponse" />
As you can see the index.jsp has 4 buttons which try GET, POST, PUT and DELETE call to the resource URL. As you can see the
GET
and POST
method worked but PUT
and DELETE
did not Creating JSON in Flex
In the Flex JSON Portlet that i blogged about yesterday, i was submitting values submitted by users for First Name, Last Name and email as form parameters, which seems natural.
Then i thought lets see if i can create a Action Script class and then convert it into JSON and submit it as JSON String to server side, so i did create my First Action Script Class like this
I used Flash Builder 4.0 for creating this class, All i had to do was to right click and say create Action Script Class and it asked me for package and class name like Java and created this class then after adding variables in it i did select each of them and said Generate Geters and setters, it marked that variable as private first and generated methods like this
After that i thought lets create
Then i made changes in the event listener for the Add Employee button so that it creates object of Contact and
Now when i try to add a new contact, the flex code creates a JSON string like this and submits it as form parameter
Then on the server side i am reading value of
Then i thought lets see if i can create a Action Script class and then convert it into JSON and submit it as JSON String to server side, so i did create my First Action Script Class like this
I used Flash Builder 4.0 for creating this class, All i had to do was to right click and say create Action Script Class and it asked me for package and class name like Java and created this class then after adding variables in it i did select each of them and said Generate Geters and setters, it marked that variable as private first and generated methods like this
package com.webspherenotes.flex
{
public class Contact
{
private var _firstName:String;
private var _lastName:String;
private var _email:String;
public function Contact()
{
}
public function get email():String
{
return _email;
}
public function set email(value:String):void
{
_email = value;
}
public function get lastName():String
{
return _lastName;
}
public function set lastName(value:String):void
{
_lastName = value;
}
public function get firstName():String
{
return _firstName;
}
public function set firstName(value:String):void
{
_firstName = value;
}
}
}
After that i thought lets create
ContactDTO
which will be used for submitting data from client side to server side so i created a class like this
package com.webspherenotes.flex
{
public class ContactDTO
{
private var _contact:Contact;
public function ContactDTO()
{
}
public function get contact():Contact
{
return _contact;
}
public function set contact(value:Contact):void
{
_contact = value;
}
}
}
Then i made changes in the event listener for the Add Employee button so that it creates object of Contact and
ContactDTO
and then makes use of JSON.encode()
method to convert that object into JSON String and submitted that string as a value of myContactStr
public function addContact():void{
trace("Entering useHttpService");
service = new HTTPService();
service.url = nextActionURL;
service.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
service.method = "POST";
var myContact:Contact =new Contact();
myContact.firstName = txtFirstName.text;
myContact.lastName = txtLastName.text;
myContact.email = txtEmail.text;
var myContactDTO:ContactDTO = new ContactDTO();
myContactDTO.contact = myContact;
var myContactJSONStr:String = JSON.encode(myContactDTO);
trace("Submitting MyContactStr " + myContactJSONStr);
var params:Object = { myContactStr: myContactJSONStr };
service.send(params);
getContactList();
trace("Entering useHttpService");
} }
Now when i try to add a new contact, the flex code creates a JSON string like this and submits it as form parameter
{"contact":{"firstName":"James","lastName":"bond","email":"jamesbond@mi5.com"}}
Then on the server side i am reading value of
myContactStr
form parameter instead of firstName, lastName and email
and then using XStream to parse that string into Contact
object with code like this
public static Contact convertJSONToJava(String jsonStr){
XStream xstream = new XStream(new JettisonMappedXmlDriver());
xstream.setMode(XStream.NO_REFERENCES);
xstream.alias("contact", Contact.class);
System.out.println("setting values of contact object");
Contact contact= (Contact)xstream.fromXML(jsonStr);
return contact;
}
Flex and JSON Portlet
I built a Sample Contact Management to demonstrate how you can use JSON as data exchange format for sending data from Portlet to Flex. You can download the sample code for portlet as well as Flex part
Note: After downloading sample first thing that you should do is go to Edit mode and click on Setup DB link, it will create Contact table in the hsqldb.
The Sample Contact application is Flex on the client side and on the server side it is JSR 286 compliant portlet, i am using HSQLDB for storing data and XStream for Converting Java Object to JSON. On the Flex side i am using as3corelib. There is Apache Maven 2 script that you can use for building the source code and downloading server side dependencies
I have a Contact table in database and when you access the portlet, it will query data in the Contact Table and display it in spread sheet format on the onload event. THere is a form at the top in Flex part which you can use for adding new record in the Contact table.
The Flex application has
There is quite a bit of ActionScript in this application but i already blogged about most of the pieces, lets take a look at them one by one
THe Server side code for this application is like this
Download the sample code of this portlet and try it.
Note: After downloading sample first thing that you should do is go to Edit mode and click on Setup DB link, it will create Contact table in the hsqldb.
The Sample Contact application is Flex on the client side and on the server side it is JSR 286 compliant portlet, i am using HSQLDB for storing data and XStream for Converting Java Object to JSON. On the Flex side i am using as3corelib. There is Apache Maven 2 script that you can use for building the source code and downloading server side dependencies
I have a Contact table in database and when you access the portlet, it will query data in the Contact Table and display it in spread sheet format on the onload event. THere is a form at the top in Flex part which you can use for adding new record in the Contact table.
<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx1="library://ns.adobe.com/flex/halo"
minWidth="500" minHeight="410"
creationComplete="initLogging()"
xmlns:mx="library://ns.adobe.com/flex/mx"
width="500" height="410">
<fx:Script>
<![CDATA[
import com.adobe.serialization.json.JSON;
import com.adobe.serialization.json.JSONDecoder;
import mx.controls.Alert;
import mx.logging.ILogger;
import mx.logging.Log;
import mx.logging.LogEventLevel;
import mx.logging.targets.TraceTarget;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
private function initLogging():void {
trace("Entering initLogging");
var logTarget:TraceTarget = new TraceTarget();
logTarget.level = LogEventLevel.ALL;
Log.addTarget(logTarget);
getContactList();
trace("Exiting initLogging");
}
private var service:HTTPService;
public function useHttpService(url:String):void {
trace("Entering useHttpService");
service = new HTTPService();
service.url = url;
service.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
service.method = "GET";
service.addEventListener("result", httpResult);
service.addEventListener("fault", httpFault);
service.send(parameters);
trace("Entering useHttpService");
}
public function httpResult(event:ResultEvent):void {
trace("Entering httpResult");
var result:Object = event.result;
var resultJSON:Object =JSON.decode(event.result.toString());
dgEmployees.dataProvider = resultJSON.contactDTO.contactList;
nextActionURL = resultJSON.contactDTO.actionURL;
trace("Entering httpResult");
}
public function httpFault(event:FaultEvent):void {
trace("Entering httpFault");
var faultstring:String = event.fault.faultString;
Alert.show(faultstring + event.fault.faultDetail + event.fault.rootCause);
trace("Entering httpFault");
}
private var nextActionURL:String;
public function getContactList():void {
var javaScriptMethodName:String = "getResourceURL";
if (ExternalInterface.available) {
var portletUrl:String = ExternalInterface.call(javaScriptMethodName);
}
trace("Resource URL " + portletUrl);
useHttpService(portletUrl);
}
public function addContact():void{
trace("Entering useHttpService");
service = new HTTPService();
service.url = nextActionURL;
service.resultFormat = HTTPService.RESULT_FORMAT_TEXT;
service.method = "POST";
var params:Object =
{ firstName: txtFirstName.text, lastName: txtLastName,email:txtEmail };
service.send(params);
getContactList();
trace("Entering useHttpService");
}
]]>
</fx:Script>
<fx:Declarations>
</fx:Declarations>
<mx:Panel x="0" y="0" width="500" height="410" layout="absolute"
title="Simple JSON Example">
<mx:DataGrid x="10" y="174" width="460" enabled="true" editable="false"
id="dgEmployees">
<mx:columns>
<mx:DataGridColumn headerText="First Name" dataField="firstName"/>
<mx:DataGridColumn headerText="Last Name" dataField="lastName"/>
<mx:DataGridColumn headerText="Email" dataField="email"/>
</mx:columns>
</mx:DataGrid>
<mx:Button x="190" y="121" label="Add Employee" id="getPerson"
click="addContact()" />
<mx:Label x="110" y="12" text="First Name"/>
<mx:TextInput x="189" y="10" id="txtFirstName" />
<mx:Label x="110" y="50" text="Last Name"/>
<mx:TextInput x="189" y="48" id="txtLastName" />
<mx:Label x="110" y="84" text="E-mail"/>
<mx:TextInput x="189" y="82" id="txtEmail" />
<mx:Label x="10" y="148" text="Employees:"/>
</mx:Panel>
</s:Application>
The Flex application has
DataGrid
which is used for displaying the Contact Summary, DataGrid has three DataGridColumn
which are used for displaying three Contact table columns. The MXML also has three input fields where user can enter values for adding new contact.There is quite a bit of ActionScript in this application but i already blogged about most of the pieces, lets take a look at them one by one
- initLogging() It is used for initializing the flex logging In addition to enabling log it also does one thing, which is to call the
getContactList()
function - getContactList() This function is used for making a Resource Request from Flex application to the portlet. Now as we know we cannot hard code the Resource URL in flex application, because the URL is dynamically generated by portal. So what i am doing is making use of JavaScript function to read the ResourceURL generated by JSP page that is including Flex object and then making HTTPService call to get it. Inside
getContactList()
, i am callinguseHttpService()
function for making actual HTTP Service call - useHttpService() This function is making a HTTP GET call to the URL supplied as parameter and attaching the
httpResult()
function as a result handler andhttpFault()
as error handler - httpResult() In This sample portlet the
serveResource()
method is returning contactlist in JSON format, so this method is used for parsing the JSON and getting contactList object out of it. I am usingJSON.decode()
function provided by as3corelib to convert the response into Flex object. Once you have the flex object you can access its fields. In my sample application the JSON response string would be something like this
{"contactDTO": {
"actionURL": "/pluto/portal/Sunils Test Page/__acJSONContactPortlet0x2JSON0x8C
ontact0x8Portlet!-318001599%7C0?",
"contactList": [
{
"firstName": "Jiya",
"lastName": "Patil",
"email": "jiya@gmail.com"
},
}
The JSON object has a ContactDTO object which has actionURL, which is a String and contactList which is list of Contact Objects which three fields. TheJSON.decode()
converts it into corresponding Flex object. Once the JSON is parsed i am assigning contactList as dataProvider for theDataGrid
dgEmployees.dataProvider = resultJSON.contactDTO.contactList
, so flex will read the list and display in the Spread sheet format. The JSON response also has action URL field which is nothing but actionURL for this portlet, As i mentioned in How to make action or render request from the Adobe Flex post, in WPS you cannot reuse the ActionURL instead you can use it only once. So i am generating the actionURL in the portlet and passing it as part of the JSON response, when the Flex code want to make action URL it will use the ActionURL sent in the last response, insideaddContact()
method - addContact() Method is event handler for Add Employee button, it reads the values entered by user in 3 input fields and submits them using HTTP POST method to the actionURL, which will result in processAction() method of the portlet being called, where i am adding that contact to the contact table. Please note that i am ignoring output of the HTTP POST call. After that call i am calling
getContactList()
for getting updated contact list and displaying in the spread sheet
THe Server side code for this application is like this
public class JSONContactPortlet extends GenericPortlet {
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering JSONContactPortlet.doView()");
response.setContentType("text/html");
getPortletContext().getRequestDispatcher("/index.jsp").include(request,
response);
System.out.println("Exiting JSONContactPortlet.doView()");
}
protected void doEdit(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering JSONContactPortlet.doEdit()");
response.setContentType("text/html");
getPortletContext().getRequestDispatcher("/setup.jsp").include(request,
response);
System.out.println("Exiting JSONContactPortlet.doEdit()");
}
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException {
System.out.println("Entering JSONContactPortlet.processAction()");
ContactDAO contactDAO = new ContactDAOImpl();
if(request.getPortletMode().equals(PortletMode.EDIT)){
contactDAO.setup();
}else if(request.getPortletMode().equals(PortletMode.VIEW)){
String firstName =request.getParameter("firstName");
String lastName =request.getParameter("lastName");
String email = request.getParameter("email");
contactDAO.insertContact(new Contact(firstName,lastName,email));
}
System.out.println("Exiting JSONContactPortlet.processAction()");
}
public void serveResource(ResourceRequest request, ResourceResponse response)
throws PortletException, IOException {
System.out.println("Entering JSONContactPortlet.serveResource()");
response.setContentType("text/html");
ContactDAO contactDAO = new ContactDAOImpl();
ArrayList contactList = contactDAO.getContactList();
ContactDTO contactDTO = new ContactDTO(response.createActionURL().toString(), contactList);
response.getWriter().println(JSONHelper.convertJavaToJSON(contactDTO));
System.out.println("Exiting JSONContactPortlet.serveResource()");
}
}
- doView() In this method i am passing control to index.jsp which generates a markup to include the swf file in the response and it also has
getResourceURL
JavaScript function that can be used for getting resource URL
() - doEdit() THis method is passing control to setup.jsp which generates action URL for setup. THe Edit mode of this portlet allows you to create Contact table in the database
- processAction() This method can be called from either the VIEW or EDIT mode, in the VIEW mode it used for inserting a row in contact table and in the EDIT mode it is used for setup, i.e. to crate CONTACT table
- serveResource() This method is fist getting list of contacts from ContactDAO and then using XStream to convert it into JSON string using code like this
public static String convertJavaToJSON(Object javaObj){
XStream xstream = new XStream(new JsonHierarchicalStreamDriver());
xstream.setMode(XStream.NO_REFERENCES);
xstream.alias("contactDTO",ContactDTO.class);
xstream.alias("contact", Contact.class);
String jsonString = xstream.toXML(javaObj);
System.out.println(jsonString);
return jsonString;
}
Download the sample code of this portlet and try it.
Validation Cache - ETag
Portlet Specification 2.0 has concept of Validation cache in addition to expiration based cache. Basic idea is Validation-based caching allows portlets to return a validation token together with the markup response and expiration time. The portlet can set the validation token on the RenderResponse or ResourceResponse via the ETAG property from within servlets/JSPs or via the CacheControl setETag method from within the portlet. If no expiration time is set, the content should be viewed by the portlet container as expired
After the content is expired the portlet container should send a render or
serveResource request to the portlet with the validation token (called ETag in HTTP) of the expired content. The portlet can access the validation token provided by the portlet container either via the property ETAG of the RenderRequest or ResourceRequest, or the getETag method of the RenderRequest or ResourceRequest. The portlet can validate if the cached content for the given ETag is still valid or not. If the content is still valid the portlet should not render any output but either set the property USE_CACHED_CONTENT RenderResponse or ResourceResponse and a new expiry time, or setUseCachedContent on the CacheControl of the RenderResponse or
ResourceResponse and set a new expiry time. The portlet should set the validation
token, expiry time or caching scope before writing to the output stream as otherwise
portals / portlet containers may ignore the values.
I tried to modify my
I was hoping that after the first invocation
After the content is expired the portlet container should send a render or
serveResource request to the portlet with the validation token (called ETag in HTTP) of the expired content. The portlet can access the validation token provided by the portlet container either via the property ETAG of the RenderRequest or ResourceRequest, or the getETag method of the RenderRequest or ResourceRequest. The portlet can validate if the cached content for the given ETag is still valid or not. If the content is still valid the portlet should not render any output but either set the property USE_CACHED_CONTENT RenderResponse or ResourceResponse and a new expiry time, or setUseCachedContent on the CacheControl of the RenderResponse or
ResourceResponse and set a new expiry time. The portlet should set the validation
token, expiry time or caching scope before writing to the output stream as otherwise
portals / portlet containers may ignore the values.
I tried to modify my
ValidationCacheSamplePortlet
to see if the ETAG works in either doView() or render method and it seems that both Pluto and WebSphere Portal 6.1.5 ignore the ETAG
public class ValidationCacheSamplePortlet extends GenericPortlet {
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ValidationCacheSamplePortlet.doView()");
response.setContentType("text/html");
if (request.getETag() != null) { // validation request
System.out.println("ETag is not null, returning from cache");
response.getCacheControl().setExpirationTime(300);
response.getCacheControl().setUseCachedContent(true);
return;
}
System.out.println("ETag is null returning new content");
// create new content with new validation tag
response.getCacheControl().setETag("ABCD");
response.getCacheControl().setExpirationTime(6000);
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(
"/validation.jsp");
rd.include(request, response);
System.out.println("Exiting ValidationCacheSamplePortlet.doView()");
}
public void serveResource(ResourceRequest request, ResourceResponse response)
throws PortletException, IOException {
System.out
.println("Entering ValidationCacheSamplePortlet.serveResource()");
if (request.getETag() != null) { // validation request
System.out.println("ETag is not null, returning resource response from cache");
response.getCacheControl().setExpirationTime(300);
response.getCacheControl().setUseCachedContent(true);
return;
}
System.out.println("ETag is null returning new content");
response.getCacheControl().setETag("1212121ABCD");
response.getCacheControl().setExpirationTime(6000);
response.getWriter().println("Hello from ValidationCacheSamplePortlet.serveResource " + new Date());
System.out
.println("Exiting ValidationCacheSamplePortlet.serveResource()");
}
}
I was hoping that after the first invocation
getETag()
would return valid value and as a result the control will go to the block that says returning cached content but it did not work, getETag()
always returns null
Validation Cache - Setting cache-control header
The portlet specification 2.0 has concept of Expiration Cache that allows you to make use of the Browser Cache to cache the response. The ValidationCacheSamplePortlet , demonstrates how to do that.
The Portlet Specification has introduced
The
In this sample code i am calling following two methods of CacheControl object in the
I tried deploying this portlet in WPS and accessing it through firefox and what i see is that portal is setting correct cache-control headers.
If you look at the Firefox cache then you will notice that the resource is cached for approx. 10 min. like this
Note: It seems that not all the portlet container support
The Portlet Specification has introduced
javax.portlet.CacheControl
object that can be used to get granular control over the caching of the response. YOu can get object of CacheControl
from either RenderResponse
or ResourceResponse
and call its method. The
ValidationCacheSamplePortlet
portlet is very simple, all it does is that in the doView()
method it forwards control to validation.jsp and in the validation.jsp it renders a button, which you click on that button it is making XHR call to the resource URL. Inside the serveResource
method i am setting caching properties
public class ValidationCacheSamplePortlet extends GenericPortlet{
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
System.out.println("Entering ValidationCacheSamplePortlet.doView()");
response.setContentType("text/html");
getPortletContext().getRequestDispatcher("/validation.jsp").include(request, response);
System.out.println("Exiting ValidationCacheSamplePortlet.doView()");
}
public void serveResource(ResourceRequest request, ResourceResponse response)
throws PortletException, IOException {
System.out.println("Entering ValidationCacheSamplePortlet.serveResource()");
response.getCacheControl().setExpirationTime(600);
response.getCacheControl().setPublicScope(true);
response.setContentType("text/html");
response.getWriter().println("Hello from ValidationCacheSamplePortlet.serveResource "
+ new Date());
System.out.println("Exiting ValidationCacheSamplePortlet.serveResource()");
}
}
In this sample code i am calling following two methods of CacheControl object in the
serveResource
- setExpirationTime(600): This method will set cache-control equal to 600, which is equivalent of telling browser that cache this response for 600 seconds or 10 minutes and dont make request to server to get new response during next 5 min.
- setPublicScope(true) This method is used for setting cache-control to public that means this response is not private to the user and its same for user A as well as user B, so even proxy can cache it. If you set this to false then that will tell proxy that this response is private and only browser can cache it
I tried deploying this portlet in WPS and accessing it through firefox and what i see is that portal is setting correct cache-control headers.
If you look at the Firefox cache then you will notice that the resource is cached for approx. 10 min. like this
Note: It seems that not all the portlet container support
CacheControl
and not uni-formally. Ex. This sample code to set cache-control header does not work in Pluto, it works in WebSphere Portal 6.1.5 in the serveResource()
method but not in doView()