Liferay supports JAX-WS and JAX-RS via the Apache CXF implementation. Apps can publish JAX web services to the CXF endpoints defined in your Liferay instance. CXF endpoints are effectively context paths the JAX web services are deployed to and accessible from. To publish any kind of JAX web service, one or more CXF endpoints must be defined in your Liferay instance. To access JAX web services, an extender must also be configured in your Liferay instance. Extenders specify where the services are deployed and whether they are augmented with handlers, providers, and so on. There are two types of extenders:
-
SOAP Extenders: Required to publish JAX-WS web services. Each SOAP extender can deploy the services to one or more CXF endpoints and can use a set of JAX-WS handlers to augment the services.
-
REST Extenders: Required to publish JAX-RS web services. REST extenders for JAX-RS services are analogous to SOAP extenders for JAX-WS services. To create JAX-RS services that can work across different JAX-RS implementations, you must provide an implementation of
javax.ws.rs.core.Application
to the OSGi framework. You can do this by registering an instance of this implementation as an OSGi service viaBundleContext
or the Declarative Services@Component
annotation. The JAX-RS application encompasses the services that represent JAX-RS endpoints and the services that represent JAX-RS providers. By specifying OSGi filters in a REST extender, you can also dynamically add endpoints or JAX-RS providers to a JAX-RS application.
SOAP extenders and REST extenders are subsystems that track the services the app
developer registers in OSGi (those matching the provided
OSGi filters),
and deploy them under the specified CXF endpoints. For example, if you create
the CXF endpoints /soap
and /rest
, you could later create a REST extender
for /rest
that publishes REST applications, and a SOAP extender for /soap
that publishes SOAP services. Of course, this is only a rough example: you can
fine tune things to your liking.
CXF endpoints and both types of extenders can be created programmatically or with Liferay’s Control Panel. This tutorial shows you how to do both, and then shows you how to publish JAX-WS and JAX-RS web services. The following topics are covered:
Configuring Endpoints and Extenders with the Control Panel
Liferay’s Control Panel lets administrators configure endpoints and extenders for JAX web services. Note that you must be an administrator in your Liferay instance to access the settings here. First, you’ll learn how to create CXF endpoints.
CXF Endpoints
To configure a CXF endpoint with the Control Panel, first go to Control Panel
→ Configuration → System Settings → Foundation. Then select
CXF Endpoints from the table. If there are any existing CXF endpoints, they’re
shown here. To add a new one, select the Add button
() in the lower right-hand corner. The form
that appears lets you configure a new CXF endpoint by filling out these fields:
-
Context path: The path the JAX web services are deployed to on the Liferay server. For example, if you define the context path
/web-services
, any services deployed there are available athttp://your-server:your-port/o/web-services
. -
AuthVerifier
properties: Any properties defined here are passed as-is to theAuthVerifier
filter. See theAuthVerifier
documentation for more details. -
Required extensions: CXF normally loads its default extension classes, but in some cases you can override them to replace the default behavior. In most cases, you can leave this field blank: overriding extensions isn’t common. By specifying custom extensions here via OSGi filters, Liferay waits until those extensions are registered in the OSGi framework before creating the CXF servlet and passing the extensions to the servlet.
Figure 1: Fill out this form to create a CXF endpoint.
Next, you’ll learn how to use the Control Panel to create SOAP extenders for JAX-WS web services.
SOAP Extenders
For an app to deploy JAX-WS web services, you must configure a SOAP extender. To
configure a SOAP extender with the Control Panel, first go to
Control Panel → Configuration → System Settings →
Foundation. Then select SOAP Extenders from the table. If there are any
existing SOAP extenders, they’re shown here. To add a new one, select the Add
button
() in the lower right-hand corner. The form
that appears lets you configure a new SOAP extender by filling out these fields:
-
Context paths: Specify at least one CXF endpoint here. This is where the services affected by this extender are deployed. In the preceding CXF endpoint example, this would be
/web-services
. Note that you can specify more than one CXF endpoint here. -
jax.ws.handler.filters: Here you can specify a set of OSGi filters that select certain services registered in the OSGi framework. The selected services should implement JAX-WS handlers and augment the JAX-WS services specified in the jax.ws.service.filters property. These JAX-WS handlers apply to each service selected in this extender.
-
jax.ws.service.filters: Here you can specify a set of OSGi filters that select the services registered in the OSGi framework that will be deployed to the CXF endpoints. These OSGi services must be proper JAX-WS services.
-
soap.descriptor.builder: Leave this option empty to use JAX-WS annotations to describe the SOAP service. To use a different way to describe the SOAP service, you can provide an OSGi filter here that selects an implementation of
com.liferay.portal.remote.soap.extender.SoapDescriptorBuilder
.
Figure 2: Fill out this form to create a SOAP extender.
Next, you’ll learn how to use the Control Panel to create REST extenders for JAX-RS web services.
REST Extenders
To configure a REST extender with the Control Panel, first go to
Control Panel → Configuration → System Settings →
Foundation. Then select REST Extender from the table. If there are any
existing REST extenders, they’re shown here. To add a new one, select the Add
button
() in the lower right-hand corner. The form
that appears lets you configure a new REST extender by filling out these fields:
-
Context paths: Specify at least one CXF endpoint here. This is where the services affected by this extender are deployed. In the preceding CXF endpoint example, this would be
/web-services
. Note that you can specify more than one CXF endpoint here. This works the same way as the Context paths setting in SOAP Extenders. -
jax.rs.application.filters: Here you can specify a set of OSGi filters that select services that implement
javax.ws.rs.core.Application
. These JAX-RS applications are deployed to the CXF endpoints specified in the Context paths property. -
jsx.rs.provider.filters: Here you can specify a set of OSGi filters that select services registered in the OSGi framework. The selected services should implement any of the interfaces supported by JAX-RS for providers. These JAX-RS providers are added to the JAX-RS application as if they had been returned by the
getSingletons()
method ofjavax.ws.rs.core.Application
. The following links list some of the supported JAX-RS providers: -
jax.rs.service.filters: Here you can specify a set of OSGi filters that selects services registered in the OSGi framework that are valid JAX-RS endpoints. These endpoints are added to the JAX-RS application as if they had been returned by the
getSingletons()
method ofjavax.ws.rs.core.Application
.
Figure 3: Fill out this form to create a REST extender.
Next, you’ll learn how to configure endpoints and extenders programmatically.
Configuring Endpoints and Extenders Programmatically
To configure endpoints or extenders programmatically, you must use Liferay’s
configurator extender. The configurator extender provides a way for OSGi modules
to deploy default configuration values. Modules that use the configurator
extender must provide a ConfigurationPath
header that points to the
configuration files’ location inside the module. For example, the following
configuration sets the ConfigurationPath
to
src/main/resources/configuration
:
Bundle-Name: Liferay Export Import Service JAX-WS
Bundle-SymbolicName: com.liferay.exportimport.service.jaxws
Bundle-Version: 1.0.0
Liferay-Configuration-Path: /configuration
Include-Resource: configuration=src/main/resources/configuration
Liferay-Releng-Module-Group-Description:
Liferay-Releng-Module-Group-Title: Data Management
There are two different configuration types in
OSGi’s ConfigurationAdmin
:
single, and factory. Factory configurations can have several configuration
instances per factory name. Liferay DXP uses factory configurations. You
must provide a factory configuration’s default values in a *.properties
file.
In this properties file, use a suffix on the end of the PID (persistent
identifier) and then provide your settings. For example, the following code uses
the -staging
suffix on the PID and creates a CXF endpoint at the context path
/staging-ws
:
com.liferay.portal.remote.cxf.common.configuration.CXFEndpointPublisherConfiguration-staging.properties:
contextPath=/staging-ws
As another example, the following code uses the suffix -stagingjaxws
on the
PID and creates a SOAP extender at the context path /staging-ws
. This code
also includes settings for the configuration fields jaxWsHandlerFilterStrings
and jaxWsServiceFilterStrings
:
com.liferay.portal.remote.soap.extender.configuration.SoapExtenderConfiguration-stagingjaxws.properties:
contextPaths=/staging-ws
jaxWsHandlerFilterStrings=(staging.jax.ws.handler=true)
jaxWsServiceFilterStrings=(staging.jax.ws.service=true)
You must then use these configuration fields in the configuration class. For
example, the SoapExtenderConfiguration
interface below contains the
configuration fields contextPaths
, jaxWsHandlerFilterStrings
, and
jaxWsServiceFilterStrings
:
@ExtendedObjectClassDefinition(
category = "foundation", factoryInstanceLabelAttribute = "contextPaths"
)
@Meta.OCD(
factory = true,
id = "com.liferay.portal.remote.soap.extender.configuration.SoapExtenderConfiguration",
localization = "content/Language", name = "soap.extender.configuration.name"
)
public interface SoapExtenderConfiguration {
@Meta.AD(required = false)
public String[] contextPaths();
@Meta.AD(name = "jax.ws.handler.filters", required = false)
public String[] jaxWsHandlerFilterStrings();
@Meta.AD(name = "jax.ws.service.filters", required = false)
public String[] jaxWsServiceFilterStrings();
@Meta.AD(name = "soap.descriptor.builder", required = false)
public String soapDescriptorBuilderFilter();
}
You can use similar techniques to create REST extenders. For example, see
the RestExtenderConfiguration
interface
in Liferay’s source code.
Next, you’ll learn how to publish JAX-WS web services.
Publishing JAX-WS Web Services
To publish JAX-WS web services via SOAP in a Liferay DXP 7.0 module, annotate the
class and its methods with standard JAX-WS annotations, and then register it as
a service in the OSGi framework. For example, the following class uses the
@WebService
annotation for the class and @WebMethod
annotations for its
methods. You must also set the jaxws
property to true
in the OSGi
@Component
annotation:
import javax.jws.WebMethod;
import javax.jws.WebService;
import org.osgi.service.component.annotations.Component;
@Component(
immediate = true, property = "jaxws=true", service = Calculator.class
)
@WebService
public class Calculator {
@WebMethod
public int divide(int a, int b) {
return a / b;
}
@WebMethod
public int multiply(int a, int b) {
return a * b;
}
@WebMethod
public int subtract(int a, int b) {
return a - b;
}
@WebMethod
public int sum(int a, int b) {
return a + b;
}
}
You should also make sure that you include org.osgi.core
and
org.osgi.service.component.annotations
as dependencies to your project.
Next, you’ll learn how to publish JAX-RS web services.
Publishing JAX-RS Web Services
You can publish JAX-RS web services in a Liferay module the same way you would outside of Liferay. You must also, however, register the class in the OSGi framework. Note that the services must match the OSGi filters provided in the respective extenders. This is how the instances that become services are selected. There’s no classpath scanning or other automatic mechanism at work here: it’s the developer’s responsibility to register the services in the OSGi framework.
The following example registers an OSGi component that publishes a JAX-RS web
service at /application-path/hello
. Get requests to this web service return a
simple “Hello!”:
import org.osgi.service.component.annotations.Component;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Application;
import java.util.Collections;
import java.util.Set;
@Component(immediate = true, service = Application.class)
@ApplicationPath("/application-path")
public class RestEndpoint extends Application {
public Set<Object> getSingletons() {
return Collections.<Object>singleton(this);
}
@GET
@Path("/hello")
@Produces("text/text")
public String sayHello() {
return "Hello!";
}
}
Nice work! Now you know how JAX-WS and JAX-RS works in Liferay.