Liferay Custom JSON web services on Multiple Tables
Objective:
Create JSON web services for combination of multiple tables means prepare JSON web services so that data should come from multiple tables which are related each other.
Download CustomJsonWebservices portlet from following location
You can find source and war file
Note:
Portlet developed in Liferay 6.1GA2 EE version
If you want deploy in CE version you just do changes in liferay-plugin-package.properties
Liferay 6.1 EE version
name= CustomJsonWebservices module-group-id=liferay-ee module-incremental-version=1 tags= short-description= change-log= page-url=http://www.liferay.com author=Liferay, Inc. licenses=EE liferay-versions=6.1.20 |
Liferay 6.1 CE version
name = CustomJsonWebservices module-group-id=liferay module-incremental-version=1 tags= short-description= change-log= page-url=http://www.liferay.com author=Liferay, Inc. licenses=LGPL liferay-versions=6.1.1 |
Procedure for deploy portlet:
You can use war file and directly place in your portal deploy folder and test or you can also use source to deploy portlet.
Once portlet is deployed successfully insert data in the following table as shown in screens
json_employee
json_address
And test web service URLs
Liferay provide JSON web services so that we can use those services in other systems.
Liferay providing two types of web services SOAP based and REST bases web services.
Liferay JSON web services come under REST based web services.
Here we are talking about only rest based web services i.e. liferay JSON web services.
Liferay have built in ability can generate JSON web services by default when we make remote-service true for service builder.
But the services which created by default we can apply on only one table means data can served from only one table or web services related to single entity.
But in real scenario the default web services not enough to full fill real requirement there we have write our custom methods to produce JSON data. This pretty easy in liferay because liferay also provides write custom methods for JSON web services in pluin portlet.
Note:
Liferay have provided to generate service layer using service builder. We will use servce builder to generate service layer classes and methods
Steps to produce Custom JSON web services in Liferay Plugin portlet environment
The Following are the steps write custom methods to produce JSON web services:
Step: 1
Create Plugin portlet in Liferay using liferay IDE and portlet is liferay MVC portlet.
Step: 2
Create service bolder for plugin portlet
Open service.xml file and define data base tables and put remote-service is true. When we put remote service true then it will create default JSON web services.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.1.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_1_0.dtd"> <service-builder package-path="com.meera.jsonwebservices.db"> <author>E5410</author> <namespace>JSON</namespace> <entity name="Employee" local-service="true" remote-service="true"> <column name="emplyeeId" type="long" primary="true" /> <column name="emplyeeName" type="String" /> <column name="employeeDesignation" type="String" /> <order by="asc"> <order-column name="emplyeeId" /> </order> </entity> <entity name="Address" local-service="true" remote-service="true"> <column name="Id" type="long" primary="true" /> <column name="emplyeeId" type="long" /> <column name="employeeAddress" type="String" /> <order by="asc"> <order-column name="emplyeeId"/> </order> <finder name="emplyeeId" return-type="Collection"> <finder-column name="emplyeeId" /> </finder> </entity> </service-builder> |
Step: 3
Now we need add JSON Web service servelet in web.xml of porltet. So that it will get web services exposing ability.
<?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>CustomJsonWebservices-portlet</display-name> <filter> <filter-name>Secure JSON Web Service Servlet Filter</filter-name> <filter-class>com.liferay.portal.kernel.servlet.PortalClassLoaderFilter</filter-class> <init-param> <param-name>filter-class</param-name> <param-value>com.liferay.portal.servlet.filters.secure.SecureFilter</param-value> </init-param> <init-param> <param-name>basic_auth</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>portal_property_prefix</param-name> <param-value>jsonws.servlet.</param-value> </init-param> </filter> <filter-mapping> <filter-name>Secure JSON Web Service Servlet Filter</filter-name> <url-pattern>/api/jsonws/*</url-pattern> </filter-mapping> <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> <jsp-config> <taglib> <taglib-uri>http://java.sun.com/portlet_2_0</taglib-uri> <taglib-location> /WEB-INF/tld/liferay-portlet.tld </taglib-location> </taglib> <taglib> <taglib-uri>http://liferay.com/tld/aui</taglib-uri> <taglib-location>/WEB-INF/tld/aui.tld</taglib-location> </taglib> </jsp-config> </web-app> |
Step: 4
Run service builder using ant build-service or in eclipse you can run this from ant view.
Step: 5
Writing Custom methods for produce JSON data
We need to decide for which entity we have to provide custom JSON services .find appropriate entity and write method XXXServiceImpl.javathis is under package of
you-base-package.service.impl package of your source.
Here XXX is entity name which is specified in service.xml
In our example I have implemented in EmployeeServiceImpl.javahere I am getting employee object by employeeId. This method return employee object. In liferay any object automatically sterilizes and produces as JSON data.
The following is example for code
public class EmployeeServiceImpl extends EmployeeServiceBaseImpl { public com.meera.jsonwebservices.db.model.Employee getEmployee( long emplyeeId) throws com.liferay.portal.kernel.exception.PortalException, com.liferay.portal.kernel.exception.SystemException { return EmployeeLocalServiceUtil.getEmployee(emplyeeId); } } |
Once we completed writing custom method in XXXServiceImpl.java
Then we need to run service builder using ant build-service command or from eclipse ant view you can run same command
Step: 6
Now finally deploy the portlet into server by using ant deploy command or from ant view you can use same command
Now we are ready with custom JSON web services
Note:
For each modification in XXXServiceImpl.java or in service layed we have run ant build-service command later we have to deploy the portlet using ant deploy then only change will be applied to the services.
Accessing JSON web services
To access the services we need to use following URL pattern
Server:
It’s your domain name or host name if you are in local the its “localhost”
Port:
It’s your application server port number.
plugin-context:
it’s our plug-in portlet context name its simple as portlet name
api/jsonws:
This is path for call JSON Web Service servlet this is configured in web.xml
service-class-name:
service-class-name is generated from the service’s class name by removing the Service or ServiceImplsuffix and making it lower case.This is our service class name where we implemented custom method or its simple a entity name which is in service.xml. In our example we implemented our method in EmployeeServiceImpl.java
Finally we have to use in URL as employeewhen we observe it’s just entity name in service.xml
service-method-name:
Service-method-name is generated from the service’s method name by converting its camel case to lower case and using dashes (-) to separate words
Finally our Complete URL like this for our Example:
Plugin context path | CustomJsonWebservices-portlet |
Web service servelet path | /api/jsonws |
Service Class Name | EmployeeServiceImpl.java converted as employee |
Service Method Name | getEmplyee(--) converted as get-employee |
Passing parameters and its values:
We can pass parameters in two ways
Query String
URL pattern and separated param name and value by slash(/)
Complete URL example with query string
Complete URL by URL pattern:
For Our Example Complete URL to access employee data by passing employee id is following like this
OR result: {"employeeDesignation":"SoftwareEngineer", "emplyeeId":1,"emplyeeName":"meera"} |
Note:
Some time its ask Authentication so we should pass admin credentials as URL headers
With authentication The URL like follow
OR result: {"employeeDesignation":"SoftwareEngineer", "emplyeeId":1,"emplyeeName":"meera"} |
Note:
All these URLs you can use from any browser and see the JSON data. Here sometimes browsers not allows @ or # symbols in URL then we have to make this as URL encoding type characters
For encode URL and test in browse use following link and encode
Encoded URL:
OR |
Well as of now we just done created Custom service and its calling
Now we will see another way to call same web service method in JSON Web Services Invoker
JSON web service Invoker:
JSON Web Services Invoker is one of the mechanism to call some complex web services very easy way.
Here we can call JSON web service Invoker by following URL pattern
URL explanation is same like i explained previous section here we need to remember is to call invoker we have to use path /api/jsonws/invoke
This call take only one parameter i.e. cmd the value of this parameter is JSON Map.
Here we need to pass parameter as query string only like follow
Our complete URL for example is
What is JSON Map?
JSON Map is simple JSON object it contains key and value here value is another JSON object which contains key and value
In JSON map key will be used as service call path and value is parameterswhich passing to service calls.
The same example we discussed above we can do like this
Take the example get employee data by passing id.
Our Normal URL call is as follows
In above URL service call is employee/get-employee/ and parameter is employeeId and its value is 1
Now the following is JSON map for above
{ "/employee/get-employee": { "emplyeeId": 1, } } |
Here key is used as Service call and value is another json object contains parameters and its value.
Now call our complete web service call using JSON web service Invoker as follows
result: {"employeeDesignation":"Software Engineer", "emplyeeId":1,"emplyeeName":"meera"} |
This is way we have to pass JSON map to JSON web service invoker.
We can assign data as some reference object like following .here we have to specify $ before variable
{ "$emp=/employee/get-employee": { "emplyeeId": 1, } } |
$emp is reference variable
In above call we will get JSON data have all columns of Employee Table
Fetching required columns from JSON Web service invoker means filtering columns
{ "$emp[emplyeeName,emplyeeId]=/employee/get-employee": { "emplyeeId": 1, } } |
The following is complete web service call to filter columns using JSON web service invoker
result: {"emplyeeId":1,"emplyeeName":"meera"} |
As of now we just call custom web service which is related to one entity or one table
Now we will see how to fetch JSON data so that data comes from multiple Tables
Some times in real requirement the data should fetch from multiple tables.
To make use of custom method we can produce JSON web services so that data come from multiple tables.
Understanding Real Scenario:
I have one employee each employee have multiple addresses.
Here I have two tables Employeeand Address tables.
If we generate JSON web services from liferay it will serve only one table data for service. But here data stored in two tables.
So now we have to produce JSON services so that data should come from Employee Tables and Address table
Employee Table:
employeeId | employeeName | employeeDesignation |
1 | meera | Software Engineer |
2 | prince | Architect |
3 | savvy | Developer |
Address Table:
Id | emplyeeId | Address |
1 | 1 | Hyderabad |
2 | 1 | Hong Kong |
2 | 1 | Landon |
When you observe above data employee meera have 3 addresses
This is where we need to apply custom JSON web services.
Assume my data should be like as followed structure
{ name:”meera”, addresess:[ “Hyderabad”,”Landon”,”Hong Kong” ] } |
We have two options
Complete manual preparation of JSON data
Use JSON Web service Invoker Nested calls
Complete manual preparation of JSON data:
In the manual preparation we will prepare JSON structure and will produce data.
In general List and any java object type can be serialize and will be produces as JSON data in liferay web services.Some scenarios some complex object will not be sterilize.If some thing complex structure then we have to prepare JSON data and we will produce as web services.
Default sterilization for list type and any object type:
In this scenario when we return some java object in custom method it will be automatically converted as JSON data. Here we need not do like this
Example for Java Object to JSON data or JSON Object:
public class EmployeeServiceImpl extends EmployeeServiceBaseImpl { public com.meera.jsonwebservices.db.model.Employee getEmployee( long emplyeeId) throws com.liferay.portal.kernel.exception.PortalException, com.liferay.portal.kernel.exception.SystemException { return EmployeeLocalServiceUtil.getEmployee(emplyeeId); } } |
In above custom method getEmplyeereturn Employee objects type. Here we need not to serialize the data and it will be converted as JSON data by default this is default behavior of liferay web services.
The following is URL to access method
OR result: {"employeeDesignation":"Software Engineer", "emplyeeId":1,"emplyeeName":"meera"} |
Note:
In the above custom method return Employee object but finally object convereted as JSON data.
In the above custom method return Employee object but finally object convereted as JSON data.
Example for List to JSON data
public class AddressServiceImpl extends AddressServiceBaseImpl { public List<com.meera.jsonwebservices.db.model.Address> getAddressList(long emplyeeId) throws com.liferay.portal.kernel.exception.PortalException, com.liferay.portal.kernel.exception.SystemException { return AddressUtil.findByemplyeeId(emplyeeId); } } |
In the above custom method return list of Address objects for particular employee id.
Here list will be converted as JSON data by default
The following is URL to call the above service
OR result: [{"employeeAddress":"Hyderabad","emplyeeId":1,"id":1}, {"employeeAddress":"Hong Kong","emplyeeId":1,"id":2}] |
Here Address is our service class and getAddressList is custom method to give list of address objects for particular employee.
Here List will be converted as JSON array simple.
Some scenarios which data is complex then default sterilization not work all times when default sterilization not work then we will prepare JSON data manually in custom method. Scenarios like list have map objects and map have list objects.
Example for specialized structure:
Take our previous scenario one employee have many address but my data structure should be like follows
{ name:”meera”, addresess:[ “Hyderabad”,”Landon”,”Hong Kong” ] } |
We can’t produce this same JSON data by default here we have to prepare JSON data manually from java objects.
The following is Example:
public class EmployeeServiceImpl extends EmployeeServiceBaseImpl { public JSONObject getEmployeeManualJsonData( long emplyeeId) throws com.liferay.portal.kernel.exception.PortalException, com.liferay.portal.kernel.exception.SystemException { JSONObject employeeData=JSONFactoryUtil.createJSONObject(); JSONArray addressArray=JSONFactoryUtil.createJSONArray(); Employee empObject=EmployeeLocalServiceUtil.getEmployee(emplyeeId); employeeData.put("name",empObject.getEmplyeeName()); for(Address empaddress:AddressUtil.findByemplyeeId(emplyeeId)){ addressArray.put(empaddress.getEmployeeAddress()); } employeeData.put("addresess", addressArray); return employeeData; } } |
Here service class is EmployeeServiceImpl.java it will be converts as employee in URL and method is getEmployeeManualJsonData it will be converted in URL as get-employee-manual-json-data
The following is URL to access above service
OR result: {"name":"meera","addresess":["Hyderabad","Hong Kong","Landon"]} |
Note:
liferay providing JSONFactoryUtil class from this we can create JSONArray and JSONObject.
By using above classes we can prepare JSON data manually in custom web service method.
Nested JSON web service Invoker:
We already know JSON Web service Invoker is one of the way to call web services in liferay.
Nested JSON web service invoker has ability serve data from multiple tables.
By using this we can get data from multiple tables which are related to each other.
Same above scenarios I want data fetch all addresses for employee by pass employeeId.
We can call following web service invoker to get employee address. From nested web service invoker we can achieve that.
The following is JSON map for fetch employee addresses
{ "$employee =/employee/get-employee": { "emplyeeId": 1, "$address = /address/get-address-list": { "@emplyeeId" : "$employee.emplyeeId" } } } |
@emplyeeId at which parameter we pass to service call
$employee.emplyeeId we will get employeeId from $employee reference object.
emplyeeId is same as column name of entity which we mention in service.xml.
Observer above JSON Map:
First we need to fetch employee object and referenced by variable called $employee we use this employee object to get employeeId and this id will be passed to /address/get-address-listservice call.
In the first service call i.e. /employee/get-employee we will get employee object and referenced by variable, in the next service call i.e. /address/get-address-list we use this reference variable to get employeeIdand that will passed as parameter value.
Here we are calling two service calls within one JSON Map object that is why we call it as Nested JSON Web Service Invoker.
Note:
Here all parameters which we passed in JSON map should be same as parameter names which we used in custom method implementation in XXXServiceImplclass.
The final web service call using Nested JSON web service invoke
result: {"emplyeeId":1,"address":[{"employeeAddress":"Hyderabad","emplyeeId":1,"id":1}, {"employeeAddress":"Hong Kong","emplyeeId":1,"id":2}, {"employeeAddress":"Landon","emplyeeId":1,"id":3}], "emplyeeName":"meera","employeeDesignation":"Software Engineer"} |
Filter columns in Nested JSON web service call
result: {"emplyeeId":1,"address":[{"employeeAddress":"Hyderabad","emplyeeId":1,"id":1}, {"employeeAddress":"Hong Kong","emplyeeId":1,"id":2}, {"employeeAddress":"Landon","emplyeeId":1,"id":3}],"emplyeeName":"meera"} |
In the above call we only get employeeIdand employeeName from employee object.
Now calling JSON web service with portal context:
As of now we have used plugin portlet context for calling web services. We can also call web services using portal context/
The following is pattern to call from portal context:
.
Note:
Conveniently, requests sent this way can leverage the user’s authentication in his current portal session. Liferay’s JavaScript API for services calls plugin services using this method.
The following is call web service from plugin context.
Note:
This calls the plugin’s service in a separate web application that is not aware of the user’s current session in the portal. As a result, accessing the service in this manner requires additional authentication. You might use this for batch services or other requests that don’t require context.
The following are dependent properties related to JSON web service in liferay
jsonws.web.service.invalid.http.methods=DELETE,POST,PUT jsonws.web.service.strict.http.method=true json.service.auth.token.enabled=true json.service.auth.token.hosts.allowed=255.255.255.255 json.deserializer.strict.mode=false jsonws.web.service.public.methods=* |
Note:
Please consider all above properties in your portal.properties file. If something you want change you can use portal-ext.properties file to override.
Important points:
- Liferay provides two kinds of web services SOAP and REST.
- JSON web services is part REST bases web services which partially follows rest specifications.
- To implement service layer in liferay we will use service builder to and if we use remote- service is true then we can get data base services and JSON web services.
- Liferay web services serve only one table data by default.
- If we want get data which is related multiple tables or some specialize requirement we have to implement customer web services.
- To implement any custom services we will use XXXServiceImpl.java class.
- We can access JSON web services from simple URL pattern URL include plugin context, service class name, service method name and its parameters used by service method.
- We can pass parameter as query string or normal URL pattern so that each param and values should be separated by slash (/)
- We can access JSON web services using plug-in portlet context and portal context too. But difference is when we call from plug-in context we need implement our own authentication mechanism for services. If we use portal context then it will use authentication mechanism which provided by liferay portal.
- JSON web service invoker is way to call web services which will fetch data from multiple tables.
- To fetch data from multiple tables we have to implement custom method so that custom method prepares JSON data manually or we can use Nested JSON web service invoker.
- By default all java objects, list and map can be sterilize automatically and it will be produced as JSON data. Some scenarios if data has complex structure then we have to prepare JSON data manually.
- To prepare JSON data manually we can JSONFcatoryUtil class this can create JSONObject and JSONArray.
Screen 1:
The following screen depicts call JSON web services in browser and result will be JSON data.
Note:
In the URL host name and port number change according to you environment and in the web service URL pass your liferay admin username and password.
Reference Links:
0 comments:
Post a Comment