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.java
this 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.java
here 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 ServiceImpl
suffix 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 employee
when 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 parameters
which 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 Employee
and 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 getEmplyee
return 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-list
service 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 employeeId
and 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 XXXServiceImpl
class.
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 employeeId
and 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:
Thanks for sharing this excellent topic, and explaining each step quite well. You should also look at that tool for the encoding/decoding
ReplyDeleteurl-decode.com/
that tool also contains 100+ web utilities as well for the users. It will definitely help the users as well.