Tuesday, April 23, 2013

Liferay: using DynamicQuery in a velocity template.

Consider a scenario if you have 1000 records in a Dynamic Data List (DDL) record set. You need to show only 20 latest records. One ways is to bring all the records and use a loop to show the records and break when a counter is greater then 20, this will cause slowness in page as you are bringing the complete record set.

To overcome this consider if you bring only latest 20 records from database something like at most list screens in liferay where pagination is used. Below is the set of lines that will allow you to achieve this;



#set ($ddlRecordSet = $serviceLocator.findService('com.liferay.portlet.dynamicdatalists.service.DDLRecordSetLocalService'))
#set ($ddlLocalService = $serviceLocator.findService('com.liferay.portlet.dynamicdatalists.service.DDLRecordLocalService'))

#set ($recordSetId = $getterUtil.getLong($reserved_record_set_id.data, 0))
#set ($DDLrecordClass = $portal.getClass().forName("com.liferay.portlet.dynamicdatalists.model.DDLRecord"))
#set ($dqfu = $portal.getClass().forName("com.liferay.portal.kernel.dao.orm.DynamicQueryFactoryUtil"))
#set ($ofu = $portal.getClass().forName("com.liferay.portal.kernel.dao.orm.OrderFactoryUtil"))
#set ($rfu = $portal.getClass().forName("com.liferay.portal.kernel.dao.orm.RestrictionsFactoryUtil"))
#set ($q = $dqfu.forClass($DDLrecordClass))
#set ($V = $q.add($rfu.eq("recordSetId", $getterUtil.getLong($recordSetId))))
#set ($V = $q.addOrder($ofu.desc("modifiedDate")))
#set ($records = $ddlLocalService.dynamicQuery($q,0,20))
#set ($recordTitle = $ddlRecordSet.getRecordSet($recordSetId))
#set ($title = $recordTitle.getName())

You can modify the dynamic query as per your needs to order data on different columns.

Thursday, December 06, 2012

Why Liferay ?


So why is Liferay Portal the best portal product in our opinion?
  1. Liferay has the lowest Total Cost of Ownership (TCO) compared to its competitors starting with its licensing and getting it up and running through development costs, operational costs, and training/support costs (from the perspective of infrastructure, developers, administrators, and end users).
  2. Second-to-none rich out-of-the-box (OOTB) functionality around core portal, content management, collaboration, social, mobile, security and more; check out http://www.liferay.com/products/liferay-portal/features/portal for more information.
  3. All portal products typically need extensions and/or additions to deliver requisite functionality – with Liferay you can simply do more within a specific budget.
  4. Product innovation – leader in introducing new capabilities whether it be AJAX or friendly URLs or mobile or social
  5. Improved business agility – it is lightweight in nature; you can quickly get it up and running, and it is easier to develop on/manage.
  6. A mature Enterprise Open Source (fully supported) product – 24x7x365 platinum support with 1 hour response time SLA; this includes access to all service packs, hot fixes, notifications of security alerts, phone and web based support.
  7. Liferay’s open architecture and its open source nature help you avoid lock-in to a single proprietary vendor.
  8. Liferay’s hook and extension plugin model allows you to tailor product behavior to your needs without rewriting from scratch and without creating upgrade hell.
  9. Liferay offers opportunities for product feature sponsorship to enable contributions back into the core product for key customizations – this allows you to offload responsibility of maintaining and enhancing your custom features back to Liferay.
  10. Liferay offers you a full choice of application servers, databases, and operating systems to run on, thereby allowing you to leverage your infrastructure and skills investment.
References:




Wednesday, December 05, 2012

Part 3: Custom Liferay Plugin JSON Web Services

Go to Previous post Part 2: Custom Liferay Plugin JSON Web Services
Below is the step by step setup to expose and consume a plugin portlet service as JSON web service.

Step 1: Create a liferay plugin portlet

Step 2: Create a custom service

Using Liferay Service builder create a service. For example

The service.xml will look like below:
<service-builder package-path="com.rdg.api">
 <author>rishidev.gupta</author>
 <namespace>ServiceAPI</namespace>

 <entity name="Bridge" remote-service = "true" local-service="true" human-name="Bridge">
  <reference package-path="com.liferay.counter" entity="Counter"></reference>
  <reference package-path="com.liferay.portal" entity="User"></reference>
  <reference package-path="com.liferay.portlet.journal" entity="JournalArticle"></reference>
 </entity>
</service-builder>

Step 3: Build the service using liferay service builder 

Step 4: Writing a custom method to be exposed

public class BridgeLocalServiceImpl extends BridgeLocalServiceBaseImpl {

 public String getBridge(String str) throws PortalException {
  System.out.println("You have successfully requested for: " + str);
  return "You have successfully requested for: " + str;

 }
}
public class BridgeServiceImpl extends BridgeServiceBaseImpl {
 public String getBridge(String str) throws PortalException {
  return bridgeLocalService.getBridge(str);
 }
}
Now rebuild the service.

Step 5: Configuring web.xml 

To make sure that your custom portlets can be scanned, and their service can become part of the JSON API. For this you must add the following in portlets web.xml:


     <servlet>
        <servlet-name>JSON Web Service Servlet</servlet-name>
        <servlet-class>com.liferay.portal.kernel.servlet.PortalClassLoaderServlet</servlet-class>
        <init-param>
            <param-name>servlet-class</param-name>
            <param-value>com.liferay.portal.jsonwebservice.JSONWebServiceServlet</param-value>
        </init-param>
        <load-on-startup>0</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>JSON Web Service Servlet</servlet-name>
        <url-pattern>/api/jsonws/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>JSON Web Service Servlet</servlet-name>
        <url-pattern>/api/secure/jsonws/*</url-pattern>
    </servlet-mapping>

This enables the servlet that is responsible for scanning JSON web services configuration. Now deploy the plugin portlet in a running tomcat server.

Step 6: Consuming JSON Web Service

 To list registered services on portlet, 
http://localhost:8080/<portlet-context>/api/jsonws
 
To access service using browser
http://localhost:8080//service-api-portlet/api/jsonws/bridge/get-bridge?&str=rishidev.gupta 

Saturday, November 10, 2012

Part 2: Accessing and Using Liferay JSON Web Services

In previous post, I have worked on introducing all basics about JSON Web Services.
In this post the focus is on accessing and consuming the the services and example

To Restrict a method from being exposed as service, just annotate the method with:
@JSONWebService(mode = JSONWebServiceMode.IGNORE)

To define custom HTTP method name instead of using the default, just annotate the method with:
@JSONWebService(value = "get-user", method = "GET")
public User getUserById( ...

Thus above service will be exposed and accessed as

http://localhost:8080/api/jsonws/user/get-user

instead of 

http://localhost:8080/api/jsonws/user/get-user-by-id

To further customize the URL and remove the serviceClassname, just annotate the method with:
@JSONWebService("/get-user")
public User getUserById(..

Thus above service will be exposed and accessed as

http://localhost:8080/api/jsonws/get-user

instead of 

http://localhost:8080/api/jsonws/user/get-user-by-id

Portal Configuration

JSON Web Services can be enabled or disabled by setting the below portal property:
json.web.service.enabled=true
by default the services are enabled and the property is set to true.

Enabling/Disabling particular HTTP methods
jsonws.web.service.invalid.http.methods=DELETE,POST,PUT
causes portal to accept only GET requests and ignore the HTTP methods specified above.

Below is the comma separated list of public service methods that can be accessed by unauthenticated user:
For all methods as unauthenticated use below
jsonws.web.service.public.methods=*  
For is methods as unauthenticated use below

jsonws.web.service.public.methods=is*

Accessing service via HTML form


<form action="http://localhost:8080/api/jsonws/user/get-user-by-id" method="GET">
        <input type="hidden" name="userId" value="10172"/>
        <input type="submit" value="submit"/>
</form>
When submitted will result in response consisting of User object as JSON string.

Accessing service via Java based Apache HttpClient API 

Below is the example to access the Liferay services using the Apache HTTPClient API
public static void get() throws Exception {
  HttpHost targetHost = new HttpHost("localhost", 8080, "http");
  DefaultHttpClient httpclient = new DefaultHttpClient();
  httpclient.getCredentialsProvider().setCredentials(
    new AuthScope(targetHost.getHostName(), targetHost.getPort()),
    new UsernamePasswordCredentials("test", "test"));

  // Create AuthCache instance
  AuthCache authCache = new BasicAuthCache();
  // Generate BASIC scheme object and add it to the local
  // auth cache
  BasicScheme basicAuth = new BasicScheme();
  authCache.put(targetHost, basicAuth);

  // Add AuthCache to the execution context
  BasicHttpContext ctx = new BasicHttpContext();
  ctx.setAttribute(ClientContext.AUTH_CACHE, authCache);

  HttpPost post = new HttpPost(
    "/api/jsonws/user/get-user-by-id"); 
  List<NameValuePair> params = new ArrayList<NameValuePair>();
  params.add(new BasicNameValuePair("userId", "10172"));
  
  UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "UTF-8");
  post.setEntity(entity);
  HttpResponse resp = httpclient.execute(targetHost, post, ctx);
  System.out.println(resp.getStatusLine());
  resp.getEntity().writeTo(System.out);
  httpclient.getConnectionManager().shutdown();
 }


When submitted will result in response consisting of User object as JSON string.

Go to  Next post Part 3: Custom Liferay Plugin JSON Web Services

References:
The article is my summarized view while working on the topic and references have been taken from Liferay community and documentation

Part 1: Introduction Liferay JSON Web Services

JSON Web Services provide convenient way access to portal service layer methods by exposing them as JSON HTTP API. This makes services methods easily accessible using HTTP requests, not only from JavaScript within the portal, but also from any JSON-speaking client.

Registering JSON web services


While using Service Builder to build services via service.xml append each entity definition with remote-service="true" to create and enable web services for the entity.  By doing this All remote-enabled services (i.e. entities with remote-service="true" in service.xml) are exposed as JSON Web Services.

When Service Builder creates each -Service.java interface for a remote-enabled service, the @JSONWebService annotation is added on the class level of that interface. Therefore, all of the public methods of that interface become registered and available as JSON Web Services.

On server startup a restricted scanning is done on portal and service jar files for the classes that uses @JSONWebService annotation. Post this the annotation are loaded and service methods are exposed as JSON based API.

For example UserService looks as below:
@JSONWebService
public interface UserService{
...
}

 Accessing Services

Below is the convention used to access any service exposed by default Liferay bundle
http://[server]:[port]/api/jsonws/[service-class-name]/[service-method-name] 
service-class-name is the name generated from service class name, 
by removing the Service or ServiceImpl suffix and converting it to a 
lowercase name.
service-method-name is generated from the service method name, 
by converting the camel-case method name to a lowercase separated-by-dash name
For example UserService can be accessed as:
@JSONWebService
public interface UserService{

 public User getUserById(long userId) {...}
}


http://localhost:8080/api/jsonws/user-service/get-user-by-id

All methods prefixed with get, has, is are pre assumed to be readonly and thus are exposed as GET methods, leaving rest as POST methods.

Non-public service methods require the user to be registered before invoking the method and accessed as

http://[server]:[port]/api/secure/jsonws/[service-class-name]/[service-method-name]http://localhost:8080/api/jsonws/user-service/get-user-by-id

Viewing list of default liferay bundle service

You can view all the registered and available service listing ay accessing the below url
http://localhost:8080/api/jsonws
 
In my next post, I have further detailed about configuring and 
accessing the JSON Web services 
 
References:
The article is my summarized view while working on the topic and references have been taken from Liferay community and documentation