Liferay 7 REST Webservices Tutorial

Liferay 7 REST Web services Tutorial for creating Jersey REST web services using OSGI.Liferay Service builder has capability to auto create JSON REST services, but still there are some pros and cons of using it and explained in the below tutorial:

 Liferay 6 Jersey REST  Integration Tutorial

Jersey Integration in liferay is easy as in other framework and this tutorial will drive you on how to create web services using jersey framework. Liferay out of the box provides axis engine framework for SOAP and JSON web services.READ MORE

Installations:

OSGI Component offers  Jersey Application as bridge to create REST service and which make easy for us create REST services on the fly than the service builder. Liferay 7 IDE has REST Template project creation to easy the development.

Let’s Jump into tutorial and If you are new to REST then  click here to access complete REST tutorials to understand basics.  As explained in this tutorial:  Liferay 7 Portlet Tutorial – Part2 , we will use the leave application to create REST API.  The new project structure would be follows as:

  • leave-core-services  :  This module offers business layer using service builder
  • leave-web  :   This module will be used for web layer
  • leave-rest :   This module is used to expose REST  API

Liferay 7 REST Example:

  • Liferay latest IDE providing REST Project template. We will create REST Component Projec, so In Eclipse, click File -> New Liferay Module Project. Select Project Template as “REST”Liferay 7 REST Component
  • Provide Component class name and package path. This package path is added to Bundle Symbolic
    Name Leave REST Package
  • bnd.bnd template file is:
    1. Bundle-Name: leave-rest
      Bundle-SymbolicName: org.javasavvy.leave.rest
      Bundle-Version: 1.0.0
      ConfigurationPath: /configuration
      Include-Resource: configuration=src/main/resources/configuration
  • update gradle dependencies with below and run the gradle build task. Refresh the gradle project again to affect the dependency changes (Right click on project  -> Select Gradle -> Select Gradle Refresh)
    1. dependencies {
       compileOnly group: "javax.ws.rs", name: "javax.ws.rs-api", version: "2.0.1"
       compileOnly group: "org.osgi", name: "org.osgi.service.component.annotations", version: "1.3.0"
       compileOnly group: "com.liferay.portal", name: "com.liferay.portal.kernel", version: "2.0.0"
       compileOnly group: "com.liferay.portal", name: "com.liferay.util.java", version: "2.0.0"
       compileOnly group: "com.liferay.portal", name: "com.liferay.portal.impl", version: "2.0.0"
       compileOnly project(":modules:leave-core:leave-core-api")
       compileOnly project(":modules:leave-core:leave-core-service")
      }
  • leave-rest project structure is: Liferay7 REST Project Structure
  • RESTExtenderConfiguraiton: What is REST Extender configuration??  OSGI is running in JVM container as OSGI container holds object,services etc.  How OSGI will detect REST Context paths??
    • REST Extenders will track the services in the OSGI on specified end points. REST Extenders are need to publish API over the contextPaths, which need to be specified in RESTExtenderConfiguration file.
    • contextPaths=/leave-rest
  • CXFPublisherEndpointPublisherConfiguration
    1. contextPath=/leave-rest
      authVerifierProperties=auth.verifier.PortalSessionAuthVerifier.urls.includes=*
  •  Liferay creates REST Component with sample methods, so edit the LeaveRestComponent class that returns Leave objects as JSON
    1. @ApplicationPath("/leave")
      @Component(immediate = true, service = Application.class)
      public class LeaveRestControllerApplication extends Application {
      
           public Set<Object> getSingletons() {
               return Collections.<Object>singleton(this);
           }
           private LeaveLocalService leaveLocalService;
       
           @Reference(unbind="-")
             public void setLeaveLocalService(LeaveLocalService leaveLocalService){
                 this.leaveLocalService=leaveLocalService;
              }
          @GET
          @Path("/morning")
          @Produces("text/plain")
           public String hello() {
             return "Good morning!";
          }
       
         @GET
         @Path("/leave-info/{leaveId}")
         @Produces(MediaType.APPLICATION_JSON)
        public String getLeave(@PathParam("leaveId") long leaveId){
           Leave leave = null;
           Status status = new Status();
           String jsonString = null;
           System.out.println("leave-info invodked");
          try {
               leave = leaveLocalService.getLeave(leaveId);
               jsonString = JSONFactoryUtil.serialize(leave);
              } catch (Exception e) {
                  if(e instanceof NoSuchLeaveException) {
                      status.setMessage("NO Leave Entity Found");
                      status.setStatus(250);
                      jsonString = JSONFactoryUtil.serialize(status);
                }
               e.printStackTrace();
           }
           return jsonString;
       }
      
      }
  •  run deploy gradle task and deploy the OSGI container.

What next??  Liferay loads REST Extender configuration and creates REST resources, but some times if you access REST URL,it may give 404 error.  We need to manually create REST Extender configuration in Control panel or update existing configuration.

Liferay REST Extender Configuration :

In this step, we need to update REST Extender configuration in the control panel section of admin

  1. Click on Control Panel -> Configuration -> System Settings Liferay 7 REST System Settings
  2. Search for RESTSearch of REST
  3. Click on REST ExtenderREST Extender
  4. You can  see that rest extender configuration will be already created.so just update this config again.REST Configuration Entries
  5. Click on the configuration and you can see the deployed  OSGI module “leave-rest” with specified context paths. Click + icon if any additional context paths required.REST Context Paths
  6. Click on update and access the web services in the url :http://localhost:8080/o/leave-rest/leave/morning
  7. Liferay 7 starts with context /o unlike in Liferay 6 with /c
  8. Hope you have the entry created already with leave-web application and access the leave info with leave Id.
    • http://localhost:8080/o/leave-rest/leave/leave-info/1
    • RESPONSE:
      1. {"javaClass":"org.javasavvy.leave.model.impl.LeaveImpl","serializable":
        {"leaveName":"Pongal Vacation","new":false,"originalUuid":"f15abe58-d0e4-cccd-77dc-a556b4e7e678","columnBitmask":0,
        "setOriginalUserId":false,"endDate":{"javaClass":"java.util.Date","time":1484697600000},"cachedModel":true,
        "groupId":20143,"userName":"Test Test","uuid":"f15abe58-d0e4-cccd-77dc-a556b4e7e678","userId":20156,
        "originalCompanyId":20116,"escapedModel":null,"setOriginalGroupId":false,"companyId":20116,"setModifiedDate":false,
        "originalGroupId":20143,"setOriginalCompanyId":false,"modifiedDate":{"javaClass":"java.util.Date","time":1485951753553},
        "originalUserId":20156,"leaveId":1,"startDate":{"javaClass":"java.util.Date","time":1484092800000},
        "createDate":{"javaClass":"java.util.Date","time":1485951753537}}}

         Click here to Download Liferay 7 REST code

7 thoughts on “Liferay 7 REST Webservices Tutorial”
  1. Hi,

    Thanks for wonderful tutorial.
    I have developed few Rest APIs on Liferay 7.
    They work fine.

    However the only problem is they do not require any authentication (just like /api/jsonws requires). How can I add liferay authentication to these custom Rest APIs

    1. Hi, I am looking for the exact same thing, could you manage to solve it?

  2. Hi,

    There is no “rest extender configuration” under System settings of Liferay Control Panel. I searched it… But it’s not present.

    How can I get, any idea…

    I am having Liferay7.0 GA4 CE

  3. Hi when i deploy the attached rest code i am facing given below error

    org.apache.cxf.service.factory.ServiceConstructionException
    at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:219)
    at com.liferay.portal.remote.rest.extender.internal.CXFJaxRsServiceRegistrator.registerApplication(CXFJaxRsServiceRegistrator.java:100)
    at com.liferay.portal.remote.rest.extender.internal.CXFJaxRsServiceRegistrator.registerApplications(CXFJaxRsServiceRegistrator.java:110)
    at com.liferay.portal.remote.rest.extender.internal.CXFJaxRsServiceRegistrator.rewire(CXFJaxRsServiceRegistrator.java:130)
    at com.liferay.portal.remote.rest.extender.internal.CXFJaxRsServiceRegistrator._addApplication(CXFJaxRsServiceRegistrator.java:148)
    at com.liferay.portal.remote.rest.extender.internal.CXFJaxRsServiceRegistrator._swapClassLoader(CXFJaxRsServiceRegistrator.java:213)
    at com.liferay.portal.remote.rest.extender.internal.CXFJaxRsServiceRegistrator.addApplication(CXFJaxRsServiceRegistrator.java:42)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.felix.dm.InvocationUtil.invokeMethod(InvocationUtil.java:111)
    at org.apache.felix.dm.InvocationUtil.invokeCallbackMethod(InvocationUtil.java:66)
    at org.apache.felix.dm.impl.ComponentImpl.invokeCallbackMethod(ComponentImpl.java:769)
    at org.apache.felix.dm.impl.dependencies.ServiceDependencyImpl.invoke(ServiceDependencyImpl.java:709)
    at org.apache.felix.dm.impl.dependencies.ServiceDependencyImpl.invoke(ServiceDependencyImpl.java:1160)
    at org.apache.felix.dm.impl.dependencies.ServiceDependencyImpl.invokeAdded(ServiceDependencyImpl.java:1114)
    at org.apache.felix.dm.impl.dependencies.ServiceDependencyImpl.doAddedService(ServiceDependencyImpl.java:858)
    at org.apache.felix.dm.impl.dependencies.ServiceDependencyImpl.access$200(ServiceDependencyImpl.java:60)
    at org.apache.felix.dm.impl.dependencies.ServiceDependencyImpl$4.run(ServiceDependencyImpl.java:317)
    at org.apache.felix.dm.impl.SerialExecutor.runTask(SerialExecutor.java:137)
    at org.apache.felix.dm.impl.SerialExecutor.runTasks(SerialExecutor.java:119)
    at org.apache.felix.dm.impl.SerialExecutor.execute(SerialExecutor.java:85)
    at org.apache.felix.dm.impl.SerialExecutor.execute(SerialExecutor.java:104)
    at org.apache.felix.dm.impl.dependencies.ServiceDependencyImpl.addedService(ServiceDependencyImpl.java:314)
    at org.apache.felix.dm.tracker.ServiceTracker$Tracked.customizerAdded(ServiceTracker.java:1331)
    at org.apache.felix.dm.tracker.AbstractTracked.trackAdding(AbstractTracked.java:290)
    at org.apache.felix.dm.tracker.AbstractTracked.track(AbstractTracked.java:236)
    at org.apache.felix.dm.tracker.ServiceTracker$Tracked.serviceChangedHideAspects(ServiceTracker.java:1212)
    at org.apache.felix.dm.tracker.ServiceTracker$Tracked.serviceChanged(ServiceTracker.java:1107)
    at org.eclipse.osgi.internal.serviceregistry.FilteredServiceListener.serviceChanged(FilteredServiceListener.java:109)
    at org.eclipse.osgi.internal.framework.BundleContextImpl.dispatchEvent(BundleContextImpl.java:917)
    at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
    at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:148)
    at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEventPrivileged(ServiceRegistry.java:862)
    at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.publishServiceEvent(ServiceRegistry.java:801)
    at org.eclipse.osgi.internal.serviceregistry.ServiceRegistrationImpl.register(ServiceRegistrationImpl.java:127)
    at org.eclipse.osgi.internal.serviceregistry.ServiceRegistry.registerService(ServiceRegistry.java:225)
    at org.eclipse.osgi.internal.framework.BundleContextImpl.registerService(BundleContextImpl.java:464)
    at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:886)
    at org.apache.felix.scr.impl.manager.AbstractComponentManager$3.register(AbstractComponentManager.java:873)
    at org.apache.felix.scr.impl.manager.RegistrationManager.changeRegistration(RegistrationManager.java:132)
    at org.apache.felix.scr.impl.manager.AbstractComponentManager.registerService(AbstractComponentManager.java:940)
    at org.apache.felix.scr.impl.manager.AbstractComponentManager.activateInternal(AbstractComponentManager.java:740)
    at org.apache.felix.scr.impl.manager.AbstractComponentManager.enableInternal(AbstractComponentManager.java:674)
    at org.apache.felix.scr.impl.manager.AbstractComponentManager.enable(AbstractComponentManager.java:429)
    at org.apache.felix.scr.impl.manager.ConfigurableComponentHolder.enableComponents(ConfigurableComponentHolder.java:657)
    at org.apache.felix.scr.impl.BundleComponentActivator.initialEnable(BundleComponentActivator.java:341)
    at org.apache.felix.scr.impl.Activator.loadComponents(Activator.java:403)
    at org.apache.felix.scr.impl.Activator.access$200(Activator.java:54)
    at org.apache.felix.scr.impl.Activator$ScrExtension.start(Activator.java:278)
    at org.apache.felix.utils.extender.AbstractExtender.createExtension(AbstractExtender.java:259)
    at org.apache.felix.utils.extender.AbstractExtender.modifiedBundle(AbstractExtender.java:232)
    at org.osgi.util.tracker.BundleTracker$Tracked.customizerModified(BundleTracker.java:482)
    at org.osgi.util.tracker.BundleTracker$Tracked.customizerModified(BundleTracker.java:1)
    at org.osgi.util.tracker.AbstractTracked.track(AbstractTracked.java:232)
    at org.osgi.util.tracker.BundleTracker$Tracked.bundleChanged(BundleTracker.java:444)
    at org.eclipse.osgi.internal.framework.BundleContextImpl.dispatchEvent(BundleContextImpl.java:905)
    at org.eclipse.osgi.framework.eventmgr.EventManager.dispatchEvent(EventManager.java:230)
    at org.eclipse.osgi.framework.eventmgr.ListenerQueue.dispatchEventSynchronous(ListenerQueue.java:148)
    at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEventPrivileged(EquinoxEventPublisher.java:165)
    at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEvent(EquinoxEventPublisher.java:75)
    at org.eclipse.osgi.internal.framework.EquinoxEventPublisher.publishBundleEvent(EquinoxEventPublisher.java:67)
    at org.eclipse.osgi.internal.framework.EquinoxContainerAdaptor.publishModuleEvent(EquinoxContainerAdaptor.java:102)
    at org.eclipse.osgi.container.Module.publishEvent(Module.java:461)
    at org.eclipse.osgi.container.Module.start(Module.java:452)
    at org.eclipse.osgi.internal.framework.EquinoxBundle.start(EquinoxBundle.java:402)
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.startBundle(DirectoryWatcher.java:1258)
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.startBundles(DirectoryWatcher.java:1230)
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.doProcess(DirectoryWatcher.java:512)
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.process(DirectoryWatcher.java:361)
    at org.apache.felix.fileinstall.internal.DirectoryWatcher.run(DirectoryWatcher.java:312)
    Caused by: org.apache.cxf.service.factory.ServiceConstructionException: There is an endpoint already running on /leave.
    at org.apache.cxf.jaxrs.JAXRSBindingFactory.addListener(JAXRSBindingFactory.java:86)
    at org.apache.cxf.endpoint.ServerImpl.start(ServerImpl.java:123)
    at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:206)

Comments are closed.