|
|
爱听歌的草稿本 · Oracle GraalVM ...· 3 周前 · |
|
|
重感情的手术刀 · Oracle GraalVM ...· 3 周前 · |
|
|
踏实的风衣 · php接收base64数据生成图片并保存_p ...· 1 年前 · |
|
|
狂野的风衣 · Pdf 转 word 和 word 转 ...· 1 年前 · |
|
|
奔跑的小虾米 · 语音服务声称文本到语音的配额是200/秒,我 ...· 2 年前 · |
|
|
安静的八宝粥 · c++11中的lock_guard和uniq ...· 2 年前 · |
|
|
不要命的西装 · 国内免费体验ChatGPT | ...· 2 年前 · |
Since JBoss AS 7, Class loading is considerably different from previous versions of JBoss AS. Class loading is based on the MODULES project. Instead of the more familiar hierarchical class loading environment, WildFly’s class loading is based on modules that have to define explicit dependencies on other modules. Deployments in WildFly are also modules, and do not have access to classes that are defined in jars in the application server unless an explicit dependency on those classes is defined.
Module names for top level deployments follow the format
deployment.myarchive.war
while sub deployments are named like
deployment.myear.ear.mywar.war
.
This means that it is possible for a deployment to import classes from another deployment using the other deployments module name, the details of how to add an explicit module dependency are explained below.
Even though in WildFly modules are isolated by default, as part of the deployment process some dependencies on modules defined by the application server are set up for you automatically. For instance, if you are deploying a Jakarta EE application a dependency on the Java EE API’s will be added to your module automatically. Similarly if your module contains a beans.xml file a dependency on Weld will be added automatically, along with any supporting modules that weld needs to operate.
For a complete list of the automatic dependencies that are added, please see Implicit module dependencies for deployments .
Automatic dependencies can be excluded through the use of
jboss-deployment-structure.xml
.
A common source of errors in Java applications is including API classes in a deployment that are also provided by the container. This can result in multiple versions of the class being created and the deployment failing to deploy properly. To prevent this in WildFly, module dependencies are added in a specific order that should prevent this situation from occurring.
In order of highest priority to lowest priority
System Dependencies - These are dependencies that are added to the module automatically by the container, including the Jakarta EE api’s.
User Dependencies - These are dependencies that are added through
jboss-deployment-structure.xml
or through the
Dependencies:
manifest
entry.
Local Resource - Class files packaged up inside the deployment
itself, e.g. class files from
WEB-INF/classes
or
WEB-INF/lib
of a
Inter deployment dependencies - These are dependencies on other deployments in an ear deployment. This can include classes in an ear’s lib directory, or classes defined in other ejb jars.
The war is considered to be a single module, so classes defined in
WEB-INF/lib
are treated the same as classes in
WEB-INF/classes
. All
classes packaged in the war will be loaded with the same class loader.
Ear deployments are multi-module deployments. This means that not all
classes inside an ear will necessarily have access to all other classes
in the ear, unless explicit dependencies have been defined. By default
the
EAR/lib
directory is a single module, and every WAR or EJB jar
deployment is also a separate module. Sub deployments (wars and
ejb-jars) always have a dependency on the parent module, which gives
them access to classes in
EAR/lib
, however they do not always have an
automatic dependency on each other. This behaviour is controlled via the
ear-subdeployments-isolated
setting in the ee subsystem configuration:
<subsystem xmlns="urn:jboss:domain:ee:1.0" >
<ear-subdeployments-isolated>false</ear-subdeployments-isolated>
</subsystem>
By default this is set to false, which allows the sub-deployments to see classes belonging to other sub-deployments within the .ear.
For example, consider the following .ear deployment:
myapp.ear |--- web.war |--- ejb1.jar |--- ejb2.jar
If the ear-subdeployments-isolated is set to false, then the classes in web.war can access classes belonging to ejb1.jar and ejb2.jar. Similarly, classes from ejb1.jar can access classes from ejb2.jar (and vice-versa).
The ear-subdeployments-isolated element value has no effect on the isolated classloader of the .war file(s). i.e. irrespective of whether this flag is set to true or false, the .war within a .ear will have an isolated classloader and other sub-deployments within that .ear will not be able to access classes from that .war. This is as per spec.
If the ear-subdeployments-isolated is set to true then no automatic
module dependencies between the sub-deployments are set up. User must
manually setup the dependency with
Class-Path
entries, or by setting
up explicit module dependencies.
Portability
The Jakarta EE specification says that portable applications should not rely on sub deployments having access to other sub deployments unless an explicit Class-Path entry is set in the MANIFEST.MF. So portable applications should always use Class-Path entry to explicitly state their dependencies. It is also possible to override the ear-subdeployments-isolated element value at a per deployment level. See the section on jboss-deployment-structure.xml below.
Dependencies:
Manifest Entries
Deployments (or more correctly modules within a deployment) may set up
dependencies on other modules by adding a
Dependencies:
manifest
entry. This entry consists of a comma separated list of module names
that the deployment requires. The available modules can be seen under
the
modules
directory in the application server distribution. For
example to add a dependency on javassist and apache velocity you can add
a manifest entry as follows:
Dependencies: org.javassist export,org.apache.velocity export services,org.antlr
Each dependency entry may also specify some of the following parameters by adding them after the module name:
export
This means that the dependencies will be exported, so any
module that depends on this module will also get access to the
dependency.
services
By default items in META-INF of a dependency are not
accessible, this makes items from
META-INF/services
accessible so
services
in
the modules can be loaded.
optional
If this is specified the deployment will not fail if the
module is not available.
meta-inf
This will make the contents of the
META-INF
directory
available (unlike
services
, which just makes
META-INF/services
available). In general this will not cause any deployment descriptors in
META-INF to be processed, with the exception of
beans.xml
. If a
beans.xml
file is present this module will be scanned by Weld and any
resulting beans will be available to the application.
annotations
If a jandex index has be created for the module these
annotations will be merged into the deployments annotation index. The
Jandex
index can be generated using
Jandex
ant task
, and must be named
META-INF/jandex.idx
. Note that it is not
necessary to break open the jar being indexed to add this to the modules
class path, a better approach is to create a jar containing just this
index, and adding it as an additional resource root in the
module.xml
file.
export
parameter it is possible to add a dependency to all
sub deployments in an ear. If a module is exported from a
Dependencies:
entry in the top level of the ear (or by a jar in the
ear/lib
directory) it will be available to all sub deployments as
well.
<groupId>
org.apache.maven.plugins
</groupId>
<artifactId>
maven-war-plugin
</artifactId>
<configuration>
<archive>
<manifestEntries>
<Dependencies>
org.slf4j
</Dependencies>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
If your deployment is a jar you must use the
maven-jar-plugin
rather
than the
maven-war-plugin
.
It is also possible to add module dependencies on other modules inside
the deployment using the
Class-Path
manifest entry. This can be used
within an ear to set up dependencies between sub deployments, and also
to allow modules access to additional jars deployed in an ear that are
not sub deployments and are not in the
EAR/lib
directory. If a jar in
the
EAR/lib
directory references a jar via
Class-Path:
then this
additional jar is merged into the parent ear’s module, and is accessible
to all sub deployments in the ear.
It is also possible to set up global modules, that are accessible to all deployments. This is done by modifying the configuration file (standalone/domain.xml).
For example, to add javassist to all deployments you can use the following XML:
<subsystem xmlns="urn:jboss:domain:ee:1.0" >
<global-modules>
<module name="org.javassist" slot="main" />
</global-modules>
</subsystem>
Note that the
slot
field is optional and defaults to
main
.
The EE subsystem allows the configuration of a global directory, which represents a directory tree scanned automatically to include .jar files and resources as a single additional dependency. This dependency is added as a system dependency to all deployed application. See Subsystem EE Global Directory to get more information about how to set up a global directory.
jboss-deployment-structure.xml
is a JBoss specific deployment
descriptor that can be used to control class loading in a fine grained
manner. It should be placed in the top level deployment, in
META-INF
(or
WEB-INF
for web deployments). It can do the following:
An example of a complete
jboss-deployment-structure.xml
file for an
ear deployment is as follows:
<jboss-deployment-structure>
<!-- Make sub deployments isolated by default, so they cannot see each others classes without a Class-Path entry -->
<ear-subdeployments-isolated>true</ear-subdeployments-isolated>
<!-- This corresponds to the top level deployment. For a war this is the war's module, for an ear -->
<!-- This is the top level ear module, which contains all the classes in the EAR's lib folder -->
<deployment>
<!-- exclude-subsystem prevents a subsystems deployment unit processors running on a deployment -->
<!-- which gives basically the same effect as removing the subsystem, but it only affects single deployment -->
<exclude-subsystems>
<subsystem name="logging" />
</exclude-subsystems>
<!-- Exclusions allow you to prevent the server from automatically adding some dependencies -->
<exclusions>
<module name="org.javassist" />
</exclusions>
<!-- This allows you to define additional dependencies, it is the same as using the Dependencies: manifest attribute -->
<dependencies>
<module name="deployment.javassist.proxy" />
<module name="deployment.myjavassist" />
<!-- Import META-INF/services for ServiceLoader impls as well -->
<module name="myservicemodule" services="import"/>
</dependencies>
<!-- These add additional classes to the module. In this case it is the same as including the jar in the EAR's lib directory -->
<resources>
<resource-root path="my-library.jar" />
</resources>
</deployment>
<sub-deployment name="myapp.war">
<!-- This corresponds to the module for a web deployment -->
<!-- it can use all the same tags as the <deployment> entry above -->
<dependencies>
<!-- Adds a dependency on a ejb jar. This could also be done with a Class-Path entry -->
<module name="deployment.myear.ear.myejbjar.jar" />
</dependencies>
<!-- Set's local resources to have the lowest priority -->
<!-- If the same class is both in the sub deployment and in another sub deployment that -->
<!-- is visible to the war, then the Class from the other deployment will be loaded, -->
<!-- rather than the class actually packaged in the war. -->
<!-- This can be used to resolve ClassCastExceptions if the same class is in multiple sub deployments-->
<local-last value="true" />
</sub-deployment>
<!-- Now we are going to define two additional modules -->
<!-- This one is a different version of javassist that we have packaged -->
<module name="deployment.myjavassist" >
<resources>
<resource-root path="javassist.jar" >
<!-- We want to use the servers version of javassist.util.proxy.* so we filter it out-->
<filter>
<exclude path="javassist/util/proxy" />
</filter>
</resource-root>
</resources>
</module>
<!-- This is a module that re-exports the containers version of javassist.util.proxy -->
<!-- This means that there is only one version of the Proxy classes defined -->
<module name="deployment.javassist.proxy" >
<dependencies>
<module name="org.javassist" >
<imports>
<include path="javassist/util/proxy" />
<exclude path="/**" />
</imports>
</module>
</dependencies>
</module>
</jboss-deployment-structure>
Not all JDK classes are exposed to a deployment by default. If your deployment uses JDK classes that are not exposed you can get access to them using jboss-deployment-structure.xml with system dependencies:
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
<deployment>
<dependencies>
<system export="true">
<paths>
<path name="com/sun/corba/se/spi/legacy/connection"/>
</paths>
</system>
</dependencies>
</deployment>
</jboss-deployment-structure>
The WildFly distribution includes a large number of modules, a great many of which are included for use by WildFly internals, with no testing of the appropriateness of their direct use by applications or any commitment to continue to ship those modules in future releases if they are no longer needed by the internals. So how can a user know whether it is advisable for their application to specify an explicit dependency on a module WildFly ships? The "jboss.api" property specified in the module’s module.xml file can tell you:
<module xmlns="urn:jboss:module:1.9" name="com.google.guava">
<properties>
<property name="jboss.api" value="private"/>
</properties>
If a module does not have a property element like the above, then it’s equivalent to one with a value of "public".
Following are the meanings of the various values you may see for the jboss.api property:
public
May be explicitly depended upon by end user applications. Will continue to be available in future releases within the same major series and should not have incompatible API changes in future releases within the same minor series, and ideally not within the same major series.
private
Intended for internal use only. Only tested according to internal usage. May not be safe for end user applications to use directly.Could change significantly or be removed in a future release without notice.
unsupported
If you see this value in a module.xml in a WildFly release, please file a bug report, as it is not applicable in WildFly. In EAP it has a meaning equivalent to "private" but that does not mean the module is "private" in WildFly; it could very easily be "public".
preview
May be explicitly depended upon by end user applications, but there are no guarantees of continued availability in future releases or that there will not be incompatible API changes. This is not a common classification in WildFly. It is not used in WildFly 10.
deprecated
May be explicitly depended upon by end user applications. Stable and reliable but an alternative should be sought. Will be removed in a future major release.
Note that these definitions are only applicable to WildFly. In EAP and other Red Hat products based on WildFly the same classifiers are used, with generally similar meaning, but the precise meaning is per the definitions on the Red Hat customer support portal.
If an application declares a direct dependency on a module marked "private", "unsupported" or "deprecated", during deployment a WARN message will be logged. The logging will be in log categories "org.jboss.as.dependency.private", "org.jboss.as.dependency.unsupported" and "org.jboss.as.dependency.deprecated" respectively. These categories are not used for other purposes, so once you feel sufficiently warned the logging can be safely suppressed by turning the log level for the relevant category to ERROR or higher.
Other than the WARN messages noted above, declaring a direct dependency on a non-public module has no impact on how WildFly processes the deployment.
In WildFly it is possible to list the module dependencies added by the container to your deployed application. This task can be achieved via the command line interface, where specific operations are available to list the module dependencies for deployments and ear-subdeployments.
You can list the module dependencies of a deployment using the list-modules operation as below:
[standalone@localhost:9990 /] /deployment=test-application.war:list-modules
In case of ear-subdeployments, the list-modules operation is also available under the subdeployment resource:
[standalone@localhost:9990 /] /deployment=test-application.ear/subdeployment=test-application.war:list-modules
If you are running WildFly in domain mode, this operation is available via the server resource at the host level:
[domain@localhost:9990 /] /host=master/server=server-one/deployment=test-application.war:list-modules
By default, the list-modules operation shows the list of dependencies in a compact view, including only the module name. You can control this output using the attribute verbose=[false*|true] to enable/disable a detailed response.
The following output shows an example of a detailed view:
[standalone@localhost:9990 /] /deployment=test-application.ear:list-modules(verbose=true)
"outcome" => "success",
"result" => {
"system-dependencies" => [
"name" => "com.fasterxml.jackson.datatype.jackson-datatype-jdk8",
"optional" => true,
"export" => false,
"import-services" => true
"name" => "com.fasterxml.jackson.datatype.jackson-datatype-jsr310",
"optional" => true,
"export" => false,
"import-services" => true
"local-dependencies" => [
"name" => "deployment.test-application.ear.test-application-ejb.jar",
"optional" => false,
"export" => false,
"import-services" => true
"user-dependencies" => [
"name" => "com.fasterxml.jackson.datatype.jackson-datatype-jdk8",
"optional" => false,
"export" => false,
"import-services" => false
"name" => "org.hibernate:5.3",
"optional" => false,
"export" => false,
"import-services" => false
The list_modules operation shows information in three different categories:
system-dependencies: These are the dependencies added implicitly by the server container.
local-dependencies: These are dependencies on other parts of the deployment.
user-dependencies: These are the dependencies defined by the user via a manifest file or deployment-structure.xml.
name: The module name and, if the slot name is not the default "main" slot, the slot name is concatenated after a ":" character separator.
optional: If the dependency was added as an optional dependency.
export: If the dependency is being exported to other modules.
import-services: If the module for the deployment or subdeployment is allowed to import services from the dependency.
As explained in the Class Loading in WildFly article,
WildFly 26 is based on module classloading. A class within a module B
isn’t visible to a class within a module A, unless module B adds a
dependency on module A. Module dependencies can be explicitly (as
explained in that classloading article) or can be "implicit". This
article will explain what implicit module dependencies mean and how,
when and which modules are added as implicit dependencies.
2.1. What’s an implicit module dependency?
Consider an application deployment which contains Jakarta Enterprise Beans. Jakarta Enterprise Beans typically
need access to classes from the javax.ejb.* package and other Jakarta EE
API packages. The jars containing these packages are already shipped in
WildFly and are available as "modules". The module which contains the
javax.ejb.* classes has a specific name and so does the module which
contains all the Jakarta EE API classes. For an application to be able to
use these classes, it has to add a dependency on the relevant modules.
Forcing the application developers to add module dependencies like these
(i.e. dependencies which can be "inferred") isn’t a productive approach.
Hence, whenever an application is being deployed, the deployers within
the server, which are processing this deployment "implicitly" add these
module dependencies to the deployment so that these classes are visible
to the deployment at runtime. This way the application developer doesn’t
have to worry about adding them explicitly. How and when these implicit
dependencies are added is explained in the next section.
2.2. How and when is an implicit module dependency added?
When a deployment is being processed by the server, it goes through a
chain of "deployment processors". Each of these processors will have a
way to check if the deployment meets a certain criteria and if it does,
the deployment processor adds an implicit module dependency to that
deployment. Let’s take an example - Consider (again) an Jakarta Enterprise Beans 3 deployment
which has the following class:
MySuperDuperBean.java
@Stateless
public class MySuperDuperBean {
As can be seen, we have a simple @Stateless Jakarta Enterprise Beans. When the deployment
containing this class is being processed, the Jakarta Enterprise Beans deployment processor
will see that the deployment contains a class with the @Stateless
annotation and thus identifies this as an Jakarta Enterprise Beans deployment. This is just
one of the several ways, various deployment processors can identify a
deployment of some specific type. The Jakarta Enterprise Beans deployment processor will
then add an implicit dependency on the Jakarta EE API module, so that all
the Jakarta EE API classes are visible to the deployment.
Some subsystems will always add API classes, even if the trigger
condition is not met. These are listed separately below.
In the next section, we’ll list down the implicit module dependencies
that are added to a deployment, by various deployers within WildFly.
2.3. Which are the implicit module dependencies?
Subsystem responsible for adding the implicit dependency
Dependencies
that are always added
Dependencies that are added if a trigger
condition is met
Trigger which leads to the implicit module dependency
being added
javaee.api
The presence of ejb-jar.xml (in valid
locations in the deployment, as specified by spec) or the presence of
annotation based Jakarta Enterprise Beans (ex: @Stateless, @Stateful, @MessageDriven etc)
Jakarta RESTful Web Services (RESTEasy) subsystem
com.fasterxml.jackson.core.jackson-annotations
com.fasterxml.jackson.core.jackson-core
com.fasterxml.jackson.core.jackson-databind
com.fasterxml.jackson.datatype.jackson-datatype-jdk8
com.fasterxml.jackson.datatype.jackson-datatype-jsr310
com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider
javax.json.api
javax.ws.rs.api
javax.xml.bind.api
org.eclipse.microprofile.restclient
org.jboss.resteasy.resteasy-atom-provider
org.jboss.resteasy.resteasy-client
org.jboss.resteasy.resteasy-client-api
org.jboss.resteasy.resteasy-client-microprofile
org.jboss.resteasy.resteasy-core
org.jboss.resteasy.resteasy-core-spi
org.jboss.resteasy.resteasy-crypto
org.jboss.resteasy.resteasy-jackson2-provider
org.jboss.resteasy.resteasy-jaxb-provider
org.jboss.resteasy.resteasy-jsapi
org.jboss.resteasy.resteasy-json-binding-provider
org.jboss.resteasy.resteasy-json-p-provider
org.jboss.resteasy.resteasy-multipart-provider
org.jboss.resteasy.resteasy-validator-provider
The presence of Jakarta RESTful Web Services annotations in the deployment
Jakarta Connectors subsystem
javax.resource.api
javax.jms.api javax.validation.api
org.jboss.logging org.jboss.ironjacamar.api org.jboss.ironjacamar.impl
org.hibernate.validator
If the deployment is a resource adaptor (RAR)
deployment.
Jakarta Persistence (Hibernate) subsystem
javax.persistence.api
javaee.api
org.jboss.as.jpa org.hibernate
The presence of an @PersistenceUnit or
@PersistenceContext annotation, or a <persistence-unit-ref> or
<persistence-context-ref> in a deployment descriptor..
Logging Subsystem
org.jboss.logging org.apache.commons.logging
org.apache.log4j org.apache.logging.log4j.api org.slf4j org.jboss.logging.jul-to-slf4j-stub
javaee.api com.sun.jsf-impl org.hibernate.validator
org.jboss.as.web org.jboss.logging
The deployment is a WAR archive. Jakarta Server Faces
is only added if used. Multiple version options exist for mojarra.
Web Services Subsystem
org.jboss.ws.api org.jboss.ws.spi
javax.persistence.api javaee.api org.javassist
org.jboss.interceptor org.jboss.as.weld org.jboss.logging
org.jboss.weld.core org.jboss.weld.api org.jboss.weld.spi
beans.xml file is detected in the deployment
This page gives a list and a description of all the valid deployment
descriptors that a WildFly deployment can use. This document is a work
in progress.
WEB-INF or META-INF
Jakarta Contexts and Dependency Injection
The presence of this descriptor
(even if empty) activates Jakarta Contexts and Dependency Injection
Weld Reference Guide
web.xml
WEB-INF
Servlet
Web deployment descriptor
jboss-web.xml
WEB-INF
JBoss Web deployment descriptor. This can be
use to override settings from web.xml, and to set WildFly specific
options
ejb-jar.xml
WEB-INF of a war, or META-INF of an Jakarta Enterprise Beans jar
Jakarta Enterprise Beans
The Jakarta Enterprise Beans
spec deployment descriptor
ejb-jar.xml schema
jboss-ejb3.xml
WEB-INF of a war, or META-INF of an Jakarta Enterprise Beans jar
JBoss Jakarta Enterprise Beans deployment descriptor, this can be used to override settings
from ejb-jar.xml, and to set WildFly specific settings
application.xml
META-INF of an EAR
Jakarta EE Platform Specification
application.xml schema
jboss-app.xml
META-INF of an EAR
JBoss application deployment
descriptor, can be used to override settings application.xml, and to set
WildFly specific settings
persistence.xml
META-INF
Jakarta Persistence
Jakarta Persistence descriptor used for defining
persistence units
Hibernate Reference Guide
jboss-ejb-client.xml
WEB-INF of a war, or META-INF of an Jakarta Enterprise Beans jar
Remote Jakarta Enterprise Beans settings. This file is used to setup the Jakarta Enterprise Beans client context
for a deployment that is used for remote Jakarta Enterprise Beans invocations
Jakarta Enterprise Beans
invocations from a remote server instance
jbosscmp-jdbc.xml
META-INF of an Jakarta Enterprise Beans jar
CMP deployment
descriptor. Used to map CMP entity beans to a database. The format is
largely unchanged from previous versions.
ra.xml
META-INF of a rar archive
Spec deployment descriptor for
resource adaptor deployments
IronJacamar Reference Guide Schema
ironjacamar.xml
META-INF of a rar archive
JBoss deployment
descriptor for resource adaptor deployments
IronJacamar Reference Guide
*-jms.xml
META-INF or WEB-INF
Jakarta Messaging message destination deployment
descriptor, used to deploy message destinations with a deployment
*-ds.xml
META-INF or WEB-INF
Datasource deployment descriptor, use
to bundle datasources with a deployment
DataSource Configuration
application-client.xml
META-INF of an application client jar
Jakarta EE
Platform Specification
The spec deployment descriptor for application
client deployments
application-client.xml schema
jboss-client.xml
META-INF of an application client jar
The WildFly
specific deployment descriptor for application client deployments
jboss-webservices.xml
META-INF for Jakarta Enterprise Beans webservice deployments or
WEB-INF for POJO webservice deployments/Jakarta Enterprise Beans webservice endpoints bundled
in .war
The JBossWS 4.0.x specific deployment descriptor for
webservice endpoints
The purpose of this page is to document tips and techniques that will
assist developers in creating fast, secure, and reliable applications.
It is also a place to note what you should avoid doing when developing
applications.
As a Jakarta EE compliant server, WildFly 26 contains an application
client. An application client is essentially a cut down server instance,
that allows you to use EE features such as injection in a client side
program.
This article is not a tutorial on application client development, rather
it covers the specifics of the WildFly application client. There are
tutorials available elsewhere that cover application client basics, such
one.
Note that the application client is different from the Jakarta Enterprise Beans client
libraries, it is perfectly possible to write a client application that does
not use the application client, but instead uses the jboss-ejb-client
library directly.
The --host argument tells the appclient the server to connect to. The
next argument is the application client deployment to use, application
clients can only run a single deployment, and this deployment must also
be deployed on the full server instance that the client is connecting
Any arguments after the deployment to use are passed directly through to
the application clients main function.
5.2. Connecting to more than one host
If you want to connect to more than one host or make use of the
clustering functionality then you need to specify a
jboss-ejb-client.properties file rather than a host:
./appclient.sh --ejb-client-properties=my-jboss-ejb-client.properties myear.ear#appClient.jar arg1
A simple example of how to package an application client and use it with
WildFly can be within the quickstart
appclient
which is located on Github .
The embedded server can be reinitialized with a different JBoss Home. However the module directory, module.path
system property, and the modules system packages, jboss.modules.system.pkgs system property, are effectively static.
This means that creating a new embedded server or host controller within the same VM will not allow overriding the
modules directory or the system packages.
You can also set a hint to indicate which log manager is being used. The hint attempts to ensure that JBoss Logging
will bind to the correct log manager. It also adds the hinted logging package to the modules system packages.
If using the embedded API with Java 11 you’ll need to add --add-module=java.se to your JVM arguments. See
MODULES-372 for details.
6.1. Standalone API
A standalone server allows you to manage the lifecycle of a server within the currently running process. The server
can be configured in admin-only mode or fully started and applications can be deployed.
6.1.1. Examples
Simple Example
final StandaloneServer server = EmbeddedProcessFactory.createStandaloneServer(Configuration.Builder.of(jbossHome).build());
server.start();
try {
// Print the listening address
final ModelControllerClient client = server.getModelControllerClient();
final ModelNode address = Operations.createAddress("interface", "public");
final ModelNode op = Operations.createReadAttributeOperation(address, "inet-address");
op.get("resolve-expressions").set(true);
final ModelNode result = client.execute(op);
if (!Operations.isSuccessfulOutcome((result))) {
throw new RuntimeException("Failed to get the public inet-address: " + Operations.getFailureDescription(result));
System.out.printf("Listening on %s%n", Operations.readResult(result).asString());
} finally {
server.stop();
Server in admin-only Example
final StandaloneServer server = EmbeddedProcessFactory.createStandaloneServer(
Configuration.Builder.of(jbossHome)
.addCommandArgument("--admin-only")
.build());
server.start();
try {
// Print the listening address
final ModelControllerClient client = server.getModelControllerClient();
final ModelNode address = Operations.createAddress();
final ModelNode op = Operations.createReadAttributeOperation(address, "running-mode");
op.get("resolve-expressions").set(true);
final ModelNode result = client.execute(op);
if (!Operations.isSuccessfulOutcome((result))) {
throw new RuntimeException("Failed to get the running-mode: " + Operations.getFailureDescription(result));
System.out.printf("Running mode is %s%n", Operations.readResult(result).asString());
} finally {
server.stop();
log4j2 Hint
final StandaloneServer server = EmbeddedProcessFactory.createStandaloneServer(Configuration.Builder.of(jbossHome)
.setLoggerHint(Configuration.LoggerHint.LOG4J2)
.build()
server.start();
try {
// Print the listening address
final ModelControllerClient client = server.getModelControllerClient();
final ModelNode address = Operations.createAddress("interface", "public");
final ModelNode op = Operations.createReadAttributeOperation(address, "inet-address");
op.get("resolve-expressions").set(true);
final ModelNode result = client.execute(op);
if (!Operations.isSuccessfulOutcome((result))) {
throw new RuntimeException("Failed to get the public inet-address: " + Operations.getFailureDescription(result));
org.apache.logging.log4j.LogManager.getFormatterLogger(Main.class).info("Listening on %s%n", Operations.readResult(result).asString());
} finally {
server.stop();
6.2. Host Controller API
The host controller API creates a host controller in the current process. The host controller is started in admin-only
mode therefore servers within the domain cannot be started. However the server configuration can be altered via
management operations.
6.2.1. Example
Simple Example
final HostController server = EmbeddedProcessFactory.createHostController(Configuration.Builder.of(jbossHome).build());
server.start();
try {
// Print the listening address
final ModelControllerClient client = server.getModelControllerClient();
final ModelNode address = new ModelNode().setEmptyList();
final ModelNode op = Operations.createOperation(ClientConstants.READ_CHILDREN_NAMES_OPERATION, address);
op.get(ClientConstants.CHILD_TYPE).set(ClientConstants.SERVER_GROUP);
final ModelNode result = client.execute(op);
if (!Operations.isSuccessfulOutcome(result)) {
throw new RuntimeException("Failed to get the public inet-address: " + Operations.getFailureDescription(result));
System.out.println("Available server groups:");
for (ModelNode value : Operations.readResult(result).asList()) {
System.out.printf("\t%s%n", value.asString());
} finally {
server.stop();
WildFly uses Weld, the CDI reference
implementation as its CDI provider. To activate CDI for a deployment
simply add a beans.xml file in any archive in the deployment.
This document is not intended to be a CDI tutorial, it only covers CDI
usage that is specific to WildFly. For some general information on CDI
see the below links:
CDI Specification
Weld Reference
Guide
The WildFly Quickstarts
7.1. Using CDI Beans from outside the deployment
For WildFly 26 onwards, it is now possible to have classes outside the
deployment be picked up as CDI beans. In order for this to work, you must
add a dependency on the external deployment that your beans are coming
from, and make sure the META-INF directory of this deployment is
imported, so that your deployment has visibility to the beans.xml file
(To import beans from outside the deployment they must be in an archive
with a beans.xml file).
There are two ways to do this, either using the MANIFEST.MF or using
jboss-deployment-structure.xml.
Using MANIFEST.MF you need to add a Dependencies entry, with
meta-inf specified after the entry, e.g.
Dependencies: com.my-cdi-module meta-inf, com.my-other-cdi-module meta-inf
Using jboss-deployment-structure.xml you need to add a dependency
entry with meta-inf="import", e.g.
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2">
<deployment>
<dependencies>
<module name="deployment.d1.jar" meta-inf="import"/>
</dependencies>
</deployment>
</jboss-deployment-structure>
Note that this can be used to create beans from both modules in the
modules directory, and from other deployments.
For more information on class loading and adding dependencies to your
deployment please see the Class
Loading Guide
7.2. Suppressing implicit bean archives
CDI 1.1 brings new options to packaging of CDI-enabled applications. In
addition to well-known explicit bean archives (basically any archive
containing the beans.xml file) the specification introduces implicit
bean archives.
An implicit bean archive is any archive that contains one or more
classes annotated with a bean defining annotation (scope annotation) or
one or more session beans. As a result, the beans.xml file is no longer
required for CDI to work in your application.
In an implicit bean archive only those classes that are either
annotated with bean defining annotations or are session beans are
recognized by CDI as beans (other classes cannot be injected).
This has a side-effect, though. Libraries exist that make use of scope
annotation (bean defining annotations) for their own convenience but are
not designed to run with CDI support. Guava would be an example of such
library. If your application bundles such library it will be recognized
as a CDI archive and may
fail the
deployment.
Fortunately, WildFly makes it possible to suppress implicit bean
archives and only enable CDI in archives that bundle the beans.xml file.
There are two ways to achieve this:
7.2.1. Per-deployment configuration
You can either set this up for your deployment only by adding the
following content to the META-INF/jboss-all.xml file of your
application:
<jboss xmlns="urn:jboss:1.0">
<weld xmlns="urn:jboss:weld:1.0" require-bean-descriptor="true"/>
</jboss>
7.2.2. Global configuration
Alternatively, you may configure this for all deployments in your
WildFly instance by executing the following command:
/subsystem=weld:write-attribute(name=require-bean-descriptor,value=true)
7.3. Development mode
WildFly 10 introduces a special mode for application development which
allows you to inspect and monitor your CDI deployments. This mode is
turned off by default and note that some features of the development
mode may have negative impact on the performance and/or functionality of
the application.
7.3.1. Per-deployment configuration
You can enable it locally in your application web.xml by setting the
Servlet initialization parameter org.jboss.weld.development to true:
<context-param>
<param-name>org.jboss.weld.development</param-name>
<param-value>true</param-value>
</context-param>
7.3.2. Global configuration
Alternatively, you can enable it globally in Weld subsystem by setting
development-mode attribute to true:
/subsystem=weld:write-attribute(name=development-mode,value=true)
For more details and example you can check
development mode.
Once the development mode is enabled you can check your applications CDI
information using Weld Probe -
Probe.
7.4. Non-portable mode
CDI 1.1 clarifies some aspects of how CDI protable extensions work. As a
result, some extensions that do not use the API properly (but were
tolerated in CDI 1.0 environment) may stop working with CDI 1.1.If this
is the case of your application you will see an exception like this:
org.jboss.weld.exceptions.IllegalStateException: WELD-001332: BeanManager method getBeans() is not available during application initialization
Fortunatelly, there is a non-portable mode available in WildFly which
skips some of the API usage checks and therefore allows the legacy
extensions to work as before.
Again, there are two ways to enable the non-portable mode:
7.4.1. Per-deployment configuration
You can either set this up for your deployment only by adding the
following content to the META-INF/jboss-all.xml file of your
application:
<jboss xmlns="urn:jboss:1.0">
<weld xmlns="urn:jboss:weld:1.0" non-portable-mode="true" />
</jboss>
7.4.2. Global configuration
Alternatively, you may configure this for all deployments in your
WildFly instance by executing the following command:
/subsystem=weld:write-attribute(name=non-portable-mode,value=true)
Note that new portable extensions should always use the
BeanManager
API properly and thus never required the non-portable mode. The
non-portable mode only exists to preserve compatibility with legacy
extensions!
EE Concurrency Utilities (JSR 236) is a technology introduced with Java
EE 7, which adapts well known Java SE concurrency utilities to the Java
EE application environment specifics. The Jakarta EE application server is
responsible for the creation (and shutdown) of every instance of the EE
Concurrency Utilities, and provide these to the applications, ready to
The EE Concurrency Utilities support the propagation of the invocation
context, capturing the existent context in the application threads to
use in their own threads, the same way a logged-in user principal is
propagated when a servlet invokes an Jakarta Enterprise Beans asynchronously. The propagation
of the invocation context includes, by default, the class loading, JNDI
and security contexts.
WildFly creates a single default instance of each EE Concurrency Utility
type in all configurations within the distribution, as mandated by the
specification, but additional instances, perhaps customised to better
serve a specific usage, may be created through WildFly’s EE Subsystem
Configuration. To learn how to configure EE Concurrency Utilities please
refer to EE Concurrency
Utilities Configuration. Additionally, the EE subsystem configuration
also includes the configuration of which instance should be considered
the default instance mandated by the Jakarta EE specification, and such
configuration is covered by
Default EE Bindings
Configuration.
8.1. Context Service
The Context Service ( javax.enterprise.concurrent.ContextService) is a
brand new concurrency utility, which applications may use to build
contextual proxies from existing objects.
A contextual proxy is an object that sets an invocation context, captured
when created, whenever is invoked, before delegating the invocation to
the original object.
Usage example:
public void onGet(...) {
Runnable task = ...;
Runnable contextualTask = contextService.createContextualProxy(task, Runnable.class);
// ...
WildFly default configurations creates a single default instance of a
Context Service, which may be retrieved through @Resource injection:
@Resource
private ContextService contextService;
To retrieve instead a non default Context Service instance,
@Resource’s `lookup attribute needs to specify the JNDI name used in
the wanted instance configuration. WildFly will always inject the
default instance, no matter what’s the name attribute value, if the
lookup attribute is not defined.
The Managed Thread Factory (
javax.enterprise.concurrent.ManagedThreadFactory) allows Java EE
applications to create Java threads. It is an extension of Java SE’s
Thread Factory ( java.util.concurrent.ThreadFactory) adapted to the
Jakarta EE platform specifics.
Managed Thread Factory instances are managed by the application server,
thus Jakarta EE applications are forbidden to invoke any lifecycle related
method.
In case the Managed Thread Factory is configured to use a Context
Service, the application’s thread context is captured when a thread
creation is requested, and such context is propagated to the thread’s
Runnable execution.
Managed Thread Factory threads implement
javax.enterprise.concurrent.ManageableThread, which allows an
application to learn about termination status.
Usage example:
public void onGet(...) {
Runnable task = ...;
Thread thread = managedThreadFactory.newThread(task);
thread.start();
// ...
WildFly default configurations creates a single default instance of a
Managed Thread Factory, which may be retrieved through @Resource
injection:
@Resource
private ManagedThreadFactory managedThreadFactory;
To retrieve instead a non default Managed Thread Factory instance,
@Resource’s `lookup attribute needs to specify the JNDI name used in
the wanted instance configuration. WildFly will always inject the
default instance, no matter what’s the name attribute value, in case
the lookup attribute is not defined.
The Managed Executor Service (
javax.enterprise.concurrent.ManagedExecutorService) allows Java EE
applications to submit tasks for asynchronous execution. It is an
extension of Java SE’s Executor Service (
java.util.concurrent.ExecutorService) adapted to the Jakarta EE platform
requirements.
Managed Executor Service instances are managed by the application
server, thus Jakarta EE applications are forbidden to invoke any lifecycle
related method.
In case the Managed Executor Service is configured to use a Context
Service, the application’s thread context is captured when the task is
submitted, and propagated to the executor thread responsible for the
task execution.
Usage example:
public void onGet(...) {
Runnable task = ...;
Future future = managedExecutorService.submit(task);
// ...
WildFly default configurations creates a single default instance of a
Managed Executor Service, which may be retrieved through @Resource
injection:
@Resource
private ManagedExecutorService managedExecutorService;
To retrieve instead a non default Managed Executor Service instance,
@Resource’s `lookup attribute needs to specify the JNDI name used in
the wanted instance configuration. WildFly will always inject the
default instance, no matter what’s the name attribute value, in case
the lookup attribute is not defined.
As mandated by the Jakarta EE specification, the default Managed Executor
Service instance’s JNDI name is
java:comp/DefaultManagedExecutorService.
The Managed Scheduled Executor Service (
javax.enterprise.concurrent.ManagedScheduledExecutorService) allows
Jakarta EE applications to schedule tasks for asynchronous execution. It is
an extension of Java SE’s Executor Service (
java.util.concurrent.ScheduledExecutorService) adapted to the Java EE
platform requirements.
Managed Scheduled Executor Service instances are managed by the
application server, thus Jakarta EE applications are forbidden to invoke
any lifecycle related method.
In case the Managed Scheduled Executor Service is configured to use a
Context Service, the application’s thread context is captured when the
task is scheduled, and propagated to the executor thread responsible for
the task execution.
Usage example:
public void onGet(...) {
Runnable task = ...;
ScheduledFuture future = managedScheduledExecutorService.schedule(task, 60, TimeUnit.SECONDS);
// ...
WildFly default configurations creates a single default instance of a
Managed Scheduled Executor Service, which may be retrieved through
@Resource injection:
@Resource
private ManagedScheduledExecutorService managedScheduledExecutorService;
To retrieve instead a non default Managed Scheduled Executor Service
instance, @Resource’s `lookup attribute needs to specify the JNDI
name used in the wanted instance configuration. WildFly will always
inject the default instance, no matter what’s the name attribute
value, in case the lookup attribute is not defined.
As mandated by the Jakarta EE specification, the default Managed Scheduled
Executor Service instance’s JNDI name is
java:comp/DefaultManagedScheduledExecutorService.
This chapter details the extensions that are available when developing
Enterprise Java Beans tm on WildFly 26.
Currently there is no support for configuring the extensions using an
implementation specific descriptor file.
9.1. Resource Adapter for Message Driven Beans
Each Message Driven Bean must be connected to a resource adapter.
9.1.1. Specification of Resource Adapter using Metadata Annotations
The ResourceAdapter annotation is used to specify the resource adapter
with which the MDB should connect.
The value of the annotation is the name of the deployment unit
containing the resource adapter. For example jms-ra.rar.
For example:
@MessageDriven(messageListenerInterface = PostmanPat.class)
@ResourceAdapter("ejb3-rar.rar")
9.2. Run-as Principal
Whenever a run-as role is specified for a given method invocation the
default anonymous principal is used as the caller principal. This
principal can be overridden by specifying a run-as principal.
9.2.1. Specification of Run-as Principal using Metadata Annotations
The RunAsPrincipal annotation is used to specify the run-as principal
to use for a given method invocation.
The value of the annotation specifies the name of the principal to
use. The actual type of the principal is undefined and should not be
relied upon.
Using this annotation without specifying a run-as role is considered an
error.
For example:
@RunAs("admin")
@RunAsPrincipal("MyBean")
9.3. Security Domain
Each Enterprise Java Bean tm can be associated with a security domain.
Only when an Jakarta Enterprise Beans are associated with a security domain will
authentication and authorization be enforced.
9.3.1. Specification of Security Domain using Metadata Annotations
The SecurityDomain annotation is used to specify the security domain
to associate with the Jakarta Enterprise Beans.
The value of the annotation is the name of the security domain to be
used.
For example:
@SecurityDomain("other")
9.4. Transaction Timeout
For any newly started transaction a transaction timeout can be specified
in seconds.
When a transaction timeout of 0 is used, then the actual transaction
timeout will default to the domain configured default.
TODO: add link to tx subsystem
Although this is only applicable when using transaction attribute
REQUIRED or REQUIRES_NEW the application server will not detect
invalid setups.
New Transactions
9.4.1. Specification of Transaction Timeout with Metadata Annotations
The TransactionTimeout annotation is used to specify the transaction
timeout for a given method.
The value of the annotation is the timeout used in the given unit
granularity. It must be a positive integer or 0. Whenever 0 is specified
the default domain configured timeout is used.
The unit specifies the granularity of the value. The actual value
used is converted to seconds. Specifying a granularity lower than
SECONDS is considered an error, even when the computed value will
result in an even amount of seconds.
For example:@TransactionTimeout(value = 10, unit = TimeUnit.SECONDS)
9.4.2. Specification of Transaction Timeout in the Deployment Descriptor
The trans-timeout element is used to define the transaction timeout
for business, home, component, and message-listener interface methods;
no-interface view methods; web service endpoint methods; and timeout
callback methods.
The trans-timeout element resides in the urn:trans-timeout namespace
and is part of the standard container-transaction element as defined
in the jboss namespace.
For the rules when a container-transaction is applicable please refer
to EJB 3.1 FR 13.3.7.2.1.
Example of trans-timeout
jboss-ejb3.xml
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:tx="urn:trans-timeout"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd
http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd
urn:trans-timeout http://www.jboss.org/j2ee/schema/trans-timeout-1_0.xsd"
version="3.1"
impl-version="2.0">
<assembly-descriptor>
<container-transaction>
<method>
<ejb-name>BeanWithTimeoutValue</ejb-name>
<method-name>*</method-name>
<method-intf>Local</method-intf>
</method>
<tx:trans-timeout>
<tx:timeout>10</tx:timeout>
<tx:unit>Seconds</tx:unit>
</tx:trans-timeout>
</container-transaction>
</assembly-descriptor>
</jboss:ejb-jar>
9.5. Timer service
The service is responsible to call the registered timeout methods of the
different session beans.
A persistent timer will be identified by the name of the EAR, the name
of the sub-deployment JAR and the Bean’s name.
If one of those names are changed (e.g. EAR name contain a version) the
timer entry became orphaned and the timer event will not longer be
fired.
In case of a server restart the timeout method of a persistent timer
will only be called directly if the specified time is elapsed.
If the timer is not persistent, it will no longer be available
if JBoss is restarted or the application is redeployed.
9.5.2. Recurring timer
The timer will be started at the specified first occurrence and after
that point at each time if the interval is elapsed.
If the timer will be started during the last execution is not finished
the execution will be suppressed with a warning to avoid concurrent
execution.
In case of server downtime for a persistent timer, the timeout method
will be called only once if one, or more than one, interval is
elapsed.
If the timer is not persistent, it will no longer be active
after the server is restarted or the application is redeployed.
9.5.3. Calendar timer
The timer will be started if the schedule expression match. It will be
automatically deactivated and removed if there will be no next
expiration possible, i.e. If you set a specific year.
For example:
@Schedule( ... dayOfMonth="1", month="1", year="2012") +
// start once at 01-01-2012 00:00:00
Programmatic calendar timer
If the timer is persistent it will be fetched at server start and the
missed timeouts are called concurrent.
If a persistent timer contains an end date it will be executed once
nevertheless how many times the execution was missed. Also a retry will
be suppressed if the timeout method throw an Exception.
In case of such expired timer access to the given Timer object might
throw a NoMoreTimeoutExcption or NoSuchObjectException.
If the timer is non persistent it will not longer be active after the
server is restarted or the application is redeployed.
TODO: clarify whether this should happen concurrently/blocked or even
fired only once like a recurring timer!
Annotated calendar timer
If the timer is non persistent it will not activated for missed events
during the server is down. In case of server start the timer is
scheduled based on the @Schedule annotation.
If the timer is persistent (default if not deactivated by annotation)
all missed events are fetched at server start and the annotated timeout
method is called concurrent.
TODO: clarify whether this should happen concurrently/blocked or even
fired only once like a recurring timer!
JBoss AS versions prior to WildFly8 allowed a JBoss specific way to
plug-in user application specific interceptors on the server side so
that those interceptors get invoked during an EJB invocation. Such
interceptors differed from the typical (portable) spec provided Jakarta EE
interceptors. The Jakarta Interceptors are expected to run after the
container has done necessary invocation processing which involves
security context propagation, transaction management and other such
duties. As a result, these Jakarta Interceptors come too late into the
picture, if the user applications have to intercept the call before
certain container specific interceptor(s) are run.
9.6.1. Typical EJB invocation call path on the server
A typical EJB invocation looks like this:
Client application
MyBeanInterface bean = lookupBean();
bean.doSomething();
The invocation on the bean.doSomething() triggers the following (only
relevant portion of the flow shown below):
The WildFly specific interceptors include the security context
propagation, transaction management and other container provided
services. In some cases, the " `container interceptors`" (let’s call
them that) might even decide break the invocation flow and not let the
invocation proceed (for example: due to the invoking caller not being
among the allowed user roles who can invoke the method on the bean).
Previous versions of JBoss AS allowed a way to plug-in the user
application specific interceptors (which relied on JBoss AS specific
libraries) into this invocation flow so that they do run some
application specific logic before the control reaches step#5 above. For
example, AS5 allowed the use of JBoss AOP interceptors to do this.
As of WildFly 8, this feature was implemented.
9.6.2. Configuring container interceptors
As you can see from the JIRA https://issues.redhat.com/browse/AS7-5897,
one of the goals of this feature implementation was to make sure that we
don’t introduce any new WildFly specific library dependencies for the
container interceptors. So we decided to allow the Jakarta Interceptors
(which are just POJO classes with lifecycle callback annotations) to be
used as container interceptors. As such you won’t need any dependency on
any WildFly specific libraries. That will allow us to support this
feature for a longer time in future versions of WildFly.
Furthermore, configuring these container interceptors is similar to
configuring the Jakarta Interceptors for EJBs. In fact, it uses the same
xsd elements that are allowed in ejb-jar.xml for 3.1 version of ejb-jar
deployment descriptor.
Container interceptors can only be configured via deployment
descriptors. There’s no annotation based way to configure container
interceptors. This was an intentional decision, taken to avoid
introducing any WildFly specific library dependency for the annotation.
Configuring the container interceptors can be done in jboss-ejb3.xml
file, which then gets placed under the META-INF folder of the EJB
deployment, just like the ejb-jar.xml. Here’s an example of how the
container interceptor(s) can be configured in jboss-ejb3.xml:
jboss-ejb3.xml
<jboss xmlns="http://www.jboss.com/xml/ns/javaee"
xmlns:jee="http://java.sun.com/xml/ns/javaee"
xmlns:ci ="urn:container-interceptors:1.0">
<jee:assembly-descriptor>
<ci:container-interceptors>
<!-- Default interceptor -->
<jee:interceptor-binding>
<ejb-name>*</ejb-name>
<interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ContainerInterceptorOne</interceptor-class>
</jee:interceptor-binding>
<!-- Class level container-interceptor -->
<jee:interceptor-binding>
<ejb-name>AnotherFlowTrackingBean</ejb-name>
<interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ClassLevelContainerInterceptor</interceptor-class>
</jee:interceptor-binding>
<!-- Method specific container-interceptor -->
<jee:interceptor-binding>
<ejb-name>AnotherFlowTrackingBean</ejb-name>
<interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.MethodSpecificContainerInterceptor</interceptor-class>
<method>
<method-name>echoWithMethodSpecificContainerInterceptor</method-name>
</method>
</jee:interceptor-binding>
<!-- container interceptors in a specific order -->
<jee:interceptor-binding>
<ejb-name>AnotherFlowTrackingBean</ejb-name>
<interceptor-order>
<interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ClassLevelContainerInterceptor</interceptor-class>
<interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.MethodSpecificContainerInterceptor</interceptor-class>
<interceptor-class>org.jboss.as.test.integration.ejb.container.interceptor.ContainerInterceptorOne</interceptor-class>
</interceptor-order>
<method>
<method-name>echoInSpecificOrderOfContainerInterceptors</method-name>
</method>
</jee:interceptor-binding>
</ci:container-interceptors>
</jee:assembly-descriptor>
</jboss>
The usage of urn:container-interceptors:1.0 namespace which allows the
container-interceptors elements to be configured
The container-interceptors element which contain the interceptor
bindings
The interceptor bindings themselves are the same elements as what the
EJB3.1 xsd allows for standard Jakarta Interceptors
The interceptors can be bound either to all EJBs in the deployment
(using the the * wildcard) or individual bean level (using the specific
EJB name) or at specific method level for the EJBs.
The interceptor classes themselves are simple POJOs and use the
@javax.annotation.AroundInvoke to mark the around invoke method which
will get invoked during the invocation on the bean. Here’s an example of
the interceptor:
Example of container interceptor
public class ClassLevelContainerInterceptor {
@AroundInvoke
private Object iAmAround(final InvocationContext invocationContext) throws Exception {
return this.getClass().getName() + " " + invocationContext.proceed();
9.6.3. Container interceptor positioning in the interceptor chain
The container interceptors configured for a EJB are guaranteed to be run
before the WildFly provided security interceptors, transaction
management interceptors and other such interceptors thus allowing the
user application specific container interceptors to setup any relevant
context data before the invocation proceeds.
9.6.4. Semantic difference between container interceptor(s) and Jakarta Interceptors API
Although the container interceptors are modeled to be similar to the
Jakarta Interceptors, there are some differences in the API semantics.
One such difference is that invoking on
javax.interceptor.InvocationContext.getTarget() method is illegal for
container interceptors since these interceptors are invoked way before
the EJB components are setup or instantiated.
9.6.5. Testcase
This testcase in the WildFly codebase can be used for reference for
implementing container interceptors in user applications
https://github.com/wildfly/wildfly/blob/master/testsuite/integration/basic/src/test/java/org/jboss/as/test/integration/ejb/container/interceptor/ContainerInterceptorsTestCase.java
WildFly now supports clustered database backed timers. The clustering
support is provided through the database, and as a result it is not
intended to be a super high performance solution that supports thousands
of timers going off a second, however properly tuned it should provide
sufficient performance for most use cases.
Note that database timers can also be used in non-clustered mode.
Note that for this to work correctly the underlying database must
support the READ_COMMITTED or SERIALIZABLE isolation mode and the
datasource must be configured accordingly
In order to use clustered timers it is necessary to add a database
backed timer store. This can be done from the CLI with the following
command:
/subsystem=ejb3/service=timer-service/database-data-store=my-clustered-store:add(allow-execution=true, datasource-jndi-name='java:/MyDatasource', refresh-interval=60000, database='postgresql', partition='mypartition')
An explanation of the parameters is below:
allow-execution - If this node is allowed to execute timers. If this
is false then timers added on this node will be added to the database
for another node to execute. This allows you to limit timer execution to
a few nodes in a cluster, which can greatly reduce database load for
large clusters.
datasource-jndi-name - The datasource to use
refresh-interval - The refresh interval in milliseconds. This is the
period of time that must elapse before this node will check the database
for new timers added by other nodes. A smaller value means that timers
will be picked up more quickly, however it will result in more load on
the database. This is most important to tune if you are adding timers
that will expire quickly. If the node that added the timer cannot
execute it (e.g. because it has failed or because allow-execution is
false), this timer may not be executed until a node has refreshed.
database - Define the type of database that is in use. Some SQL
statements are customised by database, and this tells the data store
which version of the SQL to use.
Without this attribute the server try to detected the type
automatically, current supported types are postgresql, mysql, oracle,
db2, hsql and h2.
Note that this SQL resides in the file
modules/system/layers/base/org/jboss/as/ejb3/main/timers/timer-sql.properties
And as such is it possible to modify the SQL that is executed or add
support for new databases by adding new DB specific SQL to this file (if
you do add support for a new database it would be greatly appreciated if
you could contribute the SQL back to the project).
partition - A node will only see timers from other nodes that have
the same partition name. This allows you to break a large cluster up
into several smaller clusters, which should improve performance. e.g.
instead of having a cluster of 100 nodes, where all hundred are trying
to execute and refresh the same timers, you can create 20 clusters of 5
nodes by giving ever group of 5 a different partition name.
Non clustered timers
Note that you can still use the database data store for non-clustered
timers, in which case set the refresh interval to zero and make sure
that every node has a unique partition name (or uses a different
database).
9.7.2. Using clustered timers in a deployment
It is possible to use the data store as default for all applications by
changing the default-data-store within the ejb3 subsystem:
<timer-service thread-pool-name="timer" default-data-store="clustered-store">
<data-stores>
<database-data-store name="clustered-store" datasource-jndi-name="java:jboss/datasources/ExampleDS" partition="timer"/>
</data-stores>
</timer-service>
Another option is to use a separate data store for specific
applications, all that is required is to set the timer data store name
in jboss-ejb3.xml:
<?xml version="1.1" encoding="UTF-8"?>
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:timer="urn:timer-service:1.0"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd
http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
version="3.1"
impl-version="2.0">
<assembly-descriptor>
<timer:timer>
<ejb-name>*</ejb-name>
<timer:persistence-store-name>my-clustered-store</timer:persistence-store-name>
</timer:timer>
</assembly-descriptor>
</jboss:ejb-jar>
9.7.3. Programmatically Refresh Timer
In a clustered deployment, multiple nodes updating timer datastore may cause the in-memory timer state to be temporarily
out of sync. Some application may find the refresh-interval configuration not sufficient in some cases, and
need to programmatically refresh timers. This can be done with Jakarta Interceptors configured for those business methods
that need this capability, as illustrated in the following steps:
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;
* An interceptor to enable programmatic timer refresh across multiple nodes.
@Interceptor
public class RefreshInterceptor {
@AroundInvoke
public Object intercept(InvocationContext context) throws Exception {
context.getContextData().put("wildfly.ejb.timer.refresh.enabled", Boolean.TRUE);
return context.proceed();
Configure the Jakarta Interceptors to the target stateless or singleton bean business methods.
When wildfly.ejb.timer.refresh.enabled is set to true, calling TimerService.getAllTimers()
will first refresh from timer datastore before returning timers. For example,
public void businessMethod1() {
// since wildfly.ejb.timer.refresh.enabled is set to true in interceptor for this business method,
// calling timerService.getAllTimers() will first refresh from timer datastore before returning timers.
final Collection<Timer> allTimers = timerService.getAllTimers();
Applications may configure such an interceptor to certain business methods that require this capability.
Alternatively, applications may implement a dedicated business method to programmatically refresh timers, to
be invoked by other parts of the application when needed. For example,
@Interceptors(RefreshInterceptor.class)
public List<Timer> getAllTimerInfoWithRefresh() {
return timerService.getAllTimers();
public void businessMethod1() {
final LocalBusinessInterface businessObject = sessionContext.getBusinessObject(LocalBusinessInterface.class);
businessObject.getAllTimerInfoWithRefresh();
// timer has been programmatically refreshed from datastore.
// continue with other business logic...
9.7.4. Technical details
Internally every node that is allowed to execute timers schedules a
timeout for every timer is knows about. When this timeout expires then
this node attempts to 'lock' the timer, by updating its state to
running. The query this executes looks like:
UPDATE JBOSS_EJB_TIMER SET TIMER_STATE=? WHERE ID=? AND TIMER_STATE<>? AND NEXT_DATE=?;
Due to the use of a transaction and READ_COMMITTED or SERIALIZABLE
isolation mode only one node will succeed in updating the row, and this
is the node that the timer will run on.
To enable IIOP you must have the JacORB subsystem installed, and the
<iiop/> element present in the ejb3 subsystem configuration. The
standalone-full.xml configuration that comes with the distribution has
both of these enabled.
The <iiop/> element takes two attributes that control the default
behaviour of the server, for full details see EJB3
subsystem configuration guide.
9.8.2. Enabling JTS
To enable JTS simply add a <jts/> element to the transactions
subsystem configuration.
It is also necessary to enable the JacORB transactions interceptor as
shown below.
<subsystem xmlns="urn:jboss:domain:jacorb:1.1">
<initializers transactions="on"/>
</subsystem>
9.8.3. Dynamic Stub’s
Downloading stubs directly from the server is no longer supported. If
you do not wish to pre-generate your stub classes JDK Dynamic stubs can
be used instead. The enable JDK dynamic stubs simply set the
com.sun.CORBA.ORBUseDynamicStub system property to true.
9.8.4. Configuring Jakarta Enterprise Beans IIOP settings via jboss-ejb3.xml
9.9. Jakarta Enterprise Beans over HTTP
Beginning with WildFly 11 it is now possible to use HTTP as the
transport (instead of remoting) for remote Jakarta Enterprise Beans and JNDI invocations.
Everything mentioned below is applicable for both JNDI and Jakarta Enterprise Beans
functionality.
9.9.1. Server Configuration
In order to configure the server the http-invoker needs to be enabled on
each virtual host you wish to use in the Undertow subsystem. This is
enabled by default in standard configs, but if it has been removed it
can be added via:
/subsystem=undertow/server=default-server/host=default-host/setting=http-invoker:add(http-authentication-factory=myfactory, path='/wildfly-services')
The Hhttp-invoker takes two parameters, a path (which defaults to
/wildfly-services) and a http-authentication-factory which must be a
reference to an Elytron http-authentication-factory.
Note that any deployment that wishes to use this must use Elytron
security with the same security domain that corresponds to the HTTP
authentication factory.
9.9.2. Performing Invocations
The mechanism for performing invocations is exactly the same as for the
remoting based Jakarta Enterprise Beans client, the only difference is that instead of a
'remote+http' URI you use a 'http' URI (which must include the path that
was configured in the invoker). For example if you are currently using
'remote+ http://localhost:8080' as the target URI, you would change this
to 'http://localhost:8080/wildfly-services'.
9.9.3. Implementation details
The wire protocol is detailed at
https://github.com/wildfly/wildfly-http-client/blob/master/docs/src/main/asciidoc/wire-spec-v1.asciidoc
9.10. jboss-ejb3.xml Reference
jboss-ejb3.xml is a custom deployment descriptor that can be placed in
either ejb-jar or war archives. If it is placed in an ejb-jar then it
must be placed in the META-INF folder, in a web archive it must be
placed in the WEB-INF folder.
The contents of jboss-ejb3.xml are merged with the contents of
ejb-jar.xml, with the jboss-ejb3.xml items taking precedence.
9.10.1. Example File
A simple example is shown below:
<?xml version="1.1" encoding="UTF-8"?>
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:s="urn:security:1.1"
xsi:schemaLocation="
http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-spec-2_0.xsd"
version="3.1"
impl-version="2.0">
<enterprise-beans>
<message-driven>
<ejb-name>ReplyingMDB</ejb-name>
<ejb-class>org.jboss.as.test.integration.ejb.mdb.messagedestination.ReplyingMDB</ejb-class>
<activation-config>
<activation-config-property>
<activation-config-property-name>destination</activation-config-property-name>
<activation-config-property-value>java:jboss/mdbtest/messageDestinationQueue
</activation-config-property-value>
</activation-config-property>
</activation-config>
</message-driven>
</enterprise-beans>
<assembly-descriptor>
<s:security>
<ejb-name>DDMyDomainSFSB</ejb-name>
<s:security-domain>myDomain</s:security-domain>
<s:run-as-principal>myPrincipal</s:run-as-principal>
</s:security>
</assembly-descriptor>
</jboss:ejb-jar>
As you can see the format is largely similar to ejb-jar.xml, in fact
they even use the same namespaces, however jboss-ejb3.xml adds some
additional namespaces of its own to allow for configuring non-spec info.
The format of the standard http://java.sun.com/xml/ns/javaee is well
documented elsewhere, this document will cover the non-standard
namespaces.
Assembly descriptor namespaces
The following namespaces can all be used in the <assembly-descriptor>
element. They can be used to apply their configuration to a single bean,
or to all beans in the deployment by using * as the ejb-name.
The security namespace urn:security
This allows you to set the security domain and the run-as principal for
an Jakarta Enterprise Beans.
<s:security>
<ejb-name>*</ejb-name>
<s:security-domain>myDomain</s:security-domain>
<s:run-as-principal>myPrincipal</s:run-as-principal>
</s:security>
<r:resource-adapter-binding>
<ejb-name>*</ejb-name>
<r:resource-adapter-name>myResourceAdaptor</r:resource-adapter-name>
</r:resource-adapter-binding>
The IIOP namespace urn:iiop
The IIOP namespace is where IIOP settings are configured. As there are
quite a large number of options these are covered in the
IIOP guide.
The pool namespace urn:ejb-pool:1.0
This allows you to select the pool that is used by the SLSB or MDB.
Pools are defined in the server configuration (i.e. standalone.xml or
domain.xml)
<p:pool>
<ejb-name>*</ejb-name>
<p:bean-instance-pool-ref>my-pool</p:bean-instance-pool-ref>
</p:pool>
The cache namespace urn:ejb-cache:1.0
This allows you to select the cache that is used by the SFSB. Caches are
defined in the server configuration (i.e. standalone.xml or
domain.xml)
<c:cache>
<ejb-name>*</ejb-name>
<c:cache-ref>my-cache</c:cache-ref>
</c:cache>
The clustering namespace urn:clustering:1.0
This namespace is deprecated and as of WildFly 26 its use has no effect.
The clustering behavior of Jakarta Enterprise Beans are determined by the profile in use on
the server.
9.11. Message Driven Beans Controlled Delivery
There are three mechanisms in WildFly that allow controlling if a
specific MDB is actively receiving or not messages:
9.11.1. Delivery Active
Delivery active is simply an attribute associated with the MDB that
indicates if the MDB is receiving messages or not. If an MDB is not
currently receiving messages, the messages will be saved in the queue or
topic for later, according to the rules of the topic/queue.
You can configure delivery active using xml or annotations, and you can
change its value after deployment using the cli.
In the jboss-ejb3 xml file, configure the value of active as false to
mark that the MDB will not be receiving messages as soon as it is
deployed:
<?xml version="1.1" encoding="UTF-8"?>
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:d="urn:delivery-active:1.2"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd" version="3.1"
impl-version="2.0">
<assembly-descriptor>
<d:delivery>
<ejb-name>HelloWorldQueueMDB</ejb-name>
<d:active>false</d:active>
</d:delivery>
</assembly-descriptor>
</jboss:ejb-jar>
You can use a wildcard "*" in the place of ejb-name if you want to apply
that active value to all MDBs in your application.
@MessageDriven(name = "HelloWorldMDB", activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/HELLOWORLDMDBQueue"),
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") })
@DeliveryActive(false)
public class HelloWorldMDB implements MessageListener {
public void onMessage(Message rcvMessage) {
// ...
Start-delivery and Stop-Delivery
These management operations dynamically change the value of the active
attribute, enabling or disabling delivery for the MDB. at runtime To use
them, connect to the WildFly instance you want to manage, then enter the
path of the MDB you want to manage delivery for:
[standalone@localhost:9990 /] cd deployment=jboss-helloworld-mdb.war/subsystem=ejb3/message-driven-bean=HelloWorldMDB
[standalone@localhost:9990 message-driven-bean=HelloWorldMDB] :stop-delivery
{"outcome" => "success"}
[standalone@localhost:9990 message-driven-bean=HelloWorldMDB] :start-delivery
{"outcome" => "success"}
9.11.2. Delivery Groups
Delivery groups provide a straightforward way to manage delivery for a
group of MDBs. Every MDB belonging to a delivery group has delivery
active if that group is active, and has delivery inactive
whenever the group is not active.
You can add a delivery group to the ejb3 subsystem using either the
subsystem xml or cli. Next, we will see examples of each case. In those
examples we will add only a single delivery group, but keep in mind that
you can add as many delivery groups as you need to a WildFly instance.
<delivery-groups>
<delivery-group name="mdb-group-name" active="true"/>
</delivery-groups>
</subsystem>
The example above adds a delivery group named "mdb-group-name" (you can
use whatever name suits you best as the group name). The "true" active
attribute indicates that all MDBs belonging to that group will have
delivery active right after deployment. If you mark that attribute as
false, you are indicating that every MDB belonging to the group will not
start receiving messages after deployment, a condition that will remain
until the group becomes active.
Reading and Writing the Delivery State of a Delivery Group
You can check whether delivery is active for a group by reading the
active attribute, which defaults to true:
[standalone@localhost:9990 /] ./subsystem=ejb3/mdb-delivery-group=mdb-group-name:read-attribute(name=active)
{ "outcome" => "success", "result" => true }
To make the the delivery-group inactive, just write the active attribute
with a false value:
[standalone@localhost:9990 /] ./subsystem=ejb3/mdb-delivery-group=mdb-group-name:write-attribute(name=active,value=false)
{"outcome" => "success"}
[standalone@localhost:9990 /] ./subsystem=ejb3/mdb-delivery-group=mdb-group-name:read-attribute(name=active)
{ "outcome" => "success", "result" => false }
To make it active again, write the attribute with a true value:
[standalone@localhost:9990 /] ./subsystem=ejb3/mdb-delivery-group=mdb-group-name:write-attribute(name=active,value=true)
{"outcome" => "success"}
[standalone@localhost:9990 /] ./subsystem=ejb3/mdb-delivery-group=mdb-group-name:read-attribute(name=active)
{ "outcome" => "success", "result" => true }
<?xml version="1.1" encoding="UTF-8"?>
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:d="urn:delivery-active:1.2"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
version="3.1"
impl-version="2.0">
<assembly-descriptor>
<d:delivery>
<ejb-name>HelloWorldMDB</ejb-name>
<d:group>mdb-delivery-group</d:group>
</d:delivery>
</assembly-descriptor>
</jboss:ejb-jar>
You can also use a wildcard to mark that all MDBs in your application
belong to a delivery-group. In the following example, we add all MDBs in
the application to group1, except for HelloWorldMDB, that is added to
group2:
<?xml version="1.1" encoding="UTF-8"?>
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:d="urn:delivery-active:1.2"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
version="3.1"
impl-version="2.0">
<assembly-descriptor>
<d:delivery>
<ejb-name>*</ejb-name>
<d:group>group1</d:group>
</d:delivery>
<d:delivery>
<ejb-name>HelloWorldMDB</ejb-name>
<d:group>group2</d:group>
</d:delivery>
</assembly-descriptor>
</jboss:ejb-jar>
Another option is to use org.jboss.ejb3.annotation.DeliveryGroup
annotation on each MDB class belonging to a group:
@MessageDriven(name = "HelloWorldQueueMDB", activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/HELLOWORLDMDBQueue"),
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") })
@DeliveryGroup("group2")
public class HelloWorldMDB implements MessageListener {
A MDB can belong to more than one delivery group. See the following example:
<?xml version="1.1" encoding="UTF-8"?>
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:d="urn:delivery-active:1.2"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
version="3.1"
impl-version="2.0">
<assembly-descriptor>
<d:delivery>
<ejb-name>*</ejb-name>
<d:group>mdb-delivery-group1</d:group>
</d:delivery>
<d:delivery>
<ejb-name>HelloWorldMDB</ejb-name>
<d:group>mdb-delivery-group2</d:group>
<d:group>mdb-delivery-group3</d:group>
</d:delivery>
</assembly-descriptor>
</jboss:ejb-jar>
In the example above, we use the wildcard to specify that every MDB in the
ejb-jar will belong to mdb-delivery-group1.
That means that, in order for delivery of messages to be active for those MDBs,
mdb-delivery-group1 must be active.
In addition, the configuration above specifies that HelloWorldMDB belongs also
to mdb-delivery-group2 and mdb-delivery-group3. So, delivery of messages to
HelloWorldMDB will only be active when mdb-delivery-group1,
mdb-delivery-group2, and mdb-delivery-group3 are all active.
The same could be specified using the @DeliveryGroup annotation:
@MessageDriven(name = "HelloWorldQueueMDB", activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/HELLOWORLDMDBQueue"),
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") })
@DeliveryGroup("mdb-delivery-group2")
@DeliveryGroup("mdb-delivery-group3")
public class HelloWorldMDB implements MessageListener {
Notice that all the delivery-groups used by an application must be installed in
the WildFly server upon deployment, or the deployment will fail with a message
stating that the delivery-group is missing.
9.11.3. Clustered Singleton Delivery
Delivery can be marked as singleton in a clustered environment. In this
case, only one node in the cluster will have delivery active for that
MDB, whereas in all other nodes, delivery will be inactive. This option
can be used for applications that are deployed in all nodes of the
cluster. Such applications will be active in all nodes of the cluster,
except for the MDBs that are marked as clustered singleton. For those
MDBs, only one cluster node will be processing their messages. In case
that node stops, another node will have delivery activated, guaranteeing
that there is always one node processing the messages. This node is what
we call the MDB clustered singleton master node.
Notice that applications using clustered singleton delivery can only be
deployed in clustered WildFly servers (i.e., servers that are using the
ha configuration).
To mark delivery as clustered singleton, you can use the jboss-ejb3.xml
or the @ClusteredSingleton annotation:
<?xml version="1.1" encoding="UTF-8"?>
<jboss:ejb-jar xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="urn:clustering:1.1"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
version="3.1"
impl-version="2.0">
<assembly-descriptor>
<c:clustering>
<ejb-name>HelloWorldMDB</ejb-name>
<c:clustered-singleton>true</c:clustered-singleton>
</c:clustering>
</assembly-descriptor>
</jboss:ejb-jar>
As in the previous jboss-ejb3.xml examples, a wildcard can be used in
the place of the ejb-name to indicate that all MDBs in the application
are singleton clustered.
@MessageDriven(name = "HelloWorldQueueMDB", activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType", propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination", propertyValue = "queue/HELLOWORLDMDBQueue"),
@ActivationConfigProperty(propertyName = "acknowledgeMode", propertyValue = "Auto-acknowledge") })
@ClusteredSingleton
public class HelloWorldMDB implements MessageListener { ... }
9.11.4. Using Multiple MDB Delivery Control Mechanisms
The previous delivery control mechanisms can be used together in a
single MDB. In this case, they work as a set of restrictions for
delivery to be active in a MDB.
For example, if an MDB belongs to one or more delivery groups and is also a
clustered singleton MDB, the delivery will be active for that MDB only
if the delivery groups are active in the cluster node that was elected as
the singleton master.
Also, if you use jboss-cli to stopDelivery on a MDB that belongs to one or more
delivery groups, the MDB will stop receiving messages in case all groups
were active. If one or more of the groups associated with the MDB was not active,
the MDB will continue in the same, inactive state. But, once all groups become active,
the MDB will still be prevented from receiving messages, unless a startDelivery
operation is executed to revert the previously executed stopDelivery operation.
Invoking stopDelivery on an MDB that is marked as clustered singleton
will work in a similar way: no visible effect if the current node is not
the clustered singleton master; but it will stop delivery of messages
for that MDB if the current node is the clustered singleton master. If
the current node is not the master, but eventually becomes so, the
delivery of messages will not be active for that MDB, unless a
startDelivery operation is invoked.
In other words, when more than one delivery control mechanism is used in
conjunction, they act as a set of restrictions that need all to be true
in order for the MDB to receive messages:
* MDB belongs to one delivery-group + stop-delivery was invoked*: the delivery group
needs to be active and the delivery needs to be restarted (via start-delivery) in order
for that MDB to start receiving messages;
* MDB belongs to one delivery-group + MDB is clustered singleton*: the delivery group
needs to be active and the current node needs to be the clustered singleton master
node in order for that MDB to start receiving messages;
* MDB belongs to one delivery-group + MDB is clustered singleton + stop-delivery was invoked*:
as above, the delivery-group has to be active, the current cluster node must be the
clustered singleton master node, plus, start-delivery needs to be invoked on that MDB,
only with these three factors being true the MDB will start receiving messages.
* MDB belongs to multiple delivery-groups + stop-delivery was invoked*: all the delivery
groups need to be active and the delivery needs to be restarted (via start-delivery) in
order for that MDB to start receiving messages;
* MDB belongs to multiple delivery-groups + MDB is clustered singleton*: all the delivery
groups need to be active and the current node needs to be the clustered singleton
master node in order for that MDB to start receiving messages;
* MDB belongs to multiple delivery-groups + MDB is clustered singleton + stop-delivery was
invoked*: as above, all delivery-groups must be active, and current cluster node has to be the
clustered singleton master node, plus, start-delivery needs to be invoked on that MDB, only
with these three factors being true the MDB will start receiving messages.
9.12. Securing Jakarta Enterprise Beans
The Jakarta EE spec specifies certain annotations (like @RolesAllowed,
@PermitAll, @DenyAll) which can be used on Jakarta Enterprise Beans implementation classes
and/or the business method implementations of the beans. Like with all
other configurations, these security related configurations can also be
done via the deployment descriptor (ejb-jar.xml). We won’t be going
into the details of Jakarta EE specific annotations/deployment descriptor
configurations in this chapter but instead will be looking at the vendor
specific extensions to the security configurations.
9.12.1. Security Domain
The Jakarta EE spec doesn’t mandate a specific way to configure security
domain for a bean. It leaves it to the vendor implementations to allow
such configurations, the way they wish. In WildFly 26, the use of
@org.jboss.ejb3.annotation.SecurityDomain annotation allows the
developer to configure the security domain for a bean. Here’s an
example:
import org.jboss.ejb3.annotation.SecurityDomain;
import javax.ejb.Stateless;
@Stateless
@SecurityDomain("other")
public class MyBean ...
The use of @SecurityDomain annotation lets the developer to point the
container to the name of the security domain which is configured in the
Jakarta Enterprise Beans 3 subsystem in the standalone/domain configuration. The configuration
of the security domain in the Jakarta Enterprise Beans 3 subsystem is out of the scope of this
chapter.
An alternate way of configuring a security domain, instead of using
annotation, is to use jboss-ejb3.xml deployment descriptor. Here’s an
example of how the configuration will look like:
<?xml version="1.0" encoding="UTF-8"?>
<jboss:jboss
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:s="urn:security:1.1"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-spec-2_0.xsd"
version="3.1" impl-version="2.0">
<assembly-descriptor>
<s:security>
<!-- Even wildcard * is supported -->
<ejb-name>MyBean</ejb-name>
<!-- Name of the security domain which is configured in the EJB3 subsystem -->
<s:security-domain>other</s:security-domain>
</s:security>
</assembly-descriptor>
</jboss:jboss>
As you can see we use the security-domain element to configure the
security domain.
As you can see the doSomething method is configured to be accessible
for users with role "bar". However, the bean isn’t configured for any
specific security domain. Prior to WildFly 26, the absence of an
explicitly configured security domain on the bean would leave the bean
unsecured, which meant that even if the doSomething method was
configured with @RolesAllowed("bar") anyone even without the "bar"
role could invoke on the bean.
In WildFly 26, the presence of any security metadata (like @RolesAllowed,
@PermitAll, @DenyAll, @RunAs, @RunAsPrincipal) on the bean or any
business method of the bean, makes the bean secure, even in the absence
of an explicitly configured security domain. In such cases, the security
domain name is default to "other". Users can explicitly configure an
security domain for the bean if they want to using either the annotation
or deployment descriptor approach explained earlier.
9.12.3. Access to methods without explicit security metadata, on a secured
Consider this example bean:
@Stateless
public class FooBean {
@RolesAllowed("bar")
public void doSomething() {
public void helloWorld() {
As you can see the doSomething method is marked for access for only
users with role "bar". That enables security on the bean (with security
domain defaulted to "other"). However, notice that the method
helloWorld doesn’t have any specific security configurations.
In WildFly 26, such methods which have no explicit security
configurations, in a secured bean, will be treated similar to a method
with @DenyAll configuration. What that means is, no one is allowed
access to the helloWorld method. This behaviour can be controlled via
the jboss-ejb3.xml deployment descriptor at a per bean level or a per
deployment level as follows:
<?xml version="1.0" encoding="UTF-8"?>
<jboss:jboss
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:jboss="http://www.jboss.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:s="urn:security:1.1"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-2_0.xsd http://java.sun.com/xml/ns/javaee http://www.jboss.org/j2ee/schema/jboss-ejb3-spec-2_0.xsd"
version="3.1" impl-version="2.0">
<assembly-descriptor>
<s:security>
<!-- Even wildcard * is supported where * is equivalent to all EJBs in the deployment -->
<ejb-name>FooBean</ejb-name>
<s:missing-method-permissions-deny-access>false</s:missing-method-permissions-deny-access>
</s:security>
</assembly-descriptor>
</jboss:jboss>
Notice the use of <missing-method-permissions-deny-access> element.
The value for this element can either be true or false. If this element
isn’t configured then it is equivalent to a value of true i.e. no one is
allowed access to methods, which have no explicit security
configurations, on secured beans. Setting this to false allows access to
such methods for all users i.e. the behaviour will be switched to be
similar to @PermitAll.
This behaviour can also be configured at the Jakarta Enterprise Beans 3 subsystem level so
that it applies to all Jakarta Enterprise Beans 3 deployments on the server, as follows:
<subsystem xmlns="urn:jboss:domain:ejb3:1.4">
<default-missing-method-permissions-deny-access value="true"/>
</subsystem>
Again, the default-missing-method-permissions-deny-access element
accepts either a true or false value. A value of true makes the
behaviour similar to @DenyAll and a value of false makes it behave
like @PermitAll
9.13. Jakarta Enterprise Beans Client Interceptors
9.13.1. Implementing Client Interceptors
The Jakarta Enterprise Beans client supports the notion of client side interceptors. These are interceptors
that are run before an invocation is dispatched, and can modify various properties of
the request before it is sent, as well as modifying parameters and the return value.
These interceptors are represented by the class org.jboss.ejb.client.EJBClientInterceptor,
and are generally registered placing interceptor class names in
META-INF/services/org.jboss.ejb.client.EJBClientInterceptor.
For more details about what can be modified refer to the Jakarta Enterprise Beans client JavaDoc.
9.13.2. Accessing invocation context data
It is possible for client interceptors to access data from the invocation context context data map used in the server
invocation (i.e. InvocationContext.getContextData()). To access a specific key you must call
org.jboss.ejb.client.EJBClientInvocationContext.addReturnedContextDataKey(String key) with
the name of the key you are interested in. This method must be called from the handleInvocation method
of the interceptor.
If there is data in the context map under this specific key then it will be send back to the client
and will be available in the handleInvocationResult in the client invocations context data map.
9.14. Jakarta Enterprise Beans on Kubernetes
If the WildFly server is deployed on Kubernetes then there are several
points that you need to bear in mind when you use EJBs.
When deploying on Kubernetes you should consider the use of the WildFly Operator.
It manages the Kubernetes objects in WildFly friendly way.
For example, it uses StatefulSet for the correct handling of EJB remoting and transaction recovery processing.
The rest of this chapter assumes the StatefulSet is used
as the Kubernetes API object for managing the WildFly server.
The StatefulSet provides a guarantee of persistent storage and network hostname stability
over the restarts of the pod.
These two guarantees are particularly important for the transaction manager which is a stateful component.
The persistent storage over restarts is needed as the transaction log is usually stored at the file system.
If the transaction manager creates a transaction log record it’s created only at the transaction log particular to the WildFly instance.
The hostname stability is needed as the WildFly may be contacted via EJB remote call with transaction propagation.
The WildFly has to be reachable under the same hostname even after pod restarts.
As the transaction log is bound to the particular WildFly instance it may be finished only there.
9.14.1. EJB calls on Kubernetes
The EJB caller has two options on how to configure the remote call.
It can be defined either as a remote outbound connection (see details at Admin Guide, section Outbound Connections)
or you may use a direct InitialContext lookup in your code.
If you use either case then for the Kubernetes you need to adjust the configuration of the target node.
For the target hostname, you need to use the DNS name of the very first pod managed by StatefulSet.
The StatefulSet guarantees depend on the ordering of the pods. The pods are named in the prescribed order.
If you scale your application up to 3 replicas you may expect
your pods will have names such as wildfly-server-0, wildfly-server-1, wildfly-server-2.
It’s expected a headless services
to be used along with the StatefulSet. With the headless service, there is ensured the DNS hostname for the pod.
If the application uses the WildFly Operator, a headless service will be created with a name such as wildfly-server-headless.
Then the DNS name of the very first pod will be wildfly-server-0.wildfly-server-headless.
The use of the hosname wildfly-server-0.wildfly-server-headless
guarantees that the EJB call may reach any WildFly instance connected to the cluster.
It’s a bootstrap connection which is used to initialize the EJB client
which gathers the structure of the WildFly cluster as the next step.
9.14.2. EJB configuration for Kubernetes
These are steps you need to process in order to run EJB remote calls.
Some steps are related to the server configuration, the other ones
on your application.
The clustering has to be set correctly, see the High Availability Guide, section of Kubernetes discovery.
All the socket-binding must define the client mapping for the DNS value mapped by StatefulSet headless service.
For example if the application is named wildfly-server and the StatefulSet headless service is named wildfly-server-headless
then the http socket binding has to be defined in the following way:
<socket-binding name="http" port="${jboss.http.port:8080}">
<client-mapping destination-address="${jboss.node.name}.wildfly-server-headless"/>
</socket-binding>
A small workaround is needed for the remote EJB transaction recovery on Kubernetes
(the issue could be tracked at WFCORE-4668).
The WildFly application server has to be configured with property wildfly.config.url.
The wildfly.config.url points to a XML configuration file. If we consider one being placed at $JBOSS_HOME/standalone/configuration/wildfly-config.xml
then the property is setup as JAVA_OPTS="$JAVA_OPTS -Dwildfly.config.url=$JBOSS_HOME/standalone/configuration/wildfly-config.xml".
The wildfly-config.xml defines the EJB recovery authentication to be used during transaction recovery for remote EJB calls.
The target server has to configure a user that is permitted to receive the EJB remote calls.
Such a user is then configured by standard means of security configuration.
Let’s say there is configured a user on the target server.
The user is created with script $JBOSS_HOME/bin/add-user.sh under the ApplicationRealm.
Then the caller WildFly uses the configuration in wildfly-config.xml this way
(you may copy the content below, but replace the >>PASTE_…_HERE<< with user and password you configured):
<authentication-client xmlns
="urn:elytron:1.0">
<authentication-rules>
<rule use-configuration="jta">
<match-abstract-type name="jta" authority="jboss"/>
</rule>
</authentication-rules>
<authentication-configurations>
<configuration name="jta">
<sasl-mechanism-selector selector="DIGEST-MD5"/>
<providers>
<use-service-loader />
</providers>
<set-user-name name=">>PASTE_USER_NAME_HERE<<"/>
<credentials>
<clear-password password=">>PASTE_PASSWORD_HERE<<"/>
</credentials>
<set-mechanism-realm name="ApplicationRealm" />
</configuration>
</authentication-configurations>
</authentication-client>
</configuration>
9.15. Jakarta Enterprise Beans Deployment Runtime Resources
Enterprise bean deployment exposes certain management runtime resources to help users inspect enterprise bean metadata
and monitor invocation statistics. Bean metadata is configured in the application via deployment descriptor and
annotations. Stateless, stateful, singleton session beans and message-driven beans support a common set of resources,
and also resources specific to the bean type. For example, these are some of the common enterprise bean resources:
Users can view these enterprise bean resources via WildFly CLI or Administration Console. The following sections
provides sample CLI and Administration Console output for each enterprise bean type. For complete details, refer to
WildFly Model Reference Documentation
9.15.1. Stateless Session Bean Runtime Resources
To view the management runtime resources for a deployed stateless session bean in CLI, run the following CLI command:
/deployment=<DEPLOYMENT-NAME>/subsystem=ejb3/stateless-session-bean=<BEAN-NAME>:read-resource(include-runtime)
The following is a sample output for a stateless session bean named ManagedStatelessBean in deployment ejb-management.jar:
/deployment=ejb-management.jar/subsystem=ejb3/stateless-session-bean=ManagedStatelessBean:read-resource(include-runtime)
"outcome" => "success",
"result" => {
"async-methods" => ["void async(int, int)"],
"business-local" => ["sample.ManagedStatelessBean"],
"business-remote" => ["sample.BusinessInterface"],
"component-class-name" => "sample.ManagedStatelessBean",
"declared-roles" => [
"Role3",
"Role2",
"Role1"
"execution-time" => 160L,
"invocations" => 3L,
"jndi-names" => [
"java:global/ejb-management/ManagedStatelessBean!sample.BusinessInterface",
"java:module/ManagedStatelessBean!sample.BusinessInterface",
"java:app/ejb-management/ManagedStatelessBean!sample.BusinessInterface",
"java:global/ejb-management/ManagedStatelessBean!sample.ManagedStatelessBean",
"java:module/ManagedStatelessBean!sample.ManagedStatelessBean",
"java:app/ejb-management/ManagedStatelessBean!sample.ManagedStatelessBean"
"methods" => {"doIt" => {
"execution-time" => 160L,
"invocations" => 3L,
"wait-time" => 10L
"peak-concurrent-invocations" => 1L,
"pool-available-count" => 64,
"pool-create-count" => 1,
"pool-current-size" => 1,
"pool-max-size" => 64,
"pool-name" => "slsb-strict-max-pool",
"pool-remove-count" => 0,
"run-as-role" => "Role3",
"security-domain" => "other",
"timeout-method" => "public void sample.ManagedStatelessBean.timeout(javax.ejb.Timer)",
"timers" => [{
"time-remaining" => 4735224L,
"next-timeout" => 1577768415000L,
"calendar-timer" => true,
"persistent" => false,
"info" => "timer1",
"schedule" => {
"year" => "*",
"month" => "*",
"day-of-month" => "*",
"day-of-week" => "*",
"hour" => "0",
"minute" => "0",
"second" => "15",
"timezone" => undefined,
"start" => undefined,
"end" => undefined
"transaction-type" => "CONTAINER",
"wait-time" => 10L,
"service" => {"timer-service" => undefined}
To view it in WildFly Administration Console, go to the Management Model section of the target deployment. For example,
/deployment=ejb-management.jar/subsystem=ejb3/stateful-session-bean=ManagedStatefulBean2:read-resource(include-runtime)
"outcome" => "success",
"result" => {
"after-begin-method" => "private void sample.ManagedStatefulBean2.afterBegin()",
"after-completion-method" => "private void sample.ManagedStatefulBean2.afterCompletion()",
"async-methods" => ["void async(int, int)"],
"before-completion-method" => "private void sample.ManagedStatefulBean2.beforeCompletion()",
"business-local" => ["sample.ManagedStatefulBean2"],
"business-remote" => ["sample.BusinessInterface"],
"cache-size" => 0,
"component-class-name" => "sample.ManagedStatefulBean2",
"declared-roles" => [
"Role3",
"Role2",
"Role1"
"execution-time" => 163L,
"invocations" => 4L,
"jndi-names" => [
"java:app/ejb-management/ManagedStatefulBean2!sample.BusinessInterface",
"java:global/ejb-management/ManagedStatefulBean2!sample.BusinessInterface",
"java:module/ManagedStatefulBean2!sample.BusinessInterface",
"java:app/ejb-management/ManagedStatefulBean2!sample.ManagedStatefulBean2",
"java:global/ejb-management/ManagedStatefulBean2!sample.ManagedStatefulBean2",
"java:module/ManagedStatefulBean2!sample.ManagedStatefulBean2"
"methods" => {
"doIt" => {
"execution-time" => 163L,
"invocations" => 3L,
"wait-time" => 3L
"remove" => {
"execution-time" => 0L,
"invocations" => 1L,
"wait-time" => 1L
"passivated-count" => 0,
"passivation-capable" => false,
"peak-concurrent-invocations" => 1L,
"remove-methods" => [
"bean-method" => "void remove()",
"retain-if-exception" => false
"bean-method" => "void removeTrue()",
"retain-if-exception" => true
"bean-method" => "void removeFalse()",
"retain-if-exception" => false
"run-as-role" => "Role3",
"security-domain" => "other",
"stateful-timeout" => "2 HOURS",
"total-size" => 0,
"transaction-type" => "BEAN",
"wait-time" => 4L,
"service" => undefined
To view it in WildFly Administration Console, go to the Management Model section of the target deployment. For example,
/deployment=ejb-management.jar/subsystem=ejb3/singleton-bean=ManagedSingletonBean:read-resource(include-runtime)
"outcome" => "success",
"result" => {
"async-methods" => ["void async(int, int)"],
"business-local" => ["sample.ManagedSingletonBean"],
"business-remote" => ["sample.BusinessInterface"],
"component-class-name" => "sample.ManagedSingletonBean",
"concurrency-management-type" => undefined,
"declared-roles" => [
"Role3",
"Role2",
"Role1"
"depends-on" => undefined,
"execution-time" => 156L,
"init-on-startup" => false,
"invocations" => 3L,
"jndi-names" => [
"java:module/ManagedSingletonBean!sample.ManagedSingletonBean",
"java:global/ejb-management/ManagedSingletonBean!sample.ManagedSingletonBean",
"java:app/ejb-management/ManagedSingletonBean!sample.ManagedSingletonBean",
"java:app/ejb-management/ManagedSingletonBean!sample.BusinessInterface",
"java:global/ejb-management/ManagedSingletonBean!sample.BusinessInterface",
"java:module/ManagedSingletonBean!sample.BusinessInterface"
"methods" => {"doIt" => {
"execution-time" => 156L,
"invocations" => 3L,
"wait-time" => 0L
"peak-concurrent-invocations" => 1L,
"run-as-role" => "Role3",
"security-domain" => "other",
"timeout-method" => "public void sample.ManagedSingletonBean.timeout(javax.ejb.Timer)",
"timers" => [{
"time-remaining" => 4304279L,
"next-timeout" => 1577768415000L,
"calendar-timer" => true,
"persistent" => false,
"info" => "timer1",
"schedule" => {
"year" => "*",
"month" => "*",
"day-of-month" => "*",
"day-of-week" => "*",
"hour" => "0",
"minute" => "0",
"second" => "15",
"timezone" => undefined,
"start" => undefined,
"end" => undefined
"transaction-type" => "CONTAINER",
"wait-time" => 0L,
"service" => {"timer-service" => undefined}
To view it in WildFly Administration Console, go to the Management Model section of the target deployment. For example,
/deployment=ejb-management.jar/subsystem=ejb3/message-driven-bean=ManagedMDB:read-resource(include-runtime)
"outcome" => "success",
"result" => {
"activation-config" => [
("destinationType" => "javax.jms.Queue"),
("destination" => "java:/queue/ManagedMDB-queue")
"component-class-name" => "sample.ManagedMDB",
"declared-roles" => [
"Role3",
"Role2",
"Role1"
"delivery-active" => true,
"execution-time" => 0L,
"invocations" => 0L,
"message-destination-link" => undefined,
"message-destination-type" => undefined,
"messaging-type" => "javax.jms.MessageListener",
"methods" => {},
"peak-concurrent-invocations" => 0L,
"pool-available-count" => 16,
"pool-create-count" => 0,
"pool-current-size" => 0,
"pool-max-size" => 16,
"pool-name" => "mdb-strict-max-pool",
"pool-remove-count" => 0,
"run-as-role" => "Role3",
"security-domain" => "other",
"timeout-method" => "public void sample.ManagedMDB.timeout(javax.ejb.Timer)",
"timers" => [{
"time-remaining" => 4213581L,
"next-timeout" => 1577768415000L,
"calendar-timer" => true,
"persistent" => false,
"info" => "timer1",
"schedule" => {
"year" => "*",
"month" => "*",
"day-of-month" => "*",
"day-of-week" => "*",
"hour" => "0",
"minute" => "0",
"second" => "15",
"timezone" => undefined,
"start" => undefined,
"end" => undefined
"transaction-type" => "CONTAINER",
"wait-time" => 0L,
"service" => {"timer-service" => undefined}
To view it in WildFly Administration Console, go to the Management Model section of the target deployment. For example,
The WildFly JPA subsystem implements the JPA 2.2 container-managed
requirements. Deploys the persistence unit definitions, the persistence
unit/context annotations and persistence unit/context references in the
deployment descriptor. JPA Applications use the Hibernate (version 5.3)
persistence provider, which is included with WildFly. The JPA subsystem
uses the standard SPI (javax.persistence.spi.PersistenceProvider) to
access the Hibernate persistence provider and some additional extensions
as well.
During application deployment, JPA use is detected (e.g. persistence.xml
or @PersistenceContext/Unit annotations) and injects Hibernate
dependencies into the application deployment. This makes it easy to
deploy JPA applications.
In the remainder of this documentation, "entity manager" refers to an
instance of the javax.persistence.EntityManager class.
Javadoc
for the JPA interfacesand JPA 2.2
specification.
The index of the Hibernate documentation is at
http://hibernate.org/orm/documentation/5.3/
10.2. Update your Persistence.xml for Hibernate
The persistence provider class name in Hibernate is
org.hibernate.jpa.HibernatePersistenceProvider.
Your persistence.xml can specify:
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
Or remove the persistence provider class name from your persistence.xml
(so the default provider will be used).
10.3. Entity manager
The entity manager (javax.persistence.EntityManager class) is similar to
the Hibernate Session class; applications use it to
create/read/update/delete data (and related operations). Applications
can use application-managed or container-managed entity managers. Keep
in mind that the entity manager is not thread safe, don’t share the same
entity manager instance with multiple threads.
Internally, the entity manager, has a persistence context for managing
entities. You can think of the persistence context as being closely
associated with the entity manager.
10.4. Container-managed entity manager
When you inject a container-managed entity managers into an application
variable, it is treated like an (EE container controlled) Java proxy
object, that will be associated with an underlying EntityManager
instance, for each started JTA transaction and is flushed/closed when
the JTA transaction commits. Such that when your application code
invokes EntityManager.anyMethod(), the current JTA transaction is
searched (using persistence unit name as key) for the underlying
EntityManager instance, if not found, a new EntityManager instance is
created and associated with the current JTA transaction, to be reused
for the next EntityManager invocation. Use the @PersistenceContext
annotation, to inject a container-managed entity manager into a
javax.persistence.EntityManager variable.
10.5. Application-managed entity manager
An application-managed entity manager is kept around until the
application closes it. The scope of the application-managed entity
manager is from when the application creates it and lasts until the
application closes it. Use the @PersistenceUnit annotation, to inject
a persistence unit into a javax.persistence.EntityManagerFactory
variable. The EntityManagerFactory can return an application-managed
entity manager.
10.6. Persistence Context
The JPA persistence context contains the entities managed by the entity
manager (via the JPA persistence provider). The underlying entity
manager maintains the persistence context. The persistence context acts
like a first level (transactional) cache for interacting with the
datasource. Loaded entities are placed into the persistence context
before being returned to the application. Entities changes are also
placed into the persistence context (to be saved in the database when
the transaction commits).
10.7. Transaction-scoped Persistence Context
The transaction-scoped persistence context coordinates with the (active)
JTA transaction. When the transaction commits, the persistence context
is flushed to the datasource (entity objects are detached but may still
be referenced by application code). All entity changes that are expected
to be saved to the datasource, must be made during a transaction.
Entities read outside of a transaction will be detached when the entity
manager invocation completes. Example transaction-scoped persistence
context is below.
@Stateful // will use container managed transactions
public class CustomerManager {
@PersistenceContext(unitName = "customerPU") // default type is PersistenceContextType.TRANSACTION
EntityManager em;
public customer createCustomer(String name, String address) {
Customer customer = new Customer(name, address);
em.persist(customer); // persist new Customer when JTA transaction completes (when method ends).
// internally:
// 1. Look for existing "customerPU" persistence context in active JTA transaction and use if found.
// 2. Else create new "customerPU" persistence context (e.g. instance of org.hibernate.ejb.HibernatePersistence)
// and put in current active JTA transaction.
return customer; // return Customer entity (will be detached from the persistence context when caller gets control)
} // Transaction.commit will be called, Customer entity will be persisted to the database and "customerPU" persistence context closed
10.8. Extended Persistence Context
The (ee container managed) extended persistence context can span
multiple transactions and allows data modifications to be queued up
(without an active JTA transaction), to be applied
during completion of next JTA transaction. The Container-managed extended persistence
context can only be injected into a stateful session bean.
@PersistenceContext(type = PersistenceContextType.EXTENDED, unitName = "inventoryPU")
EntityManager em;
10.8.1. Extended Persistence Context Inheritance
JPA 2.2 specification section 7.6.3.1
If a stateful session bean instantiates a stateful session bean (executing in the same EJB container instance) which also has such an extended persistence context with the same synchronization type, the extended persistence context of the first stateful session bean is inherited by the second stateful session bean and bound to it, and this rule recursively applies independently of whether transactions are active or not at the point of the creation of the stateful session beans. If the stateful session beans differ in declared synchronization type, the EJBException is thrown by the container. If the persistence context has been inherited by any stateful session beans, the container does not close the persistence context until all such stateful session beans have been removed or otherwise destroyed.
By default, the current stateful session bean being created, will (
deeply) inherit the extended persistence context from any stateful
session bean executing in the current Java thread. The deep
inheritance of extended persistence context includes walking multiple
levels up the stateful bean call stack (inheriting from parent beans).
The deep inheritance of extended persistence context includes sibling
beans. For example, parentA references child beans beanBwithXPC &
beanCwithXPC. Even though parentA doesn’t have an extended persistence
context, beanBwithXPC & beanCwithXPC will share the same extended
persistence context.
Some other EE application servers, use shallow inheritance, where
stateful session bean only inherit from the parent stateful session bean
(if there is a parent bean). Sibling beans do not share the same
extended persistence context unless their (common) parent bean also has
the same extended persistence context.
Applications can include a (top-level) jboss-all.xml deployment
descriptor that specifies either the (default) DEEP extended
persistence context inheritance or SHALLOW.
The WF/docs/schema/jboss-jpa_1_0.xsd describes the jboss-jpa
deployment descriptor that may be included in the jboss-all.xml. Below
is an example of using SHALLOW extended persistence context
inheritance:
<jboss>
<jboss-jpa xmlns="http://www.jboss.com/xml/ns/javaee">
<extended-persistence inheritance="SHALLOW"/>
</jboss-jpa>
</jboss>
<jboss>
<jboss-jpa xmlns="http://www.jboss.com/xml/ns/javaee">
<extended-persistence inheritance="DEEP"/>
</jboss-jpa>
</jboss>
The AS console/cli can change the default extended persistence context
setting (DEEP or SHALLOW). The following cli commands will read the
current JPA settings and enable SHALLOW extended persistence context
inheritance for applications that do not include the jboss-jpa
deployment descriptor:
@PersistenceContext EntityManager em;
Integer bomPk = getIndexKeyValue();
BillOfMaterials bom = em.find(BillOfMaterials.class, bomPk); // read existing table row into BillOfMaterials class
BillOfMaterials createdBom = new BillOfMaterials("..."); // create new entity
em.persist(createdBom); // createdBom is now managed and will be saved to database when the current JTA transaction completes
The entity lifecycle is managed by the underlying persistence provider.
New (transient): an entity is new if it has just been instantiated
using the new operator, and it is not associated with a persistence
context. It has no persistent representation in the database and no
identifier value has been assigned.
Managed (persistent): a managed entity instance is an instance with a
persistent identity that is currently associated with a persistence
context.
Detached: the entity instance is an instance with a persistent
identity that is no longer associated with a persistence context,
usually because the persistence context was closed or the instance was
evicted from the context.
Removed: a removed entity instance is an instance with a persistent
identity, associated with a persistence context, but scheduled for
removal from the database.
The persistence.xml contains the persistence unit configuration (e.g.
datasource name) and as described in the JPA 2.0 spec (section 8.2), the
jar file or directory whose META-INF directory contains the
persistence.xml file is termed the root of the persistence unit. In Java
EE environments, the root of a persistence unit must be one of the
following (quoted directly from the JPA 2.0 specification):
The persistence.xml can specify either a JTA datasource or a non-JTA
datasource. The JTA datasource is expected to be used within the EE
environment (even when reading data without an active transaction). If a
datasource is not specified, the default-datasource will instead be used
(must be configured).
Java Persistence 1.0 supported use of a jar file in the root of
the EAR as the root of a persistence unit. This use is no longer
supported. Portable applications should use the EAR library directory
for this case instead.
Answer: No, the above may deploy but it could include other archives
also in the EAR, so you may have deployment issues for other reasons.
Better to put the persistence.xml in an EAR/lib/somePuJar.jar.
10.11. Troubleshooting
The org.jboss.as.jpa logging can be enabled to get the following
information:
INFO - when persistence.xml has been parsed, starting of persistence
unit service (per deployed persistence.xml), stopping of persistence
unit service
DEBUG - informs about entity managers being injected, creating/reusing
transaction scoped entity manager for active transaction
TRACE - shows how long each entity manager operation took in
milliseconds, application searches for a persistence unit, parsing of
persistence.xml
To enable TRACE, open the as/standalone/configuration/standalone.xml (or
as/domain/configuration/domain.xml) file. Search for <subsystem
xmlns="urn:jboss:domain:logging:1.0"> and add the org.jboss.as.jpa
category. You need to change the console-handler level from INFO to
TRACE.
<subsystem xmlns="urn:jboss:domain:logging:1.0">
<console-handler name="CONSOLE">
<level name="TRACE" />
</console-handler>
</periodic-rotating-file-handler>
<logger category="com.arjuna">
<level name="WARN" />
</logger>
<logger category="org.jboss.as.jpa">
<level name="TRACE" />
</logger>
<logger category="org.apache.tomcat.util.modeler">
<level name="WARN" />
</logger>
To see what is going on at the JDBC level, enable jboss.jdbc.spy TRACE
and add spy="true" to the datasource.
<datasource jndi-name="java:jboss/datasources/..." pool-name="..." enabled="true" spy="true">
<logger category="jboss.jdbc.spy">
<level name="TRACE"/>
</logger>
To troubleshoot issues with the Hibernate second level cache, try
enabling trace for org.hibernate.SQL + org.hibernate.cache.infinispan
org.infinispan:
<subsystem xmlns="urn:jboss:domain:logging:1.0">
<console-handler name="CONSOLE">
<level name="TRACE" />
</console-handler>
</periodic-rotating-file-handler>
<logger category="com.arjuna">
<level name="WARN" />
</logger>
<logger category="org.hibernate.SQL">
<level name="TRACE" />
</logger>
<logger category="org.hibernate">
<level name="TRACE" />
</logger>
<logger category="org.infinispan">
<level name="TRACE" />
</logger>
<logger category="org.apache.tomcat.util.modeler">
<level name="WARN" />
</logger>
10.12. Using the Infinispan second level cache
To enable the second level cache with Hibernate, just set the
hibernate.cache.use_second_level_cache property to true or
set shared-cache-mode to one of the following:
Infinispan is the cache provider for JPA applications, so you don’t need to specify
anything in addition. The Infinispan version that is included in
WildFly is expected to work with the Hibernate version that is included
with WildFly. Example persistence.xml settings:
<?xml version="1.0" encoding="UTF-8"?><persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
<persistence-unit name="2lc_example_pu">
<description>example of enabling the second level cache.</description>
<jta-data-source>java:jboss/datasources/mydatasource</jta-data-source>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
</persistence-unit>
</persistence>
Here is an example of enabling the second level cache for a Hibernate
native API hibernate.cfg.xml file:
<property name="hibernate.cache.region.factory_class" value="org.infinispan.hibernate.cache.v53.InfinispanRegionFactory"/>
<property name="hibernate.cache.infinispan.shared" value="false"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
The Hibernate native API application will also need a MANIFEST.MF:
Dependencies: org.infinispan,org.hibernate
Infinispan
Hibernate/JPA second level cache provider documentation contains
advanced configuration information but you should bear in mind that when
Hibernate runs within WildFly 26, some of those configuration options,
such as region factory, are not needed. Moreover, the application server
providers you with option of selecting a different cache container for
Infinispan via hibernate.cache.infinispan.container persistence
property. To reiterate, this property is not mandatory and a default
container is already deployed for by the application server to host the
second level cache.
Here is an example of what the Hibernate cache settings may currently be
in your standalone.xml:
<cache-container name="hibernate" module="org.infinispan.hibernate-cache">
<local-cache name="entity">
<transaction mode="NON_XA"/>
<object-memory size="10000"/>
<expiration max-idle="100000"/>
</local-cache>
<local-cache name="local-query">
<object-memory size="10000"/>
<expiration max-idle="100000"/>
</local-cache>
<local-cache name="timestamps"/>
</cache-container>
Below is an example of customizing the "entity", "immutable-entity",
"local-query", "pending-puts", "timestamps" cache configuration may look
like:
<cache-container name="hibernate" module="org.infinispan.hibernate-cache" default-cache="immutable-entity">
<local-cache name="entity">
<transaction mode="NONE"/>
<eviction max-entries="-1"/>
<expiration max-idle="120000"/>
</local-cache>
<local-cache name="immutable-entity">
<transaction mode="NONE"/>
<eviction max-entries="-1"/>
<expiration max-idle="120000"/>
</local-cache>
<local-cache name="local-query">
<eviction max-entries="-1"/>
<expiration max-idle="300000"/>
</local-cache>
<local-cache name="pending-puts">
<transaction mode="NONE"/>
<eviction strategy="NONE"/>
<expiration max-idle="60000"/>
</local-cache>
<local-cache name="timestamps">
<transaction mode="NONE"/>
<eviction strategy="NONE"/>
</local-cache>
</cache-container>
Persistence.xml to use the above custom settings:
<properties>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.cache.infinispan.immutable-entity.cfg" value="immutable-entity"/>
<property name="hibernate.cache.infinispan.timestamps.cfg" value="timestamps"/>
<property name="hibernate.cache.infinispan.pending-puts.cfg" value="pending-puts"/>
</properties>
10.13. Using Hibernate Search
WildFly includes Hibernate Search. If you want to use the bundled version of Hibernate Search, which requires to use the default Hibernate ORM persistence provider, this will be automatically enabled. Having this enabled means that, provided your application includes any entity which is annotated with org.hibernate.search.annotations.Indexed, the module org.hibernate.search.orm:main will be made available to your deployment; this will also include the required version of Apache Lucene.
If you do not want this module to be exposed to your deployment, set the persistence property wildfly.jpa.hibernate.search.module to either none to not automatically inject any Hibernate Search module, or to any other module identifier to inject a different module. For example you could set wildfly.jpa.hibernate.search.module=org.hibernate.search.orm:5.11.0.Alpha1 to use the experimental version 5.11.0.Alpha1 instead of the provided module; in this case you’ll have to download and add the custom modules to the application server as other versions are not included. When setting wildfly.jpa.hibernate.search.module=none you might also opt to include Hibernate Search and its dependencies within your application but we highly recommend the modules approach.
10.14. Packaging the Hibernate JPA persistence provider with your application
WildFly allows the packaging of Hibernate persistence provider jars with
the application. The JPA deployer will detect the presence of a
persistence provider in the application and
jboss.as.jpa.providerModule needs to be set to application.
<?xml version="1.0" encoding="UTF-8"?> +
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
version="1.0"> +
<persistence-unit name="myOwnORMVersion_pu"> +
<description>Hibernate Persistence Unit.</description> +
<jta-data-source>java:jboss/datasources/PlannerDS</jta-data-source> +
<properties> +
<property name="jboss.as.jpa.providerModule" value="application" /> +
</properties> +
</persistence-unit> +
</persistence>
10.15. Migrating from OpenJPA
You need to copy the OpenJPA jar (e.g. openjpa-all.jar) into
the WildFly modules/org/apache/openjpa/main folder and update
modules/org/apache/openjpa/main/module.xml to include the same jar file
name that you copied in. This will help you get your application that
depends on OpenJPA, to deploy on WildFly.
<module xmlns="urn:jboss:module:1.9" name="org.apache.openjpa">
<resources>
<resource-root path="jipijapa-openjpa-1.0.1.Final.jar"/>
<resource-root path="openjpa-all.jar">
<filter>
<exclude path="javax/**" />
</filter>
</resource-root>
</resources>
<dependencies>
<module name="javax.annotation.api"/>
<module name="javax.enterprise.api"/>
<module name="javax.persistence.api"/>
<module name="javax.transaction.api"/>
<module name="javax.validation.api"/>
<module name="javax.xml.bind.api"/>
<module name="org.jboss.as.jpa.spi"/>
<module name="org.jboss.logging"/>
<module name="org.jboss.jandex"/>
</dependencies>
</module>
10.16. Migrating from EclipseLink
You need to copy the EclipseLink jar (e.g. eclipselink-2.6.0.jar or
eclipselink.jar as in the example below) into the WildFly
modules/org/eclipse/persistence/main folder and update
modules/org/eclipse/persistence/main/module.xml to include the
EclipseLink jar (take care to use the jar name that you copied in). If
you happen to leave the EclipseLink version number in the jar name, the
module.xml should reflect that. This will help you get your application
that depends on EclipseLink, to deploy on WildFly.
<module xmlns="urn:jboss:module:1.9" name="org.eclipse.persistence">
<resources>
<resource-root path="jipijapa-eclipselink-10.0.0.Final.jar"/>
<resource-root path="eclipselink.jar"> <filter>
<exclude path="javax/**" />
</filter>
</resource-root>
</resources>
<dependencies>
<module name="asm.asm"/>
<module name="javax.api"/>
<module name="javax.annotation.api"/>
<module name="javax.enterprise.api"/>
<module name="javax.persistence.api"/>
<module name="javax.transaction.api"/>
<module name="javax.validation.api"/>
<module name="javax.xml.bind.api"/>
<module name="org.antlr"/>
<module name="org.apache.commons.collections"/>
<module name="org.dom4j"/>
<module name="org.jboss.as.jpa.spi"/>
<module name="org.jboss.logging"/>
<module name="org.jboss.vfs"/>
</dependencies>
</module>
You should then be able to deploy applications with persistence.xml that
include;
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
Also refer to page
how to use
EclipseLink with WildFly guide here.
10.17. Native Hibernate use
Applications that use the Hibernate API directly, can be referred to
as native Hibernate applications. Native Hibernate applications, can
choose to use the Hibernate jars included with WildFly or they can
package their own copy of the Hibernate jars. Applications that utilize
JPA will automatically have the Hibernate classes injected onto the
application deployment classpath. Meaning that JPA applications, should
expect to use the Hibernate jars included in WildFly.
Example MANIFEST.MF entry to add dependency for Hibernate native
applications:
Manifest-Version: 1.0
Dependencies: org.hibernate
10.18. Injection of Hibernate Session and SessionFactory
You can inject a org.hibernate.Session and org.hibernate.SessionFactory
directly, just as you can do with EntityManagers and
EntityManagerFactorys.
import org.hibernate.Session;
import org.hibernate.SessionFactory;
@Stateful public class MyStatefulBean ... {
@PersistenceContext(unitName="crm") Session session1;
@PersistenceContext(unitName="crm2", type=EXTENDED) Session extendedpc;
@PersistenceUnit(unitName="crm") SessionFactory factory;
10.19. Hibernate ORM 5.1 native API bytecode transformer
The Hibernate ORM 5.1 native API bytecode transformer, can rewrite application bytecode,
to handle APIs that have changed between Hibernate ORM 5.1.x + 5.3.x. For applications that use the Hibernate ORM 5.1
native APIs, you may either rewrite them for Hibernate ORM 5.3 or use the transformer, to rewrite your application byte-code
during application deployment (only the in-memory copy of your application is rewritten). If your application is only using the
JPA specification APIs, you do not need to transform your application.
For details on the Hibernate 5.3 native API changes, review the ORM 5.2
+ ORM 5.3 migration guides.
The Hibernate ORM 5.1 transformer is already deprecated, meaning it could be removed in the next release.
For this reason, we made it easy to enable the transformer via either a system property or jboss-deployment-structure change.
<jboss-deployment-structure>
<deployment>
<transformers>
<transformer class="org.jboss.as.hibernate.Hibernate51CompatibilityTransformer"/>
</transformers>
<dependencies>
<module name="org.hibernate" export="true" />
</dependencies>
</deployment>
<sub-deployment name="main.war">
<transformers>
<transformer class="org.jboss.as.hibernate.Hibernate51CompatibilityTransformer"/>
</transformers>
</sub-deployment>
</jboss-deployment-structure>
In user type classes, when calling org.hibernate.* class methods, cast SessionImplementor to SharedSessionContractImplementor.
Change calls to org.hibernate.BasicQueryContract.getFlushMode(), to instead call BasicQueryContract.getHibernateFlushMode().
Change calls to org.hibernate.Session.getFlushMode(), to instead call Session.getHibernateFlushMode().
Change calls to org.hibernate.Query.getFirstResult(), to instead call Query.getHibernateFirstResult(), so null can be returned when the value is uninitialized (note that 0 is now returned instead of negative values).
Change calls to org.hibernate.Query.getMaxResults(), to instead call Query.getHibernateMaxResults(), so that null will be returned when the value is uninitialized or org.hibernate.Query#setMaxResults() was called with a value ⇐ 0.
Change calls to org.hibernate.Query.setFirstResult(), to instead call Query.setHibernateFirstResult(), so that calls to set a value < 0 results in pagination starting with the 0th row as was done in Hibernate ORM 5.1.
Change calls to org.hibernate.Query.setMaxResults(), to instead call Query.setHibernateMaxResults(), so that passed values ⇐ 0 are treated the same as uninitialized.
Change references to NEVER field in enum org.hibernate.FlushMode, to instead reference FlushMode.MANUAL.
TRACE + DEBUG level logging can be enabled via the "org.jboss.as.hibernate.transformer" logger category.
Example output:
Hibernate51CompatibilityTransformer transformed application classes in 'deployment.org.jboss.as.test.compat.jpa.hibernate.transformer.VerifyHibernate51CompatibilityPropertyAndJDSEnabledTransformerTestCase.ear', class 'org/jboss/as/test/compat/jpa/hibernate/transformer/BitSetType' is calling method org/hibernate/type/AbstractSingleColumnStandardBasicType.replace, which must be changed to use SharedSessionContractImplementor as parameter.
if true, the transformer will transform ORM 5.1.x native calls to, ORM 5.3 native calls, in every application deployment
false
Hibernate51CompatibilityTransformer.disableAmbiguousChanges
disable transformation of org.hibernate.Query.setFirstResult(int) + org.hibernate.Query.setMaxResults(int), since these methods are in both Hibernate ORM 5.1.x + 5.3.x
false
Hibernate51CompatibilityTransformer.showTransformedClassFolder
specifies the folder name to create bytecode level description of applications run through the transformer. The specified folder must already exist. If the transformer actually makes a change to your application, a (boolean) class variable will be added "$org_jboss_as_hibernate_Hibernate51CompatibilityTransformer_transformed$", that ensures that the application is only transformed once
disabled
10.20. Hibernate properties
WildFly automatically sets the following Hibernate properties (if
not already set in persistence unit definition):
hibernate.id.new_generator_mappings =true
New applications should let
this default to true, older applications with existing data might need
to set to false (see note below). It really depends on whether your
application uses the @GeneratedValue(AUTO) which will generates new key
values for newly created entities. The application can override this
value (in the persistence.xml).
hibernate.transaction.jta.platform= instance of
org.hibernate.service.jta.platform.spi.JtaPlatform interface
transaction manager, user transaction and transaction synchronization
registry is passed into Hibernate via this class.
hibernate.ejb.resource_scanner = instance of
org.hibernate.ejb.packaging.Scanner interface
Instance of entity
scanning class is passed in that knows how to use the AS annotation
indexer (for faster deployment).
hibernate.transaction.manager_lookup_class
This property is removed if
found in the persistence.xml (could conflict with JtaPlatform)
hibernate.session_factory_name = qualified persistence unit name
set to the application name + persistence unit name (application can
specify a different value but it needs to be unique across all
application deployments on the AS instance).
hibernate.session_factory_name_is_jndi = false
only set if the
application didn’t specify a value for hibernate.session_factory_name.
hibernate.ejb.entitymanager_factory_name = qualified persistence unit
Is set to the application name + persistence unit name
(application can specify a different value but it needs to be unique
across all application deployments on the AS instance).
hibernate.query.jpaql_strict_compliance=true
hibernate.auto_quote_keyword=false
hibernate.implicit_naming_strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl
hibernate.model.generator_name_as_sequence_name=true
hibernate.jpa.compliance.transaction=true
hibernate.jpa.compliance.closed=true
hibernate.jpa.compliance.query=true
hibernate.jpa.compliance.list=true
hibernate.jpa.compliance.caching=true
hibernate.jpa.compliance.proxy=true
hibernate.enable_lazy_load_no_trans=false
hibernate.jpa.compliance.global_id_generators=true
hibernate.search.index_uninverting_allowed=true
10.21. Persistence unit properties
The following properties are supported in the persistence unit
definition (in the persistence.xml file):
jboss.as.jpa.providerModule
name of the persistence provider module
(default is org.hibernate). Should be application, if a persistence
provider is packaged with the application. See note below about some
module names that are built in (based on the provider).
jboss.as.jpa.adapterModule
name of the integration classes that help
WildFly to work with the persistence provider.
jboss.as.jpa.adapterClass
class name of the integration adapter.
jboss.as.jpa.managed
set to false to disable container managed JPA
access to the persistence unit. The default is true, which enables
container managed JPA access to the persistence unit. This is typically
set to false for Spring applications.
jboss.as.jpa.classtransformer
set to false to disable class
transformers for the persistence unit. Set to true, to allow entity
class enhancing/rewriting.
wildfly.jpa.default-unit
set to true to choose the default persistence
unit in an application. This is useful if you inject a persistence
context without specifying the unitName (@PersistenceContext
EntityManager em) but have multiple persistence units specified in your
persistence.xml.
wildfly.jpa.twophasebootstrap
persistence providers (like Hibernate
ORM 4.3+ via EntityManagerFactoryBuilder), allow a two phase persistence
unit bootstrap, which improves JPA integration with Jakarta Contexts and Dependency Injection. Setting the
wildfly.jpa.twophasebootstrap hint to false, disables the two phase
bootstrap (for the persistence unit that contains the hint).
wildfly.jpa.applicationdatasource
set to true when using an application defined DataSource or resource reference to a global DataSource.
wildfly.jpa.allowdefaultdatasourceuse
set to false to prevent
persistence unit from using the default data source. Defaults to true.
This is only important for persistence units that do not specify a
datasource.
jboss.as.jpa.deferdetach
Controls whether a transaction scoped
persistence context used in a non-JTA transaction thread will detach
loaded entities after each EntityManager invocation or when the
persistence context is closed (e.g. business method ends). Defaults to
false (entities are cleared after EntityManager invocation) and if set
to true, the detach is deferred until the context is closed.
wildfly.jpa.skipquerydetach
Controls whether a transaction scoped
persistence context used in a non-JTA transaction thread will detach
Query results immediately. Defaults to
false (Query results are detached immediately) and if set
to true, the detach is deferred until the persistence context is closed.
wildfly.jpa.hibernate.search.module
Controls which version of
Hibernate Search to include on classpath. Only makes sense when using
Hibernate as JPA implementation. The default is auto; other valid values
are none or a full module identifier to use an alternative version.
jboss.as.jpa.scopedname
Specify the qualified (application scoped)
persistence unit name to be used. By default, this is internally set to
the application name + persistence unit name. The
hibernate.cache.region_prefix will default to whatever you set
jboss.as.jpa.scopedname to. Make sure you set the
jboss.as.jpa.scopedname value to a value not already in use by other
applications deployed on the same application server instance.
wildfly.jpa.allowjoinedunsync
If set to true, allows an
SynchronizationType.UNSYNCHRONIZED persistence context that has been
joined to the active JTA transaction, to be propagated into a
SynchronizationType.SYNCHRONIZED persistence context. Otherwise, an
IllegalStateException exception would of been thrown that complains that
an unsychronized persistence context cannot be propagated into a
synchronized persistence context. Defaults to false.
wildfly.jpa.skipmixedsynctypechecking
Set to true to disable the
throwing of an IllegalStateException exception when propagating an
SynchronizationType.UNSYNCHRONIZED persistence context into a
SynchronizationType.SYNCHRONIZED persistence context. This is a
workaround intended to allow applications that used to incorrectly not
get IllegalStateException exception with extended persistence contexts,
to avoid the IllegalStateException, so they don’t have to change their
application right away (for compatibility purposes). This hint may be
deprecated in a future release. See WFLY-7108 for more details. Defaults
to false.
wildfly.jpa.regionfactory
Only applies to Hibernate ORM 5.3+, set to false to disable automatic use of Infinispan as second level cache (hibernate.cache.region.factory_class).
wildfly.jpa.jtaplatform
Only applies to Hibernate ORM 5.3+, set to false to disable automatic configuring of the JTA integration platform (hibernate.transaction.jta.platform).
10.22. Determine the persistence provider module
As mentioned above, if the jboss.as.jpa.providerModule property is not
specified, the provider module name is determined by the provider name
specified in the persistence.xml. The mapping is:
org.datanucleus.store.appengine.jpa.DatastorePersistenceProvider
org.datanucleus:appengine
org.apache.openjpa.persistence.PersistenceProviderImpl
org.apache.openjpa
10.23. Binding EntityManagerFactory/EntityManager to JNDI
By default WildFly does not bind the entity manager factory to JNDI.
However, you can explicitly configure this in the persistence.xml of
your application by setting the jboss.entity.manager.factory.jndi.name
hint. The value of that property should be the JNDI name to which the entity manager factory should be bound.
You can also bind a container managed (transaction scoped) entity manager to JNDI as well, }}via hint
jboss.entity.manager.jndi.name\{
}{{. As a reminder, a transaction scoped entity manager (persistence context), acts as a proxy that always gets an unique underlying entity manager (at the persistence provider level).
Here’s an example:
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="myPU">
<!-- If you are running in a production environment, add a managed
data source, the example data source is just for proofs of concept! -->
<jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
<properties>
<!-- Bind entity manager factory to JNDI at java:jboss/myEntityManagerFactory -->
<property name="jboss.entity.manager.factory.jndi.name" value="java:jboss/myEntityManagerFactory" />
<property name="jboss.entity.manager.jndi.name" value="java:/myEntityManager"/>
</properties>
</persistence-unit>
</persistence>
@Stateful
public class ExampleSFSB {
public void createSomeEntityWithTransactionScopedEM(String name) {
Context context = new InitialContext();
javax.persistence.EntityManager entityManager = (javax.persistence.EntityManager) context.lookup("java:/myEntityManager");
SomeEntity someEntity = new SomeEntity();
someEntity.setName(name); entityManager.persist(name);
References in this document to Java Transaction API(JTA) refer to Jakarta Transactions unless otherwise noted.
References in this document to Java Persistence API(JPA) refer to the Jakarta Persistence unless otherwise noted.
WildFly uses Narayana as the implementation of the JTA specification.
Narayana manages transactions in the application server.
11.1. Transactions in Enterprise Java applications
Transactions are one of the core functionalities provided by the container
for the applications. A developer can manage transactions either with
the programmatic approach - with API defined by JTA specification.
Or he can use annotations. There are two types.
first - EJB annotations, their meaning and capabilities are defined under
EJB specification and are valid when used in the EJB component
second - CDI annotations, their meaning is defined under JTA specification
The JTA API is defined under
javax.transaction
package. The package could be considered a bit misguiding as it presents classes
which are expected to be used by application developer. These classes
are intended as an API for the application server (as the WildFly is).
The user is expected to interact with
javax.transaction.UserTransaction
which defines the transaction boundaries. The UserTransaction could be used
in case the transaction is not defined by annotation at the method
- aka. the transaction is not managed by the container. Those are places like
servlets, CDI bean which is not annotated with @Transactional and
EJBs defined as bean managed.
UserTransaction is available via CDI injection
@Inject
UserTransaction txn;
or with EJB @Resource injection where you can additionally define the JNDI
name to be looked-up. The specification says it’s java:comp/UserTransaction.
@Resource(lookup = "java:comp/UserTransaction")
UserTransaction txn;
The code can then look like
@WebServlet(name="TestServlet", urlPatterns={"/"})
public class TestServlet extends HttpServlet {
private static final Logger LOG = Logger.getLogger(TestServlet.class);
@Inject
private UserTransaction txn;
@PersistenceContext
private EntityManager em;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
txn.begin();
em.persist(new TestEntity(1, "Mr. Transaction"));
txn.commit();
} catch (NotSupportedException beginExceptions) {
LOG.errorf(beginExceptions, "Cannot start transaction: '%s'", txn);
} catch (SecurityException
| IllegalStateException | RollbackException
| HeuristicMixedException | HeuristicRollbackException commitExceptions) {
LOG.errorf(commitExceptions, "Cannot commit transaction: '%s'", txn);
} catch (SystemException systemException) {
LOG.errorf(systemException, "Unexpected error condition on work with transaction: '%s'", txn);
javax.transaction.TransactionManager
is not meant to be used by business logic but if you need to register
a synchronization
or you need to change transaction timeout (before the begin() method is called)
then it will be the option for you.
The TransactionManager could be taken with injection
and looked-up with the JNDI name
@Inject
TransactionManager tm;
// or
@Resource(lookup = "java:/TransactionManager")
TransactionManager tm;
11.1.2. Transactions in CDI beans
If you start using CDI beans then you can manage transactions either programmatically
or you can use annotations.
The @Transactional
defines transaction boundaries
(start of the method starts the transaction, while the transaction is finished at its end).
If the method finishes sucessfully the transaction is committed.
If the transaction ends with RuntimeException then it’s rolled-back.
When you define the method to be @Transactional you define the behaviour
of incoming (or non-existent) transactional context.
Please refer the to documentation for
Transactional.TxType.
It’s important to note that the TxType influences the transaction management
only when called with the managed CDI bean. If you call the method directly -
without the CDI container wraps the invocation then the TxType has no effect.
@RequestScope
public class MyCDIBean {
@Inject
MyCDIBean myBean;
@Transactional(TxType.REQUIRED)
public void mainMethod() {
// CDI container does not wrap the invocation
// no new transaction is started
innerFunctionality();
// CDI container starts a new transaction
// the method uses TxType.REQUIRES_NEW and is called from the CDI bean
myBean.innerFunctionality();
@Transactional(TxType.REQUIRES_NEW)
private void innerFunctionality() {
// some business logic
if you use the @Transactional for managing transactions boundaries
you won’t be permitted to use the UserTransaction methods.
If you do so you can expect a runtime exception being thrown.
11.1.3. Transactions in EJBs
Transaction management in EJB is administered with two annotations:
@TransactionManagement
@TransactionAttribute.
when you use message-driven bean then the @TransactionTimeout does not work
and you need to define the timeout through the @ActivationConfigProperty:
@ActivationConfigProperty(propertyName="transactionTimeout", propertyValue="1")
when the EJB method is invoked - a new transaction is started when no transaction context is available,
or the method joins the existing transactions when the transaction context is passed by the call
Container managed transactions
Using the @TransactionManagement(TransactionManagementType.CONTAINER) means
the container is responsible to manage transactions. The boundary of the transaction
is defined by the start and end of the method and you influence the behaviour by using
@TransactionAttribute.
If java.lang.RuntimeException is thrown the transaction is rolled back.
EJBContext
could be used to define the transaction should be rolled-back
by the end of the method when setRollbackOnly is used.
@Stateless
public class MyBean {
@PersistenceContext
private EntityManager em;
@Resource
EJBContext ctx;
public void method() {
em.persist(new TestEntity());
// at the end of the method the rollback is called
ctx.setRollbackOnly();
the EJBContext let you get the UserTransaction but you are not allowed
to do any operation with that when you run container managed transaction.
You can expect to receive a runtime exception in such case.
Bean managed transactions
Using the @TransactionManagement(TransactionManagementType.BEAN) means
the transaction will be managed manually with the use of the JTA API.
That’s with the UserTransaction injections and methods on it.
You can inject the EJBContext to get the UserTransaction instance too.
if a call is made from the container-managed method,
passing the transaction context to the bean managed method
then the context is suspended. It’s similar(!) to
call transaction managed bean annotated with
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
Transaction synchronization
JTA API gives a chance to react to the event of a finishing transaction.
The definition says that transaction manager announces the even of beforeCompletion and afterCompletion
which are defined by the interface javax.transaction.Synchronization.
The beforeCompletion
callback is invoked at time the transaction manager starts to commit the global transaction. The invocation is processed in the transaction context.
The afterCompletion
is invoked after the transaction is committed or rolled-back (and is processed outside of the transaction context).
The user needs just to create a simple Java POJO and implement the interface.
public class MySynchronization
implements javax.transaction.Synchronization {
@Override
public void beforeCompletion() {
System.out.println("Transaction is about to be finished"):
@Override
public void afterCompletion(int status) {
System.out.println("Transaction finished with status " + status):
For registration of the synchronization callback, the user can inject the
javax.transaction.TransactionSynchronizationRegistry.
(the mandated JNDI location for the object is at java:comp/TransactionSynchronizationRegistry) and then to register
the synchronization instance. The instance is bound to the currently active transaction.
@Resource
TransactionSynchronizationRegistry transactionSynchronizationRegistry;
public void method() {
transactionSynchronizationRegistry
.registerInterposedSynchronization(new MySynchronization());
The transaction synchronization registry adds other useful methods which are putResource(Object key, Object value)
and getResource(Object key). Their purpose is saving data objects alongside the transaction context.
When the transaction is active you can store and retrieve the saved data. When the transaction is finished
and there is no transaction context available (e.g. at afterCompletion) the java.lang.IllegalStateException
is thrown.
The other option for the user is to use the transaction object
to register the synchronization.
@Resource(lookup = "java:/TransactionManager")
TransactionManager tm;
public void method() {
tm.getTransaction().registerSynchronization(new MySynchronization());
When the user runs the Stateful Session Bean he can implement
interface javax.ejb.SessionSynchronization
(or to use annotations) for the definition of the synchronization callbacks onto the bean.
The session synchronization defines three methods.
Of these three methods afterBegin is not connected to the transaction synchronization so we will not discuss it further.
The following example works with annotations but the bean may just implement the SessionSynchronization
interface and it would work the same way.
// only(!) SFSB can use the capability of SessionSynchronization
@Stateful
public class MyStatefulBean {
public void method() {
System.out.println("Running an important business logic...");
Thread.sleep(42000);
@BeforeCompletion
public void beforeCompletion() {
System.out.println("Transaction is about to be finished"):
@AfterCompletion
public void afterCompletion(boolean committed) {
System.out.println("Transaction finished with the outcome "
+ (committed ? "committed" : "rolled-back")):
The WildFly classloading is based
on the jboss modules
which define the modular class loading system.
The transactions for CDI comes as the extension and because of it
this extension has to be available at the application classpath.
If the application/deployment uses annotations @Transactional
or @TransactionScoped then class loading handling is done automatically.
There is one limitation with the CDI with this approach.
If your application adds the transactional annotations dynamically
(you adds the annotations dynamically during runtime) then the
transaction module has to be
explicitly added
to the application classpath.
This can be done with creating META-INF/MANIFEST.MF or
with use of jboss-deployment-structure.xml descriptor. The MANIFEST.MF
could look like
Manifest-Version: 1.0
Dependencies: org.jboss.jts export services
11.3. Transactions troubleshooting
The Narayana component is configured to log only messages with level WARN
(see category com.arjuna in the standalone-*.xml).
If you struggle issues of the transactional handling
you can get a better insight into transaction processing by setting the level to TRACE.
/subsystem=logging/logger=com.arjuna:write-attribute(name=level,value=TRACE)
The TRACE could overwhelm you with information from the transactions subsystem.
Let’s quickly review what are the most important points to look at in the log.
[section 1]
2019-02-05 14:19:39,745 TRACE [com.arjuna.ats.jta] (default task-1) BaseTransaction.begin
2019-02-05 14:19:39,745 TRACE [com.arjuna.ats.arjuna] (default task-1) StateManager::StateManager( 2, 0 )
2019-02-05 14:19:39,745 TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::BasicAction()
2019-02-05 14:19:39,745 TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::Begin() for action-id 0:ffff0a28050c:-a09a5fe:5c598d64:3b
2019-02-05 14:19:39,745 TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::actionInitialise() for action-id 0:ffff0a28050c:-a09a5fe:5c598d64:3b
2019-02-05 14:19:39,745 TRACE [com.arjuna.ats.arjuna] (default task-1) ActionHierarchy::ActionHierarchy(1)
2019-02-05 14:19:39,745 TRACE [com.arjuna.ats.arjuna] (default task-1) ActionHierarchy::add(0:ffff0a28050c:-a09a5fe:5c598d64:3b, 1)
2019-02-05 14:19:39,745 TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::addChildThread () action 0:ffff0a28050c:-a09a5fe:5c598d64:3b adding Thread[default task-1,5,main]
2019-02-05 14:19:39,745 TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::addChildThread () action 0:ffff0a28050c:-a09a5fe:5c598d64:3b adding Thread[default task-1,5,main] result = true
2019-02-05 14:19:39,745 TRACE [com.arjuna.ats.arjuna] (default task-1) TransactionReaper::insert ( BasicAction: 0:ffff0a28050c:-a09a5fe:5c598d64:3b status: ActionStatus.RUNNING, 300 )
2019-02-05 14:19:39,745 TRACE [com.arjuna.ats.arjuna] (default task-1) ReaperElement::ReaperElement ( BasicAction: 0:ffff0a28050c:-a09a5fe:5c598d64:3b status: ActionStatus.RUNNING, 300 )
2019-02-05 14:19:39,745 TRACE [com.arjuna.ats.jta] (default task-1) TransactionImple.registerSynchronization - Class: class org.wildfly.transaction.client.AbstractTransaction$AssociatingSynchronization HashCode: 1114413551 toString: org.wildfly.transaction.client.AbstractTransaction$AssociatingSynchronization@426c99ef
[section 2]
TRACE [com.arjuna.ats.jta] (default task-1) TransactionImple.enlistResource ( TestXAResource(TestXAResourceCommon(id:944, xid:null, timeout:299, prepareReturn:0)) )
TRACE [com.arjuna.ats.jta] (default task-1) TransactionImple.getStatus: javax.transaction.Status.STATUS_ACTIVE
TRACE [com.arjuna.ats.arjuna] (default task-1) OutputObjectState::OutputObjectState()
TRACE [com.arjuna.ats.arjuna] (default task-1) FileSystemStore.write_committed(0:ffff0a28050c:-a09a5fe:5c598d64:43, EISNAME)
TRACE [com.arjuna.ats.arjuna] (default task-1) ShadowingStore.write_state(0:ffff0a28050c:-a09a5fe:5c598d64:43, EISNAME, StateType.OS_ORIGINAL)
TRACE [com.arjuna.ats.arjuna] (default task-1) ShadowingStore.genPathName(0:ffff0a28050c:-a09a5fe:5c598d64:43, EISNAME, StateType.OS_ORIGINAL)
TRACE [com.arjuna.ats.arjuna] (default task-1) FileSystemStore.genPathName(0:ffff0a28050c:-a09a5fe:5c598d64:43, EISNAME, 11)
TRACE [com.arjuna.ats.arjuna] (default task-1) FileSystemStore.openAndLock(data/tx-object-store/ShadowNoFileLockStore/defaultStore/EISNAME/0_ffff0a28050c_-a09a5fe_5c598d64_43, FileLock.F_WRLCK, true)
TRACE [com.arjuna.ats.arjuna] (default task-1) FileSystemStore.closeAndUnlock(data/tx-object-store/ShadowNoFileLockStore/defaultStore/EISNAME/0_ffff0a28050c_-a09a5fe_5c598d64_43, null, java.io.FileOutputStream@72d0d91)
TRACE [com.arjuna.ats.arjuna] (default task-1) StateManager::StateManager( 1, 0 )
TRACE [com.arjuna.ats.arjuna] (default task-1) AbstractRecord::AbstractRecord (0:ffff0a28050c:-a09a5fe:5c598d64:45, 1)
TRACE [com.arjuna.ats.jta] (default task-1) XAResourceRecord.XAResourceRecord ( < formatId=131077, gtrid_length=29, bqual_length=36, tx_uid=0:ffff0a28050c:-a09a5fe:5c598d64:3b, node_name=1, branch_uid=0:ffff0a28050c:-a09a5fe:5c598d64:44, subordinatenodename=null, eis_name=java:/TestXAResource >, TestXAResource(TestXAResourceCommon(id:944, xid:null, timeout:300, prepareReturn:0)) ), record id=0:ffff0a28050c:-a09a5fe:5c598d64:45
TRACE [com.arjuna.ats.arjuna] (default task-1) RecordList::insert(RecordList: empty) : appending /StateManager/AbstractRecord/XAResourceRecord for 0:ffff0a28050c:-a09a5fe:5c598d64:45
[section 3]
TRACE [com.arjuna.ats.jta] (default task-1) BaseTransaction.commit
TRACE [com.arjuna.ats.jta] (default task-1) TransactionImple.commitAndDisassociate
TRACE [com.arjuna.ats.jta] (default task-1) TransactionImple.getStatus: javax.transaction.Status.STATUS_ACTIVE
TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::End() for action-id 0:ffff0a28050c:-a09a5fe:5c598d64:3b
[section 4]
TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::prepare () for action-id 0:ffff0a28050c:-a09a5fe:5c598d64:3b
TRACE [com.arjuna.ats.jta] (default task-1) XAResourceRecord.topLevelPrepare for XAResourceRecord < resource:TestXAResource(TestXAResourceCommon(id:944, xid:< formatId=131077, gtrid_length=29, bqual_length=36, tx_uid=0:ffff0a28050c:-a09a5fe:5c598d64:3b, node_name=1, branch_uid=0:ffff0a28050c:-a09a5fe:5c598d64:44, subordinatenodename=null, eis_name=java:/TestXAResource >, timeout:300, prepareReturn:0)), txid:< formatId=131077, gtrid_length=29, bqual_length=36, tx_uid=0:ffff0a28050c:-a09a5fe:5c598d64:3b, node_name=1, branch_uid=0:ffff0a28050c:-a09a5fe:5c598d64:44, subordinatenodename=null, eis_name=java:/TestXAResource >, heuristic: TwoPhaseOutcome.FINISH_OK, product: Crash Recovery Test/EAP Test, jndiName: java:/TestXAResource com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord@6454bcb3 >, record id=0:ffff0a28050c:-a09a5fe:5c598d64:45
TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::doPrepare() result for action-id (0:ffff0a28050c:-a09a5fe:5c598d64:3b) on record id: (0:ffff0a28050c:-a09a5fe:5c598d64:45) is (TwoPhaseOutcome.PREPARE_OK) node id: (1)
TRACE [com.arjuna.ats.arjuna] (default task-1) RecordList::insert(RecordList: empty) : appending /StateManager/AbstractRecord/XAResourceRecord for 0:ffff0a28050c:-a09a5fe:5c598d64:45
TRACE [com.arjuna.ats.arjuna] (default task-1) OutputObjectState::OutputObjectState(0:ffff0a28050c:-a09a5fe:5c598d64:3b, /StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction)
TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::save_state ()
TRACE [com.arjuna.ats.arjuna] (default task-1) StateManager.packHeader for object-id 0:ffff0a28050c:-a09a5fe:5c598d64:3b birth-date 1549372780127
TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::save_state - next record to pack is a 171 record /StateManager/AbstractRecord/XAResourceRecord should save it? = true
[section 5]
TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::phase2Commit() for action-id 0:ffff0a28050c:-a09a5fe:5c598d64:3b
TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::doCommit (XAResourceRecord < resource:TestXAResource(TestXAResourceCommon(id:944, xid:< formatId=131077, gtrid_length=29, bqual_length=36, tx_uid=0:ffff0a28050c:-a09a5fe:5c598d64:3b, node_name=1, branch_uid=0:ffff0a28050c:-a09a5fe:5c598d64:44, subordinatenodename=null, eis_name=java:/TestXAResource >, timeout:300, prepareReturn:0)), txid:< formatId=131077, gtrid_length=29, bqual_length=36, tx_uid=0:ffff0a28050c:-a09a5fe:5c598d64:3b, node_name=1, branch_uid=0:ffff0a28050c:-a09a5fe:5c598d64:44, subordinatenodename=null, eis_name=java:/TestXAResource >, heuristic: TwoPhaseOutcome.FINISH_OK, product: Crash Recovery Test/EAP Test, jndiName: java:/TestXAResource com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord@6454bcb3 >)
TRACE [com.arjuna.ats.jta] (default task-1) XAResourceRecord.topLevelCommit for XAResourceRecord < resource:TestXAResource(TestXAResourceCommon(id:944, xid:< formatId=131077, gtrid_length=29, bqual_length=36, tx_uid=0:ffff0a28050c:-a09a5fe:5c598d64:3b, node_name=1, branch_uid=0:ffff0a28050c:-a09a5fe:5c598d64:44, subordinatenodename=null, eis_name=java:/TestXAResource >, timeout:300, prepareReturn:0)), txid:< formatId=131077, gtrid_length=29, bqual_length=36, tx_uid=0:ffff0a28050c:-a09a5fe:5c598d64:3b, node_name=1, branch_uid=0:ffff0a28050c:-a09a5fe:5c598d64:44, subordinatenodename=null, eis_name=java:/TestXAResource >, heuristic: TwoPhaseOutcome.FINISH_OK, product: Crash Recovery Test/EAP Test, jndiName: java:/TestXAResource com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord@6454bcb3 >, record id=0:ffff0a28050c:-a09a5fe:5c598d64:45
TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::doCommit() result for action-id (0:ffff0a28050c:-a09a5fe:5c598d64:3b) on record id: (0:ffff0a28050c:-a09a5fe:5c598d64:45) is (TwoPhaseOutcome.FINISH_OK) node id: (1)
[section 6]
TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::updateState() for action-id 0:ffff0a28050c:-a09a5fe:5c598d64:3b
TRACE [com.arjuna.ats.arjuna] (default task-1) FileSystemStore.remove_committed(0:ffff0a28050c:-a09a5fe:5c598d64:3b, /StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction)
TRACE [com.arjuna.ats.arjuna] (default task-1) ShadowingStore.remove_state(0:ffff0a28050c:-a09a5fe:5c598d64:3b, /StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction, StateType.OS_ORIGINAL)
TRACE [com.arjuna.ats.arjuna] (default task-1) FileSystemStore.closeAndUnlock(data/tx-object-store/ShadowNoFileLockStore/defaultStore/StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction/0_ffff0a28050c_-a09a5fe_5c598d64_3b, null, null)
TRACE [com.arjuna.ats.arjuna] (default task-1) BasicAction::End() result for action-id (0:ffff0a28050c:-a09a5fe:5c598d64:3b) is (TwoPhaseOutcome.FINISH_OK) node id: (1)
TRACE [com.arjuna.ats.jta] (default task-1) SynchronizationImple.afterCompletion - Class: class org.wildfly.transaction.client.AbstractTransaction$AssociatingSynchronization HashCode: 1685304571 toString: org.wildfly.transaction.client.AbstractTransaction$AssociatingSynchronization@6473b4fb
TRACE [com.arjuna.ats.jta] (default task-1) SynchronizationImple.afterCompletion - Class: class org.wildfly.transaction.client.provider.jboss.JBossLocalTransactionProvider$1 HashCode: 1429380276 toString: org.wildfly.transaction.client.provider.jboss.JBossLocalTransactionProvider$1@55329cb4
TRACE [com.arjuna.ats.arjuna] (default task-1) TransactionReaper::remove ( BasicAction: 0:ffff0a28050c:-a09a5fe:5c598d64:3b status: ActionStatus.COMMITTED )
It’s good to consider to follow with the thread id (in the log above it’s default-task-1).
The transaction could be suspended and started at the different thread
but it’s not usual.
The log shows the Narayana processes the two-phase commit. Bear in mind that the example above
shows only one resource to be part of the two-phase commit handling.
That’s intentional for the log not being too long.
the section-1 refers to the point where the transaction is started. The
JTA synchronizations
are registered and the transaction is added to be handled by transaction reaper
(the transaction reaper is an independent thread taking care of transaction timeouts,
see more at section Transaction timeouts
in the Narayana documentation).
At this place consider the BasicAction (a Narayana abstraction for the transaction)
is identified by string 0:ffff0a28050c:-a09a5fe:5c598d64:3b.
It refers to the transaction id. You can track it through the log and follow
what is happening with the particular transaction.
the section-2 refers to the part of business logic processing. That’s the time
when a database insertion is run or Jakarta Messaging sends a message to a queue.
That’s where you spot message containing enlistResource. After the resource
is enlisted to the transaction the transaction manager saves a record
persistently under transaction log store.
the section-3 refers to the time when the transaction is about to be committed.
That means all business logic was finished (that could be the time a method
annotated with @Transactional reached its end).
the section-4 refers to the first phase of 2PC which is prepare. You can see
XAResourceRecord.topLevelPrepare informing what’s the global transaction id
(already defined at the start of the transaction) and the branch id
(particular to each resource). The resource is then prepared.
when whole prepare phase finishes Narayana saves the state into object store
the section-5 refers to the second phase of 2PC which is commit. You can see
XAResourceRecord.topLevelCommit with similar information as for prepare.
the section-6 shows the transaction is finished, information about the transaction
is removed from the Narayana object store and unregistered from the transaction reaper.
11.4. Transactions configuration
Configuration related to the behaviour of the Narayana transaction manager
is covered under transactions subsystem. For the details refer to
Admin Guide Transactions subsystem.
To check the subsystem model you can use the WildFly model reference
or list all the configuration options of the subsystem in jboss-cli
/subsystem=transactions:read-resource-description(recursive=true)
References in this document to CDI refer to Jakarta Contexts and Dependency Injection unless otherwise noted.
References in this document to Java Transaction API(JTA) refer to Jakarta Transactions unless otherwise noted.
References in this document to Enterprise JavaBeans(EJB) refer to the Jakarta Enterprise Beans unless otherwise noted.
WildFly offers several mechanisms to retrieve components by name. Every
WildFly instance has it’s own local JNDI namespace ( java:) which is
unique per JVM. The layout of this namespace is primarily governed by
the Jakarta EE specification. Applications which share the same WildFly
instance can use this namespace to intercommunicate. In addition to
local JNDI, a variety of mechanisms exist to access remote components.
Client JNDI - This is a mechanism by which remote components can be
accessed using the JNDI APIs, but without network round-trips . This
approach is the most efficient, and removes a potential single point
of failure . For this reason, it is highly recommended to use Client
JNDI over traditional remote JNDI access. However, to make this
possible, it does require that all names follow a strict layout, so user
customizations are not possible. Currently only access to remote Jakarta Enterprise Beans
are supported via the ejb: namespace. Future revisions will likely add a
Jakarta Messaging client JNDI namespace.
Traditional Remote JNDI - This is a more familiar approach to EE
application developers, where the client performs a remote component
name lookup against a server, and a proxy/stub to the component is
serialized as part of the name lookup and returned to the client. The
client then invokes a method on the proxy which results in another
remote network call to the underlying service. In a nutshell,
traditional remote JNDI involves two calls to invoke an EE component,
whereas Client JNDI requires one. It does however allow for customized
names, and for a centralised directory for multiple application servers.
This centralized directory is, however, a single point of failure.
EE Application Client / Server-To-Server Delegation - This approach is
where local names are bound as an alias to a remote name using one of
the above mechanisms. This is useful in that it allows applications to
only ever reference standard portable Jakarta EE names in both code and
deployment descriptors. It also allows for the application to be unaware
of network topology details/ This can even work with Java SE clients by
using the little known EE Application Client feature. This feature
allows you to run an extremely minimal AS server around your
application, so that you can take advantage of certain core services
such as naming and injection.
java:comp - The namespace is scoped to the current component (i.e.
Jakarta Enterprise Beans)
java:module - Scoped to the current module
java:app - Scoped to the current application
java:global - Scoped to the application server
13.1. Binding entries to JNDI
There are several methods that can be used to bind entries into JNDI in
WildFly.
13.1.1. Using a deployment descriptor
For Jakarta EE applications the recommended way is to use a
deployment descriptor to create the binding. For
example the following web.xml binds the string "Hello World" to
java:global/mystring and the string "Hello Module" to
java:comp/env/hello (any non absolute JNDI name is relative to
java:comp/env context).
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<env-entry>
<env-entry-name>java:global/mystring</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>Hello World</env-entry-value>
</env-entry>
<env-entry>
<env-entry-name>hello</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>Hello Module</env-entry-value>
</env-entry>
</web-app>
For more details, see the Java EE
Platform Specification.
13.1.2. Programmatically
Jakarta EE Applications
Standard Jakarta EE applications may use the standard JNDI API, included
with Java SE, to bind entries in the global namespaces (the standard
java:comp, java:module and java:app namespaces are read-only, as
mandated by the Jakarta EE Platform Specification).
InitialContext initialContext = new InitialContext();
initialContext.bind("java:global/a", 100);
There is no need to unbind entries created programmatically, since
WildFly tracks which bindings belong to a deployment, and the bindings
are automatically removed when the deployment is undeployed.
WildFly Modules and Extensions
With respect to code in WildFly Modules/Extensions, which is executed
out of a Jakarta EE application context, using the standard JNDI API may
result in a UnsupportedOperationException if the target namespace uses a
WritableServiceBasedNamingStore. To work around that, the bind()
invocation needs to be wrapped using WildFly proprietary APIs:
InitialContext initialContext = new InitialContext();
WritableServiceBasedNamingStore.pushOwner(serviceTarget);
try {
initialContext.bind("java:global/a", 100);
} finally {
WritableServiceBasedNamingStore.popOwner();
The ServiceTarget removes the bind when uninstalled, thus using one out
of the module/extension domain usage should be avoided, unless entries
are removed using unbind().
13.1.3. Naming Subsystem Configuration
It is also possible to bind to one of the three global namespaces using
configuration in the naming subsystem. This can be done by either
editing the standalone.xml/domain.xml file directly, or through the
management API.
Four different types of bindings are supported:
Object Factory - This allows to specify the
javax.naming.spi.ObjectFactory that is used to create the looked up
value.
External Context - An external context to federate, such as an LDAP
Directory Service
Lookup - The allows to create JNDI aliases, when this entry is looked
up it will lookup the target and return the result.
<subsystem xmlns="urn:jboss:domain:naming:2.0
" >
<bindings>
<simple name="java:global/a" value="100" type="int" />
<simple name="java:global/jbossDocs" value="https://docs.jboss.org" type="java.net.URL" />
<object-factory name="java:global/b" module="com.acme" class="org.acme.MyObjectFactory" />
<external-context name="java:global/federation/ldap/example" class="javax.naming.directory.InitialDirContext" cache="true">
<environment>
<property name="java.naming.factory.initial" value="com.sun.jndi.ldap.LdapCtxFactory" />
<property name="java.naming.provider.url" value="ldap://ldap.example.com:389" />
<property name="java.naming.security.authentication" value="simple" />
<property name="java.naming.security.principal" value="uid=admin,ou=system" />
<property name="java.naming.security.credentials" value="secret" />
</environment>
</external-context>
<lookup name="java:global/c" lookup="java:global/b" />
</bindings>
</subsystem>
The CLI may also be used to bind an entry. As an example:
/subsystem=naming/binding=java\:global\/mybinding:add(binding-type=simple, type=long, value=1000)
13.2.1. Resource Injection
For Jakarta EE applications the recommended way to lookup a JNDI entry is
to use @Resource injection:
@Resource(lookup = "java:global/mystring")
private String myString;
@Resource(name = "hello")
private String hello;
@Resource
ManagedExecutorService executor;
Note that @Resource is more than a JNDI lookup, it also binds an entry
in the component’s JNDI environment. The new bind JNDI name is defined
by @Resource’s `name attribute, which value, if unspecified, is the
Java type concatenated with / and the field’s name, for instance
java.lang.String/myString. More, similar to when using deployment
descriptors to bind JNDI entries. unless the name is an absolute JNDI
name, it is considered relative to java:comp/env. For instance, with
respect to the field named myString above, the @Resource’s `lookup
attribute instructs WildFly to lookup the value in
java:global/mystring, bind it in
java:comp/env/java.lang.String/myString, and then inject such value
into the field.
With respect to the field named hello, there is no lookup attribute
value defined, so the responsibility to provide the entry’s value is
delegated to the deployment descriptor. Considering that the deployment
descriptor was the web.xml previously shown, which defines an
environment entry with same hello name, then WildFly inject the valued
defined in the deployment descriptor into the field.
The executor field has no attributes specified, so the bind’s name
would default to
java:comp/env/javax.enterprise.concurrent.ManagedExecutorService/executor,
but there is no such entry in the deployment descriptor, and when that
happens it’s up to WildFly to provide a default value or null, depending
on the field’s Java type. In this particular case WildFly would inject
the default instance of a managed executor service, the value in
java:comp/DefaultManagedExecutorService, as mandated by the EE
Concurrency Utilities 1.0 Specification (JSR 236).
13.2.2. Standard Java SE JNDI API
Jakarta EE applications may use, without any additional configuration
needed, the standard JNDI API to lookup an entry from JNDI:
String myString = (String) new InitialContext().lookup("java:global/mystring");
or simply
String myString = InitialContext.doLookup("java:global/mystring");
14.1. http-remoting:
The http-remoting: protocol implementation is provided by JBoss Remote
Naming project, and uses http upgrade to lookup items from the servers
local JNDI. To use it, you must have the appropriate jars on the class
path, if you are maven user can be done simply by adding the following
to your pom.xml dependencies:
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-ejb-client-bom</artifactId>
<version>11.0.0.Final</version>
<type>pom</type>
</dependency>
If you are not using maven a shaded jar that contains all required
classes
can be found in the bin/client directory of WildFly’s distribution.
final Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
env.put(Context.PROVIDER_URL, "http-remoting://localhost:8080");
// the property below is required ONLY if there is no ejb client configuration loaded (such as a
// jboss-ejb-client.properties in the class path) and the context will be used to lookup EJBs
env.put("jboss.naming.client.ejb.context", true);
InitialContext remoteContext = new InitialContext(env);
RemoteCalculator ejb = (RemoteCalculator) remoteContext.lookup("wildfly-http-remoting-ejb/CalculatorBean!"
+ RemoteCalculator.class.getName());
The http-remoting client assumes JNDI names in remote lookups are
relative to java:jboss/exported namespace, a lookup of an absolute JNDI
name will fail.
The ejb: namespace implementation is provided by the jboss-ejb-client
library, and allows the lookup of EJB’s using their application name,
module name, ejb name and interface type. To use it, you must have the
appropriate jars on the class path, if you are maven user can be done
simply by adding the following to your pom.xml dependencies:
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-ejb-client-bom</artifactId>
<version>11.0.0.Final</version>
<type>pom</type>
</dependency>
If you are not using maven a shaded jar that contains all required
classes
can be found in the bin/client directory of WildFly’s distribution.
This is a client side JNDI implementation. Instead of looking up an EJB
on the server the lookup name contains enough information for the client
side library to generate a proxy with the EJB information. When you
invoke a method on this proxy it will use the current EJB client context
to perform the invocation. If the current context does not have a
connection to a server with the specified EJB deployed then an error
will occur. Using this protocol it is possible to look up EJB’s that do
not actually exist, and no error will be thrown until the proxy is
actually used. The exception to this is stateful session beans, which
need to connect to a server when they are created in order to create the
session bean instance on the server.
final Properties env = new Properties();
env.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
InitialContext remoteContext = new InitialContext(env);
MyRemoteInterface myRemote = (MyRemoteInterface) remoteContext.lookup("ejb:myapp/myejbjar/MyEjbName\!com.test.MyRemoteInterface");
MyStatefulRemoteInterface myStatefulRemote = (MyStatefulRemoteInterface) remoteContext.lookup("ejb:myapp/myejbjar/MyStatefulName\!comp.test.MyStatefulRemoteInterface?stateful");
The first example is a lookup of a singleton, stateless or EJB 2.x home
interface. This lookup will not hit the server, instead a proxy will be
generated for the remote interface specified in the name. The second
example is for a stateful session bean, in this case the JNDI lookup
will hit the server, in order to tell the server to create the SFSB
session.
For more details on how the server connections are configured, including
the required jboss ejb client setup, please see
EJB invocations from a remote client using JNDI.
This page outlines the three options you have for deploying Jakarta RESTful Web Services
applications in WildFly 26. These three methods are specified in the
JAX-RS 2.0 specification in section 2.3.2.
15.1. Subclassing javax.ws.rs.core.Application and using @ApplicationPath
This is the easiest way and does not require any xml configuration.
Simply include a subclass of javax.ws.rs.core.Application in your
application, and annotate it with the path that you want your JAX-RS
classes to be available. For example:
@ApplicationPath("/mypath")
public class MyApplication extends Application {
This will make your Jakarta RESTful Web Services resources available under /
mywebappcontext /mypath.
15.2. Subclassing javax.ws.rs.core.Application and using web.xml
If you do not wish to use @ApplicationPath but still need to subclass
Application you can set up the Jakarta RESTful Web Services mapping in web.xml:
public class MyApplication extends Application {
<servlet-mapping>
<servlet-name>com.acme.MyApplication</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
This will make your Jakarta RESTful Web Services resources available under /
mywebappcontext /hello.
15.3. Using web.xml
If you don’t wan’t to subclass Application you can set the Jakarta RESTful Web Services
mapping in web.xml as follows:
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/hello/*</url-pattern>
</servlet-mapping>
This will make your Jakarta RESTful Web Services resources available under /
mywebappcontext /hello.
Note that you only have to add the mapping, not the corresponding
servlet. The server is responsible for adding the corresponding servlet
automatically.
Undertow allows you to share sessions between wars in an ear, if it is
explicitly configured to do so. Note that if you use this feature your
applications may not be portable, as this is not a standard servlet
feature.
In order to enable this you must include a shared-session-config
element in the jboss-all.xml file in the META-INF directory of the
<jboss xmlns="urn:jboss:1.0">
<shared-session-config xmlns="urn:jboss:shared-session-config:2.0
">
<session-config>
<cookie-config>
<path>/</path>
</cookie-config>
</session-config>
</shared-session-config>
</jboss>
This element is used to configure the shared session manager that will
be used by all wars in the ear. For full details of all the options
provided by this file please see the schema at
https://github.com/wildfly/wildfly/blob/master/undertow/src/main/resources/schema/shared-session-config_2_0.xsd,
however in general it mimics the options that are available in
jboss-web.xml for configuring the session.
The Web Services functionalities of WildFly are provided by the JBossWS
project integration.
The latest project documentation is available
here.
This section covers the most relevant topics for the JBossWS version
available on WildFly 26.
16.1. Jakarta XML Web Services User Guide
The Java API for XML-Based Web
Services (JAX-WS / JSR-224) defines the mapping between WSDL and Java
as well as the classes to be used for accessing webservices and
publishing them. JBossWS implements the latest JAX-WS specification,
hence users can reference it for any vendor agnostic webservice usage
need. Below is a brief overview of the most basic functionalities.
16.1.1. Web Service Endpoints
Jakarta XML Web Services simplifies the development model for a web service endpoint a
great deal. In short, an endpoint implementation bean is annotated with
Jakarta XML Web Services annotations and deployed to the server. The server automatically
generates and publishes the abstract contract (i.e. wsdl+schema) for
client consumption. All marshalling/unmarshalling is delegated to
JAXB.
Plain old Java Object (POJO)
Let’s take a look at simple POJO endpoint implementation. All endpoint
associated metadata is provided via
JSR-181 annotations
@WebService
@SOAPBinding(style = SOAPBinding.Style.RPC)
public class JSEBean01
@WebMethod
public String echo(String input)
The endpoint as a web application
A Jakarta XML Web Services java service endpoint (JSE) is deployed as a web application.
Here is a sample web.xml descriptor:
<web-app ...>
<servlet>
<servlet-name>TestService</servlet-name>
<servlet-class>org.jboss.test.ws.jaxws.samples.jsr181pojo.JSEBean01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestService</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Packaging the endpoint
A JSR-181 java service endpoint (JSE) is packaged as a web application
in a war file.
<war warfile="${build.dir}/libs/jbossws-samples-jsr181pojo.war" webxml="${build.resources.dir}/samples/jsr181pojo/WEB-INF/web.xml">
<classes dir="${build.dir}/classes">
<include name="org/jboss/test/ws/samples/jsr181pojo/JSEBean01.class"/>
</classes>
Note, only the endpoint implementation bean and web.xml are required.
Accessing the generated WSDL
A successfully deployed service endpoint will show up in the WildFly
managent console. You can get the deployed endpoint wsdl address there
Note, it is also possible to generate the abstract contract off line
using JBossWS tools. For details of that please see Bottom-Up (Java to
WSDL).
Jakarta Enterprise Beans 3 Stateless Session Bean (SLSB)
The Jakarta XML Web Services programming model supports the same set of annotations on
Jakarta Enterprise Beans 3 stateless session beans as on POJO endpoints.
@Stateless
@Remote(EJB3RemoteInterface.class)
@RemoteBinding(jndiBinding = "/ejb3/EJB3EndpointInterface")
@WebService
@SOAPBinding(style = SOAPBinding.Style.RPC)
public class EJB3Bean01 implements EJB3RemoteInterface
@WebMethod
public String echo(String input)
Above you see an Enterprise Beans-3.0 stateless session bean that exposes one method
both on the remote interface and as an endpoint operation.
Packaging the endpoint
A JSR-181 Jakarta Enterprise Beans service endpoint is packaged as an ordinary ejb
deployment.
<jar jarfile="${build.dir}/libs/jbossws-samples-jsr181ejb.jar">
<fileset dir="${build.dir}/classes">
<include name="org/jboss/test/ws/samples/jsr181ejb/EJB3Bean01.class"/>
<include name="org/jboss/test/ws/samples/jsr181ejb/EJB3RemoteInterface.class"/>
</fileset>
Accessing the generated WSDL
A successfully deployed service endpoint will show up in the WildFly
managent console. You can get the deployed endpoint wsdl address there
Note, it is also possible to generate the abstract contract off line
using JBossWS tools. For details of that please see Bottom-Up (Java to
WSDL).
Endpoint Provider
Jakarta XML Web Services services typically implement a native Java service endpoint
interface (SEI), perhaps mapped from a WSDL port type, either directly
or via the use of annotations.
Java SEIs provide a high level Java-centric abstraction that hides the
details of converting between Java objects and their XML representations
for use in XML-based messages. However, in some cases it is desirable
for services to be able to operate at the XML message level. The
Provider interface offers an alternative to SEIs and may be implemented
by services wishing to work at the XML message level.
A Provider based service instances invoke method is called for each
message received for the service.
@WebServiceProvider(wsdlLocation = "WEB-INF/wsdl/Provider.wsdl")
@ServiceMode(value = Service.Mode.PAYLOAD)
public class ProviderBeanPayload implements Provider<Source>
public Source invoke(Source req)
// Access the entire request PAYLOAD and return the response PAYLOAD
Note, Service.Mode.PAYLOAD is the default and does not have to be
declared explicitly. You can also use Service.Mode.MESSAGE to access
the entire SOAP message (i.e. with MESSAGE the Provider can also see
SOAP Headers)
The abstract contract for a provider endpoint cannot be
derived/generated automatically. Therefore it is necessary to specify
the wsdlLocation with the @ WebServiceProvider annotation.
Service is an abstraction that represents a WSDL service. A WSDL
service is a collection of related ports, each of which consists of a
port type bound to a particular protocol and available at a particular
endpoint address.
For most clients, you will start with a set of stubs generated from the
WSDL. One of these will be the service, and you will create objects of
that class in order to work with the service (see "static case" below).
Service Usage
Static case
Most clients will start with a WSDL file, and generate some stubs using
JBossWS tools like wsconsume. This usually gives a mass of files, one
of which is the top of the tree. This is the service implementation
class.
The generated implementation class can be recognised as it will have two
public constructors, one with no arguments and one with two arguments,
representing the wsdl location (a java.net.URL) and the service name
(a javax.xml.namespace.QName) respectively.
Usually you will use the no-argument constructor. In this case the WSDL
location and service name are those found in the WSDL. These are set
implicitly from the @WebServiceClient annotation that decorates the
generated class.
The following code snippet shows the generated constructors from the
generated class:
// Generated Service Class
@WebServiceClient(name="StockQuoteService", targetNamespace="http://example.com/stocks", wsdlLocation="http://example.com/stocks.wsdl")
public class StockQuoteService extends javax.xml.ws.Service
public StockQuoteService()
super(new URL("http://example.com/stocks.wsdl"), new QName("http://example.com/stocks", "StockQuoteService"));
public StockQuoteService(String wsdlLocation, QName serviceName)
super(wsdlLocation, serviceName);
Section Dynamic Proxy explains how to obtain a port from the service and
how to invoke an operation on the port. If you need to work with the XML
payload directly or with the XML representation of the entire SOAP
message, have a look at Dispatch.
Dynamic case
In the dynamic case, when nothing is generated, a web service client
uses Service.create to create Service instances, the following code
illustrates this process.
URL wsdlLocation = new URL("http://example.org/my.wsdl");
QName serviceName = new QName("http://example.org/sample", "MyService");
Service service = Service.create(wsdlLocation, serviceName);
Handler Resolver
Jakarta XML Web Services provides a flexible plug-in framework for message processing
modules, known as handlers, that may be used to extend the capabilities
of a Jakarta XML Web Services runtime system. Handler Framework describes the handler
framework in detail. A Service instance provides access to a
HandlerResolver via a pair of getHandlerResolver /
setHandlerResolver methods that may be used to configure a set of
handlers on a per-service, per-port or per-protocol binding basis.
When a Service instance is used to create a proxy or a Dispatch instance
then the handler resolver currently registered with the service is used
to create the required handler chain. Subsequent changes to the handler
resolver configured for a Service instance do not affect the handlers on
previously created proxies, or Dispatch instances.
Executor
Service instances can be configured with a
java.util.concurrent.Executor. The executor will then be used to
invoke any asynchronous callbacks requested by the application. The
setExecutor and getExecutor methods of Service can be used to
modify and retrieve the executor configured for a service.
* The getPort method returns a proxy. A service client
* uses this proxy to invoke operations on the target
* service endpoint. The <code>serviceEndpointInterface</code>
* specifies the service endpoint interface that is supported by
* the created dynamic proxy instance.
public <T> T getPort(QName portName, Class<T> serviceEndpointInterface)
* The getPort method returns a proxy. The parameter
* <code>serviceEndpointInterface</code> specifies the service
* endpoint interface that is supported by the returned proxy.
* In the implementation of this method, the Jakarta XML Web Services
* runtime system takes the responsibility of selecting a protocol
* binding (and a port) and configuring the proxy accordingly.
* The returned proxy should not be reconfigured by the client.
public <T> T getPort(Class<T> serviceEndpointInterface)
The service endpoint interface (SEI) is usually generated using tools.
For details see Top Down (WSDL to Java)
A generated static Service usually also offers typed methods to get
ports. These methods also return dynamic proxies that implement the SEI.
@WebServiceClient(name = "TestEndpointService", targetNamespace = "http://org.jboss.ws/wsref",
wsdlLocation = "http://localhost.localdomain:8080/jaxws-samples-webserviceref?wsdl")
public class TestEndpointService extends Service
public TestEndpointService(URL wsdlLocation, QName serviceName) {
super(wsdlLocation, serviceName);
@WebEndpoint(name = "TestEndpointPort")
public TestEndpoint getTestEndpointPort()
return (TestEndpoint)super.getPort(TESTENDPOINTPORT, TestEndpoint.class);
The @WebServiceRef annotation is used to declare a reference to a Web
service. It follows the resource pattern exemplified by the
javax.annotation.Resource annotation in
JSR-250.
There are two uses to the WebServiceRef annotation:
To define a reference whose type is a generated service class. In
this case, the type and value element will both refer to the generated
service class type. Moreover, if the reference type can be inferred by
the field/method declaration the annotation is applied to, the type and
value elements MAY have the default value (Object.class, that is). If
the type cannot be inferred, then at least the type element MUST be
present with a non-default value.
To define a reference whose type is a SEI. In this case, the type
element MAY be present with its default value if the type of the
reference can be inferred from the annotated field/method declaration,
but the value element MUST always be present and refer to a generated
service class type (a subtype of javax.xml.ws.Service). The wsdlLocation
element, if present, overrides theWSDL location information specified in
the WebService annotation of the referenced generated service class.
public class EJB3Client implements EJB3Remote
@WebServiceRef
public TestEndpointService service4;
@WebServiceRef
public TestEndpoint port3;
XMLWeb Services use XML messages for communication between services and
service clients. The higher level Jakarta XML Web Services APIs are designed to hide the
details of converting between Java method invocations and the
corresponding XML messages, but in some cases operating at the XML
message level is desirable. The Dispatch interface provides support for
this mode of interaction.
Dispatch supports two usage modes, identified by the constants
javax.xml.ws.Service.Mode.MESSAGE and
javax.xml.ws.Service.Mode.PAYLOAD respectively:
Message In this mode, client applications work directly with
protocol-specific message structures. E.g., when used with a SOAP
protocol binding, a client application would work directly with a SOAP
message.
Message Payload In this mode, client applications work with the
payload of messages rather than the messages themselves. E.g., when used
with a SOAP protocol binding, a client application would work with the
contents of the SOAP Body rather than the SOAP message as a whole.
Dispatch is a low level API that requires clients to construct messages
or message payloads as XML and requires an intimate knowledge of the
desired message or payload structure. Dispatch is a generic class that
supports input and output of messages or message payloads of any type.
Service service = Service.create(wsdlURL, serviceName);
Dispatch dispatch = service.createDispatch(portName, StreamSource.class, Mode.PAYLOAD);
String payload = "<ns1:ping xmlns:ns1='http://oneway.samples.jaxws.ws.test.jboss.org/'/>";
dispatch.invokeOneWay(new StreamSource(new StringReader(payload)));
payload = "<ns1:feedback xmlns:ns1='http://oneway.samples.jaxws.ws.test.jboss.org/'/>";
Source retObj = (Source)dispatch.invoke(new StreamSource(new StringReader(payload)));
Asynchronous Invocations
The BindingProvider interface represents a component that provides a
protocol binding for use by clients, it is implemented by proxies and is
extended by the Dispatch interface.
BindingProvider instances may provide asynchronous operation
capabilities. When used, asynchronous operation invocations are
decoupled from the BindingProvider instance at invocation time such
that the response context is not updated when the operation completes.
Instead a separate response context is made available using the
Response interface.
public void testInvokeAsync() throws Exception
URL wsdlURL = new URL("http://" + getServerHost() + ":8080/jaxws-samples-asynchronous?wsdl");
QName serviceName = new QName(targetNS, "TestEndpointService");
Service service = Service.create(wsdlURL, serviceName);
TestEndpoint port = service.getPort(TestEndpoint.class);
Response response = port.echoAsync("Async");
// access future
String retStr = (String) response.get();
assertEquals("Async", retStr);
@Oneway indicates that the given web method has only an input message
and no output. Typically, a oneway method returns the thread of control
to the calling application prior to executing the actual business
method.
@WebService (name="PingEndpoint")
@SOAPBinding(style = SOAPBinding.Style.RPC)
public class PingEndpointImpl
private static String feedback;
@WebMethod
@Oneway
publicvoid ping()
log.info("ping");
feedback = "ok";
@WebMethod
public String feedback()
log.info("feedback");
return feedback;
Timeout Configuration
There are two properties to configure the http connection timeout and
client receive time out:
public void testConfigureTimeout() throws Exception
//Set timeout until a connection is established
((BindingProvider)port).getRequestContext().put("javax.xml.ws.client.connectionTimeout", "6000");
//Set timeout until the response is received
((BindingProvider) port).getRequestContext().put("javax.xml.ws.client.receiveTimeout", "1000");
port.echo("testTimeout");
This sections describes concepts that apply equally to Web Service
Endpoints and Web Service Clients.
Handler Framework
The handler framework is implemented by a Jakarta XML Web Services protocol binding in
both client and server side runtimes. Proxies, and Dispatch instances,
known collectively as binding providers, each use protocol bindings to
bind their abstract functionality to specific protocols.
Client and server-side handlers are organized into an ordered list known
as a handler chain. The handlers within a handler chain are invoked each
time a message is sent or received. Inbound messages are processed by
handlers prior to binding provider processing. Outbound messages are
processed by handlers after any binding provider processing.
Handlers are invoked with a message context that provides methods to
access and modify inbound and outbound messages and to manage a set of
properties. Message context properties may be used to facilitate
communication between individual handlers and between handlers and
client and service implementations. Different types of handlers are
invoked with different types of message context.
Logical Handler
Handlers that only operate on message context properties and message
payloads. Logical handlers are protocol agnostic and are unable to
affect protocol specific parts of a message. Logical handlers are
handlers that implement javax.xml.ws.handler.LogicalHandler.
Protocol Handler
Handlers that operate on message context properties and protocol
specific messages. Protocol handlers are specific to a particular
protocol and may access and change protocol specific aspects of a
message. Protocol handlers are handlers that implement any interface
derived from javax.xml.ws.handler.Handler except
javax.xml.ws.handler.LogicalHandler.
Service endpoint handlers
On the service endpoint, handlers are defined using the @HandlerChain
annotation.
@WebService
@HandlerChain(file = "jaxws-server-source-handlers.xml")
public class SOAPEndpointSourceImpl
The location of the handler chain file supports 2 formats
\1. An absolute java.net.URL in externalForm. (ex:
http://myhandlers.foo.com/handlerfile1.xml)
\2. A relative path from the source file or class file. (ex:
bar/handlerfile1.xml)
Service client handlers
On the client side, handler can be configured using the @HandlerChain
annotation on the SEI or dynamically using the API.
Service service = Service.create(wsdlURL, serviceName);
Endpoint port = (Endpoint)service.getPort(Endpoint.class);
BindingProvider bindingProvider = (BindingProvider)port;
List<Handler> handlerChain = new ArrayList<Handler>();
handlerChain.add(new LogHandler());
handlerChain.add(new AuthorizationHandler());
handlerChain.add(new RoutingHandler());
bindingProvider.getBinding().setHandlerChain(handlerChain); // important!
Message Context
MessageContext is the super interface for all Jakarta XML Web Services message contexts.
It extends Map<String,Object> with additional methods and constants to
manage a set of properties that enable handlers in a handler chain to
share processing related state. For example, a handler may use the put
method to insert a property in the message context that one or more
other handlers in the handler chain may subsequently obtain via the get
method.
Properties are scoped as either APPLICATION or HANDLER. All properties
are available to all handlers for an instance of an MEP on a particular
endpoint. E.g., if a logical handler puts a property in the message
context, that property will also be available to any protocol handlers
in the chain during the execution of an MEP instance. APPLICATION scoped
properties are also made available to client applications (see section
4.2.1) and service endpoint implementations. The defaultscope for a
property is HANDLER.
Logical Message Context
Logical Handlers are passed a message context of type
LogicalMessageContext when invoked. LogicalMessageContext extends
MessageContext with methods to obtain and modify the message payload,
it does not provide access to the protocol specific aspects of amessage.
A protocol binding defines what component of a message are available via
a logical message context. The SOAP binding defines that a logical
handler deployed in a SOAP binding can access the contents of the SOAP
body but not the SOAP headers whereas the XML/HTTP binding defines that
a logical handler can access the entire XML payload of a message.
SOAP Message Context
SOAP handlers are passed a SOAPMessageContext when invoked.
SOAPMessageContext extends MessageContext with methods to obtain and
modify the SOAP message payload.
SOAPFactory factory = SOAPFactory.newInstance();
SOAPFault fault = factory.createFault("this is a fault string!", new QName("http://foo", "FooCode"));
fault.setFaultActor("mr.actor");
fault.addDetail().addChildElement("test");
thrownew SOAPFaultException(fault);
or an application specific user exception
public void throwApplicationException() throws UserException
thrownew UserException("validation", 123, "Some validation error");
javax.xml.ws.ServiceMode
The ServiceMode annotation is used to specify the mode for a provider
class, i.e. whether a provider wants to have access to protocol message
payloads (e.g. a SOAP body) or the entire protocol messages (e.g. a SOAP
envelope).
javax.xml.ws.WebFault
The WebFault annotation is used when mapping WSDL faults to Java
exceptions, see section 2.5. It is used to capture the name of the fault
element used when marshalling the Jakarta XML Binding type generated from the global
element referenced by the WSDL fault message. It can also be used to
customize the mapping of service specific exceptions to WSDL faults.
javax.xml.ws.RequestWrapper
The RequestWrapper annotation is applied to the methods of an SEI. It
is used to capture the Jakarta XML Binding generated request wrapper bean and the
element name and namespace for marshalling / unmarshalling the bean. The
default value of localName element is the operationName as defined in
WebMethod annotation and the default value for the targetNamespace
element is the target namespace of the SEI.When starting from Java, this
annotation is used to resolve overloading conflicts in document literal
mode. Only the className element is required in this case.
javax.xml.ws.ResponseWrapper
The ResponseWrapper annotation is applied to the methods of an SEI. It
is used to capture the Jakarta XML Binding generated response wrapper bean and the
element name and namespace for marshalling / unmarshalling the bean. The
default value of the localName element is the operationName as defined
in the WebMethod appended with "Response" and the default value of the
targetNamespace element is the target namespace of the SEI. When
starting from Java, this annotation is used to resolve overloading
conflicts in document literal mode. Only the className element is
required in this case.
javax.xml.ws.WebServiceClient
The WebServiceClient annotation is specified on a generated service
class (see 2.7). It is used to associate a class with a specific Web
service, identify by a URL to a WSDL document and the qualified name of
a wsdl:service element.
javax.xml.ws.WebEndpoint
The WebEndpoint annotation is specified on the getPortName() methods
of a generated service class (see 2.7). It is used to associate a get
method with a specific wsdl:port, identified by its local name (a
NCName).
javax.xml.ws.WebServiceProvider
The WebServiceProvider annotation is specified on classes that
implement a strongly typed javax.xml.ws.Provider. It is used to
declare that a class that satisfies the requirements for a provider (see
5.1) does indeed define a Web service endpoint, much like the
WebService annotation does for SEI-based endpoints.
The WebServiceProvider and WebService annotations are mutually
exclusive.
javax.xml.ws.BindingType
The BindingType annotation is applied to an endpoint implementation
class. It specifies the binding to use when publishing an endpoint of
this type.
The default binding for an endpoint is the SOAP 1.1/HTTP one.
javax.xml.ws.WebServiceRef
The WebServiceRef annotation is used to declare a reference to a Web
service. It follows the resource pattern exemplified by the
javax.annotation.Resource annotation in JSR-250 [JBWS:32]. The
WebServiceRef annotation is required to be honored when running on the
Jakarta EE platform, where it is subject to the common resource injection
rules described by the platform specification [JBWS:33].
javax.xml.ws.WebServiceRefs
The WebServiceRefs annotation is used to declare multiple references
to Web services on a single class. It is necessary to work around the
limition against specifying repeated annotations of the same type on any
given class, which prevents listing multiple javax.ws.WebServiceRef
annotations one after the other. This annotation follows the resource
pattern exemplified by the javax.annotation.Resources annotation in
JSR-250.
Since no name and type can be inferred in this case, each
WebServiceRef annotation inside a WebServiceRefs MUST contain name and
type elements with non-default values. The WebServiceRef annotation is
required to be honored when running on the Jakarta EE platform, where it
is subject to the common resource injection rules described by the
platform specification.
javax.xml.ws.Action
The Action annotation is applied to the methods of a SEI. It used to
generate the wsa:Action on wsdl:input and wsdl:output of each
wsdl:operation mapped from the annotated methods.
javax.xml.ws.FaultAction
The FaultAction annotation is used within the Action annotation to
generate the wsa:Action element on the wsdl:fault element of each
wsdl:operation mapped from the annotated methods.
16.1.5. JSR-181 Annotations
JSR-181 defines the syntax and semantics of Java Web Service (JWS)
metadata and default values.
For details, see JSR 181 - Web
Services Metadata for the Java Platform.
javax.jws.WebService
Marks a Java class as implementing a Web Service, or a Java interface as
defining a Web Service interface.
javax.jws.WebMethod
Customizes a method that is exposed as a Web Service operation.
javax.jws.OneWay
Indicates that the given web method has only an input message and no
output. Typically, a oneway method returns the thread of control to the
calling application prior to executing the actual business method. A
JSR-181 processor is REQUIRED to report an error if an operation marked
@Oneway has a return value, declares any checked exceptions or has any
INOUT or OUT parameters.
javax.jws.WebParam
Customizes the mapping of an individual parameter to a Web Service
message part and XML element.
javax.jws.WebResult
Customizes the mapping of the return value to a WSDL part and XML
element.
javax.jws.SOAPBinding
Specifies the mapping of the Web Service onto the SOAP message protocol.
The SOAPBinding annotation has a target of TYPE and METHOD. The
annotation may be placed on a method if and only if the
SOAPBinding.style is DOCUMENT. Implementations MUST report an error
if the SOAPBinding annotation is placed on a method with a
SOAPBinding.style of RPC. Methods that do not have a SOAPBinding
annotation accept the SOAPBinding behavior defined on the type.
javax.jws.HandlerChain
The @HandlerChain annotation associates the Web Service with an
externally defined handler chain.
It is an error to combine this annotation with the
@SOAPMessageHandlers annotation.
The @HandlerChain annotation MAY be present on the endpoint interface
and service implementation bean. The service implementation bean’s
@HandlerChain is used if @HandlerChain is present on both.
The @HandlerChain annotation MAY be specified on the type only. The
annotation target includes METHOD and FIELD for use by Jakarta XML Web Services Specification-2.x.
16.2. Jakarta XML Web Services Tools
The Jakarta XML Web Services tools provided by JBossWS can be used in a variety of ways.
First we will look at server-side development strategies, and then
proceed to the client.
16.2.1. Server side
When developing a Web Service Endpoint (the server-side) you have the
option of starting from Java ( bottom-up development), or from the
abstact contract (WSDL) that defines your service ( top-down
development). If this is a new service (no existing contract), the
bottom-up approach is the fastest route; you only need to add a few
annotations to your classes to get a service up and running. However, if
you are developing a service with an already defined contract, it is far
simpler to use the top-down approach, since the provided tool will
generate the annotated code for you.
Bottom-up use cases:
Replacing the implementation of an existing Web Service, and you can’t
break compatibility with older clients
Exposing a service that conforms to a contract specified by a third
party (e.g. a vender that calls you back using an already defined
protocol).
Creating a service that adheres to the XML Schema and WSDL you
developed by hand up front
wsprovide
Generates Jakarta XML Web Services portable artifacts, and provides the
abstract contract. Used for bottom-up development.
wsconsume
Consumes the abstract contract (WSDL and Schema files), and
produces artifacts for both a server and client. Used for top-down and
client development
Bottom-Up (Using wsprovide)
The bottom-up strategy involves developing the Java code for your
service, and then annotating it using Jakarta XML Web Services annotations. These
annotations can be used to customize the contract that is generated for
your service. For example, you can change the operation name to map to
anything you like. However, all of the annotations have sensible
defaults, so only the @WebService annotation is required.
This can be as simple as creating a single class:
package echo;
@javax.jws.WebService
public class Echo
public String echo(String input)
return input;
A JSE or Jakarta Enterprise Beans 3 deployment can be built using this class, and it is the
only Java code needed to deploy on JBossWS. The WSDL, and all other Java
artifacts called "wrapper classes" will be generated for you at deploy
time. This actually goes beyond the Jakarta XML Web Services specification, which requires
that wrapper classes be generated using an offline tool. The reason for
this requirement is purely a vender implementation problem, and since we
do not believe in burdening a developer with a bunch of additional
steps, we generate these as well. However, if you want your deployment
to be portable to other application servers, you will unfortunately need
to use a tool and add the generated classes to your deployment.
This is the primary purpose of the wsprovide tool, to generate
portable Jakarta XML Web Services artifacts. Additionally, it can be used to "provide" the
abstract contract (WSDL file) for your service. This can be obtained by
invoking wsprovide using the "-w" option:
$ javac -d . -classpath jboss-jaxws.jar Echo.java
$ wsprovide -w echo.Echo
Generating WSDL:
EchoService.wsdl
Writing Classes:
echo/jaxws/Echo.class
echo/jaxws/EchoResponse.class
Inspecting the WSDL reveals a service called EchoService:
<service name='EchoService'>
<port binding='tns:EchoBinding' name='EchoPort'>
<soap:address location='REPLACE_WITH_ACTUAL_URL'/>
</port>
</service>
As expected, this service defines one operation, " echo":
<portType name='Echo'>
<operation name='echo' parameterOrder='echo'>
<input message='tns:Echo_echo'/>
<output message='tns:Echo_echoResponse'/>
</operation>
</portType>
Remember that when deploying on JBossWS you do not need to run this
tool. You only need it for generating portable artifacts and/or the
abstract contract for your service.
<web-app 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"
version="2.4">
<servlet>
<servlet-name>Echo</servlet-name>
<servlet-class>echo.Echo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Echo</servlet-name>
<url-pattern>/Echo</url-pattern>
</servlet-mapping>
</web-app>
The web.xml and the single class can now be used to create a war:
$ mkdir -p WEB-INF/classes
$ cp -rp echo WEB-INF/classes/
$ cp web.xml WEB-INF
$ jar cvf echo.war WEB-INF
added manifest
adding: WEB-INF/(in = 0) (out= 0)(stored 0%)
adding: WEB-INF/classes/(in = 0) (out= 0)(stored 0%)
adding: WEB-INF/classes/echo/(in = 0) (out= 0)(stored 0%)
adding: WEB-INF/classes/echo/Echo.class(in = 340) (out= 247)(deflated 27%)
adding: WEB-INF/web.xml(in = 576) (out= 271)(deflated 52%)
The war can then be deployed to the JBoss Application Server.The war can
then be deployed to the JBoss Application Server; this will internally
invoke wsprovide, which will generate the WSDL. If deployment was
successful, and you are using the default settings, it should be
available in the server management console.
For a portable Jakarta XML Web Services deployment, the wrapper classes generated earlier
could be added to the deployment.
Top-Down (Using wsconsume)
The top-down development strategy begins with the abstract contract for
the service, which includes the WSDL file and zero or more schema files.
The wsconsume tool is then used to consume this contract, and produce
annotated Java classes (and optionally sources) that define it.
Using the WSDL file from the bottom-up example, a new Java
implementation that adheres to this service can be generated. The "-k"
option is passed to wsconsume to preserve the Java source files that
are generated, instead of providing just classes:
$ wsconsume -k EchoService.wsdl
echo/Echo.java
echo/EchoResponse.java
echo/EchoService.java
echo/Echo_Type.java
echo/ObjectFactory.java
echo/package-info.java
echo/Echo.java
echo/EchoResponse.java
echo/EchoService.java
echo/Echo_Type.java
echo/ObjectFactory.java
echo/package-info.java
The following table shows the purpose of each generated file:
Examining the Service Endpoint Interface reveals annotations that are
more explicit than in the class written by hand in the bottom-up
example, however, these evaluate to the same contract:
@WebService(name = "Echo", targetNamespace = "http://echo/")
public interface Echo {
@WebMethod
@WebResult(targetNamespace = "")
@RequestWrapper(localName = "echo", targetNamespace = "http://echo/", className = "echo.Echo_Type")
@ResponseWrapper(localName = "echoResponse", targetNamespace = "http://echo/", className = "echo.EchoResponse")
public String echo(
@WebParam(name = "arg0", targetNamespace = "")
String arg0);
The only missing piece (besides for packaging) is the implementation
class, which can now be written, using the above interface.
package echo;
@javax.jws.WebService(endpointInterface="echo.Echo")
public class EchoImpl implements Echo
public String echo(String arg0)
return arg0;
16.2.2. Client Side
Before going to detail on the client-side it is important to understand
the decoupling concept that is central to Web Services. Web Services are
not the best fit for internal RPC, even though they can be used in this
way. There are much better technologies for this (CORBA, and RMI for
example). Web Services were designed specifically for interoperable
coarse-grained correspondence. There is no expectation or guarantee that
any party participating in a Web Service interaction will be at any
particular location, running on any particular OS, or written in any
particular programming language. So because of this, it is important to
clearly separate client and server implementations. The only thing they
should have in common is the abstract contract definition. If, for
whatever reason, your software does not adhere to this principal, then
you should not be using Web Services. For the above reasons, the
recommended methodology for developing a client is to follow the
top-down approach , even if the client is running on the same server.
Let’s repeat the process of the top-down section, although using the
deployed WSDL, instead of the one generated offline by wsprovide. The
reason why we do this is just to get the right value for soap:address.
This value must be computed at deploy time, since it is based on
container configuration specifics. You could of course edit the WSDL
file yourself, although you need to ensure that the path is correct.
Offline version:
<service name='EchoService'>
<port binding='tns:EchoBinding' name='EchoPort'>
<soap:address location='REPLACE_WITH_ACTUAL_URL'/>
</port>
</service>
Online version:
<service name="EchoService">
<port binding="tns:EchoBinding" name="EchoPort">
<soap:address location="http://localhost.localdomain:8080/echo/Echo"/>
</port>
</service>
Using the online deployed version with wsconsume:
$ wsconsume -k http://localhost:8080/echo/Echo?wsdl
echo/Echo.java
echo/EchoResponse.java
echo/EchoService.java
echo/Echo_Type.java
echo/ObjectFactory.java
echo/package-info.java
echo/Echo.java
echo/EchoResponse.java
echo/EchoService.java
echo/Echo_Type.java
echo/ObjectFactory.java
echo/package-info.java
The one class that was not examined in the top-down section, was
EchoService.java. Notice how it stores the location the WSDL was
obtained from.
@WebServiceClient(name = "EchoService", targetNamespace = "http://echo/", wsdlLocation = "http://localhost:8080/echo/Echo?wsdl")
public class EchoService extends Service
private final static URL ECHOSERVICE_WSDL_LOCATION;
static {
URL url = null;
url = new URL("http://localhost:8080/echo/Echo?wsdl");
catch (MalformedURLException e)
e.printStackTrace();
ECHOSERVICE_WSDL_LOCATION = url;
public EchoService(URL wsdlLocation, QName serviceName)
super(wsdlLocation, serviceName);
public EchoService()
super(ECHOSERVICE_WSDL_LOCATION, new QName("http://echo/", "EchoService"));
@WebEndpoint(name = "EchoPort")
public Echo getEchoPort()
return (Echo)super.getPort(new QName("http://echo/", "EchoPort"), Echo.class);
As you can see, this generated class extends the main client entry point
in Jakarta XML Web Services, javax.xml.ws.Service. While you can use Service directly,
this is far simpler since it provides the configuration info for you.
The only method we really care about is the getEchoPort() method,
which returns an instance of our Service Endpoint Interface. Any WS
operation can then be called by just invoking a method on the returned
interface.
It’s not recommended to refer to a remote WSDL URL in a production
application. This causes network I/O every time you instantiate the
Service Object. Instead, use the tool on a saved local copy, or use the
URL version of the constructor to provide a new WSDL location.
EchoService service = new EchoService();
Echo echo = service.getEchoPort();
System.out.println("Server said: " + echo.echo(args0));
It is easy to change the endpoint address of your operation at runtime,
setting the ENDPOINT_ADDRESS_PROPERTY as shown below:
EchoService service = new EchoService();
Echo echo = service.getEchoPort();
/* Set NEW Endpoint Location */
String endpointURL = "http://NEW_ENDPOINT_URL";
BindingProvider bp = (BindingProvider)echo;
bp.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointURL);
System.out.println("Server said: " + echo.echo(args0));
wsconsume is a command line tool and ant task that "consumes" the
abstract contract (WSDL file) and produces portable Jakarta XML Web Services and
client artifacts.
16.3.1. Command Line Tool
The command line tool has the following usage:
usage: wsconsume [options] <wsdl-url>
options:
-h, --help Show this help message
-b, --binding=<file> One or more Jakarta XML Web Services or Jakarta XML Binding binding files
-k, --keep Keep/Generate Java source
-c --catalog=<file> Oasis XML Catalog file for entity resolution
-j --clientjar=<name> Create a jar file of the generated artifacts for calling the webservice
-p --package=<name> The target package for generated source
-w --wsdlLocation=<loc> Value to use for @WebServiceClient.wsdlLocation
-o, --output=<directory> The directory to put generated artifacts
-s, --source=<directory> The directory to put Java source
-t, --target=<2.0|2.1|2.2> The Jakarta XML Web Services specification target
-q, --quiet Be somewhat more quiet
-v, --verbose Show full exception stack traces
-l, --load-consumer Load the consumer and exit (debug utility)
-e, --extension Enable SOAP 1.2 binding extension
-a, --additionalHeaders Enables processing of implicit SOAP headers
-n, --nocompile Do not compile generated sources
The wsdlLocation is used when creating the Service to be used by clients
and will be added to the @WebServiceClient annotation, for an endpoint
implementation based on the generated service endpoint interface you
will need to manually add the wsdlLocation to the @WebService annotation
on your web service implementation and not the service endpoint
interface.
The wsconsume tools is included in the
org.jboss.ws.plugins:jaxws-tools-maven-plugin plugin. The plugin has
two goals for running the tool, wsconsume and wsconsume-test, which
basically do the same during different maven build phases (the former
triggers the sources generation during generate-sources phase, the
latter during the generate-test-sources one).
The wsconsume plugin has the following parameters:
Each classpathElement provides alibrary file to be
added to classpath
$\{project.compileClasspathElements}or$\{project.testClasspathElements}
catalog
Oasis XML Catalog file for entity resolution
targetPackage
The target Java package for generated code.
generated
bindingFiles
One or more Jakarta XML Web Services or Jakarta XML Binding binding file
wsdlLocation
Value to use for @WebServiceClient.wsdlLocation
generated
outputDirectory
The output directory for generated artifacts.
$\{project.build.outputDirectory}or$\{project.build.testOutputDirectory}
sourceDirectory
The output directory for Java source.
$\{project.build.directory}/wsconsume/java
verbose
Enables more informational output about command progress.
false
wsdls
The WSDL files or URLs to consume
extension
Enable SOAP 1.2 binding extension.
false
encoding
The charset encoding to use for generated sources.
$\{project.build.sourceEncoding}
argLine
An optional additional argline to be used when running in fork
mode;can be used to set endorse dir, enable debugging,
etc.Example<argLine>-Djava.endorsed.dirs=…</argLine>
Whether or not to run the generation task in a separate VM.
false
target
A preference for the Jakarta XML Web Services specification target
Depends on
the underlying stack and endorsed dirs if any
You can use wsconsume in your own project build simply referencing the
jaxws-tools-maven-plugin in the configured plugins in your pom.xml
file.
The following example makes the plugin consume the test.wsdl file and
generate SEI and wrappers' java sources. The generated sources are then
compiled together with the other project classes.
<build>
<plugins>
<plugin>
<groupId>org.jboss.ws.plugins</groupId>
<artifactId>jaxws-tools-maven-plugin</artifactId>
<version>1.2.0.Beta1</version>
<configuration>
<wsdls>
<wsdl>${basedir}/test.wsdl</wsdl>
</wsdls>
</configuration>
<executions>
<execution>
<goals>
<goal>wsconsume</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
You can also specify multiple wsdl files, as well as force the target
package, enable SOAP 1.2 binding and turn the tool’s verbose mode on:
<build>
<plugins>
<plugin>
<groupId>org.jboss.ws.plugins</groupId>
<artifactId>jaxws-tools-maven-plugin</artifactId>
<version>1.2.0.Beta1</version>
<configuration>
<wsdls>
<wsdl>${basedir}/test.wsdl</wsdl>
<wsdl>${basedir}/test2.wsdl</wsdl>
</wsdls>
<targetPackage>foo.bar</targetPackage>
<extension>true</extension>
<verbose>true</verbose>
</configuration>
<executions>
<execution>
<goals>
<goal>wsconsume</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Finally, if the wsconsume invocation is required for consuming a wsdl to
be used in your testsuite only, you might want to use the
wsconsume-test goal as follows:
<build>
<plugins>
<plugin>
<groupId>org.jboss.ws.plugins</groupId>
<artifactId>jaxws-tools-maven-plugin</artifactId>
<version>1.2.0.Beta1</version>
<configuration>
<wsdls>
<wsdl>${basedir}/test.wsdl</wsdl>
</wsdls>
</configuration>
<executions>
<execution>
<goals>
<goal>wsconsume-test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Plugin stack dependencyThe plugin itself does not have an explicit
dependency to a JBossWS stack, as it’s meant for being used with
implementations of any supported version of the JBossWS SPI. So the
user is expected to set a dependency in his own pom.xml to the desired
JBossWS stack version. The plugin will rely on the that for using the
proper tooling.
<dependencies>
<dependency>
<groupId>org.jboss.ws.cxf</groupId>
<artifactId>jbossws-cxf-client</artifactId>
<version>4.0.0.GA</version>
</dependency>
</dependencies>
Be careful when using this plugin with the Maven War Plugin as that
include any project dependency into the generated application war
archive. You might want to set <scope>provided</scope> for the
JBossWS stack dependency to avoid that.
The wsdlLocation is used when creating the Service to be used by clients
and will be added to the @WebServiceClient annotation, for an endpoint
implementation based on the generated service endpoint interface you
will need to manually add the wsdlLocation to the @WebService annotation
on your web service implementation and not the service endpoint
interface.
Examples
Generate Jakarta XML Web Services source and classes in a separate JVM with separate
directories, a custom wsdl location attribute, and a list of binding
files from foo.wsdl:
<wsconsume
fork="true"
verbose="true"
destdir="output"
sourcedestdir="gen-src"
keep="true"
wsdllocation="handEdited.wsdl"
wsdl="foo.wsdl">
<binding dir="binding-files" includes="*.xml" excludes="bad.xml"/>
</wsconsume>
wsprovide is a command line tool, Maven plugin and Ant task that
generates portable Jakarta XML Web Services artifacts for a service endpoint
implementation. It also has the option to "provide" the abstract
contract for offline usage.
16.4.1. Command Line Tool
The command line tool has the following usage:
usage: wsprovide [options] <endpoint class name>
options:
-h, --help Show this help message
-k, --keep Keep/Generate Java source
-w, --wsdl Enable WSDL file generation
-a, --address The generated port soap:address in wsdl
-c. --classpath=<path> The classpath that contains the endpoint
-o, --output=<directory> The directory to put generated artifacts
-r, --resource=<directory> The directory to put resource artifacts
-s, --source=<directory> The directory to put Java source
-e, --extension Enable SOAP 1.2 binding extension
-q, --quiet Be somewhat more quiet
-t, --show-traces Show full exception stack traces
Examples
Generating wrapper classes for portable artifacts in the "generated"
directory:
wsprovide -o generated foo.Endpoint
Generating wrapper classes and WSDL in the "generated" directory
wsprovide -o generated -w foo.Endpoint
Using an endpoint that references other jars
wsprovide -o generated -c application1.jar:application2.jar foo.Endpoint
The wsprovide tools is included in the
org.jboss.ws.plugins:jaxws-tools- maven- plugin plugin. The plugin
has two goals for running the tool, wsprovide and wsprovide-test,
which basically do the same during different Maven build phases (the
former triggers the sources generation during process-classes phase,
the latter during the process-test-classes one).
The wsprovide plugin has the following parameters:
testClasspathElements
Each classpathElement provides alibrary file to
be added to classpath
$\{project.compileClasspathElements}or$\{project.testClasspathElements}
outputDirectory
The output directory for generated artifacts.
$\{project.build.outputDirectory}or$\{project.build.testOutputDirectory}
resourceDirectory
The output directory for resource artifacts
(WSDL/XSD).
$\{project.build.directory}/wsprovide/resources
sourceDirectory
The output directory for Java source.
$\{project.build.directory}/wsprovide/java
extension
Enable SOAP 1.2 binding extension.
false
generateWsdl
Whether or not to generate WSDL.
false
verbose
Enables more informational output about command progress.
false
portSoapAddress
The generated port soap:address in the WSDL
endpointClass
Service Endpoint Implementation.
You can use wsprovide in your own project build simply referencing the
maven-jaxws-tools-plugin in the configured plugins in your pom.xml
file.
The following example makes the plugin provide the wsdl file and
artifact sources for the specified endpoint class:
<build>
<plugins>
<plugin>
<groupId>org.jboss.ws.plugins</groupId>
<artifactId>jaxws-tools-maven-plugin</artifactId>
<version>1.2.0.Beta1</version>
<configuration>
<verbose>true</verbose>
<endpointClass>org.jboss.test.ws.plugins.tools.wsprovide.TestEndpoint</endpointClass>
<generateWsdl>true</generateWsdl>
</configuration>
<executions>
<execution>
<goals>
<goal>wsprovide</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
The following example does the same, but is meant for use in your own
testsuite:
<build>
<plugins>
<plugin>
<groupId>org.jboss.ws.plugins</groupId>
<artifactId>jaxws-tools-maven-plugin</artifactId>
<version>1.2.0.Beta1</version>
<configuration>
<verbose>true</verbose>
<endpointClass>org.jboss.test.ws.plugins.tools.wsprovide.TestEndpoint2</endpointClass>
<generateWsdl>true</generateWsdl>
</configuration>
<executions>
<execution>
<goals>
<goal>wsprovide-test</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Plugin stack dependencyThe plugin itself does not have an explicit
dependency to a JBossWS stack, as it’s meant for being used with
implementations of any supported version of the JBossWS SPI. So the
user is expected to set a dependency in his own pom.xml to the desired
JBossWS stack version. The plugin will rely on the that for using the
proper tooling.
<dependencies>
<dependency>
<groupId>org.jboss.ws.cxf</groupId>
<artifactId>jbossws-cxf-client</artifactId>
<version>5.0.0.CR1</version>
</dependency>
</dependencies>
Be careful when using this plugin with the Maven War Plugin as that
include any project dependency into the generated application war
archive. You might want to set <scope>provided</scope> for the
JBossWS stack dependency to avoid that.
<target name="test-wsproivde" depends="init">
<taskdef name="wsprovide" classname="org.jboss.ws.tools.ant.WSProvideTask">
<classpath refid="core.classpath"/>
</taskdef>
<wsprovide
fork="false"
keep="true"
destdir="out"
resourcedestdir="out-resource"
sourcedestdir="out-source"
genwsdl="true"
verbose="true"
sei="org.jboss.test.ws.jaxws.jsr181.soapbinding.DocWrappedServiceImpl">
<classpath>
<pathelement path="${tests.output.dir}/classes"/>
</classpath>
</wsprovide>
</target>
Logging of inbound and outbound messages is a common need. Different
approaches are available for achieving that:
Jakarta XML Web Services Handler approach
A portable way of performing logging is writing a simple Jakarta XML Web Services handler
dumping the messages that are passed in it; the handler can be added to
the desired client/endpoints (programmatically / using @HandlerChain
Jakarta XML Web Services annotation).
The predefined client and endpoint configuration
mechanism allows user to add the logging handler to any client/endpoint
or to some of them only (in which case the @EndpointConfig annotation
/ JBossWS API is required though).
Apache CXF approach
Apache CXF also comes with logging interceptors that can be easily used
to log messages to the console or configured client/server log files.
Those interceptors can be added to client, endpoint and buses in
multiple ways:
System property
Setting the org.apache.cxf.logging.enabled system property to true
causes the logging interceptors to be added to any Bus instance being
created on the JVM.
On WildFly, the system property is easily set by adding what follows to
the standalone / domain server configuration just after the extensions
section:
Manual interceptor addition and logging feature
Logging interceptors can be selectively added to endpoints using the
Apache CXF annotations @org.apache.cxf.interceptor.InInterceptors and
@org.apache.cxf.interceptor.OutInterceptors. The same is achieved on
client side by programmatically adding new instances of the logging
interceptors to the client or the bus.
Alternatively, Apache CXF also comes with a
org.apache.cxf.feature.LoggingFeature that can be used on clients and
endpoints (either annotating them with
@org.apache.cxf.feature.Features or directly with
@org.apache.cxf.annotations.Logging).
Please refer to the
Apache
CXF documentation for more details.
16.5.2. WS-* support
JBossWS includes most of the WS-* specification functionalities through
the integration with Apache CXF. In particular, the whole WS-Security
Policy framework is fully supported, enabling full contract driven
configuration of complex features like WS-Security.
In details information available further down in this documentation
book.
16.5.3. Address rewrite
JBossWS allows users to configure the soap:address attribute in the
wsdl contract of deployed services.
Server configuration options
The configuration options are part of the
webservices
subsystem section of the application server domain model.
<subsystem xmlns="urn:jboss:domain:webservices:1.1" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xmlns:jaxwsconfig="urn:jboss:jbossws-jaxws-config:4.0">
<wsdl-host>localhost</wsdl-host>
<modify-wsdl-address>true</modify-wsdl-address>
<wsdl-port>8080</wsdl-port>
<wsdl-secure-port>8443</wsdl-secure-port>
</subsystem>
If the content of <soap:address> in the wsdl is a valid URL, JBossWS
will not rewrite it unless modify-wsdl-address is true. If the content
of <soap:address> is not a valid URL instead, JBossWS will always
rewrite it using the attribute values given below. Please note that the
variable $\{jboss.bind.address} can be used to set the address which
the application is bound to at each startup.
The wsdl-secure-port and wsdl-port attributes are used to explicitly
define the ports to be used for rewriting the SOAP address. If these
attributes are not set, the ports will be identified by querying the
list of installed connectors. If multiple connectors are found the port
of the first connector is used.
Dynamic rewrite
When the application server is bound to multiple addresses or
non-trivial real-world network architectures cause request for different
external addresses to hit the same endpoint, a static rewrite of the
soap:address may not be enough. JBossWS allows for both the soap:address
in the wsdl and the wsdl address in the console to be rewritten with the
host use in the client request. This way, users always get the right
wsdl address assuming they’re connecting to an instance having the
endpoint they’re looking for. To trigger this behaviour, the
jbossws.undefined.host value has to be specified for the wsdl-host
element.
<wsdl-host>jbossws.undefined.host</wsdl-host>
<modify-wsdl-address>true</modify-wsdl-address>
Of course, when a confidential transport address is required, the
addresses are always rewritten using https protocol and the port
currently configured for the https/ssl connector.
16.5.4. Configuration through deployment descriptor
The jboss-webservices.xml deployment descriptor can be used to provide
additional configuration for a given deployment. The expected location
of it is:
config-name and config-file elements
Elements <config-name> and <config-file> can be used to associate
any endpoint provided in the deployment with a given
endpoint configuration. Endpoint configuration are
either specified in the referenced config file or in the WildFly domain
model (webservices subsystem). For further details on the endpoint
configurations and their management in the domain model, please see the
related
documentation.
<webservices>
<config-name>Standard WSSecurity Endpoint</config-name>
<config-file>META-INF/custom.xml</config-file>
</webservices>
<property> elements can be used to setup simple property values to
configure the ws stack behavior. Allowed property names and values are
mentioned in the guide under related topics.
<property>
<name>prop.name</name>
<value>prop.value</value>
</property>
port-component element
Element <port-component> can be used to customize Jakarta Enterprise Beans endpoint target
URI or to configure security related properties.
<webservices>
<port-component>
<ejb-name>TestService</ejb-name>
<port-component-name>TestServicePort</port-component-name>
<port-component-uri>/*</port-component-uri>
<auth-method>BASIC</auth-method>
<transport-guarantee>NONE</transport-guarantee>
<secure-wsdl-access>true</secure-wsdl-access>
</port-component>
</webservices>
webservice-description element
Element <webservice-description> can be used to customize (override)
webservice WSDL publish location.
<webservices>
<webservice-description>
<webservice-description-name>TestService</webservice-description-name>
<wsdl-publish-location>file:///bar/foo.wsdl</wsdl-publish-location>
</webservice-description>
</webservices>
16.5.5. Schema validation of SOAP messages
Apache CXF has a feature for validating incoming and outgoing SOAP
messages on both client and server side. The validation is performed
against the relevant schema in the endpoint wsdl contract (server side)
or the wsdl contract used for building up the service proxy (client
side).
Schema validation can be turned on programmatically on client side
((BindingProvider)proxy).getRequestContext().put("schema-validation-enabled", true);
or using the @org.apache.cxf.annotations.SchemaValidation annotation
on server side
import javax.jws.WebService;
import org.apache.cxf.annotations.SchemaValidation;
@WebService(...)
@SchemaValidation
public class ValidatingHelloImpl implements Hello {
Alternatively, any endpoint and client running in-container can be
associated to a JBossWS predefined configuration
having the schema-validation-enabled property set to true in the
referenced config file.
Finally, JBossWS also allows for server-wide (default) setup of schema
validation by using the Standard-Endpoint-Config and
Standard-Client-Config special configurations (which apply to any
client / endpoint unless a different configuration is specified for
them)
<subsystem xmlns="urn:jboss:domain:webservices:1.2">
<endpoint-config name="Standard-Endpoint-Config">
<property name="schema-validation-enabled" value="true"/>
</endpoint-config>
<client-config name="Standard-Client-Config">
<property name="schema-validation-enabled" value="true"/>
</client-config>
</subsystem>
As Kohsuke Kawaguchi wrote on
blog, one common complaint from the Jakarta XML Binding users is the lack of support
for binding 3rd party classes. The scenario is this: you are trying to
annotate your classes with Jakarta XML Binding annotations to make it XML bindable, but
some of the classes are coming from libraries and JDK, and thus you
cannot put necessary Jakarta XML Binding annotations on it.
To solve this Jakarta XML Binding has been designed to provide hooks for programmatic
introduction of annotations to the runtime.
This is currently leveraged by the JBoss Jakarta XML Binding Introductions project,
using which users can define annotations in XML and make Jakarta XML Binding see those
as if those were in the class files (perhaps coming from 3rd party
libraries).
Take a look at the JAXB
Introductions page on the wiki and at the examples in the sources.
16.5.7. WSDL system properties expansion
See Published WSDL customization
16.5.8. Predefined client and endpoint configurations
JBossWS permits extra setup configuration data to be predefined and
associated with an endpoint or a client. Configurations can include
Jakarta XML Web Services handlers and key/value property declarations that control JBossWS
and Apache CXF internals. Predefined configurations can be used for
Jakarta XML Web Services client and Jakarta XML Web Services endpoint setup.
Configurations can be defined in the webservice subsystem and in an
application’s deployment descriptor file. There can be many
configuration definitions in the webservice subsystem and in an
application. Each configuration must have a name that is unique within
the server. Configurations defined in an application are local to the
application. Endpoint implementations declare the use of a specific
configuration through the use of the
org.jboss.ws.api.annotation.EndpointConfig annotation. An endpoint
configuration defined in the webservices subsystem is available to all
deployed applications on the server container and can be referenced by
name in the annotation. An endpoint configuration defined in an
application must be referenced by both deployment descriptor file name
and configuration name by the annotation.
Handlers
Each endpoint configuration may be associated with zero or more PRE and
POST handler chains. Each handler chain may include JAXWS handlers. For
outbound messages the PRE handler chains are executed before any handler
that is attached to the endpoint using the standard means, such as with
annotation @HandlerChain, and POST handler chains are executed after
those objects have executed. For inbound messages the POST handler
chains are executed before any handler that is attached to the endpoint
using the standard means and the PRE handler chains are executed after
those objects have executed.
* Server inbound messages
Client --> ... --> POST HANDLER --> ENDPOINT HANDLERS --> PRE HANDLERS --> Endpoint
* Server outbound messages
Endpoint --> PRE HANDLER --> ENDPOINT HANDLERS --> POST HANDLERS --> ... --> Client
The same applies for client configurations.
Properties
Key/value properties are used for controlling both some Apache CXF
internals and some JBossWS options. Specific supported values are
mentioned where relevant in the rest of the documentation.
Assigning configurations
Endpoints and clients are assigned configuration through different
means. Users can explicitly require a given configuration or rely on
container defaults. The assignment process can be split up as follows:
Explicit assignment through annotations (for endpoints) or API
programmatic usage (for clients)
Automatic assignment of configurations from default descriptors
Automatic assignment of configurations from container
Endpoint configuration assignment
The explicit configuration assignment is meant for developer that know
in advance their endpoint or client has to be setup according to a
specified configuration. The configuration is either coming from a
descriptor that is included in the application deployment, or is
included in the application server webservices subsystem management
model.
Endpoint Configuration Deployment Descriptor
Jakarta EE archives that can contain Jakarta XML Web Services client and endpoint
implementations can also contain predefined client and endpoint
configuration declarations. All endpoint/client configuration
definitions for a given archive must be provided in a single deployment
descriptor file, which must be an implementation of schema
jbossws-jaxws-config.
Many endpoint/client configurations can be defined in the deployment
descriptor file. Each configuration must have a name that is unique
within the server on which the application is deployed. The
configuration name can’t be referred to by endpoint/client
implementations outside the application. Here is an example of a
descriptor, containing two endpoint configurations:
<?xml version="1.0" encoding="UTF-8"?>
<jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd">
<endpoint-config>
<config-name>org.jboss.test.ws.jaxws.jbws3282.Endpoint4Impl</config-name>
<pre-handler-chains>
<javaee:handler-chain>
<javaee:handler>
<javaee:handler-name>Log Handler</javaee:handler-name>
<javaee:handler-class>org.jboss.test.ws.jaxws.jbws3282.LogHandler</javaee:handler-class>
</javaee:handler>
</javaee:handler-chain>
</pre-handler-chains>
<post-handler-chains>
<javaee:handler-chain>
<javaee:handler>
<javaee:handler-name>Routing Handler</javaee:handler-name>
<javaee:handler-class>org.jboss.test.ws.jaxws.jbws3282.RoutingHandler</javaee:handler-class>
</javaee:handler>
</javaee:handler-chain>
</post-handler-chains>
</endpoint-config>
<endpoint-config>
<config-name>EP6-config</config-name>
<post-handler-chains>
<javaee:handler-chain>
<javaee:handler>
<javaee:handler-name>Authorization Handler</javaee:handler-name>
<javaee:handler-class>org.jboss.test.ws.jaxws.jbws3282.AuthorizationHandler</javaee:handler-class>
</javaee:handler>
</javaee:handler-chain>
</post-handler-chains>
</endpoint-config>
</jaxws-config>
Similarly, client configurations can be specified in descriptors (still
implementing the schema mentioned above):
<?xml version="1.0" encoding="UTF-8"?>
<jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd">
<client-config>
<config-name>Custom Client Config</config-name>
<pre-handler-chains>
<javaee:handler-chain>
<javaee:handler>
<javaee:handler-name>Routing Handler</javaee:handler-name>
<javaee:handler-class>org.jboss.test.ws.jaxws.clientConfig.RoutingHandler</javaee:handler-class>
</javaee:handler>
<javaee:handler>
<javaee:handler-name>Custom Handler</javaee:handler-name>
<javaee:handler-class>org.jboss.test.ws.jaxws.clientConfig.CustomHandler</javaee:handler-class>
</javaee:handler>
</javaee:handler-chain>
</pre-handler-chains>
</client-config>
<client-config>
<config-name>Another Client Config</config-name>
<post-handler-chains>
<javaee:handler-chain>
<javaee:handler>
<javaee:handler-name>Routing Handler</javaee:handler-name>
<javaee:handler-class>org.jboss.test.ws.jaxws.clientConfig.RoutingHandler</javaee:handler-class>
</javaee:handler>
</javaee:handler-chain>
</post-handler-chains>
</client-config>
</jaxws-config>
Application server configurations
WildFly allows declaring JBossWS client and server predefined
configurations in the webservices subsystem section of the server
model. As a consequence it is possible to declare server-wide handlers
to be added to the chain of each endpoint or client assigned to a given
configuration.
Please refer to the
WildFly
documentation for details on managing the webservices subsystem such
as adding, removing and modifying handlers and properties.
The allowed contents in the webservices subsystem are defined by the
schema
included in the application server.
Standard configurations
Clients running in-container as well as endpoints are assigned standard
configurations by default. The defaults are used unless different
configurations are set as described on this page. This enables
administrators to tune the default handler chains for client and
endpoint configurations. The names of the default client and endpoint
configurations, used in the webservices subsystem are
Standard-Client-Config and Standard-Endpoint-Config respectively.
Handlers classloading
When setting a server-wide handler, please note the handler class needs
to be available through each ws deployment classloader. As a consequence
proper module dependencies might need to be specified in the deployments
that are going to leverage a given predefined configuration. A shortcut
is to add a dependency to the module containing the handler class in one
of the modules which are already automatically set as dependencies to
any deployment, for instance org.jboss.ws.spi.
Examples
JBoss AS 7.2 default configurations
<subsystem xmlns="urn:jboss:domain:webservices:2.0">
<!-- ... -->
<endpoint-config name="Standard-Endpoint-Config"/>
<endpoint-config name="Recording-Endpoint-Config">
<pre-handler-chain name="recording-handlers" protocol-bindings="##SOAP11_HTTP ##SOAP11_HTTP_MTOM ##SOAP12_HTTP ##SOAP12_HTTP_MTOM">
<handler name="RecordingHandler" class="org.jboss.ws.common.invocation.RecordingServerHandler"/>
</pre-handler-chain>
</endpoint-config>
<client-config name="Standard-Client-Config"/>
</subsystem>
A configuration file for a deployment specific ws-security endpoint
setup
<jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:javaee="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd">
<endpoint-config>
<config-name>Custom WS-Security Endpoint</config-name>
<property>
<property-name>ws-security.signature.properties</property-name>
<property-value>bob.properties</property-value>
</property>
<property>
<property-name>ws-security.encryption.properties</property-name>
<property-value>bob.properties</property-value>
</property>
<property>
<property-name>ws-security.signature.username</property-name>
<property-value>bob</property-value>
</property>
<property>
<property-name>ws-security.encryption.username</property-name>
<property-value>alice</property-value>
</property>
<property>
<property-name>ws-security.callback-handler</property-name>
<property-value>org.jboss.test.ws.jaxws.samples.wsse.policy.basic.KeystorePasswordCallback</property-value>
</property>
</endpoint-config>
</jaxws-config>
JBoss AS 7.2 default configurations modified to default to SOAP
messages schema-validation on
<subsystem
xmlns="urn:jboss:domain:webservices:2.0">
<!-- ... -->
<endpoint-config name="Standard-Endpoint-Config">
<property name="schema-validation-enabled" value="true"/>
</endpoint-config>
<!-- ... -->
<client-config name="Standard-Client-Config">
<property name="schema-validation-enabled" value="true"/>
</client-config>
</subsystem>
Once a configuration is available to a given application, the
org.jboss.ws.api.annotation.EndpointConfig annotation is used to
assign an endpoint configuration to a Jakarta XML Web Services endpoint implementation.
When assigning a configuration that is defined in the webservices
subsystem only the configuration name is specified. When assigning a
configuration that is defined in the application, the relative path to
the deployment descriptor and the configuration name must be specified.
@EndpointConfig(configFile = "WEB-INF/my-endpoint-config.xml", configName = "Custom WS-Security Endpoint")
public class ServiceImpl implements ServiceIface
public String sayHello()
return "Secure Hello World!";
The most practical way of setting a configuration is using
org.jboss.ws.api.configuration.ClientConfigFeature, a JAXWS Feature
extension provided by JBossWS:
import org.jboss.ws.api.configuration.ClientConfigFeature;
Service service = Service.create(wsdlURL, serviceName);
Endpoint port = service.getPort(Endpoint.class, new ClientConfigFeature("META-INF/my-client-config.xml", "Custom Client Config"));
port.echo("Kermit");
... or ....
port = service.getPort(Endpoint.class, new ClientConfigFeature("META-INF/my-client-config.xml", "Custom Client Config"), true); //setup properties too from the configuration
port.echo("Kermit");
... or ...
port = service.getPort(Endpoint.class, new ClientConfigFeature(null, testConfigName)); //reads from current container configurations if available
port.echo("Kermit");
JBossWS parses the specified configuration file. The configuration file
must be found as a resource by the classloader of the current thread.
jbossws-jaxws-config
schema defines the descriptor contents and is included in the
jbossws-spi artifact.
Explicit setup through API
Alternatively, JBossWS API comes with facility classes that can be used
for assigning configurations when building a client. JAXWS handlers read
from client configurations as follows:
import org.jboss.ws.api.configuration.ClientConfigUtil;
import org.jboss.ws.api.configuration.ClientConfigurer;
Service service = Service.create(wsdlURL, serviceName);
Endpoint port = service.getPort(Endpoint.class);
BindingProvider bp = (BindingProvider)port;
ClientConfigUtil.setConfigHandlers(bp, "META-INF/my-client-config.xml", "Custom Client Config 1");
port.echo("Kermit");
ClientConfigurer configurer = ClientConfigUtil.resolveClientConfigurer();
configurer.setConfigHandlers(bp, "META-INF/my-client-config.xml", "Custom Client Config 2");
port.echo("Kermit");
configurer.setConfigHandlers(bp, "META-INF/my-client-config.xml", "Custom Client Config 3");
port.echo("Kermit");
configurer.setConfigHandlers(bp, null, "Container Custom Client Config"); //reads from current container configurations if available
port.echo("Kermit");
import org.jboss.ws.api.configuration.ClientConfigUtil;
import org.jboss.ws.api.configuration.ClientConfigurer;
Service service = Service.create(wsdlURL, serviceName);
Endpoint port = service.getPort(Endpoint.class);
ClientConfigUtil.setConfigProperties(port, "META-INF/my-client-config.xml", "Custom Client Config 1");
port.echo("Kermit");
ClientConfigurer configurer = ClientConfigUtil.resolveClientConfigurer();
configurer.setConfigProperties(port, "META-INF/my-client-config.xml", "Custom Client Config 2");
port.echo("Kermit");
configurer.setConfigProperties(port, "META-INF/my-client-config.xml", "Custom Client Config 3");
port.echo("Kermit");
configurer.setConfigProperties(port, null, "Container Custom Client Config"); //reads from current container configurations if available
port.echo("Kermit");
configurer.setConfigProperties(port, null, null); //reads from current Elytron client configuration if available
port.echo("Kermit");
The default ClientConfigurer implementation parses the specified
configuration file, if any, after having resolved it as a resources
using the current thread context classloader. The
jbossws-jaxws-config
schema defines the descriptor contents and is included in the
jbossws-spi artifact.
If WildFly Elytron client configuration is present, the client will automatically use SSL context and credentials (for HTTP Basic authentication or Username Token Profile) from this configuration if these were not already configured.
Automatic configuration from default descriptors
In some cases, the application developer might not be aware of the
configuration that will need to be used for its client and endpoint
implementation, perhaps because that’s a concern of the application
deployer. In other cases, explicit usage (compile time dependency) of
JBossWS API might not be accepted. To cope with such scenarios, JBossWS
allows including default client ( jaxws-client-config.xml) and
endpoint ( jaxws-endpoint-config.xml) descriptor within the
application (in its root), which are parsed for getting configurations
any time a configuration file name is not specified.
If the configuration name is also not specified, JBossWS automatically
looks for a configuration named the same as
the endpoint implementation class (full qualified name), in case of
Jakarta XML Web Services endpoints;
the service endpoint interface (full qualified name), in case of
Jakarta XML Web Services clients.
So, for instance, an endpoint implementation class
org.foo.bar.EndpointImpl for which no pre-defined configuration is
explicitly set will cause JBossWS to look for a
org.foo.bar.EndpointImpl named configuration within a
jaxws-endpoint-config.xml descriptor in the root of the application
deployment. Similarly, on client side, a client proxy implementing
org.foo.bar.Endpoint interface (SEI) will have the setup read from a
org.foo.bar.Endpoint named configuration in jaxws-client-config.xml
descriptor.
Automatic configuration assignment from container setup
JBossWS fall-backs to getting predefined configurations from the
container setup whenever no explicit configuration has been provided and
the default descriptors are either not available or do not contain
relevant configurations. This gives additional control on the Jakarta XML Web Services
client and endpoint setup to administrators, as the container setup can
be managed independently from the deployed applications.
JBossWS hence accesses the webservices subsystem the same as explained
above for explicitly named configuration; the default configuration
names used for look are
the endpoint implementation class (full qualified name), in case of
Jakarta XML Web Services endpoints;
the service endpoint interface (full qualified name), in case of
Jakarta XML Web Services clients.
Dispatch clients are not automatically configured. If no configuration
is found using names computed as above, the Standard-Client-Config and
Standard-Endpoint-Config configurations are used for clients and
endpoints respectively
First we secure the access to the SLSB as we would do for normal (non
web service) invocations: this can be easily done through the
@RolesAllowed, @PermitAll, @DenyAll annotation. The allowed user roles
can be set with these annotations both on the bean class and on any of
its business methods.
@Stateless
@RolesAllowed("friend")
public class EndpointEJB implements EndpointInterface
Similarly POJO endpoints are secured the same way as we do for normal
web applications in web.xml:
<security-constraint>
<web-resource-collection>
<web-resource-name>All resources</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>friend</role-name>
</auth-constraint>
</security-constraint>
<security-role>
<role-name>friend</role-name>
</security-role>
Specify the security domain
Next, specify the security domain for this deployment. This is performed
using the @SecurityDomain annotation for EJB3 endpoints
@Stateless
@SecurityDomain("JBossWS")
@RolesAllowed("friend")
public class EndpointEJB implements EndpointInterface
or modifying the jboss-web.xml for POJO endpoints
<jboss-web>
<security-domain>JBossWS</security-domain>
</jboss-web>
The security domain as well as its the authentication and authorization
mechanisms are defined differently depending on the application server
version in use.
Use BindingProvider to set principal/credential
A web service client may use the javax.xml.ws.BindingProvider
interface to set the username/password combination
URL wsdlURL = new File("resources/jaxws/samples/context/WEB-INF/wsdl/TestEndpoint.wsdl").toURL();
QName qname = new QName("http://org.jboss.ws/jaxws/context", "TestEndpointService");
Service service = Service.create(wsdlURL, qname);
port = (TestEndpoint)service.getPort(TestEndpoint.class);
BindingProvider bp = (BindingProvider)port;
bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "
kermit");
bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "thefrog");
Using HTTP Basic Auth for security
To enable HTTP Basic authentication you use the @WebContext annotation
on the bean class
@Stateless
@SecurityDomain("JBossWS")
@RolesAllowed("friend")
@WebContext(contextRoot="/my-cxt", urlPattern="/*", authMethod="BASIC", transportGuarantee="NONE", secureWSDLAccess=false)
public class EndpointEJB implements EndpointInterface
For POJO endpoints, we modify the web.xml adding the auth-method
element:
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>Test Realm</realm-name>
</login-config>
JBossWS integration layer with Apache CXF
All Jakarta XML Web Services functionalities provided by JBossWS on top of WildFly are
currently served through a proper integration of the JBoss Web Services
stack with most of the Apache CXF project
modules.
Apache CXF is an open source services framework. It allows building and
developing services using frontend programming APIs (including Jakarta XML Web Services),
with services speaking a variety of protocols such as SOAP and XML/HTTP
over a variety of transports such as HTTP and Jakarta Messaging.
The integration layer ( JBossWS-CXF in short hereafter) is mainly
meant for:
allowing using standard webservices APIs (including Jakarta XML Web Services) on
WildFly; this is performed internally leveraging Apache CXF without
requiring the user to deal with it;
allowing using Apache CXF advanced features (including WS-*) on top of
WildFly without requiring the user to deal with / setup / care about the
required integration steps for running in such a container.
In order for achieving the goals above, the JBossWS-CXF integration
supports the JBoss ws endpoint deployment mechanism and comes with many
internal customizations on top of Apache CXF.
In the next sections a list of technical suggestions and notes on the
integration is provided; please also refer to the
Apache CXF official documentation
for in-depth details on the CXF architecture.
Building WS applications the JBoss way
The Apache CXF client and endpoint configuration as explained in the
Apache CXF official user guide is
heavily based on Spring. Apache CXF basically parses Spring cxf.xml
descriptors; those may contain any basic bean plus specific ws client
and endpoint beans which CXF has custom parsers for. Apache CXF can be
used to deploy webservice endpoints on any servlet container by
including its libraries in the deployment; in such a scenario Spring
basically serves as a convenient configuration option, given direct
Apache CXF API usage won’t be very handy. Similar reasoning applies on
client side, where a Spring based descriptor offers a shortcut for
setting up Apache CXF internals.
This said, nowadays almost any Apache CXF functionality can be
configured and used through direct API usage, without Spring. As a
consequence of that and given the considerations in the sections below,
the JBossWS integration with Apache CXF does not rely on Spring
descriptors.
Portable applications
WildFly is much more then a servlet container; it actually provides
users with a fully compliant target platform for Jakarta EE applications.
Generally speaking, users are encouraged to write portable
applications by relying only on Jakarta XML Web Services specification whenever
possible. That would by the way ensure easy migrations to and from other
compliant platforms. Being a Jakarta EE container, WildFly already comes
with a Jakarta XML Web Services compliant implementation, which is basically Apache CXF
plus the JBossWS-CXF integration layer. So users just need to write
their Jakarta XML Web Services application; no need for embedding any Apache CXF or any
ws related dependency library in user deployments. Please refer to the
Jakarta XML Web Services User Guide section of the documentation for
getting started.
WS-* usage (including WS-Security, WS-Addressing, WS-ReliableMessaging,
…) should also be configured in the most portable way; that is by
relying on proper WS-Policy assertions on the endpoint WSDL contracts,
so that client and endpoint configuration is basically a matter of
setting few ws context properties. The WS-* related sections of this
documentation cover all the details on configuring applications making
use of WS-* through policies.
As a consequence of the reasoning above, the JBossWS-CXF integration is
currently built directly on the Apache CXF API and aims at allowing
users to configure webservice clients and endpoints without Spring
descriptors.
Direct Apache CXF API usage
Whenever users can’t really meet their application requirements with
Jakarta XML Web Services plus WS-Policy, it is of course still possible to rely on direct
Apache CXF API usage (given that’s included in the AS), loosing the Java
EE portability of the application. That could be the case of a user
needing specific Apache CXF functionalities, or having to consume WS-*
enabled endpoints advertised through legacy wsdl contracts without
WS-Policy assertions.
On server side, direct Apache CXF API usage might not be always possible
or end up being not very easy. For this reason, the JBossWS integration
comes with a convenient alternative through customization options in the
jboss-webservices.xml descriptor described below on this page.
Properties can be declared in jboss-webservices.xml to control Apache
CXF internals like interceptors, features, etc.
Most of the Apache CXF features are configurable using the
org.apache.cxf.Bus class. While for basic Jakarta XML Web Services usage the user might
never need to explicitly deal with Bus, using Apache CXF specific
features generally requires getting a handle to a org.apache.cxf.Bus
instance. This can happen on client side as well as in a ws endpoint or
handler business code.
New Bus instances are produced by the currently configured
org.apache.cxf.BusFactory implementation the following way:
Bus bus = BusFactory.newInstance().createBus();
The algorithm for selecting the actual implementation of BusFactory to
be used leverages the Service API, basically looking for optional
configurations in META-INF/services/… location using the current
thread context classloader. JBossWS-CXF integration comes with its own
implementation of BusFactory,
org.jboss.wsf.stack.cxf.client.configuration.JBossWSBusFactory, that
allows for seamless setup of JBossWS customizations on top of Apache
CXF. So, assuming the JBossWS-CXF libraries are available in the current
thread context classloader, the JBossWSBusFactory is automatically
retrieved by the BusFactory.newInstance() call above.
JBossWS users willing to explicitly use functionalities of
org.apache.cxf.bus.CXFBusFactory , get the same API with JBossWS
additions through JBossWSBusFactory:
Map<Class, Object> myExtensions = new HashMap<Class, Object>();
myExtensions.put(...);
Bus bus = new JBossWSBusFactory().createBus(myExtensions);
Using existing Bus instances
Apache CXF keeps reference to a global default Bus instance as well as
to a thread default bus for each thread. That is performed through
static members in org.apache.cxf.BusFactory , which also comes with
the following methods in the public API:
public static synchronized Bus getDefaultBus()
public static synchronized Bus getDefaultBus(boolean createIfNeeded)
public static synchronized void setDefaultBus(Bus bus)
public static Bus getThreadDefaultBus()
public static Bus getThreadDefaultBus(boolean createIfNeeded)
public static void setThreadDefaultBus(Bus bus)
Please note that the default behaviour of getDefaultBus() /
getDefaultBus(true) / getThreadDefaultBus() /
getThreadDefaultBus(true) is to create a new Bus instance if that’s
not set yet. Moreover getThreadDefaultBus() and
getThreadDefaultBus(true) first fallback to retrieving the configured
global default bus before actually trying creating a new instance (and
the created new instance is set as global default bus if that was not
set there yet).
The drawback of this mechanism (which is basically fine in JSE
environment) is that when running in WildFly container you need to be
careful in order not to (mis)use a bus over multiple applications
(assuming the Apache CXF classes are loaded by the same classloader,
which is currently the case with WildFly).
Here is a list of general suggestions to avoid problems when running
in-container:
forget about the global default bus; you don’t need that, so don’t do
getDefaultBus() / getDefaultBus(true) / setDefaultBus() in
your code;
avoid getThreadDefaultBus() / getThreadDefaultBus(true) unless
you already know for sure the default bus is already set;
keep in mind thread pooling whenever you customize a thread default
bus instance (for instance adding bus scope interceptors, …), as that
thread and bus might be later reused; so either shutdown the bus when
you’re done or explicitly remove it from the BusFactory thread
association.
Finally, remember that each time you explictly create a new Bus instance
(factory.createBus()) that is set as thread default bus and global
default bus if those are not set yet. The JAXWS Provider
implementation also creates Bus instances internally, in particular
the JBossWS version of JAXWS Provider makes sure the default bus is
never internally used and instead a new Bus is created if required
(more details on this in the next paragraph).
Bus selection strategies for JAXWS clients
JAXWS clients require an Apache CXF Bus to be available; the client is
registered within the Bus and the Bus affects the client behavior (e.g.
through the configured CXF interceptors). The way a bus is internally
selected for serving a given JAXWS client is very important, especially
for in-container clients; for this reason, JBossWS users can choose the
preferred Bus selection strategy. The strategy is enforced in the
javax.xml.ws.spi.Provider implementation from the JBossWS integration,
being that called whenever a JAXWS Service (client) is requested.
Thread bus strategy (THREAD_BUS)
Each time the vanilla JAXWS api is used to create a Bus, the JBossWS-CXF
integration will automatically make sure a Bus is currently associated
to the current thread in the BusFactory. If that’s not the case, a new
Bus is created and linked to the current thread (to prevent the user
from relying on the default Bus). The Apache CXF engine will then create
the client using the current thread Bus.
This is the default strategy, and the most straightforward one in Java
SE environments; it lets users automatically reuse a previously created
Bus instance and allows using customized Bus that can possibly be
created and associated to the thread before building up a JAXWS client.
The drawback of the strategy is that the link between the Bus instance
and the thread needs to be eventually cleaned up (when not needed
anymore). This is really evident in a Jakarta EE environment (hence when
running in-container), as threads from pools (e.g. serving web requests)
are re-used.
When relying on this strategy, the safest approach to be sure of
cleaning up the link is to surround the JAXWS client with a
try/finally block as below:
try {
Service service = Service.create(wsdlURL, serviceQName);
MyEndpoint port = service.getPort(MyEndpoint.class);
//...
} finally {
BusFactory.setThreadDefaultBus(null);
// OR (if you don't need the bus and the client anymore)
Bus bus = BusFactory.getThreadDefaultBus(false);
bus.shutdown(true);
New bus strategy (NEW_BUS)
Another strategy is to have the JAXWS Provider from the JBossWS
integration create a new Bus each time a JAXWS client is built. The main
benefit of this approach is that a fresh bus won’t rely on any formerly
cached information (e.g. cached WSDL / schemas) which might have changed
after the previous client creation. The main drawback is of course worse
performance as the Bus creation takes time.
If there’s a bus already associated to the current thread before the
JAXWS client creation, that is automatically restored when returning
control to the user; in other words, the newly created bus will be used
only for the created JAXWS client but won’t stay associated to the
current thread at the end of the process. Similarly, if the thread was
not associated to any bus before the client creation, no bus will be
associated to the thread at the end of the client creation.
Thread context classloader bus strategy (TCCL_BUS)
The last strategy is to have the bus created for serving the client be
associated to the current thread context classloader (TCCL). That
basically means the same Bus instance is shared by JAXWS clients running
when the same TCCL is set. This is particularly interesting as each web
application deployment usually has its own context classloader, so this
strategy is possibly a way to keep the number of created Bus instances
bound to the application number in WildFly container.
If there’s a bus already associated to the current thread before the
JAXWS client creation, that is automatically restored when returning
control to the user; in other words, the bus corresponding to the
current thread context classloader will be used only for the created
JAXWS client but won’t stay associated to the current thread at the end
of the process. If the thread was not associated to any bus before the
client creation, a new bus will be created (and later user for any other
client built with this strategy and the same TCCL in place); no bus will
be associated to the thread at the end of the client creation.
Strategy configuration
Users can request a given Bus selection strategy to be used for the
client being built by specifying one of the following JBossWS features
(which extend javax . xml . ws . WebServiceFeature):
If no feature is explicitly specified, the system default strategy is
used, which can be modified through the
org.jboss.ws.cxf.jaxws-client.bus.strategy system property when
starting the JVM. The valid values for the property are THREAD_BUS,
NEW_BUS and TCCL_BUS. The default is THREAD_BUS.
Server Side Integration Customization
The JBossWS-CXF server side integration takes care of internally
creating proper Apache CXF structures (including a Bus instance, of
course) for the provided ws deployment. Should the deployment include
multiple endpoints, those would all live within the same Apache CXF Bus,
which would of course be completely separated by the other deployments'
bus instances.
While JBossWS sets sensible defaults for most of the Apache CXF
configuration options on server side, users might want to fine tune the
Bus instance that’s created for their deployment; a
jboss-webservices.xml descriptor can be used for deployment level
customizations.
Deployment descriptor properties
The jboss-webservices.xml descriptor can be used to
provide property values.
<webservices xmlns="http://www.jboss.com/xml/ns/javaee" version="1.2">
<property>
<name>...</name>
<value>...</value>
</property>
</webservices>
JBossWS-CXF integration comes with a set of allowed property names to
control Apache CXF internals.
WorkQueue configuration
Apache CXF uses WorkQueue instances for dealing with some operations
(e.g. @Oneway requests processing). A
WorkQueueManager
is installed in the Bus as an extension and allows for adding / removing
queues as well as controlling the existing ones.
On server side, queues can be provided by using the
cxf.queue.<queue-name>.* properties in jboss-webservices.xml (e.g.
cxf.queue.default.maxQueueSize for controlling the max queue size of
the default workqueue). At deployment time, the JBossWS integration
can add new instances of
AutomaticWorkQueueImpl
to the currently configured WorkQueueManager; the properties below are
used to fill in parameter into the
AutomaticWorkQueueImpl
constructor:
Policy alternative selector
The Apache CXF policy engine supports different strategies to deal with
policy alternatives. JBossWS-CXF integration currently defaults to the
MaximalAlternativeSelector,
but still allows for setting different selector implementation using the
cxf.policy.alternativeSelector property in jboss-webservices.xml.
MBean management
Apache CXF allows managing its MBean objects that are installed into the
WildFly MBean server. The feature is enabled on a deployment basis
through the cxf.management.enabled property in
jboss-webservices.xml. The
cxf.management.installResponseTimeInterceptors property can also be
used to control installation of CXF response time interceptors, which
are added by default when enabling MBean management, but might not be
desired in some cases. Here is an example:
<webservices xmlns="http://www.jboss.com/xml/ns/javaee" version="1.2">
<property>
<name>cxf.management.enabled</name>
<value>true</value>
</property>
<property>
<name>cxf.management.installResponseTimeInterceptors</name>
<value>false</value>
</property>
</webservices>
Schema validation of exchanged messages can also be enabled in
jboss-webservices.xml. Further details available
here.
Interceptors
The jboss-webservices.xml descriptor also allows specifying the
cxf.interceptors.in and cxf.interceptors.out properties; those
allows declaring interceptors to be attached to the Bus instance that’s
created for serving the deployment.
<?xml version="1.1" encoding="UTF-8"?>
<webservices
xmlns="http://www.jboss.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="1.2"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee">
<property>
<name>cxf.interceptors.in</name>
<value>org.jboss.test.ws.jaxws.cxf.interceptors.BusInterceptor</value>
</property>
<property>
<name>cxf.interceptors.out</name>
<value>org.jboss.test.ws.jaxws.cxf.interceptors.BusCounterInterceptor</value>
</property>
</webservices>
The jboss-webservices.xml descriptor also allows specifying the
cxf.features property; that allows declaring features to be attached
to any endpoint belonging to the Bus instance that’s created for serving
the deployment.
<?xml version="1.1" encoding="UTF-8"?>
<webservices
xmlns="http://www.jboss.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="1.2"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee">
<property>
<name>cxf.features</name>
<value>org.apache.cxf.feature.FastInfosetFeature</value>
</property>
</webservices>
WS-Discovery enablement
WS-Discovery support can be turned on in jboss-webservices for the
current deployment. Further details available here.
Apache CXF interceptors
Apache CXF supports declaring interceptors using one of the following
approaches:
Annotation usage on endpoint classes (
@org.apache.cxf.interceptor.InInterceptor,
@org.apache.cxf.interceptor.OutInterceptor)
Direct API usage on client side (through the
org.apache.cxf.interceptor.InterceptorProvider interface)
Spring descriptor usage ( cxf.xml)
As the Spring descriptor usage is not supported, the JBossWS integration
adds an additional descriptor based approach to avoid requiring
modifications to the actual client/endpoint code. Users can declare
interceptors within predefined client and endpoint
configurations by specifying a list of interceptor class names for the
cxf.interceptors.in and cxf.interceptors.out properties.
<?xml version="1.0" encoding="UTF-8"?>
<jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd">
<endpoint-config>
<config-name>org.jboss.test.ws.jaxws.cxf.interceptors.EndpointImpl</config-name>
<property>
<property-name>cxf.interceptors.in</property-name>
<property-value>org.jboss.test.ws.jaxws.cxf.interceptors.EndpointInterceptor,org.jboss.test.ws.jaxws.cxf.interceptors.FooInterceptor</property-value>
</property>
<property>
<property-name>cxf.interceptors.out</property-name>
<property-value>org.jboss.test.ws.jaxws.cxf.interceptors.EndpointCounterInterceptor</property-value>
</property>
</endpoint-config>
</jaxws-config>
A new instance of each specified interceptor class will be added to the
client or endpoint the configuration is assigned to. The interceptor
classes must have a no-argument constructor.
Apache CXF features
Apache CXF supports declaring features using one of the following
approaches:
Direct API usage on client side (through extensions of the
org.apache.cxf.feature.AbstractFeature class)
Spring descriptor usage ( cxf.xml)
As the Spring descriptor usage is not supported, the JBossWS integration
adds an additional descriptor based approach to avoid requiring
modifications to the actual client/endpoint code. Users can declare
features within predefined client and endpoint
configurations by specifying a list of feature class names for the
cxf.features property.
<?xml version="1.0" encoding="UTF-8"?>
<jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd">
<endpoint-config>
<config-name>Custom FI Config</config-name>
<property>
<property-name>cxf.features</property-name>
<property-value>org.apache.cxf.feature.FastInfosetFeature</property-value>
</property>
</endpoint-config>
</jaxws-config>
A new instance of each specified feature class will be added to the
client or endpoint the configuration is assigned to. The feature classes
must have a no-argument constructor.
Properties driven bean creation
Sections above explain how to declare CXF interceptors and features
through properties either in a client/endpoint predefined configuration
or in a jboss-webservices.xml descriptor. By getting the
feature/interceptor class name only specified, the container simply
tries to create a bean instance using the class default constructor.
This sets a limitation on the feature/interceptor configuration, unless
custom extensions of vanilla CXF classes are provided, with the default
constructor setting properties before eventually using the super
constructor.
To cope with this issue, JBossWS integration comes with a mechanism for
configuring simple bean hierarchies when building them up from
properties. Properties can have bean reference values, that is strings
starting with ##. Property reference keys are used to specify the bean
class name and the value for for each attribute. So for instance the
following properties:
The mechanism assumes that the classes are valid beans with proper
getter and setter methods; value objects are cast to the correct
primitive type by inspecting the class definition. Nested beans can of
course be configured.
HTTPConduit configuration
HTTP transport setup in Apache CXF is achieved through
org.apache.cxf.transport.http.HTTPConduit
configurations.
When running on top of the JBossWS integration, conduits can be
programmatically modified using the Apache CXF API as follows:
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
//set chunking threshold before using a Jakarta XML Web Services port client
HTTPConduit conduit = (HTTPConduit)ClientProxy.getClient(port).getConduit();
HTTPClientPolicy client = conduit.getClient();
client.setChunkingThreshold(8192);
Users can also control the default values for the most common
HTTPConduit parameters by setting specific system properties; the
provided values will override Apache CXF defaut values.
cxf.client.allowChunking
A boolean to tell Apache CXF whether to allow
send messages using chunking.
cxf.client.chunkingThreshold
An integer value to tell Apache CXF the
threshold at which switching from non-chunking to chunking mode.
cxf.client.connectionTimeout
A long value to tell Apache CXF how many
milliseconds to set the connection timeout to
cxf.client.receiveTimeout
A long value to tell Apache CXF how many
milliseconds to set the receive timeout to
cxf.client.connection
A string to tell Apache CXF to use Keep-Alive or
close connection type
cxf.tls-client.disableCNCheck
A boolean to tell Apache CXF whether
disabling CN host name check or not
16.5.11. WS-Addressing
JBoss Web Services inherits full WS-Addressing capabilities from the
underlying Apache CXF implementation. Apache CXF provides support for
2004-08 and 1.0 versions of
WS-Addressing.
Enabling WS-Addressing
WS-Addressing can be turned on in multiple standard ways:
[http://www.w3.org/2005/02/addressing/wsdl]UsingAddressing
[http://schemas.xmlsoap.org/ws/2004/08/addressing/policy]UsingAddressing
[http://www.w3.org/2006/05/addressing/wsdl]UsingAddressing
[http://www.w3.org/2007/05/addressing/metadata]Addressing
Alternatively, Apache CXF proprietary ways are also available:
specifying the [http://cxf.apache.org/ws/addressing]addressing
feature for a given client/endpoint
using the org.apache.cxf.ws.addressing.WSAddressingFeature feature
through the API
manually configuring the Apache CXF addressing interceptors (
org.apache.cxf.ws.addressing.MAPAggregator and
org.apache.cxf.ws.addressing.soap.MAPCodec)
setting the org.apache.cxf.ws.addressing.using property in the
message context
Please refer to the the Apache CXF documentation for further information
on the proprietary
WS-Addressing setup and
configuration details.
WS-Addressing Policy
The WS-Addressing support is also perfectly integrated with the Apache
CXF WS-Policy engine.
This basically means that the WSDL contract generation for code-first
endpoint deployment is policy-aware: users can annotate endpoints with
the @ javax.xml.ws.soap. Addressing annotation and expect the
published WSDL contract to contain proper WS-Addressing policy (assuming
no wsdlLocation is specified in the endpoint’s @WebService
annotation).
Similarly, on client side users do not need to manually specify the
javax.xml.ws.soap.AddressingFeature feature, as the policy engine is
able to properly process the WS-Addressing policy in the consumed WSDL
and turn on addressing as requested.
Example
Here is an example showing how to simply enable WS-Addressing through
WS-Policy.
Endpoint
A simple Jakarta XML Web Services endpoint is prepared using a java-first approach;
WS-Addressing is enforced through @Addressing annotation and no
wsdlLocation is provided in @WebService:
package org.jboss.test.ws.jaxws.samples.wsa;
import javax.jws.WebService;
import javax.xml.ws.soap.Addressing;
import org.jboss.logging.Logger;
@WebService
portName = "AddressingServicePort",
serviceName = "AddressingService",
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/wsaddressing",
endpointInterface = "org.jboss.test.ws.jaxws.samples.wsa.ServiceIface"
@Addressing(enabled=true, required=true)
public class ServiceImpl implements ServiceIface
private Logger log = Logger.getLogger(this.getClass());
public String sayHello(String name)
return "Hello " + name + "!";
The WSDL contract that’s generated at deploy time and published looks
like this:
<wsdl:definitions ....>
<wsdl:binding name="AddressingServiceSoapBinding" type="tns:ServiceIface">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsaw:UsingAddressing wsdl:required="true"/>
<wsp:PolicyReference URI="#AddressingServiceSoapBinding_WSAM_Addressing_Policy"/>
<wsdl:operation name="sayHello">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="sayHello">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="sayHelloResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="AddressingService">
<wsdl:port binding="tns:AddressingServiceSoapBinding" name="AddressingServicePort">
<soap:address location="http://localhost:8080/jaxws-samples-wsa"/>
</wsdl:port>
</wsdl:service>
<wsp:Policy wsu:Id="AddressingServiceSoapBinding_WSAM_Addressing_Policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsam:Addressing xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata">
<wsp:Policy/>
</wsam:Addressing>
</wsp:Policy>
</wsdl:definitions>
QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wsaddressing", "AddressingService");
URL wsdlURL = new URL("http://localhost:8080/jaxws-samples-wsa?wsdl");
Service service = Service.create(wsdlURL, serviceName);
ServiceIface proxy = (ServiceIface)service.getPort(ServiceIface.class);
proxy.sayHello("World");
WS-Security overview
WS-Security provides the means to secure your services beyond transport
level protocols such as HTTPS. Through a number of standards such as
XML-Encryption, and headers defined
in the
WS-Security
standard, it allows you to:
WS-Security makes heavy use of public and private key cryptography. It
is helpful to understand these basics to really understand how to
configure WS-Security. With public key cryptography, a user has a pair
of public and private keys. These are generated using a large prime
number and a key function.
The keys are related mathematically, but cannot be derived from one
another. With these keys we can encrypt messages. For example, if Bob
wants to send a message to Alice, he can encrypt a message using her
public key. Alice can then decrypt this message using her private key.
Only Alice can decrypt this message as she is the only one with the
private key.
Messages can also be signed. This allows you to ensure the authenticity
of the message. If Alice wants to send a message to Bob, and Bob wants
to be sure that it is from Alice, Alice can sign the message using her
private key. Bob can then verify that the message is from Alice by using
her public key.
JBoss Web Services supports many real world scenarios requiring
WS-Security functionalities. This includes signature and encryption
support through X509 certificates, authentication and authorization
through username tokens as well as all ws-security configurations
covered by WS-
SecurityPolicy
specification.
As well as for other WS-* features, the core of
WS-Security functionalities is provided through the Apache CXF engine.
On top of that the JBossWS integration adds few configuration
enhancements to simplify the setup of WS-Security enabled endpoints.
Apache CXF WS-Security implementation
Apache CXF features a top class WS-Security module supporting multiple
configurations and easily extendible.
The system is based on interceptors that delegate to
Apache WSS4J for the low level security
operations. Interceptors can be configured in different ways, either
through Spring configuration files or directly using Apache CXF client
API. Please refer to the
Apache CXF documentation if
you’re looking for more details.
Recent versions of Apache CXF, however, introduced support for
WS-Security Policy, which aims at moving most of the security
configuration into the service contract (through policies), so that
clients can easily be configured almost completely automatically from
that. This way users do not need to manually deal with configuring /
installing the required interceptors; the Apache CXF WS-Policy engine
internally takes care of that instead.
WS-Security Policy support
WS-SecurityPolicy describes the actions that are required to securely
communicate with a service advertised in a given WSDL contract. The WSDL
bindings / operations reference WS-Policy fragments with the security
requirements to interact with the service. The
WS-SecurityPolicy
specification allows for specifying things like asymmetric/symmetric
keys, using transports (https) for encryption, which parts/headers to
encrypt or sign, whether to sign then encrypt or encrypt then sign,
whether to include timestamps, whether to use derived keys, etc.
However some mandatory configuration elements are not covered by
WS-SecurityPolicy, basically because they’re not meant to be public /
part of the published endpoint contract; those include things such as
keystore locations, usernames and passwords, etc. Apache CXF allows
configuring these elements either through Spring xml descriptors or
using the client API / annotations. Below is the list of supported
configuration properties:
ws-security.password
The password used for UsernameToken policy
assertions. If not specified, the callback handler will be called.
ws-security.callback-handler
The WSS4J security CallbackHandler that
will be used to retrieve passwords for keystores and UsernameTokens.
ws-security.signature.properties
The properties file/object that
contains the WSS4J properties for configuring the signature keystore and
crypto objects
ws-security.encryption.properties
The properties file/object that
contains the WSS4J properties for configuring the encryption keystore
and crypto objects
ws-security.signature.username
The username or alias for the key in
the signature keystore that will be used. If not specified, it uses the
the default alias set in the properties file. If that’s also not set,
and the keystore only contains a single key, that key will be used.
ws-security.encryption.username
The username or alias for the key in
the encryption keystore that will be used. If not specified, it uses the
the default alias set in the properties file. If that’s also not set,
and the keystore only contains a single key, that key will be used. For
the web service provider, the useReqSigCert keyword can be used to
accept (encrypt to) any client whose public key is in the service’s
truststore (defined in ws-security.encryption.properties.)
ws-security.signature.crypto
Instead of specifying the signature
properties, this can point to the full WSS4J Crypto object. This can
allow easier "programmatic" configuration of the Crypto information."
ws-security.encryption.crypto
Instead of specifying the encryption
properties, this can point to the full WSS4J Crypto object. This can
allow easier "programmatic" configuration of the Crypto information."
ws-security.enable.streaming
Enable streaming (StAX based) processing
of WS-Security messages
Map<String, Object> ctx = ((BindingProvider)port).getRequestContext();
ctx.put("ws-security.encryption.properties", properties);
port.echoString("hello");
Please refer to the
Apache CXF
documentation for additional configuration details.
JBossWS configuration additions
In order for removing the need of Spring on server side for setting up
WS-Security configuration properties not covered by policies, the
JBossWS integration allows for getting those pieces of information from
a defined endpoint configuration. Endpoint
configurationscan include property declarations and endpoint
implementations can be associated with a given endpoint configuration
using the @EndpointConfig annotation.
<?xml version="1.0" encoding="UTF-8"?>
<jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:javaee="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd">
<endpoint-config>
<config-name>Custom WS-Security Endpoint</config-name>
<property>
<property-name>ws-security.signature.properties</property-name>
<property-value>bob.properties</property-value>
</property>
<property>
<property-name>ws-security.encryption.properties</property-name>
<property-value>bob.properties</property-value>
</property>
<property>
<property-name>ws-security.signature.username</property-name>
<property-value>bob</property-value>
</property>
<property>
<property-name>ws-security.encryption.username</property-name>
<property-value>alice</property-value>
</property>
<property>
<property-name>ws-security.callback-handler</property-name>
<property-value>org.jboss.test.ws.jaxws.samples.wsse.policy.basic.KeystorePasswordCallback</property-value>
</property>
</endpoint-config>
</jaxws-config>
portName = "SecurityServicePort",
serviceName = "SecurityService",
wsdlLocation = "WEB-INF/wsdl/SecurityService.wsdl",
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy",
endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.basic.ServiceIface"
@EndpointConfig(configFile = "WEB-INF/jaxws-endpoint-config.xml", configName = "Custom WS-Security Endpoint")
public class ServiceImpl implements ServiceIface
public String sayHello()
return "Secure Hello World!";
Apache CXF annotations
The JBossWS configuration additions allow for a descriptor approach to
the WS-Security Policy engine configuration. If you prefer to provide
the same information through an annotation approach, you can leverage
the Apache CXF @org.apache.cxf.annotations.EndpointProperties
annotation:
@WebService(
@EndpointProperties(value = {
@EndpointProperty(key = "ws-security.signature.properties", value = "bob.properties"),
@EndpointProperty(key = "ws-security.encryption.properties", value = "bob.properties"),
@EndpointProperty(key = "ws-security.signature.username", value = "bob"),
@EndpointProperty(key = "ws-security.encryption.username", value = "alice"),
@EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.basic.KeystorePasswordCallback")
public class ServiceImpl implements ServiceIface {
In this section some sample of WS-Security service endpoints and clients
are provided. Please note they’re only meant as tutorials; you should
really careful isolate the ws-security policies / assertion that best
suite your security needs before going to production environment.
The following sections provide directions and examples on understanding
some of the configuration options for WS-Security engine. Please note
the implementor remains responsible for assessing the application
requirements and choosing the most suitable security policy for them.
Endpoint
First of all you need to create the web service endpoint using Jakarta XML Web Services.
While this can generally be achieved in different ways, it’s required to
use a contract-first approach when using WS-Security, as the policies
declared in the wsdl are parsed by the Apache CXF engine on both server
and client sides. So, here is an example of WSDL contract enforcing
signature and encryption using X 509 certificates (the referenced schema
is omitted):
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions targetNamespace="http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy" name="SecurityService"
xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsaws="http://www.w3.org/2005/08/addressing"
xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<types>
<xsd:schema>
<xsd:import namespace="http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy" schemaLocation="SecurityService_schema1.xsd"/>
</xsd:schema>
</types>
<message name="sayHello">
<part name="parameters" element="tns:sayHello"/>
</message>
<message name="sayHelloResponse">
<part name="parameters" element="tns:sayHelloResponse"/>
</message>
<portType name="ServiceIface">
<operation name="sayHello">
<input message="tns:sayHello"/>
<output message="tns:sayHelloResponse"/>
</operation>
</portType>
<binding name="SecurityServicePortBinding" type="tns:ServiceIface">
<wsp:PolicyReference URI="#SecurityServiceSignThenEncryptPolicy"/>
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="sayHello">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="SecurityService">
<port name="SecurityServicePort" binding="tns:SecurityServicePortBinding">
<soap:address location="http://localhost:8080/jaxws-samples-wssePolicy-sign-encrypt"/>
</port>
</service>
<wsp:Policy wsu:Id="SecurityServiceSignThenEncryptPolicy" xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:ExactlyOne>
<wsp:All>
<sp:AsymmetricBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"
>
<wsp:Policy>
<sp:InitiatorToken>
<wsp:Policy>
<sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssX509V1Token11/>
</wsp:Policy>
</sp:X509Token>
</wsp:Policy>
</sp:InitiatorToken>
<sp:RecipientToken>
<wsp:Policy>
<sp:X509Token sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Never">
<wsp:Policy>
<sp:WssX509V1Token11/>
</wsp:Policy>
</sp:X509Token>
</wsp:Policy>
</sp:RecipientToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:TripleDesRsa15/>
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Lax/>
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp/>
<sp:EncryptSignature/>
<sp:OnlySignEntireHeadersAndBody/>
<sp:SignBeforeEncrypting/>
</wsp:Policy>
</sp:AsymmetricBinding>
<sp:SignedParts xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<sp:Body/>
</sp:SignedParts>
<sp:EncryptedParts xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<sp:Body/>
</sp:EncryptedParts>
<sp:Wss10 xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:MustSupportRefIssuerSerial/>
</wsp:Policy>
</sp:Wss10>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
</definitions>
The service endpoint can be generated using the wsconsume tool and
then enriched with a @EndpointConfig annotation:
package org.jboss.test.ws.jaxws.samples.wsse.policy.basic;
import javax.jws.WebService;
import org.jboss.ws.api.annotation.EndpointConfig;
@WebService
portName = "SecurityServicePort",
serviceName = "SecurityService",
wsdlLocation = "WEB-INF/wsdl/SecurityService.wsdl",
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy",
endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.basic.ServiceIface"
@EndpointConfig(configFile = "WEB-INF/jaxws-endpoint-config.xml", configName = "Custom WS-Security Endpoint")
public class ServiceImpl implements ServiceIface
public String sayHello()
return "Secure Hello World!";
The referenced jaxws-endpoint-config.xml descriptor is used to provide
a custom endpoint configuration with the required server side
configuration properties; this tells the engine which certificate / key
to use for signature / signature verification and for encryption /
decryption:
<?xml version="1.0" encoding="UTF-8"?>
<jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:javaee="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd">
<endpoint-config>
<config-name>Custom WS-Security Endpoint</config-name>
<property>
<property-name>ws-security.signature.properties</property-name>
<property-value>bob.properties</property-value>
</property>
<property>
<property-name>ws-security.encryption.properties</property-name>
<property-value>bob.properties</property-value>
</property>
<property>
<property-name>ws-security.signature.username</property-name>
<property-value>bob</property-value>
</property>
<property>
<property-name>ws-security.encryption.username</property-name>
<property-value>alice</property-value>
</property>
<property>
<property-name>ws-security.callback-handler</property-name>
<property-value>org.jboss.test.ws.jaxws.samples.wsse.policy.basic.KeystorePasswordCallback</property-value>
</property>
</endpoint-config>
</jaxws-config>
the bob.properties configuration file is also referenced above; it
includes the WSS4J Crypto properties which in turn link to the keystore
file, type and the alias/password to use for accessing it:
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=password
org.apache.ws.security.crypto.merlin.keystore.alias=bob
org.apache.ws.security.crypto.merlin.keystore.file=bob.jks
A callback handler for the letting Apache CXF access the keystore is
also provided:
package org.jboss.test.ws.jaxws.samples.wsse.policy.basic;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
public class KeystorePasswordCallback implements CallbackHandler {
private Map<String, String> passwords = new HashMap<String, String>();
public KeystorePasswordCallback() {
passwords.put("alice", "password");
passwords.put("bob", "password");
* It attempts to get the password from the private
* alias/passwords map.
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pc = (WSPasswordCallback)callbacks[i];
String pass = passwords.get(pc.getIdentifier());
if (pass != null) {
pc.setPassword(pass);
return;
* Add an alias/password pair to the callback mechanism.
public void setAliasPassword(String alias, String password) {
passwords.put(alias, password);
Assuming the bob.jks keystore has been properly generated and contains
Bob’s (server) full key (private/certificate + public key) as well as
Alice’s (client) public key, we can proceed to packaging the endpoint.
Here is the expected content (the endpoint is a POJO one in a war
archive, but EJB3 endpoints in jar archives are of course also
supported):
alessio@inuyasha /dati/jbossws/stack/cxf/trunk $ jar -tvf ./modules/testsuite/cxf-tests/target/test-libs/jaxws-samples-wsse-policy-sign-encrypt.war
0 Thu Jun 16 18:50:48 CEST 2011 META-INF/
140 Thu Jun 16 18:50:46 CEST 2011 META-INF/MANIFEST.MF
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/
586 Thu Jun 16 18:50:44 CEST 2011 WEB-INF/web.xml
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/basic/
1687 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/basic/KeystorePasswordCallback.class
383 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/basic/ServiceIface.class
1070 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/basic/ServiceImpl.class
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaxws/
705 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaxws/SayHello.class
1069 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaxws/SayHelloResponse.class
1225 Thu Jun 16 18:50:44 CEST 2011 WEB-INF/jaxws-endpoint-config.xml
0 Thu Jun 16 18:50:44 CEST 2011 WEB-INF/wsdl/
4086 Thu Jun 16 18:50:44 CEST 2011 WEB-INF/wsdl/SecurityService.wsdl
653 Thu Jun 16 18:50:44 CEST 2011 WEB-INF/wsdl/SecurityService_schema1.xsd
1820 Thu Jun 16 18:50:44 CEST 2011 WEB-INF/classes/bob.jks
311 Thu Jun 16 18:50:44 CEST 2011 WEB-INF/classes/bob.properties
As you can see, the jaxws classes generated by the tools are of course
also included, as well as a basic web.xml referencing the endpoint
bean:
<?xml version="1.0" encoding="UTF-8"?>
<web-app
version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>TestService</servlet-name>
<servlet-class>org.jboss.test.ws.jaxws.samples.wsse.policy.basic.ServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestService</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.1
Created-By: 17.0-b16 (Sun Microsystems Inc.)
Dependencies: org.apache.ws.security
You start by consuming the published WSDL contract using the wsconsume
tool on client side too. Then you simply invoke the the endpoint as a
standard Jakarta XML Web Services one:
QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy", "SecurityService");
URL wsdlURL = new URL(serviceURL + "?wsdl");
Service service = Service.create(wsdlURL, serviceName);
ServiceIface proxy = (ServiceIface)service.getPort(ServiceIface.class);
((BindingProvider)proxy).getRequestContext().put(SecurityConstants.CALLBACK_HANDLER, new KeystorePasswordCallback());
((BindingProvider)proxy).getRequestContext().put(SecurityConstants.SIGNATURE_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource("META-INF/alice.properties"));
((BindingProvider)proxy).getRequestContext().put(SecurityConstants.ENCRYPT_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource("META-INF/alice.properties"));
((BindingProvider)proxy).getRequestContext().put(SecurityConstants.SIGNATURE_USERNAME, "alice");
((BindingProvider)proxy).getRequestContext().put(SecurityConstants.ENCRYPT_USERNAME, "bob");
proxy.sayHello();
As you can see, the WS-Security properties are set in the request
context. Here the KeystorePasswordCallback is the same as on server
side above, you might want/need different implementation in real world
scenarios, of course.
The alice.properties file is the client side equivalent of the server
side bob.properties and references the alice.jks keystore file,
which has been populated with Alice’s (client) full key
(private/certificate + public key) as well as Bob’s (server) public key.
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=password
org.apache.ws.security.crypto.merlin.keystore.alias=alice
org.apache.ws.security.crypto.merlin.keystore.file=META-INF/alice.jks
The Apache CXF WS-Policy engine will digest the security requirements in
the contract and ensure a valid secure communication is in place for
interacting with the server endpoint.
Endpoint serving multiple clients
The server side configuration described above implies the endpoint is
configured for serving a given client which a service agreement has been
established for. In some real world scenarios though, the same server
might be expected to be able to deal with (including decrypting and
encrypting) messages coming from and being sent to multiple clients.
Apache CXF supports that through the useReqSigCert value for the
ws-security.encryption.username configuration parameter.
Of course the referenced server side keystore then needs to contain the
public key of all the clients that are expected to be served.
Authentication and authorization
The Username Token Profile can be used to provide client’s credentials
to a WS-Security enabled target endpoint.
Apache CXF provides means for setting basic password callback handlers
on both client and server sides to set/check passwords; the
ws-security.username and ws-security.callback-handler properties can
be used similarly as shown in the signature and encryption example.
Things become more interesting when requiring a given user to be
authenticated (and authorized) against a security domain on the target
application server.
On server side, you need to install two additional interceptors that act
as bridges towards the application server authentication layer:
an interceptor for performing authentication and populating a valid
SecurityContext; the provided interceptor should extend
org.apache.cxf.ws.interceptor.security.AbstractUsernameTokenInInterceptor,
in particular JBossWS integration comes with
org.jboss.wsf.stack.cxf.security.authentication.SubjectCreatingInterceptor
for this;
an interceptor for performing authorization; CXF requires that to
extend
org.apache.cxf.interceptor.security.AbstractAuthorizingInInterceptor,
for instance the SimpleAuthorizingInterceptor can be used for simply
mapping endpoint operations to allowed roles.
So, here follows an example of WS-SecurityPolicy endpoint using Username
Token Profile for authenticating through the application server security
domain system.
Endpoint
As in the other example, we start with a wsdl contract containing the
proper WS-Security Policy:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions targetNamespace="http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy" name="SecurityService"
xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsaws="http://www.w3.org/2005/08/addressing">
<types>
<xsd:schema>
<xsd:import namespace="http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy" schemaLocation="SecurityService_schema1.xsd"/>
</xsd:schema>
</types>
<message name="sayHello">
<part name="parameters" element="tns:sayHello"/>
</message>
<message name="sayHelloResponse">
<part name="parameters" element="tns:sayHelloResponse"/>
</message>
<message name="greetMe">
<part name="parameters" element="tns:greetMe"/>
</message>
<message name="greetMeResponse">
<part name="parameters" element="tns:greetMeResponse"/>
</message>
<portType name="ServiceIface">
<operation name="sayHello">
<input message="tns:sayHello"/>
<output message="tns:sayHelloResponse"/>
</operation>
<operation name="greetMe">
<input message="tns:greetMe"/>
<output message="tns:greetMeResponse"/>
</operation>
</portType>
<binding name="SecurityServicePortBinding" type="tns:ServiceIface">
<wsp:PolicyReference URI="#SecurityServiceUsernameUnsecureTransportPolicy"/>
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="sayHello">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
<operation name="greetMe">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="SecurityService">
<port name="SecurityServicePort" binding="tns:SecurityServicePortBinding">
<soap:address location="http://localhost:8080/jaxws-samples-wsse-username-jaas"/>
</port>
</service>
<wsp:Policy wsu:Id="SecurityServiceUsernameUnsecureTransportPolicy">
<wsp:ExactlyOne>
<wsp:All>
<sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
>
<wsp:Policy>
<sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10/>
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SupportingTokens>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
</definitions>
<wsp:ExactlyOne>
<wsp:All>
<sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:HashPassword/>
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SupportingTokens>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
Please note the specified JBoss security domain needs to be properly
configured for computing digests.
The service endpoint can be generated using the wsconsume tool and
then enriched with a @EndpointConfig annotation and @InInterceptors
annotation to add the two interceptors mentioned above for JAAS
integration:
package org.jboss.test.ws.jaxws.samples.wsse.policy.jaas;
import javax.jws.WebService;
import org.apache.cxf.interceptor.InInterceptors;
import org.jboss.ws.api.annotation.EndpointConfig;
@WebService
portName = "SecurityServicePort",
serviceName = "SecurityService",
wsdlLocation = "WEB-INF/wsdl/SecurityService.wsdl",
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy",
endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.jaas.ServiceIface"
@EndpointConfig(configFile = "WEB-INF/jaxws-endpoint-config.xml", configName = "Custom WS-Security Endpoint")
@InInterceptors(interceptors = {
"org.jboss.wsf.stack.cxf.security.authentication.SubjectCreatingPolicyInterceptor",
"org.jboss.test.ws.jaxws.samples.wsse.policy.jaas.POJOEndpointAuthorizationInterceptor"}
public class ServiceImpl implements ServiceIface
public String sayHello()
return "Secure Hello World!";
public String greetMe()
return "Greetings!";
The POJOEndpointAuthorizationInterceptor is included into the
deployment and deals with the roles cheks:
package org.jboss.test.ws.jaxws.samples.wsse.policy.jaas;
import java.util.HashMap;
import java.util.Map;
import org.apache.cxf.interceptor.security.SimpleAuthorizingInterceptor;
public class POJOEndpointAuthorizationInterceptor extends SimpleAuthorizingInterceptor
public POJOEndpointAuthorizationInterceptor()
super();
readRoles();
private void readRoles()
//just an example, this might read from a configuration file or such
Map<String, String> roles = new HashMap<String, String>();
roles.put("sayHello", "friend");
roles.put("greetMe", "snoopies");
setMethodRolesMap(roles);
The jaxws-endpoint-config.xml descriptor is used to provide a custom
endpoint configuration with the required server side configuration
properties; in particular for this Username Token case that’s just a CXF
configuration option for leaving the username token validation to the
configured interceptors:
<?xml version="1.0" encoding="UTF-8"?>
<jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:javaee="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd">
<endpoint-config>
<config-name>Custom WS-Security Endpoint</config-name>
<property>
<property-name>ws-security.validate.token</property-name>
<property-value>false</property-value>
</property>
</endpoint-config>
</jaxws-config>
In order for requiring a given JBoss security domain to be used to
protect access to the endpoint (a POJO one in this case), we declare
that in a jboss-web.xml descriptor (the JBossWS security domain is
used):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.4//EN" "http://www.jboss.org/j2ee/dtd/jboss-web_4_0.dtd">
<jboss-web>
<security-domain>java:/jaas/JBossWS</security-domain>
</jboss-web
Finally, the web.xml is as simple as usual:
<?xml version="1.0" encoding="UTF-8"?>
<web-app
version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>TestService</servlet-name>
<servlet-class>org.jboss.test.ws.jaxws.samples.wsse.policy.jaas.ServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestService</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
The endpoint is packaged into a war archive, including the JAXWS classes
generated by wsconsume:
alessio@inuyasha /dati/jbossws/stack/cxf/trunk $ jar -tvf ./modules/testsuite/cxf-tests/target/test-libs/jaxws-samples-wsse-policy-username-jaas.war
0 Thu Jun 16 18:50:48 CEST 2011 META-INF/
155 Thu Jun 16 18:50:46 CEST 2011 META-INF/MANIFEST.MF
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/
585 Thu Jun 16 18:50:44 CEST 2011 WEB-INF/web.xml
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaas/
982 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaas/POJOEndpointAuthorizationInterceptor.class
412 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaas/ServiceIface.class
1398 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaas/ServiceImpl.class
0 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaxws/
701 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaxws/GreetMe.class
1065 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaxws/GreetMeResponse.class
705 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaxws/SayHello.class
1069 Thu Jun 16 18:50:48 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/samples/wsse/policy/jaxws/SayHelloResponse.class
556 Thu Jun 16 18:50:44 CEST 2011 WEB-INF/jaxws-endpoint-config.xml
241 Thu Jun 16 18:50:44 CEST 2011 WEB-INF/jboss-web.xml
0 Thu Jun 16 18:50:44 CEST 2011 WEB-INF/wsdl/
3183 Thu Jun 16 18:50:44 CEST 2011 WEB-INF/wsdl/SecurityService.wsdl
1012 Thu Jun 16 18:50:44 CEST 2011 WEB-INF/wsdl/SecurityService_schema1.xsd
If you’re deploying the endpoint archive on WildFly, remember to add a
dependency to org.apache.ws.security and org.apache.cxf module (due
to the @InInterceptor annotation) in the MANIFEST.MF file.
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.7.1
Created-By: 17.0-b16 (Sun Microsystems Inc.)
Dependencies: org.apache.ws.security,org.apache.cxf
Here too you start by consuming the published WSDL contract using the
wsconsume tool. Then you simply invoke the the endpoint as a standard
Jakarta XML Web Services one:
QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy", "SecurityService");
URL wsdlURL = new URL(serviceURL + "?wsdl");
Service service = Service.create(wsdlURL, serviceName);
ServiceIface proxy = (ServiceIface)service.getPort(ServiceIface.class);
((BindingProvider)proxy).getRequestContext().put(SecurityConstants.USERNAME, "kermit");
((BindingProvider)proxy).getRequestContext().put(SecurityConstants.CALLBACK_HANDLER,
"org.jboss.test.ws.jaxws.samples.wsse.policy.jaas.UsernamePasswordCallback");
proxy.sayHello();
The UsernamePasswordCallback class is shown below and is responsible
for setting the passwords on client side just before performing the
invocations:
package org.jboss.test.ws.jaxws.samples.wsse.policy.jaas;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
public class UsernamePasswordCallback implements CallbackHandler
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
WSPasswordCallback pc = (WSPasswordCallback)callbacks[0];
if ("kermit".equals(pc.getIdentifier()))
pc.setPassword("
thefrog");
If everything has been done properly, you should expect to calls to
sayHello() fail when done with user "snoopy" and pass with user
"kermit" (and credential "thefrog"); moreover, you should get an
authorization error when trying to call greetMe() with user "kermit",
as that does not have the "snoopies" role.
Another quite common use case is using WS-Security Username Token
Profile over a secure transport (HTTPS). A scenario like this is
implemented similarly to what’s described in the previous example,
except for few differences explained below.
First of all, here is an excerpt of a wsdl wth a sample security policy
for Username Token over HTTPS:
<binding name="SecurityServicePortBinding" type="tns:ServiceIface">
<wsp:PolicyReference URI="#SecurityServiceBindingPolicy"/>
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="sayHello">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<service name="SecurityService">
<port name="SecurityServicePort" binding="tns:SecurityServicePortBinding">
<soap:address location="https://localhost:8443/jaxws-samples-wsse-policy-username"/>
</port>
</service>
<wsp:Policy wsu:Id="SecurityServiceBindingPolicy">
<wsp:ExactlyOne>
<wsp:All>
<foo:unknownPolicy xmlns:foo="http://cxf.apache.org/not/a/policy"/>
</wsp:All>
<wsp:All>
<wsaws:UsingAddressing xmlns:wsaws="http://www.w3.org/2006/05/addressing/wsdl"/>
<sp:TransportBinding>
<wsp:Policy>
<sp:TransportToken>
<wsp:Policy>
<sp:HttpsToken RequireClientCertificate="false"/>
</wsp:Policy>
</sp:TransportToken>
<sp:Layout>
<wsp:Policy>
<sp:Lax/>
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp/>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic128/>
</wsp:Policy>
</sp:AlgorithmSuite>
</wsp:Policy>
</sp:TransportBinding>
<sp:Wss10>
<wsp:Policy>
<sp:MustSupportRefKeyIdentifier/>
</wsp:Policy>
</sp:Wss10>
<sp:SignedSupportingTokens>
<wsp:Policy>
<sp:UsernameToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10/>
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
The endpoint then needs of course to be actually available on HTTPS
only, so we have a web.xml setting the transport-guarantee such as
below:
<?xml version="1.0" encoding="UTF-8"?>
<web-app
version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>TestService</servlet-name>
<servlet-class>org.jboss.test.ws.jaxws.samples.wsse.policy.basic.ServiceImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestService</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<security-constraint>
<web-resource-collection>
<web-resource-name>TestService</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
</web-app>
Apache CXF supports
WS-SecureConversation
specification, which is about improving performance by allowing client
and server to negotiate initial security keys to be used for later
communication encryption/signature. This is done by having two policies
in the wsdl contract, an outer one setting the security requirements to
actually communicate with the endpoint and a bootstrap one, related to
the communication for establishing the secure conversation keys. The
client will be automatically sending an initial message to the server
for negotiating the keys, then the actual communication to the endpoint
takes place. As a consequence, Apache CXF needs a way to specify which
WS-Security configuration properties are intended for the bootstrap
policy and which are intended for the actual service policy. To
accomplish this, properties intended for the bootstrap policy are
appended with .sct.
((BindingProvider)proxy).getRequestContext().put("ws-security.signature.username.sct", "alice");
((BindingProvider)proxy).getRequestContext().put("ws-security.encryption.username.sct", "bob");
@EndpointProperties(value = {
@EndpointProperty(key = "ws-security.encryption.properties.sct", value = "bob.properties"),
@EndpointProperty(key = "ws-security.signature.properties.sct", value = "bob.properties"),
public class ServiceImpl implements ServiceIface {
WS-Trust is a Web
service specification that defines extensions to WS-Security. It is a
general framework for implementing security in a distributed system. The
standard is based on a centralized Security Token Service, STS, which is
capable of authenticating clients and issuing tokens containing various
kinds of authentication and authorization data. The specification
describes a protocol used for issuance, exchange, and validation of
security tokens, however the following specifications play an important
role in the WS-Trust architecture:
WS-SecurityPolicy
1.2,
2.0,
Username
Token Profile,
X.509
Token Profile,
Token Profile, and
Kerberos
Token Profile.
The WS-Trust extensions address the needs of applications that span
multiple domains and requires the sharing of security keys by providing
a standards based trusted third party web service (STS) to broker trust
relationships between a Web service requester and a Web service
provider. This architecture also alleviates the pain of service updates
that require credential changes by providing a common location for this
information. The STS is the common access point from which both the
requester and provider retrieves and verifies security tokens.
There are three main components of the WS-Trust specification.
The Security Token Service (STS), a web service that issues, renews,
and validates security tokens.
The message formats for security token requests and responses.
The mechanisms for key exchange
The Security Token Service, STS, is the core of the WS-Trust
specification. It is a standards based mechanism for authentication and
authorization. The STS is an implementation of the WS-Trust
specification’s protocol for issuing, exchanging, and validating
security tokens, based on token format, namespace, or trust boundaries.
The STS is a web service that acts as a trusted third party to broker
trust relationships between a Web service requester and a Web service
provider. It is a common access point trusted by both requester and
provider to provide interoperable security tokens. It removes the need
for a direct relationship between the two. Because the STS is a
standards based mechanism for authentication, it helps ensure
interoperability across realms and between different platforms.
The STS’s WSDL contract defines how other applications and processes
interact with it. In particular the WSDL defines the WS-Trust and
WS-Security policies that a requester must fulfill in order to
successfully communicate with the STS’s endpoints. A web service
requester consumes the STS’s WSDL and with the aid of an STSClient
utility, generates a message request compliant with the stated security
policies and submits it to the STS endpoint. The STS validates the
request and returns an appropriate response.
Apache CXF support
Apache CXF is an open-source, fully featured Web services framework. The
JBossWS open source project integrates the JBoss Web Services (JBossWS)
stack with the Apache CXF project modules thus providing WS-Trust and
other Jakarta XML Web Services functionality in WildFly. This integration makes it easy to
deploy CXF STS implementations, however WildFly can run any WS-Trust
compliant STS. In addition the Apache CXF API provides a STSClient
utility to facilitate web service requester communication with its STS.
Detailed information about the Apache CXF’s WS-Trust implementation can
be found
here.
A Basic WS-Trust Scenario
Here is an example of a basic WS-Trust scenario. It is comprised of a
Web service requester (ws-requester), a Web service provider
(ws-provider), and a Security Token Service (STS). The ws-provider
requires a SAML 2.0 token issued from a designed STS to be presented by
the ws-requester using asymmetric binding. These communication
requirements are declared in the ws-provider’s WSDL. The STS requires
ws-requester credentials be provided in a WSS UsernameToken format
request using symmetric binding. The STS’s response is provided
containing a SAML 2.0 token. These communication requirements are
declared in the STS’s WSDL.
A ws-requester contacts the ws-provider and consumes its WSDL. Upon
finding the security token issuer requirement, it creates and configures
a STSClient with the information it requires to generate a proper
request.
The STSClient contacts the STS and consumes its WSDL. The security
policies are discovered. The STSClient creates and sends an
authentication request, with appropriate credentials.
The STS verifies the credentials.
In response, the STS issues a security token that provides proof
that the ws-requester has authenticated with the STS.
The STClient presents a message with the security token to the
ws-provider.
The ws-provider verifies the token was issued by the STS, thus
proving the ws-requester has successfully authenticated with the STS.
The ws-provider executes the requested service and returns the
results to the the ws-requester.
This section examines the crucial elements in providing endpoint
security in the web service provider described in the basic WS-Trust
scenario. The components that will be discussed are.
Web service provider WSDL
The web service provider is a contract-first endpoint. All the WS-trust
and security policies for it are declared in the WSDL,
SecurityService.wsdl. For this scenario a ws-requester is required to
present a SAML 2.0 token issued from a designed STS. The address of the
STS is provided in the WSDL. An asymmetric binding policy is used to
encrypt and sign the SOAP body of messages that pass back and forth
between ws-requester and ws-provider. X.509 certificates are use for the
asymmetric binding. The rules for sharing the public and private keys in
the SOAP request and response messages are declared. A detailed
explanation of the security settings are provided in the comments in the
listing below.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions targetNamespace="http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy" name="SecurityService"
xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsaws="http://www.w3.org/2005/08/addressing"
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
xmlns:t="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<types>
<xsd:schema>
<xsd:import namespace="http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy" schemaLocation="SecurityService_schema1.xsd"/>
</xsd:schema>
</types>
<message name="sayHello">
<part name="parameters" element="tns:sayHello"/>
</message>
<message name="sayHelloResponse">
<part name="parameters" element="tns:sayHelloResponse"/>
</message>
<portType name="ServiceIface">
<operation name="sayHello">
<input message="tns:sayHello"/>
<output message="tns:sayHelloResponse"/>
</operation>
</portType>
The wsp:PolicyReference binds the security requirments on all the STS endpoints.
The wsp:Policy wsu:Id="#AsymmetricSAML2Policy" element is defined later in this file.
<binding name="SecurityServicePortBinding" type="tns:ServiceIface">
<wsp:PolicyReference URI="#AsymmetricSAML2Policy" />
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="sayHello">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
<wsp:PolicyReference URI="#Input_Policy" />
</input>
<output>
<soap:body use="literal"/>
<wsp:PolicyReference URI="#Output_Policy" />
</output>
</operation>
</binding>
<service name="SecurityService">
<port name="SecurityServicePort" binding="tns:SecurityServicePortBinding">
<soap:address location="http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust/SecurityService"/>
</port>
</service>
<wsp:Policy wsu:Id="AsymmetricSAML2Policy">
<wsp:ExactlyOne>
<wsp:All>
The wsam:Addressing element, indicates that the endpoints of this
web service MUST conform to the WS-Addressing specification. The
attribute wsp:Optional="false" enforces this assertion.
<wsam:Addressing wsp:Optional="false">
<wsp:Policy />
</wsam:Addressing>
The sp:AsymmetricBinding element indicates that security is provided
at the SOAP layer. A public/private key combinations is required to
protect the message. The initiator will use it's private key to sign
the message and the recipient's public key is used to encrypt the message.
The recipient of the message will use it's private key to decrypt it and
initiator's public key to verify the signature.
<sp:AsymmetricBinding>
<wsp:Policy>
The sp:InitiatorToken element specifies the elements required in
generating the initiator request to the ws-provider's service.
<sp:InitiatorToken>
<wsp:Policy>
The sp:IssuedToken element asserts that a SAML 2.0 security token is
expected from the STS using a public key type. The
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
attribute instructs the runtime to include the initiator's public key
with every message sent to the recipient.
The sp:RequestSecurityTokenTemplate element directs that all of the
children of this element will be copied directly into the body of the
RequestSecurityToken (RST) message that is sent to the STS when the
initiator asks the STS to issue a token.
<sp:IssuedToken
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<sp:RequestSecurityTokenTemplate>
<t:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</t:TokenType>
<t:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/PublicKey</t:KeyType>
</sp:RequestSecurityTokenTemplate>
<wsp:Policy>
<sp:RequireInternalReference />
</wsp:Policy>
The sp:Issuer element defines the STS's address and endpoint information
This information is used by the STSClient.
<sp:Issuer>
<wsaws:Address>http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-sts/SecurityTokenService</wsaws:Address>
<wsaws:Metadata xmlns:wsdli="http://www.w3.org/2006/01/wsdl-instance"
wsdli:wsdlLocation="http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-sts/SecurityTokenService?wsdl">
<wsaw:ServiceName xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:stsns="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
EndpointName="UT_Port">stsns:SecurityTokenService</wsaw:ServiceName>
</wsaws:Metadata>
</sp:Issuer>
</sp:IssuedToken>
</wsp:Policy>
</sp:InitiatorToken>
The sp:RecipientToken element asserts the type of public/private key-pair
expected from the recipient. The
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
attribute indicates that the initiator's public key will never be included
in the reply messages.
The sp:WssX509V3Token10 element indicates that an X509 Version 3 token
should be used in the message.
<sp:RecipientToken>
<wsp:Policy>
<sp:X509Token
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
<wsp:Policy>
<sp:WssX509V3Token10 />
<sp:RequireIssuerSerialReference
/>
</wsp:Policy>
</sp:X509Token>
</wsp:Policy>
</sp:RecipientToken>
The sp:Layout element, indicates the layout rules to apply when adding
items to the security header. The sp:Lax sub-element indicates items
are added to the security header in any order that conforms to
WSS: SOAP Message Security.
<sp:Layout>
<wsp:Policy>
<sp:Lax />
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp />
<sp:OnlySignEntireHeadersAndBody />
The sp:AlgorithmSuite element, requires the Basic256 algorithm suite
be used in performing cryptographic operations.
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256 />
</wsp:Policy>
</sp:AlgorithmSuite>
</wsp:Policy>
</sp:AsymmetricBinding>
The sp:Wss11 element declares WSS: SOAP Message Security 1.1 options
to be supported by the STS. These particular elements generally refer
to how keys are referenced within the SOAP envelope. These are normally
handled by CXF.
<sp:Wss11>
<wsp:Policy>
<sp:MustSupportRefIssuerSerial />
<sp:MustSupportRefThumbprint />
<sp:MustSupportRefEncryptedKey />
</wsp:Policy>
</sp:Wss11>
The sp:Trust13 element declares controls for WS-Trust 1.3 options.
They are policy assertions related to exchanges specifically with
client and server challenges and entropy behaviors. Again these are
normally handled by CXF.
<sp:Trust13>
<wsp:Policy>
<sp:MustSupportIssuedTokens />
<sp:RequireClientEntropy />
<sp:RequireServerEntropy />
</wsp:Policy>
</sp:Trust13>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="Input_Policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:EncryptedParts>
<sp:Body />
</sp:EncryptedParts>
<sp:SignedParts>
<sp:Body />
<sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing" />
</sp:SignedParts>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="Output_Policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:EncryptedParts>
<sp:Body />
</sp:EncryptedParts>
<sp:SignedParts>
<sp:Body />
<sp:Header Name="To" Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="From" Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="FaultTo" Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="ReplyTo" Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="MessageID" Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="RelatesTo" Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="Action" Namespace="http://www.w3.org/2005/08/addressing" />
</sp:SignedParts>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
</definitions>
Web service provider Interface
The web service provider interface class, ServiceIface, is a simple
straight forward web service definition.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.service;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy"
public interface ServiceIface
@WebMethod
String sayHello();
Web service provider Implementation
The web service provider implementation class, ServiceImpl, is a simple
POJO. It uses the standard WebService annotation to define the service
endpoint. In addition there are two Apache CXF annotations,
EndpointProperties and EndpointProperty used for configuring the
endpoint for the CXF runtime. These annotations come from the
Apache WSS4J project, which provides a
Java implementation of the primary WS-Security standards for Web
Services. These annotations are programmatically adding properties to
the endpoint. With plain Apache CXF, these properties are often set via
the <jaxws:properties> element on the <jaxws:endpoint> element in the
Spring config; these annotations allow the properties to be configured
in the code.
WSS4J uses the Crypto interface to get keys and certificates for
encryption/decryption and for signature creation/verification. As is
asserted by the WSDL, X509 keys and certificates are required for this
service. The WSS4J configuration information being provided by
ServiceImpl is for Crypto’s Merlin implementation. More information will
be provided about this in the keystore section.
The first EndpointProperty statement in the listing is declaring the
user’s name to use for the message signature. It is used as the alias
name in the keystore to get the user’s cert and private key for
signature. The next two EndpointProperty statements declares the Java
properties file that contains the (Merlin) crypto configuration
information. In this case both for signing and encrypting the messages.
WSS4J reads this file and extra required information for message
handling. The last EndpointProperty statement declares the
ServerCallbackHandler implementation class. It is used to obtain the
user’s password for the certificates in the keystore file.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.service;
import javax.jws.WebService;
import org.apache.cxf.annotations.EndpointProperties;
import org.apache.cxf.annotations.EndpointProperty;
@WebService
portName = "SecurityServicePort",
serviceName = "SecurityService",
wsdlLocation = "WEB-INF/wsdl/SecurityService.wsdl",
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy",
endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.service.ServiceIface"
@EndpointProperties(value = {
@EndpointProperty(key = "ws-security.signature.username", value = "myservicekey"),
@EndpointProperty(key = "ws-security.signature.properties", value = "serviceKeystore.properties"),
@EndpointProperty(key = "ws-security.encryption.properties", value = "serviceKeystore.properties"),
@EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.service.ServerCallbackHandler")
public class ServiceImpl implements ServiceIface
public String sayHello()
return "WS-Trust Hello World!";
ServerCallbackHandler
ServerCallbackHandler is a callback handler for the WSS4J Crypto API. It
is used to obtain the password for the private key in the keystore. This
class enables CXF to retrieve the password of the user name to use for
the message signature. A certificates' password is not discoverable. The
creator of the certificate must record the password he assigns and
provide it when requested through the CallbackHandler. In this scenario
skpass is the password for user myservicekey.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.service;
import java.util.HashMap;
import java.util.Map;
import org.jboss.wsf.stack.cxf.extensions.security.PasswordCallbackHandler;
public class ServerCallbackHandler extends PasswordCallbackHandler
public ServerCallbackHandler()
super(getInitMap());
private static Map<String, String> getInitMap()
Map<String, String> passwords = new HashMap<String, String>();
passwords.put("myservicekey", "skpass");
return passwords;
Crypto properties and keystore files
WSS4J’s Crypto implementation is loaded and configured via a Java
properties file that contains Crypto configuration data. The file
contains implementation-specific properties such as a keystore location,
password, default alias and the like. This application is using the
Merlin implementation. File serviceKeystore.properties contains this
information.
File servicestore.jks, is a Java KeyStore (JKS) repository. It contains
self signed certificates for myservicekey and mystskey. Self signed
certificates are not appropriate for production use.
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=sspass
org.apache.ws.security.crypto.merlin.keystore.alias=myservicekey
org.apache.ws.security.crypto.merlin.keystore.file=servicestore.jks
When deployed on WildFly this application requires access to the JBossWs
and CXF APIs provided in module org.jboss.ws.cxf.jbossws-cxf-client. The
dependency statement directs the server to provide them at deployment.
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.7.0_25-b15 (Oracle Corporation)
Dependencies: org.jboss.ws.cxf.jbossws-cxf-client
Security Token Service (STS)
This section examines the crucial elements in providing the Security
Token Service functionality described in the basic WS-Trust scenario.
The components that will be discussed are.
The STS is a contract-first endpoint. All the WS-trust and security
policies for it are declared in the WSDL, ws-trust-1.4-service.wsdl. A
symmetric binding policy is used to encrypt and sign the SOAP body of
messages that pass back and forth between ws-requester and the STS. The
ws-requester is required to authenticate itself by providing WSS
UsernameToken credentials. The rules for sharing the public and private
keys in the SOAP request and response messages are declared. A detailed
explanation of the security settings are provided in the comments in the
listing below.
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
targetNamespace="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
xmlns:tns="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
xmlns:wstrust="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsap10="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata">
<wsdl:types>
<xs:schema elementFormDefault="qualified" targetNamespace='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>
<xs:element name='RequestSecurityToken' type='wst:AbstractRequestSecurityTokenType' />
<xs:element name='RequestSecurityTokenResponse' type='wst:AbstractRequestSecurityTokenType' />
<xs:complexType name='AbstractRequestSecurityTokenType' >
<xs:sequence>
<xs:any namespace='##any' processContents='lax' minOccurs='0' maxOccurs='unbounded' />
</xs:sequence>
<xs:attribute name='Context' type='xs:anyURI' use='optional' />
<xs:anyAttribute namespace='##other' processContents='lax' />
</xs:complexType>
<xs:element name='RequestSecurityTokenCollection' type='wst:RequestSecurityTokenCollectionType' />
<xs:complexType name='RequestSecurityTokenCollectionType' >
<xs:sequence>
<xs:element name='RequestSecurityToken' type='wst:AbstractRequestSecurityTokenType' minOccurs='2' maxOccurs='unbounded'/>
</xs:sequence>
</xs:complexType>
<xs:element name='RequestSecurityTokenResponseCollection' type='wst:RequestSecurityTokenResponseCollectionType' />
<xs:complexType name='RequestSecurityTokenResponseCollectionType' >
<xs:sequence>
<xs:element ref='wst:RequestSecurityTokenResponse' minOccurs='1' maxOccurs='unbounded' />
</xs:sequence>
<xs:anyAttribute namespace='##other' processContents='lax' />
</xs:complexType>
</xs:schema>
</wsdl:types>
<!-- WS-Trust defines the following GEDs -->
<wsdl:message name="RequestSecurityTokenMsg">
<wsdl:part name="request" element="wst:RequestSecurityToken" />
</wsdl:message>
<wsdl:message name="RequestSecurityTokenResponseMsg">
<wsdl:part name="response"
element="wst:RequestSecurityTokenResponse" />
</wsdl:message>
<wsdl:message name="RequestSecurityTokenCollectionMsg">
<wsdl:part name="requestCollection"
element="wst:RequestSecurityTokenCollection"/>
</wsdl:message>
<wsdl:message name="RequestSecurityTokenResponseCollectionMsg">
<wsdl:part name="responseCollection"
element="wst:RequestSecurityTokenResponseCollection"/>
</wsdl:message>
<!-- This portType an example of a Requestor (or other) endpoint that
Accepts SOAP-based challenges from a Security Token Service -->
<wsdl:portType name="WSSecurityRequestor">
<wsdl:operation name="Challenge">
<wsdl:input message="tns:RequestSecurityTokenResponseMsg"/>
<wsdl:output message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
</wsdl:portType>
<!-- This portType is an example of an STS supporting full protocol -->
The wsdl:portType and data types are XML elements defined by the
WS_Trust specification. The wsdl:portType defines the endpoints
supported in the STS implementation. This WSDL defines all operations
that an STS implementation can support.
<wsdl:portType name="STS">
<wsdl:operation name="Cancel">
<wsdl:input wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Cancel" message="tns:RequestSecurityTokenMsg"/>
<wsdl:output wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/CancelFinal" message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
<wsdl:operation name="Issue">
<wsdl:input wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue" message="tns:RequestSecurityTokenMsg"/>
<wsdl:output wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTRC/IssueFinal" message="tns:RequestSecurityTokenResponseCollectionMsg"/>
</wsdl:operation>
<wsdl:operation name="Renew">
<wsdl:input wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Renew" message="tns:RequestSecurityTokenMsg"/>
<wsdl:output wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/RenewFinal" message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
<wsdl:operation name="Validate">
<wsdl:input wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Validate" message="tns:RequestSecurityTokenMsg"/>
<wsdl:output wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/ValidateFinal" message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
<wsdl:operation name="KeyExchangeToken">
<wsdl:input wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/KET" message="tns:RequestSecurityTokenMsg"/>
<wsdl:output wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/KETFinal" message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
<wsdl:operation name="RequestCollection">
<wsdl:input message="tns:RequestSecurityTokenCollectionMsg"/>
<wsdl:output message="tns:RequestSecurityTokenResponseCollectionMsg"/>
</wsdl:operation>
</wsdl:portType>
<!-- This portType is an example of an endpoint that accepts
Unsolicited RequestSecurityTokenResponse messages -->
<wsdl:portType name="SecurityTokenResponseService">
<wsdl:operation name="RequestSecurityTokenResponse">
<wsdl:input message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
</wsdl:portType>
The wsp:PolicyReference binds the security requirments on all the STS endpoints.
The wsp:Policy wsu:Id="UT_policy" element is later in this file.
<wsdl:binding name="UT_Binding" type="wstrust:STS">
<wsp:PolicyReference URI="#UT_policy" />
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="Issue">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue" />
<wsdl:input>
<wsp:PolicyReference
URI="#Input_policy" />
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<wsp:PolicyReference
URI="#Output_policy" />
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Validate">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Validate" />
<wsdl:input>
<wsp:PolicyReference
URI="#Input_policy" />
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<wsp:PolicyReference
URI="#Output_policy" />
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Cancel">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Cancel" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Renew">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Renew" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="KeyExchangeToken">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/KeyExchangeToken" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="RequestCollection">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/RequestCollection
" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="SecurityTokenService">
<wsdl:port name="UT_Port" binding="tns:UT_Binding">
<soap:address location="http://localhost:8080/SecurityTokenService/UT" />
</wsdl:port>
</wsdl:service>
<wsp:Policy wsu:Id="UT_policy">
<wsp:ExactlyOne>
<wsp:All>
The sp:UsingAddressing element, indicates that the endpoints of this
web service conforms to the WS-Addressing specification. More detail
can be found here: [http://www.w3.org/TR/2006/CR-ws-addr-wsdl-20060529]
<wsap10:UsingAddressing/>
The sp:SymmetricBinding element indicates that security is provided
at the SOAP layer and any initiator must authenticate itself by providing
WSS UsernameToken credentials.
<sp:SymmetricBinding
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
In a symmetric binding, the keys used for encrypting and signing in both
directions are derived from a single key, the one specified by the
sp:ProtectionToken element. The sp:X509Token sub-element declares this
key to be a X.509 certificate and the
IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never"
attribute adds the requirement that the token MUST NOT be included in
any messages sent between the initiator and the recipient; rather, an
external reference to the token should be used. Lastly the WssX509V3Token10
sub-element declares that the Username token presented by the initiator
should be compliant with Web Services Security UsernameToken Profile
1.0 specification. [ http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf ]
<sp:ProtectionToken>
<wsp:Policy>
<sp:X509Token
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
<wsp:Policy>
<sp:RequireDerivedKeys />
<sp:RequireThumbprintReference />
<sp:WssX509V3Token10 />
</wsp:Policy>
</sp:X509Token>
</wsp:Policy>
</sp:ProtectionToken>
The sp:AlgorithmSuite element, requires the Basic256 algorithm suite
be used in performing cryptographic operations.
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256 />
</wsp:Policy>
</sp:AlgorithmSuite>
The sp:Layout element, indicates the layout rules to apply when adding
items to the security header. The sp:Lax sub-element indicates items
are added to the security header in any order that conforms to
WSS: SOAP Message Security.
<sp:Layout>
<wsp:Policy>
<sp:Lax />
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp />
<sp:EncryptSignature />
<sp:OnlySignEntireHeadersAndBody />
</wsp:Policy>
</sp:SymmetricBinding>
The sp:SignedSupportingTokens element declares that the security header
of messages must contain a sp:UsernameToken and the token must be signed.
The attribute IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"
on sp:UsernameToken indicates that the token MUST be included in all
messages sent from initiator to the recipient and that the token MUST
NOT be included in messages sent from the recipient to the initiator.
And finally the element sp:WssUsernameToken10 is a policy assertion
indicating the Username token should be as defined in Web Services
Security UsernameToken Profile 1.0
<sp:SignedSupportingTokens
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:UsernameToken
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10 />
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
The sp:Wss11 element declares WSS: SOAP Message Security 1.1 options
to be supported by the STS. These particular elements generally refer
to how keys are referenced within the SOAP envelope. These are normally
handled by CXF.
<sp:Wss11
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:MustSupportRefKeyIdentifier />
<sp:MustSupportRefIssuerSerial />
<sp:MustSupportRefThumbprint />
<sp:MustSupportRefEncryptedKey />
</wsp:Policy>
</sp:Wss11>
The sp:Trust13 element declares controls for WS-Trust 1.3 options.
They are policy assertions related to exchanges specifically with
client and server challenges and entropy behaviors. Again these are
normally handled by CXF.
<sp:Trust13
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:MustSupportIssuedTokens />
<sp:RequireClientEntropy />
<sp:RequireServerEntropy />
</wsp:Policy>
</sp:Trust13>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="Input_policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:SignedParts
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<sp:Body />
<sp:Header Name="To"
Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="From"
Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="FaultTo"
Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="ReplyTo"
Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="MessageID"
Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="RelatesTo"
Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="Action"
Namespace="http://www.w3.org/2005/08/addressing" />
</sp:SignedParts>
<sp:EncryptedParts
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<sp:Body />
</sp:EncryptedParts>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="Output_policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:SignedParts
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<sp:Body />
<sp:Header Name="To"
Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="From"
Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="FaultTo"
Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="ReplyTo"
Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="MessageID"
Namespace="
http://www.w3.org/2005/08/addressing" />
<sp:Header Name="RelatesTo"
Namespace="http://www.w3.org/2005/08/addressing" />
<sp:Header Name="Action"
Namespace="http://www.w3.org/2005/08/addressing" />
</sp:SignedParts>
<sp:EncryptedParts
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<sp:Body />
</sp:EncryptedParts>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
</wsdl:definitions>
The Apache CXF’s STS, SecurityTokenServiceProvider, is a web service
provider that is compliant with the protocols and functionality defined
by the WS-Trust specification. It has a modular architecture. Many of
its components are configurable or replaceable and there are many
optional features that are enabled by implementing and configuring
plug-ins. Users can customize their own STS by extending from
SecurityTokenServiceProvider and overriding the default settings.
Extensive information about the CXF’s STS configurable and pluggable
components can be found
here.
This STS implementation class, SimpleSTS, is a POJO that extends from
SecurityTokenServiceProvider. Note that the class is defined with a
WebServiceProvider annotation and not a WebService annotation. This
annotation defines the service as a Provider-based endpoint, meaning it
supports a more messaging-oriented approach to Web services. In
particular, it signals that the exchanged messages will be XML documents
of some type. SecurityTokenServiceProvider is an implementation of the
javax.xml.ws.Provider interface. In comparison the WebService annotation
defines a (service endpoint interface) SEI-based endpoint which supports
message exchange via SOAP envelopes.
As was done in the ServiceImpl class, the WSS4J annotations
EndpointProperties and EndpointProperty are providing endpoint
configuration for the CXF runtime. This was previous described
here.
The InInterceptors annotation is used to specify a JBossWS integration
interceptor to be used for authenticating incoming requests; JAAS
integration is used here for authentication, the username/passoword
coming from the UsernameToken in the ws-requester message are used for
authenticating the requester against a security domain on the
application server hosting the STS deployment.
In this implementation we are customizing the operations of token
issuance, token validation and their static properties.
StaticSTSProperties is used to set select properties for configuring
resources in the STS. You may think this is a duplication of the
settings made with the WSS4J annotations. The values are the same but
the underlaying structures being set are different, thus this
information must be declared in both places.
The setIssuer setting is important because it uniquely identifies the
issuing STS. The issuer string is embedded in issued tokens and, when
validating tokens, the STS checks the issuer string value. Consequently,
it is important to use the issuer string in a consistent way, so that
the STS can recognize the tokens that it has issued.
The setEndpoints call allows the declaration of a set of allowed token
recipients by address. The addresses are specified as reg-ex patterns.
TokenIssueOperation and TokenValidateOperation have a modular structure.
This allows custom behaviors to be injected into the processing of
messages. In this case we are overriding the
SecurityTokenServiceProvider’s default behavior and performing SAML
token processing and validation. CXF provides an implementation of a
SAMLTokenProvider and SAMLTokenValidator which we are using rather than
writing our own.
Learn more about the SAMLTokenProvider
here.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import javax.xml.ws.WebServiceProvider;
import org.apache.cxf.annotations.EndpointProperties;
import org.apache.cxf.annotations.EndpointProperty;
import org.apache.cxf.interceptor.InInterceptors;
import org.apache.cxf.sts.StaticSTSProperties;
import org.apache.cxf.sts.operation.TokenIssueOperation;
import org.apache.cxf.sts.operation.TokenValidateOperation;
import org.apache.cxf.sts.service.ServiceMBean;
import org.apache.cxf.sts.service.StaticService;
import org.apache.cxf.sts.token.provider.SAMLTokenProvider;
import org.apache.cxf.sts.token.validator.SAMLTokenValidator;
import org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider;
@WebServiceProvider(serviceName = "SecurityTokenService",
portName = "UT_Port",
targetNamespace = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/",
wsdlLocation = "WEB-INF/wsdl/ws-trust-1.4-service.wsdl")
@EndpointProperties(value = {
@EndpointProperty(key = "ws-security.signature.username", value = "mystskey"),
@EndpointProperty(key = "ws-security.signature.properties", value = "stsKeystore.properties"),
@EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.STSCallbackHandler"),
//to let the JAAS integration deal with validation through the interceptor below
@EndpointProperty(key = "ws-security.validate.token", value = "false")
@InInterceptors(interceptors = {"org.jboss.wsf.stack.cxf.security.authentication.SubjectCreatingPolicyInterceptor"})
public class SampleSTS extends SecurityTokenServiceProvider
public SampleSTS() throws Exception
super();
StaticSTSProperties props = new StaticSTSProperties();
props.setSignaturePropertiesFile("stsKeystore.properties");
props.setSignatureUsername("mystskey");
props.setCallbackHandlerClass(STSCallbackHandler.class.getName());
props.setIssuer("DoubleItSTSIssuer");
List<ServiceMBean> services = new LinkedList<ServiceMBean>();
StaticService service = new StaticService();
service.setEndpoints(Arrays.asList(
"http://localhost:(\\d)*/jaxws-samples-wsse-policy-trust/SecurityService",
"http://\\[::1\\]:(\\d)*/jaxws-samples-wsse-policy-trust/SecurityService",
"http://\\[0:0:0:0:0:0:0:1\\]:(\\d)*/jaxws-samples-wsse-policy-trust/SecurityService"
services.add(service);
TokenIssueOperation issueOperation = new TokenIssueOperation();
issueOperation.setServices(services);
issueOperation.getTokenProviders().add(new SAMLTokenProvider());
issueOperation.setStsProperties(props);
TokenValidateOperation validateOperation = new TokenValidateOperation();
validateOperation.getTokenValidators().add(new SAMLTokenValidator());
validateOperation.setStsProperties(props);
this.setIssueOperation(issueOperation);
this.setValidateOperation(validateOperation);
STSCallbackHandler
STSCallbackHandler is a callback handler for the WSS4J Crypto API. It is
used to obtain the password for the private key in the keystore. This
class enables CXF to retrieve the password of the user name to use for
the message signature.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.sts;
import java.util.HashMap;
import java.util.Map;
import org.jboss.wsf.stack.cxf.extensions.security.PasswordCallbackHandler;
public class STSCallbackHandler extends PasswordCallbackHandler
public STSCallbackHandler()
super(getInitMap());
private static Map<String, String> getInitMap()
Map<String, String> passwords = new HashMap<String, String>();
passwords.put("mystskey", "stskpass");
return passwords;
Crypto properties and keystore files
WSS4J’s Crypto implementation is loaded and configured via a Java
properties file that contains Crypto configuration data. The file
contains implementation-specific properties such as a keystore location,
password, default alias and the like. This application is using the
Merlin implementation. File stsKeystore.properties contains this
information.
File servicestore.jks, is a Java KeyStore (JKS) repository. It contains
self signed certificates for myservicekey and mystskey. Self signed
certificates are not appropriate for production use.
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=stsspass
org.apache.ws.security.crypto.merlin.keystore.file=stsstore.jks
When deployed on WildFly, this application requires access to the
JBossWs and CXF APIs provided in modules
org.jboss.ws.cxf.jbossws-cxf-client and org.apache.cxf. The Apache CXF
internals, org.apache.cxf.impl, are needed to build the STS
configuration in the SampleSTS constructor. The dependency statement
directs the server to provide them at deployment.
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.7.0_25-b15 (Oracle Corporation)
Dependencies: org.jboss.ws.cxf.jbossws-cxf-client,org.apache.cxf.impl
The STS requires a JBoss security domain be configured. The
jboss-web.xml descriptor declares a named security
domain,"JBossWS-trust-sts" to be used by this service for
authentication. This security domain requires two properties files and
the addition of a security-domain declaration in the JBoss server
configuration file.
For this scenario the domain needs to contain user alice, password
clarinet, and role friend. See the listings below for
jbossws-users.properties and jbossws-roles.properties. In addition the
following XML must be added to the JBoss security subsystem in the
server configuration file. Replace " SOME_PATH" with appropriate
information.
<security-domain name="JBossWS-trust-sts">
<authentication>
<login-module code="UsersRoles" flag="required">
<module-option name="usersProperties" value="/SOME_PATH/jbossws-users.properties"/>
<module-option name="unauthenticatedIdentity" value="anonymous"/>
<module-option name
="rolesProperties" value="/SOME_PATH/jbossws-roles.properties"/>
</login-module>
</authentication>
</security-domain>
jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.4//EN" ">
<jboss-web>
<security-domain>java:/jaas/JBossWS-trust-sts</security-domain>
</jboss-web>
jbossws-users.properties
# A sample users.properties file for use with the UsersRolesLoginModule
alice=clarinet
jbossws-roles.properties
# A sample roles.properties file for use with the UsersRolesLoginModule
alice=friend
WS-MetadataExchange and interoperability
To achieve better interoperability, you might consider allowing the STS
endpoint to reply to WS-MetadataExchange messages directed to the /mex
URL sub-path (e.g.
http://localhost:8080/jaxws-samples-wsse-policy-trust-sts/SecurityTokenService/mex).
This can be done by tweaking the url-pattern for the underlying
endpoint servlet, for instance by adding a web.xml descriptor as
follows to the deployment:<?xml version="1.0" encoding="UTF-8"?>
<web-app
version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<servlet>
<servlet-name>TestSecurityTokenService</servlet-name>
<servlet-class>org.jboss.test.ws.jaxws.samples.wsse.policy.trust.SampleSTS</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestSecurityTokenService</servlet-name>
<url-pattern>/SecurityTokenService/*</url-pattern>
</servlet-mapping>
</web-app>
As a matter of fact, at the time of writing some webservices
implementations (including Metro) assume the /mex URL as the default
choice for directing WS-MetadataExchange requests to and use that to
retrieve STS wsdl contracts.
Web service requester
This section examines the crucial elements in calling a web service that
implements endpoint security as described in the basic WS-Trust
scenario. The components that will be discussed are.
Web service requester Implementation
The ws-requester, the client, uses standard procedures for creating a
reference to the web service in the first four line. To address the
endpoint security requirements, the web service’s "Request Context" is
configured with the information needed in message generation. In
addition, the STSClient that communicates with the STS is configured
with similar values. Note the key strings ending with a ".it" suffix.
This suffix flags these settings as belonging to the STSClient. The
internal CXF code assigns this information to the STSClient that is
auto-generated for this service call.
There is an alternate method of setting up the STSCLient. The user may
provide their own instance of the STSClient. The CXF code will use this
object and not auto-generate one. This is used in the ActAs and
OnBehalfOf examples. When providing the STSClient in this way, the user
must provide a org.apache.cxf.Bus for it and the configuration keys must
not have the ".it" suffix.
QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy", "SecurityService");
URL wsdlURL = new URL(serviceURL + "?wsdl");
Service service = Service.create(wsdlURL, serviceName);
ServiceIface proxy = (ServiceIface) service.getPort(ServiceIface.class);
// set the security related configuration information for the service "request"
Map<String, Object> ctx = ((BindingProvider) proxy).getRequestContext();
ctx.put(SecurityConstants.CALLBACK_HANDLER, new ClientCallbackHandler());
ctx.put(SecurityConstants.SIGNATURE_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.ENCRYPT_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myclientkey");
ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myservicekey");
//-- Configuration settings that will be transfered to the STSClient
// "alice" is the name provided for the WSS Username. Her password will
// be retreived from the ClientCallbackHander by the STSClient.
ctx.put(SecurityConstants.USERNAME + ".it", "alice");
ctx.put(SecurityConstants.CALLBACK_HANDLER + ".it", new ClientCallbackHandler());
ctx.put(SecurityConstants.ENCRYPT_PROPERTIES + ".it",
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.ENCRYPT_USERNAME + ".it", "mystskey");
// alias name in the keystore to get the user's public key to send to the STS
ctx.put(SecurityConstants.STS_TOKEN_USERNAME + ".it", "myclientkey");
// Crypto property configuration to use for the STS
ctx.put(SecurityConstants.STS_TOKEN_PROPERTIES + ".it",
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
// write out an X509Certificate structure in UseKey/KeyInfo
ctx.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO + ".it", "true");
// Setting indicates the STSclient should not try using the WS-MetadataExchange
// call using STS EPR WSA address when the endpoint contract does not contain
// WS-MetadataExchange info.
ctx.put("ws-security.sts.disable-wsmex-call-using-epr-address", "true");
proxy.sayHello();
ClientCallbackHandler
ClientCallbackHandler is a callback handler for the WSS4J Crypto API. It
is used to obtain the password for the private key in the keystore. This
class enables CXF to retrieve the password of the user name to use for
the message signature. Note that "alice" and her password have been
provided here. This information is not in the (JKS) keystore but
provided in the WildFly security domain. It was declared in file
jbossws-users.properties.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.shared;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
public class ClientCallbackHandler implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof WSPasswordCallback) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
if ("myclientkey".equals(pc.getIdentifier())) {
pc.setPassword("ckpass");
break;
} else if ("alice".equals(pc.getIdentifier())) {
pc.setPassword("clarinet");
break;
Requester Crypto properties and keystore files
WSS4J’s Crypto implementation is loaded and configured via a Java
properties file that contains Crypto configuration data. The file
contains implementation-specific properties such as a keystore location,
password, default alias and the like. This application is using the
Merlin implementation. File clientKeystore.properties contains this
information.
File clientstore.jks, is a Java KeyStore (JKS) repository. It contains
self signed certificates for myservicekey and mystskey. Self signed
certificates are not appropriate for production use.
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=cspass
org.apache.ws.security.crypto.merlin.keystore.alias=myclientkey
org.apache.ws.security.crypto.merlin.keystore.file=META-INF/clientstore.jks
The ActAs feature is used in scenarios that require composite
delegation. It is commonly used in multi-tiered systems where an
application calls a service on behalf of a logged in user or a service
calls another service on behalf of the original caller.
ActAs is nothing more than a new sub-element in the RequestSecurityToken
(RST). It provides additional information about the original caller when
a token is negotiated with the STS. The ActAs element usually takes the
form of a token with identity claims such as name, role, and
authorization code, for the client to access the service.
The ActAs scenario is an extension of
the basic
WS-Trust scenario. In this example the ActAs service calls the
ws-service on behalf of a user. There are only a couple of additions to
the basic scenario’s code. An ActAs web service provider and callback
handler have been added. The ActAs web services' WSDL imposes the same
security policies as the ws-provider. UsernameTokenCallbackHandler is
new. It is a utility that generates the content for the ActAs element.
And lastly there are a couple of code additions in the STS to support
the ActAs request.
Web service provider
This section examines the web service elements from the basic WS-Trust
scenario that have been changed to address the needs of the ActAs
example. The components are
Web service provider WSDL
The ActAs web service provider’s WSDL is a clone of the ws-provider’s
WSDL. The wsp:Policy section is the same. There are changes to the
service endpoint, targetNamespace, portType, binding name, and service.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions targetNamespace="http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy" name="ActAsService"
xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsaws="http://www.w3.org/2005/08/addressing"
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
xmlns:t="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<types>
<xsd:schema>
<xsd:import
namespace="http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy"
schemaLocation="ActAsService_schema1.xsd"/>
</xsd:schema>
</types>
<message name="sayHello">
<part name="parameters" element="tns:sayHello"/>
</message>
<message name="sayHelloResponse">
<part name="parameters" element="tns:sayHelloResponse"/>
</message>
<portType name="ActAsServiceIface">
<operation name="sayHello">
<input message="tns:sayHello"/>
<output message="tns:sayHelloResponse"/>
</operation>
</portType>
<binding name="ActAsServicePortBinding" type="tns:ActAsServiceIface">
<wsp:PolicyReference URI="#AsymmetricSAML2Policy" />
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="sayHello">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
<wsp:PolicyReference URI="#Input_Policy" />
</input>
<output>
<soap:body use="literal"/>
<wsp:PolicyReference URI="#Output_Policy" />
</output>
</operation>
</binding>
<service name="ActAsService">
<port name="ActAsServicePort" binding="tns:ActAsServicePortBinding">
<soap:address location="http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-actas/ActAsService"/>
</port>
</service>
</definitions>
Web Service Interface
The web service provider interface class, ActAsServiceIface, is a simple
web service definition.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy"
public interface ActAsServiceIface
@WebMethod
String sayHello();
Web Service Implementation
The web service provider implementation class, ActAsServiceImpl, is a
simple POJO. It uses the standard WebService annotation to define the
service endpoint and two Apache WSS4J annotations, EndpointProperties
and EndpointProperty used for configuring the endpoint for the CXF
runtime. The WSS4J configuration information provided is for WSS4J’s
Crypto Merlin implementation.
ActAsServiceImpl is calling ServiceImpl acting on behalf of the user.
Method setupService performs the requisite configuration setup.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas;
import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.annotations.EndpointProperties;
import org.apache.cxf.annotations.EndpointProperty;
import org.apache.cxf.ws.security.SecurityConstants;
import org.apache.cxf.ws.security.trust.STSClient;
import org.jboss.test.ws.jaxws.samples.wsse.policy.trust.service.ServiceIface;
import org.jboss.test.ws.jaxws.samples.wsse.policy.trust.shared.WSTrustAppUtils;
import javax.jws.WebService;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
@WebService
portName = "ActAsServicePort",
serviceName = "ActAsService",
wsdlLocation = "WEB-INF/wsdl/ActAsService.wsdl",
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy",
endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas.ActAsServiceIface"
@EndpointProperties(value = {
@EndpointProperty(key = "ws-security.signature.username", value = "myactaskey"),
@EndpointProperty(key = "ws-security.signature.properties", value = "actasKeystore.properties"),
@EndpointProperty(key = "ws-security.encryption.properties", value = "actasKeystore.properties"),
@EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas.ActAsCallbackHandler")
public class ActAsServiceImpl implements ActAsServiceIface
public String sayHello() {
try {
ServiceIface proxy = setupService();
return "ActAs " + proxy.sayHello();
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
private ServiceIface setupService()throws MalformedURLException {
ServiceIface proxy = null;
Bus bus = BusFactory.newInstance().createBus();
try {
BusFactory.setThreadDefaultBus(bus);
final String serviceURL = "http://" + WSTrustAppUtils.getServerHost() + ":8080/jaxws-samples-wsse-policy-trust/SecurityService";
final QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy", "SecurityService");
final URL wsdlURL = new URL(serviceURL + "?wsdl");
Service service = Service.create(wsdlURL, serviceName);
proxy = (ServiceIface) service.getPort(ServiceIface.class);
Map<String, Object> ctx = ((BindingProvider) proxy).getRequestContext();
ctx.put(SecurityConstants.CALLBACK_HANDLER, new ActAsCallbackHandler());
ctx.put(SecurityConstants.SIGNATURE_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource("actasKeystore.properties" ));
ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myactaskey" );
ctx.put(SecurityConstants.ENCRYPT_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource("../../META-INF/clientKeystore.properties" ));
ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myservicekey");
STSClient stsClient = new STSClient(bus);
Map<String, Object> props = stsClient.getProperties();
props.put(SecurityConstants.USERNAME, "alice");
props.put(SecurityConstants.ENCRYPT_USERNAME, "mystskey");
props.put(SecurityConstants.STS_TOKEN_USERNAME, "myactaskey" );
props.put(SecurityConstants.STS_TOKEN_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource("actasKeystore.properties" ));
props.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO, "true");
ctx.put(SecurityConstants.STS_CLIENT, stsClient);
} finally {
bus.shutdown(true);
return proxy;
ActAsCallbackHandler
ActAsCallbackHandler is a callback handler for the WSS4J Crypto API. It
is used to obtain the password for the private key in the keystore. This
class enables CXF to retrieve the password of the user name to use for
the message signature. This class has been revised to return the
passwords for this service, myactaskey and the "actas" user, alice.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.actas;
import org.jboss.wsf.stack.cxf.extensions.security.PasswordCallbackHandler;
import java.util.HashMap;
import java.util.Map;
public class ActAsCallbackHandler extends PasswordCallbackHandler {
public ActAsCallbackHandler()
super(getInitMap());
private static Map<String, String> getInitMap()
Map<String, String> passwords = new HashMap<String, String>();
passwords.put("myactaskey", "aspass");
passwords.put("alice", "clarinet");
return passwords;
UsernameTokenCallbackHandler
The ActAs and OnBeholdOf sub-elements of the RequestSecurityToken are
required to be defined as WSSE Username Tokens. This utility generates
the properly formated element.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.shared;
import org.apache.cxf.helpers.DOMUtils;
import org.apache.cxf.message.Message;
import org.apache.cxf.ws.security.SecurityConstants;
import org.apache.cxf.ws.security.trust.delegation.DelegationCallback;
import org.apache.ws.security.WSConstants;
import org.apache.ws.security.message.token.UsernameToken;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSSerializer;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import java.io.IOException;
import java.util.Map;
* A utility to provide the 3 different input parameter types for jaxws property
* "ws-security.sts.token.act-as" and "ws-security.sts.token.on-behalf-of".
* This implementation obtains a username and password via the jaxws property
* "ws-security.username" and "ws-security.password" respectively, as defined
* in SecurityConstants. It creates a wss UsernameToken to be used as the
* delegation token.
public class UsernameTokenCallbackHandler implements CallbackHandler {
public void handle(Callback[] callbacks)
throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof DelegationCallback) {
DelegationCallback callback = (DelegationCallback) callbacks[i];
Message message = callback.getCurrentMessage();
String username =
(String)message.getContextualProperty(SecurityConstants.USERNAME);
String password =
(String)message.getContextualProperty(SecurityConstants.PASSWORD);
if (username != null) {
Node contentNode = message.getContent(Node.class);
Document doc = null;
if (contentNode != null) {
doc = contentNode.getOwnerDocument();
} else {
doc = DOMUtils.createDocument();
UsernameToken usernameToken = createWSSEUsernameToken(username,password, doc);
callback.setToken(usernameToken.getElement());
} else {
throw new UnsupportedCallbackException(callbacks[i], "Unrecognized Callback");
* Provide UsernameToken as a string.
* @param ctx
* @return
public String getUsernameTokenString(Map<String, Object> ctx){
Document doc = DOMUtils.createDocument();
String result = null;
String username = (String)ctx.get(SecurityConstants.USERNAME);
String password = (String)ctx.get(SecurityConstants.PASSWORD);
if (username != null) {
UsernameToken usernameToken = createWSSEUsernameToken(username,password, doc);
result = toString(usernameToken.getElement().getFirstChild().getParentNode());
return result;
* @param username
* @param password
* @return
public String getUsernameTokenString(String username, String password){
Document doc = DOMUtils.createDocument();
String result = null;
if (username != null) {
UsernameToken usernameToken = createWSSEUsernameToken(username,password, doc);
result = toString(usernameToken.getElement().getFirstChild().getParentNode());
return result;
* Provide UsernameToken as a DOM Element.
* @param ctx
* @return
public Element getUsernameTokenElement(Map<String, Object> ctx){
Document doc = DOMUtils.createDocument();
Element result = null;
UsernameToken usernameToken = null;
String username = (String)ctx.get(SecurityConstants.USERNAME);
String password = (String)ctx.get(SecurityConstants.PASSWORD);
if (username != null) {
usernameToken = createWSSEUsernameToken(username,password, doc);
result = usernameToken.getElement();
return result;
* @param username
* @param password
* @return
public Element getUsernameTokenElement(String username, String password){
Document doc = DOMUtils.createDocument();
Element result = null;
UsernameToken usernameToken = null;
if (username != null) {
usernameToken = createWSSEUsernameToken(username,password, doc);
result = usernameToken.getElement();
return result;
private UsernameToken createWSSEUsernameToken(String username, String password, Document doc) {
UsernameToken usernameToken = new UsernameToken(true, doc,
(password == null)? null: WSConstants.PASSWORD_TEXT);
usernameToken.setName(username);
usernameToken.addWSUNamespace();
usernameToken.addWSSENamespace();
usernameToken.setID("id-" + username);
if (password != null){
usernameToken.setPassword(password);
return usernameToken;
private String toString(Node node) {
String str = null;
if (node != null) {
DOMImplementationLS lsImpl = (DOMImplementationLS)
node.getOwnerDocument().getImplementation().getFeature("LS", "3.0");
LSSerializer serializer = lsImpl.createLSSerializer();
serializer.getDomConfig().setParameter("xml-declaration", false); //by default its true, so set it to false to get String without xml-declaration
str = serializer.writeToString(node);
return str;
Crypto properties and keystore files
The ActAs service must provide its own credentials. The requisite
properties file, actasKeystore.properties, and keystore, actasstore.jks,
were created.
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=aapass
org.apache.ws.security.crypto.merlin.keystore.alias=myactaskey
org.apache.ws.security.crypto.merlin.keystore.file=actasstore.jks
When deployed on WildFly this application requires access to the JBossWs
and CXF APIs provided in modules org.jboss.ws.cxf.jbossws-cxf-client and
org.apache.cxf. The Apache CXF internals, org.apache.cxf.impl, are
needed in handling the ActAs and OnBehalfOf extensions. The dependency
statement directs the server to provide them at deployment.
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.7.0_25-b15 (Oracle Corporation)
Dependencies: org.jboss.ws.cxf.jbossws-cxf-client, org.apache.cxf.impl
Security Token Service
This section examines the STS elements from the basic WS-Trust scenario
that have been changed to address the needs of the ActAs example. The
components are.
The declaration of the set of allowed token recipients by address has
been extended to accept ActAs addresses and OnBehalfOf addresses. The
addresses are specified as reg-ex patterns.
The TokenIssueOperation requires class, UsernameTokenValidator be
provided in order to validate the contents of the OnBehalfOf claims and
class, UsernameTokenDelegationHandler to be provided in order to process
the token delegation request of the ActAs on OnBehalfOf user.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.sts;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import javax.xml.ws.WebServiceProvider;
import org.apache.cxf.annotations.EndpointProperties;
import org.apache.cxf.annotations.EndpointProperty;
import org.apache.cxf.interceptor.InInterceptors;
import org.apache.cxf.sts.StaticSTSProperties;
import org.apache.cxf.sts.operation.TokenIssueOperation;
import org.apache.cxf.sts.operation.TokenValidateOperation;
import org.apache.cxf.sts.service.ServiceMBean;
import org.apache.cxf.sts.service.StaticService;
import org.apache.cxf.sts.token.delegation.UsernameTokenDelegationHandler;
import org.apache.cxf.sts.token.provider.SAMLTokenProvider;
import org.apache.cxf.sts.token.validator.SAMLTokenValidator;
import org.apache.cxf.sts.token.validator.UsernameTokenValidator;
import org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider;
@WebServiceProvider(serviceName = "SecurityTokenService",
portName = "UT_Port",
targetNamespace = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/",
wsdlLocation = "WEB-INF/wsdl/ws-trust-1.4-service.wsdl")
//be sure to have dependency on org.apache.cxf module when on AS7, otherwise Apache CXF annotations are ignored
@EndpointProperties(value = {
@EndpointProperty(key = "ws-security.signature.username", value = "mystskey"),
@EndpointProperty(key = "ws-security.signature.properties", value = "stsKeystore.properties"),
@EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.sts.STSCallbackHandler
"),
@EndpointProperty(key = "ws-security.validate.token", value = "false") //to let the JAAS integration deal with validation through the interceptor below
@InInterceptors(interceptors = {"org.jboss.wsf.stack.cxf.security.authentication.SubjectCreatingPolicyInterceptor"})
public class SampleSTS extends SecurityTokenServiceProvider
public SampleSTS() throws Exception
super();
StaticSTSProperties props = new StaticSTSProperties();
props.setSignatureCryptoProperties("stsKeystore.properties");
props.setSignatureUsername("mystskey");
props.setCallbackHandlerClass(STSCallbackHandler.class.getName());
props.setIssuer("DoubleItSTSIssuer");
List<ServiceMBean> services = new LinkedList<ServiceMBean>();
StaticService service = new StaticService();
service.setEndpoints(Arrays.asList(
"http://localhost:(\\d)*/jaxws-samples-wsse-policy-trust/SecurityService",
"http://\\[::1\\]:(\\d)*/jaxws-samples-wsse-policy-trust/SecurityService",
"http://\\[0:0:0:0:0:0:0:1\\]:(\\d)*/jaxws-samples-wsse-policy-trust/SecurityService",
"http://localhost:(\\d)*/jaxws-samples-wsse-policy-trust-actas/ActAsService",
"http://\\[::1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-actas/ActAsService",
"http://\\[0:0:0:0:0:0:0:1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-actas/ActAsService",
"http://localhost:(\\d)*/jaxws-samples-wsse-policy-trust-onbehalfof/OnBehalfOfService",
"http://\\[::1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-onbehalfof/OnBehalfOfService",
"http://\\[0:0:0:0:0:0:0:1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-onbehalfof/OnBehalfOfService"
services.add(service);
TokenIssueOperation issueOperation = new TokenIssueOperation();
issueOperation.setServices(services);
issueOperation.getTokenProviders().add(new SAMLTokenProvider());
// required for OnBehalfOf
issueOperation.getTokenValidators().add(new UsernameTokenValidator());
// added for OnBehalfOf and ActAs
issueOperation.getDelegationHandlers().add(new UsernameTokenDelegationHandler());
issueOperation.setStsProperties(props);
TokenValidateOperation validateOperation = new TokenValidateOperation();
validateOperation.getTokenValidators().add(new SAMLTokenValidator());
validateOperation.setStsProperties(props);
this.setIssueOperation(issueOperation);
this.setValidateOperation(validateOperation);
STSCallbackHandler
The user, alice, and corresponding password was required to be added for
the ActAs example.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.sts;
import java.util.HashMap;
import java.util.Map;
import org.jboss.wsf.stack.cxf.extensions.security.PasswordCallbackHandler;
public class STSCallbackHandler extends PasswordCallbackHandler
public STSCallbackHandler()
super(getInitMap());
private static Map<String, String> getInitMap()
Map<String, String> passwords = new HashMap<String, String>();
passwords.put("mystskey", "stskpass");
passwords.put("alice", "clarinet");
return passwords;
Web service requester
This section examines the ws-requester elements from the basic WS-Trust
scenario that have been changed to address the needs of the ActAs
example. The component is
Web service requester Implementation
The ActAs ws-requester, the client, uses standard procedures for
creating a reference to the web service in the first four lines. To
address the endpoint security requirements, the web service’s "Request
Context" is configured via the BindingProvider. Information needed in
the message generation is provided through it. The ActAs user,
myactaskey, is declared in this section and UsernameTokenCallbackHandler
is used to provide the contents of the ActAs element to the STSClient.
In this example a STSClient object is created and provided to the
proxy’s request context. The alternative is to provide keys tagged with
the ".it" suffix as was done in
Basic Scenario client. The use of ActAs is configured through the props
map using the SecurityConstants.STS_TOKEN_ACT_AS key. The alternative is
to use the STSClient.setActAs method.
final QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/actaswssecuritypolicy", "ActAsService");
final URL wsdlURL = new URL(serviceURL + "?wsdl");
Service service = Service.create(wsdlURL, serviceName);
ActAsServiceIface proxy = (ActAsServiceIface) service.getPort(ActAsServiceIface.class);
Bus bus = BusFactory.newInstance().createBus();
try {
BusFactory.setThreadDefaultBus(bus);
Map<String, Object> ctx = proxy.getRequestContext();
ctx.put(SecurityConstants.CALLBACK_HANDLER, new ClientCallbackHandler());
ctx.put(SecurityConstants.ENCRYPT_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myactaskey");
ctx.put(SecurityConstants.SIGNATURE_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myclientkey");
// Generate the ActAs element contents and pass to the STSClient as a string
UsernameTokenCallbackHandler ch = new UsernameTokenCallbackHandler();
String str = ch.getUsernameTokenString("alice","clarinet");
ctx.put(SecurityConstants.STS_TOKEN_ACT_AS, str);
STSClient stsClient = new STSClient(bus);
Map<String, Object> props = stsClient.getProperties();
props.put(SecurityConstants.USERNAME, "bob");
props.put(SecurityConstants.CALLBACK_HANDLER, new ClientCallbackHandler());
props.put(SecurityConstants.ENCRYPT_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
props.put(SecurityConstants.ENCRYPT_USERNAME, "mystskey");
props.put(SecurityConstants.STS_TOKEN_USERNAME, "myclientkey");
props.put(SecurityConstants.STS_TOKEN_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
props.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO, "true");
ctx.put(SecurityConstants.STS_CLIENT, stsClient);
} finally {
bus.shutdown(true);
proxy.sayHello();
OnBehalfOf WS-Trust Scenario
The OnBehalfOf feature is used in scenarios that use the proxy pattern.
In such scenarios, the client cannot access the STS directly, instead it
communicates through a proxy gateway. The proxy gateway authenticates
the caller and puts information about the caller into the OnBehalfOf
element of the RequestSecurityToken (RST) sent to the real STS for
processing. The resulting token contains only claims related to the
client of the proxy, making the proxy completely transparent to the
receiver of the issued token.
OnBehalfOf is nothing more than a new sub-element in the RST. It
provides additional information about the original caller when a token
is negotiated with the STS. The OnBehalfOf element usually takes the
form of a token with identity claims such as name, role, and
authorization code, for the client to access the service.
The OnBehalfOf scenario is an extension of
basic WS-Trust scenario. In this example the OnBehalfOf service calls
the ws-service on behalf of a user. There are only a couple of additions
to the basic scenario’s code. An OnBehalfOf web service provider and
callback handler have been added. The OnBehalfOf web services' WSDL
imposes the same security policies as the ws-provider.
UsernameTokenCallbackHandler is a utility shared with ActAs. It
generates the content for the OnBehalfOf element. And lastly there are
code additions in the STS that both OnBehalfOf and ActAs share in
common.
Infor here [
Source Security: Apache CXF 2.5.1 STS updates ]
Web service provider
This section examines the web service elements from the basic WS-Trust
scenario that have been changed to address the needs of the OnBehalfOf
example. The components are.
The OnBehalfOf web service provider’s WSDL is a clone of the
ws-provider’s WSDL. The wsp:Policy section is the same. There are
changes to the service endpoint, targetNamespace, portType, binding
name, and service.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions targetNamespace="http://www.jboss.org/jbossws/ws-extensions/onbehalfofwssecuritypolicy" name="OnBehalfOfService"
xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/onbehalfofwssecuritypolicy"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsaws="http://www.w3.org/2005/08/addressing"
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
xmlns:t="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<types>
<xsd:schema>
<xsd:import namespace="http://www.jboss.org/jbossws/ws-extensions/onbehalfofwssecuritypolicy"
schemaLocation="OnBehalfOfService_schema1.xsd"/>
</xsd:schema>
</types>
<message name="sayHello">
<part name="parameters" element="tns:sayHello"/>
</message>
<message name="sayHelloResponse">
<part name="parameters" element="tns:sayHelloResponse"/>
</message>
<portType name="OnBehalfOfServiceIface">
<operation name="sayHello">
<input message="tns:sayHello"/>
<output message="tns:sayHelloResponse"/>
</operation>
</portType>
<binding name="OnBehalfOfServicePortBinding" type="tns:OnBehalfOfServiceIface">
<wsp:PolicyReference URI="#AsymmetricSAML2Policy" />
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="sayHello">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
<wsp:PolicyReference URI="#Input_Policy" />
</input>
<output>
<soap:body use="literal"/>
<wsp:PolicyReference URI="#Output_Policy" />
</output>
</operation>
</binding>
<service name="OnBehalfOfService">
<port name="OnBehalfOfServicePort" binding="tns:OnBehalfOfServicePortBinding">
<soap:address location="http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-onbehalfof/OnBehalfOfService"/>
</port>
</service>
</definitions>
Web Service Interface
The web service provider interface class, OnBehalfOfServiceIface, is a
simple web service definition.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.onbehalfof;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/onbehalfofwssecuritypolicy"
public interface OnBehalfOfServiceIface
@WebMethod
String sayHello();
Web Service Implementation
The web service provider implementation class, OnBehalfOfServiceImpl, is
a simple POJO. It uses the standard WebService annotation to define the
service endpoint and two Apache WSS4J annotations, EndpointProperties
and EndpointProperty used for configuring the endpoint for the CXF
runtime. The WSS4J configuration information provided is for WSS4J’s
Crypto Merlin implementation.
OnBehalfOfServiceImpl is calling the ServiceImpl acting on behalf of the
user. Method setupService performs the requisite configuration setup.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.onbehalfof;
import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.annotations.EndpointProperties;
import org.apache.cxf.annotations.EndpointProperty;
import org.apache.cxf.ws.security.SecurityConstants;
import org.apache.cxf.ws.security.trust.STSClient;
import org.jboss.test.ws.jaxws.samples.wsse.policy.trust.service.ServiceIface;
import org.jboss.test.ws.jaxws.samples.wsse.policy.trust.shared.WSTrustAppUtils;
import javax.jws.WebService;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import java.net.*;
import java.util.Map;
@WebService
portName = "OnBehalfOfServicePort",
serviceName = "OnBehalfOfService",
wsdlLocation = "WEB-INF/wsdl/OnBehalfOfService.wsdl",
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/onbehalfofwssecuritypolicy",
endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.onbehalfof.OnBehalfOfServiceIface"
@EndpointProperties(value = {
@EndpointProperty(key = "ws-security.signature.username", value = "myactaskey"),
@EndpointProperty(key = "ws-security.signature.properties", value = "actasKeystore.properties"),
@EndpointProperty(key = "ws-security.encryption.properties", value = "actasKeystore.properties"),
@EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.onbehalfof.OnBehalfOfCallbackHandler")
public class OnBehalfOfServiceImpl implements OnBehalfOfServiceIface
public String sayHello() {
try {
ServiceIface proxy = setupService();
return "OnBehalfOf " + proxy.sayHello();
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
* @return
* @throws MalformedURLException
private ServiceIface setupService()throws MalformedURLException {
ServiceIface proxy = null;
Bus bus = BusFactory.newInstance().createBus();
try {
BusFactory.setThreadDefaultBus(bus);
final String serviceURL = "http://" + WSTrustAppUtils.getServerHost() + ":8080/jaxws-samples-wsse-policy-trust/SecurityService";
final QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wssecuritypolicy", "SecurityService");
final URL wsdlURL = new URL(serviceURL + "?wsdl");
Service service = Service.create(wsdlURL, serviceName);
proxy = (ServiceIface) service.getPort(ServiceIface.class);
Map<String, Object> ctx = ((BindingProvider) proxy).getRequestContext();
ctx.put(SecurityConstants.CALLBACK_HANDLER, new OnBehalfOfCallbackHandler());
ctx.put(SecurityConstants.SIGNATURE_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"actasKeystore.properties" ));
ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myactaskey" );
ctx.put(SecurityConstants.ENCRYPT_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"../../META-INF/clientKeystore.properties" ));
ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myservicekey");
STSClient stsClient = new STSClient(bus);
Map<String, Object> props = stsClient.getProperties();
props.put(SecurityConstants.USERNAME, "bob");
props.put(SecurityConstants.ENCRYPT_USERNAME, "mystskey");
props.put(SecurityConstants.STS_TOKEN_USERNAME, "myactaskey" );
props.put(SecurityConstants.STS_TOKEN_PROPERTIES,
Thread
.currentThread().getContextClassLoader().getResource(
"actasKeystore.properties" ));
props.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO, "true");
ctx.put(SecurityConstants.STS_CLIENT, stsClient);
} finally {
bus.shutdown(true);
return proxy;
OnBehalfOfCallbackHandler
OnBehalfOfCallbackHandler is a callback handler for the WSS4J Crypto
API. It is used to obtain the password for the private key in the
keystore. This class enables CXF to retrieve the password of the user
name to use for the message signature. This class has been revised to
return the passwords for this service, myactaskey and the "OnBehalfOf"
user, alice.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.onbehalfof;
import org.jboss.wsf.stack.cxf.extensions.security.PasswordCallbackHandler;
import java.util.HashMap;
import java.util.Map;
public class OnBehalfOfCallbackHandler extends PasswordCallbackHandler {
public OnBehalfOfCallbackHandler()
super(getInitMap());
private static Map<String, String> getInitMap()
Map<String, String> passwords = new HashMap<String, String>();
passwords.put("myactaskey", "aspass");
passwords.put("alice", "clarinet");
passwords.put("bob", "trombone");
return passwords;
Web service requester
This section examines the ws-requester elements from the basic WS-Trust
scenario that have been changed to address the needs of the OnBehalfOf
example. The component is
Web service requester Implementation
The OnBehalfOf ws-requester, the client, uses standard procedures for
creating a reference to the web service in the first four lines. To
address the endpoint security requirements, the web service’s "Request
Context" is configured via the BindingProvider. Information needed in
the message generation is provided through it. The OnBehalfOf user,
alice, is declared in this section and the callbackHandler,
UsernameTokenCallbackHandler is provided to the STSClient for generation
of the contents for the OnBehalfOf message element. In this example a
STSClient object is created and provided to the proxy’s request context.
The alternative is to provide keys tagged with the ".it" suffix as was
done in
Basic Scenario client. The use of OnBehalfOf is configured by the
method call stsClient.setOnBehalfOf. The alternative is to use the key
SecurityConstants.STS_TOKEN_ON_BEHALF_OF and a value in the props map.
final QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/onbehalfofwssecuritypolicy", "OnBehalfOfService");
final URL wsdlURL = new URL(serviceURL + "?wsdl");
Service service = Service.create(wsdlURL, serviceName);
OnBehalfOfServiceIface proxy = (OnBehalfOfServiceIface) service.getPort(OnBehalfOfServiceIface.class);
Bus bus = BusFactory.newInstance().createBus();
try {
BusFactory.setThreadDefaultBus(bus);
Map<String, Object> ctx = proxy.getRequestContext();
ctx.put(SecurityConstants.CALLBACK_HANDLER, new ClientCallbackHandler());
ctx.put(SecurityConstants.ENCRYPT_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myactaskey");
ctx.put(SecurityConstants.SIGNATURE_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myclientkey");
// user and password OnBehalfOf user
// UsernameTokenCallbackHandler will extract this information when called
ctx.put(SecurityConstants.USERNAME,"alice");
ctx.put(SecurityConstants.PASSWORD, "clarinet");
STSClient stsClient = new STSClient(bus);
// Providing the STSClient the mechanism to create the claims contents for OnBehalfOf
stsClient.setOnBehalfOf(new UsernameTokenCallbackHandler());
Map<String, Object> props = stsClient.getProperties();
props.put(SecurityConstants.CALLBACK_HANDLER, new ClientCallbackHandler());
props.put(SecurityConstants.ENCRYPT_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
props.put(SecurityConstants.ENCRYPT_USERNAME, "mystskey");
props.put(SecurityConstants.STS_TOKEN_USERNAME, "myclientkey");
props.put(SecurityConstants.STS_TOKEN_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
props.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO, "true");
ctx.put(SecurityConstants.STS_CLIENT, stsClient);
} finally {
bus.shutdown(true);
proxy.sayHello();
SAML Bearer Assertion Scenario
WS-Trust deals with managing software security tokens. A SAML assertion
is a type of security token. In the SAML Bearer scenario, the service
provider automatically trusts that the incoming SOAP request came from
the subject defined in the SAML token after the service verifies the
tokens signature.
Implementation of this scenario has the following requirements.
SAML tokens with a Bearer subject confirmation method must be
protected so the token can not be snooped. In most cases, a bearer token
combined with HTTPS is sufficient to prevent "a man in the middle"
getting possession of the token. This means a security policy that uses
a sp:TransportBinding and sp:HttpsToken.
A bearer token has no encryption or signing keys associated with it,
therefore a sp:IssuedToken of bearer keyType should be used with a
sp:SupportingToken or a sp:SignedSupportingTokens.
Web service provider WSDL
The web service provider is a contract-first endpoint. All the WS-trust
and security policies for it are declared in WSDL, BearerService.wsdl.
For this scenario a ws-requester is required to present a SAML 2.0
Bearer token issued from a designed STS. The address of the STS is
provided in the WSDL. HTTPS, a TransportBinding and HttpsToken policy
are used to protect the SOAP body of messages that pass back and forth
between ws-requester and ws-provider. A detailed explanation of the
security settings are provided in the comments in the listing below.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions targetNamespace="http://www.jboss.org/jbossws/ws-extensions/bearerwssecuritypolicy"
name="BearerService"
xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/bearerwssecuritypolicy"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsaws="http://www.w3.org/2005/08/addressing"
xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex"
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
xmlns:t="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<types>
<xsd:schema>
<xsd:import namespace="http://www.jboss.org/jbossws/ws-extensions/bearerwssecuritypolicy"
schemaLocation="BearerService_schema1.xsd"/>
</xsd:schema>
</types>
<message name="sayHello">
<part name="parameters" element="tns:sayHello"/>
</message>
<message name="sayHelloResponse">
<part name="parameters" element="tns:sayHelloResponse"/>
</message>
<portType name="BearerIface">
<operation name="sayHello">
<input message="tns:sayHello"/>
<output message="tns:sayHelloResponse"/>
</operation>
</portType>
The wsp:PolicyReference binds the security requirments on all the endpoints.
The wsp:Policy wsu:Id="#TransportSAML2BearerPolicy" element is defined later in this file.
<binding name="BearerServicePortBinding" type="tns:BearerIface">
<wsp:PolicyReference URI="#TransportSAML2BearerPolicy" />
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"
/>
<operation name="sayHello">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
The soap:address has been defined to use JBoss's https port, 8443. This is
set in conjunction with the sp:TransportBinding policy for https.
<service name="BearerService">
<port name="BearerServicePort" binding="tns:BearerServicePortBinding">
<soap:address location="https://@jboss.bind.address@:8443/jaxws-samples-wsse-policy-trust-bearer/BearerService"/>
</port>
</service>
<wsp:Policy wsu:Id="TransportSAML2BearerPolicy">
<wsp:ExactlyOne>
<wsp:All>
The wsam:Addressing element, indicates that the endpoints of this
web service MUST conform to the WS-Addressing specification. The
attribute wsp:Optional="false" enforces this assertion.
<wsam:Addressing wsp:Optional="false">
<wsp:Policy />
</wsam:Addressing>
The sp:TransportBinding element indicates that security is provided by the
message exchange transport medium, https. WS-Security policy specification
defines the sp:HttpsToken for use in exchanging messages transmitted over HTTPS.
<sp:TransportBinding
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:TransportToken>
<wsp:Policy>
<sp:HttpsToken>
<wsp:Policy/>
</sp:HttpsToken>
</wsp:Policy>
</sp:TransportToken>
The sp:AlgorithmSuite element, requires the TripleDes algorithm suite
be used in performing cryptographic operations.
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:TripleDes />
</wsp:Policy>
</sp:AlgorithmSuite>
The sp:Layout element, indicates the layout rules to apply when adding
items to the security header. The sp:Lax sub-element indicates items
are added to the security header in any order that conforms to
WSS: SOAP Message Security.
<sp:Layout>
<wsp:Policy>
<sp:Lax />
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp />
</wsp:Policy>
</sp:TransportBinding>
The sp:SignedSupportingTokens element causes the supporting tokens
to be signed using the primary token that is used to sign the message.
<sp:SignedSupportingTokens
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
The sp:IssuedToken element asserts that a SAML 2.0 security token of type
Bearer is expected from the STS. The
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
attribute instructs the runtime to include the initiator's public key
with every message sent to the recipient.
The sp:RequestSecurityTokenTemplate element directs that all of the
children of this element will be copied directly into the body of the
RequestSecurityToken (RST) message that is sent to the STS when the
initiator asks the STS to issue a token.
<sp:IssuedToken
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<sp:RequestSecurityTokenTemplate>
<t:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</t:TokenType>
<t:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</t:KeyType>
</sp:RequestSecurityTokenTemplate>
<wsp:Policy>
<sp:RequireInternalReference />
</wsp:Policy>
The sp:Issuer element defines the STS's address and endpoint information
This information is used by the STSClient.
<sp:Issuer>
<wsaws:Address>http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-sts-bearer/SecurityTokenService</wsaws:Address>
<wsaws:Metadata
xmlns:wsdli="http://www.w3.org/2006/01/wsdl-instance"
wsdli:wsdlLocation="http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-sts-bearer/SecurityTokenService?wsdl">
<wsaw:ServiceName
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:stsns="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
EndpointName="UT_Port">stsns:SecurityTokenService</wsaw:ServiceName>
</wsaws:Metadata>
</sp:Issuer>
</sp:IssuedToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
The sp:Wss11 element declares WSS: SOAP Message Security 1.1 options
to be supported by the STS. These particular elements generally refer
to how keys are referenced within the SOAP envelope. These are normally
handled by CXF.
<sp:Wss11>
<wsp:Policy>
<sp:MustSupportRefIssuerSerial />
<sp:MustSupportRefThumbprint />
<sp:MustSupportRefEncryptedKey />
</wsp:Policy>
</sp:Wss11>
The sp:Trust13 element declares controls for WS-Trust 1.3 options.
They are policy assertions related to exchanges specifically with
client and server challenges and entropy behaviors. Again these are
normally handled by CXF.
<sp:Trust13>
<wsp:Policy>
<sp:MustSupportIssuedTokens />
<sp:RequireClientEntropy />
<sp:RequireServerEntropy />
</wsp:Policy>
</sp:Trust13>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
</definitions>
This web service is using https, therefore the JBoss server must be
configured to provide SSL support in the Web subsystem. There are 2
components to SSL configuration.
Follow the directions in the, " Using the pure Java implementation
supplied by JSSE" section in the
SSL Setup
Guide.
Here is an example of an SSL connector declaration.
<subsystem xmlns="urn:jboss:domain:web:1.4" default-virtual-server="default-host" native="false">
.....
<connector name="jbws-https-connector" protocol="HTTP/1.1" scheme="https" socket-binding="https" secure="true" enabled="true">
<ssl key-alias="tomcat" password="changeit" certificate-key-file="/myJbossHome/security/test.keystore" verify-client="false"/>
</connector>
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.bearer;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/bearerwssecuritypolicy"
public interface BearerIface
@WebMethod
String sayHello();
Web service Implementation
The web service provider implementation class, BearerImpl, is a simple
POJO. It uses the standard WebService annotation to define the service
endpoint. In addition there are two Apache CXF annotations,
EndpointProperties and EndpointProperty used for configuring the
endpoint for the CXF runtime. These annotations come from the
Apache WSS4J project, which provides a
Java implementation of the primary WS-Security standards for Web
Services. These annotations are programmatically adding properties to
the endpoint. With plain Apache CXF, these properties are often set via
the <jaxws:properties> element on the <jaxws:endpoint> element in the
Spring config; these annotations allow the properties to be configured
in the code.
WSS4J uses the Crypto interface to get keys and certificates for
signature creation/verification, as is asserted by the WSDL for this
service. The WSS4J configuration information being provided by
BearerImpl is for Crypto’s Merlin implementation. More information will
be provided about this in the keystore section.
Because the web service provider automatically trusts that the incoming
SOAP request came from the subject defined in the SAML token there is no
need for a Crypto callbackHandler class or a signature username, unlike
in prior examples, however in order to verify the message signature, the
Java properties file that contains the (Merlin) crypto configuration
information is still required.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.bearer;
import org.apache.cxf.annotations.EndpointProperties;
import org.apache.cxf.annotations.EndpointProperty;
import javax.jws.WebService;
@WebService
portName = "BearerServicePort",
serviceName = "BearerService",
wsdlLocation = "WEB-INF/wsdl/BearerService.wsdl",
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/bearerwssecuritypolicy",
endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.bearer.BearerIface"
@EndpointProperties(value = {
@EndpointProperty
(key = "ws-security.signature.properties", value = "serviceKeystore.properties")
public class BearerImpl implements BearerIface
public String sayHello()
return "Bearer WS-Trust Hello World!";
Crypto properties and keystore files
WSS4J’s Crypto implementation is loaded and configured via a Java
properties file that contains Crypto configuration data. The file
contains implementation-specific properties such as a keystore location,
password, default alias and the like. This application is using the
Merlin implementation. File serviceKeystore.properties contains this
information.
File servicestore.jks, is a Java KeyStore (JKS) repository. It contains
self signed certificates for myservicekey and mystskey. Self signed
certificates are not appropriate for production use.
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=sspass
org.apache.ws.security.crypto.merlin.keystore.alias=myservicekey
org.apache.ws.security.crypto.merlin.keystore.file=servicestore.jks
When deployed on WildFly this application requires access to the JBossWs
and CXF APIs provided in module org.jboss.ws.cxf.jbossws-cxf-client. The
dependency statement directs the server to provide them at deployment.
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.7.0_25-b15 (Oracle Corporation)
Dependencies: org.jboss.ws.cxf.jbossws-cxf-client
Bearer Security Token Service
This section examines the crucial elements in providing the Security
Token Service functionality for providing a SAML Bearer token. The
components that will be discussed are.
The STS requires a JBoss security domain be configured. The
jboss-web.xml descriptor declares a named security
domain,"JBossWS-trust-sts" to be used by this service for
authentication. This security domain requires two properties files and
the addition of a security-domain declaration in the JBoss server
configuration file.
For this scenario the domain needs to contain user alice, password
clarinet, and role friend. See the listings below for
jbossws-users.properties and jbossws-roles.properties. In addition the
following XML must be added to the JBoss security subsystem in the
server configuration file. Replace " SOME_PATH" with appropriate
information.
<security-domain name="JBossWS-trust-sts">
<authentication>
<login-module code="UsersRoles" flag="required">
<module-option name="usersProperties" value="/SOME_PATH/jbossws-users.properties"/>
<module-option name="unauthenticatedIdentity" value="anonymous"/>
<module-option name="rolesProperties" value="/SOME_PATH/jbossws-roles.properties"/>
</login-module>
</authentication>
</security-domain>
jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 2.4//EN" ">
<jboss-web>
<security-domain>java:/jaas/JBossWS-trust-sts</security-domain>
</jboss-web>
jbossws-users.properties
# A sample users.properties file for use with the UsersRolesLoginModule
alice=clarinet
jbossws-roles.properties
# A sample roles.properties file for use with the UsersRolesLoginModule
alice=friend
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
targetNamespace="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
xmlns:tns="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
xmlns:wstrust="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsap10="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata">
<wsdl:types>
<xs:schema elementFormDefault="qualified"
targetNamespace='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>
<xs:element name='RequestSecurityToken'
type='wst:AbstractRequestSecurityTokenType'/>
<xs:element name='RequestSecurityTokenResponse'
type='wst:AbstractRequestSecurityTokenType'/>
<xs:complexType name='AbstractRequestSecurityTokenType'>
<xs:sequence>
<xs:any namespace='##any' processContents='lax' minOccurs='0'
maxOccurs='unbounded'/>
</xs:sequence>
<xs:attribute name='Context' type='xs:anyURI' use='optional'/>
<xs:anyAttribute namespace='##other' processContents='lax'/>
</xs:complexType>
<xs:element name='RequestSecurityTokenCollection'
type='wst:RequestSecurityTokenCollectionType'/>
<xs:complexType name='RequestSecurityTokenCollectionType'>
<xs:sequence>
<xs:element name='RequestSecurityToken'
type='wst:AbstractRequestSecurityTokenType' minOccurs='2'
maxOccurs='unbounded'/>
</xs:sequence>
</xs:complexType>
<xs:element name='RequestSecurityTokenResponseCollection'
type='wst:RequestSecurityTokenResponseCollectionType'/>
<xs:complexType name='RequestSecurityTokenResponseCollectionType'>
<xs:sequence>
<xs:element ref='wst:RequestSecurityTokenResponse' minOccurs='1'
maxOccurs='unbounded'/>
</xs:sequence>
<xs:anyAttribute namespace='##other' processContents='lax'/>
</xs:complexType>
</xs:schema>
</wsdl:types>
<!-- WS-Trust defines the following GEDs -->
<wsdl:message name="RequestSecurityTokenMsg">
<wsdl:part name="request" element="wst:RequestSecurityToken"/>
</wsdl:message>
<wsdl:message name="RequestSecurityTokenResponseMsg">
<wsdl:part name="response"
element="wst:RequestSecurityTokenResponse"/>
</wsdl:message>
<wsdl:message name="RequestSecurityTokenCollectionMsg">
<wsdl:part name="requestCollection"
element="wst:RequestSecurityTokenCollection"/>
</wsdl:message>
<wsdl:message
name="RequestSecurityTokenResponseCollectionMsg">
<wsdl:part name="responseCollection"
element="wst:RequestSecurityTokenResponseCollection"/>
</wsdl:message>
<!-- This portType an example of a Requestor (or other) endpoint that
Accepts SOAP-based challenges from a Security Token Service -->
<wsdl:portType name="WSSecurityRequestor">
<wsdl:operation name="Challenge">
<wsdl:input message="tns:RequestSecurityTokenResponseMsg"/>
<wsdl:output message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
</wsdl:portType>
<!-- This portType is an example of an STS supporting full protocol -->
The wsdl:portType and data types are XML elements defined by the
WS_Trust specification. The wsdl:portType defines the endpoints
supported in the STS implementation. This WSDL defines all operations
that an STS implementation can support.
<wsdl:portType name="STS">
<wsdl:operation name="Cancel">
<wsdl:input
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Cancel"
message="tns:RequestSecurityTokenMsg"/>
<wsdl:output
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/CancelFinal"
message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
<wsdl:operation name="Issue">
<wsdl:input
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue"
message="tns:RequestSecurityTokenMsg"/>
<wsdl:output
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTRC/IssueFinal"
message="tns:RequestSecurityTokenResponseCollectionMsg"/>
</wsdl:operation>
<wsdl:operation name="Renew">
<wsdl:input
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Renew"
message="tns:RequestSecurityTokenMsg"/>
<wsdl:output
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/RenewFinal"
message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
<wsdl:operation name="Validate">
<wsdl:input
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Validate"
message="tns:RequestSecurityTokenMsg"/>
<wsdl:output
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/ValidateFinal"
message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
<wsdl:operation name="KeyExchangeToken">
<wsdl:input
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/KET"
message="tns:RequestSecurityTokenMsg"/>
<wsdl:output
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/KETFinal"
message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
<wsdl:operation name="RequestCollection">
<wsdl:input message="tns:RequestSecurityTokenCollectionMsg"/>
<wsdl:output message="tns:RequestSecurityTokenResponseCollectionMsg"/>
</wsdl:operation>
</wsdl:portType>
<!-- This portType is an example of an endpoint that accepts
Unsolicited RequestSecurityTokenResponse messages -->
<wsdl:portType name="SecurityTokenResponseService">
<wsdl:operation name="RequestSecurityTokenResponse">
<wsdl:input message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
</wsdl:portType>
The wsp:PolicyReference binds the security requirments on all the STS endpoints.
The wsp:Policy wsu:Id="UT_policy" element is later in this file.
<wsdl:binding name="UT_Binding" type="wstrust:STS">
<wsp:PolicyReference URI="#UT_policy"/>
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="Issue">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue"/>
<wsdl:input>
<wsp:PolicyReference
URI="#Input_policy"/>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<wsp:PolicyReference
URI="#Output_policy"/>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Validate">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Validate"/>
<wsdl:input>
<wsp:PolicyReference
URI="#Input_policy"/>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<wsp:PolicyReference
URI="#Output_policy"/>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Cancel">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Cancel"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Renew">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Renew"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal
"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="KeyExchangeToken">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/KeyExchangeToken"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="RequestCollection">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/RequestCollection"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="SecurityTokenService">
<wsdl:port name="UT_Port" binding="tns:UT_Binding">
<soap:address location="http://localhost:8080/SecurityTokenService/UT"/>
</wsdl:port>
</wsdl:service>
<wsp:Policy wsu:Id="UT_policy">
<wsp:ExactlyOne>
<wsp:All>
The sp:UsingAddressing element, indicates that the endpoints of this
web service conforms to the WS-Addressing specification. More detail
can be found here: [http://www.w3.org/TR/2006/CR-ws-addr-wsdl-20060529]
<wsap10:UsingAddressing/>
The sp:SymmetricBinding element indicates that security is provided
at the SOAP layer and any initiator must authenticate itself by providing
WSS UsernameToken credentials.
<sp:SymmetricBinding
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
In a symmetric binding, the keys used for encrypting and signing in both
directions are derived from a single key, the one specified by the
sp:ProtectionToken element. The sp:X509Token sub-element declares this
key to be a X.509 certificate and the
IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never"
attribute adds the requirement that the token MUST NOT be included in
any messages sent between the initiator and the recipient; rather, an
external reference to the token should be used. Lastly the WssX509V3Token10
sub-element declares that the Username token presented by the initiator
should be compliant with Web Services Security UsernameToken Profile
1.0 specification. [ http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf ]
<sp:ProtectionToken>
<wsp:Policy>
<sp:X509Token
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
<wsp:Policy>
<sp:RequireDerivedKeys/>
<sp:RequireThumbprintReference/>
<sp:WssX509V3Token10/>
</wsp:Policy>
</sp:X509Token>
</wsp:Policy>
</sp:ProtectionToken>
The sp:AlgorithmSuite element, requires the Basic256 algorithm suite
be used in performing cryptographic operations.
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256/>
</wsp:Policy>
</sp:AlgorithmSuite>
The sp:Layout element, indicates the layout rules to apply when adding
items to the security header. The sp:Lax sub-element indicates items
are added to the security header in any order that conforms to
WSS: SOAP Message Security.
<sp:Layout>
<wsp:Policy>
<sp:Lax/>
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp/>
<sp:EncryptSignature/>
<sp:OnlySignEntireHeadersAndBody/>
</wsp:Policy>
</sp:SymmetricBinding>
The sp:SignedSupportingTokens element declares that the security header
of messages must contain a sp:UsernameToken and the token must be signed.
The attribute IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"
on sp:UsernameToken indicates that the token MUST be included in all
messages sent from initiator to the recipient and that the token MUST
NOT be included in messages sent from the recipient to the initiator.
And finally the element sp:WssUsernameToken10 is a policy assertion
indicating the Username token should be as defined in Web Services
Security UsernameToken Profile 1.0
<sp:SignedSupportingTokens
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:UsernameToken
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10/>
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
The sp:Wss11 element declares WSS: SOAP Message Security 1.1 options
to be supported by the STS. These particular elements generally refer
to how keys are referenced within the SOAP envelope. These are normally
handled by CXF.
<sp:Wss11
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:MustSupportRefKeyIdentifier/>
<sp:MustSupportRefIssuerSerial/>
<sp:MustSupportRefThumbprint/>
<sp:MustSupportRefEncryptedKey/>
</wsp:Policy>
</sp:Wss11>
The sp:Trust13 element declares controls for WS-Trust 1.3 options.
They are policy assertions related to exchanges specifically with
client and server challenges and entropy behaviors. Again these are
normally handled by CXF.
<sp:Trust13
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:MustSupportIssuedTokens/>
<sp:RequireClientEntropy/>
<sp:RequireServerEntropy/>
</wsp:Policy>
</sp:Trust13>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="Input_policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:SignedParts
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<sp:Body/>
<sp:Header Name="To"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="From"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="FaultTo"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="ReplyTo"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="MessageID"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="RelatesTo"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="Action"
Namespace="http://www.w3.org/2005/08/addressing"/>
</sp:SignedParts>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="Output_policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:SignedParts
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<sp:Body/>
<sp:Header Name="To"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="From"
Namespace="http://www.w3.org/2005/08/addressing
"/>
<sp:Header Name="FaultTo"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="ReplyTo"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="MessageID"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="RelatesTo"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="Action"
Namespace="http://www.w3.org/2005/08/addressing"/>
</sp:SignedParts>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
</wsdl:definitions>
STS’s implementation class
The Apache CXF’s STS, SecurityTokenServiceProvider, is a web service
provider that is compliant with the protocols and functionality defined
by the WS-Trust specification. It has a modular architecture. Many of
its components are configurable or replaceable and there are many
optional features that are enabled by implementing and configuring
plug-ins. Users can customize their own STS by extending from
SecurityTokenServiceProvider and overriding the default settings.
Extensive information about the CXF’s STS configurable and pluggable
components can be found
here.
This STS implementation class, SampleSTSBearer, is a POJO that extends
from SecurityTokenServiceProvider. Note that the class is defined with a
WebServiceProvider annotation and not a WebService annotation. This
annotation defines the service as a Provider-based endpoint, meaning it
supports a more messaging-oriented approach to Web services. In
particular, it signals that the exchanged messages will be XML documents
of some type. SecurityTokenServiceProvider is an implementation of the
javax.xml.ws.Provider interface. In comparison the WebService annotation
defines a (service endpoint interface) SEI-based endpoint which supports
message exchange via SOAP envelopes.
As was done in the BearerImpl class, the WSS4J annotations
EndpointProperties and EndpointProperty are providing endpoint
configuration for the CXF runtime. The first EndpointProperty statement
in the listing is declaring the user’s name to use for the message
signature. It is used as the alias name in the keystore to get the
user’s cert and private key for signature. The next two EndpointProperty
statements declares the Java properties file that contains the (Merlin)
crypto configuration information. In this case both for signing and
encrypting the messages. WSS4J reads this file and extra required
information for message handling. The last EndpointProperty statement
declares the STSBearerCallbackHandler implementation class. It is used
to obtain the user’s password for the certificates in the keystore file.
In this implementation we are customizing the operations of token
issuance, token validation and their static properties.
StaticSTSProperties is used to set select properties for configuring
resources in the STS. You may think this is a duplication of the
settings made with the WSS4J annotations. The values are the same but
the underlaying structures being set are different, thus this
information must be declared in both places.
The setIssuer setting is important because it uniquely identifies the
issuing STS. The issuer string is embedded in issued tokens and, when
validating tokens, the STS checks the issuer string value. Consequently,
it is important to use the issuer string in a consistent way, so that
the STS can recognize the tokens that it has issued.
The setEndpoints call allows the declaration of a set of allowed token
recipients by address. The addresses are specified as reg-ex patterns.
TokenIssueOperation has a modular structure. This allows custom
behaviors to be injected into the processing of messages. In this case
we are overriding the SecurityTokenServiceProvider’s default behavior
and performing SAML token processing. CXF provides an implementation of
a SAMLTokenProvider which we are using rather than writing our own.
Learn more about the SAMLTokenProvider
here.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.stsbearer;
import org.apache.cxf.annotations.EndpointProperties;
import org.apache.cxf.annotations.EndpointProperty;
import org.apache.cxf.sts.StaticSTSProperties;
import org.apache.cxf.sts.operation.TokenIssueOperation;
import org.apache.cxf.sts.service.ServiceMBean;
import org.apache.cxf.sts.service.StaticService;
import org.apache.cxf.sts.token.provider.SAMLTokenProvider;
import org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider;
import javax.xml.ws.WebServiceProvider;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
@WebServiceProvider(serviceName = "SecurityTokenService",
portName = "UT_Port",
targetNamespace = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/",
wsdlLocation = "WEB-INF/wsdl/bearer-ws-trust-1.4-service.wsdl")
//be sure to have dependency on org.apache.cxf module when on AS7, otherwise Apache CXF annotations are ignored
@EndpointProperties(value = {
@EndpointProperty(key = "ws-security.signature.username", value = "mystskey"),
@EndpointProperty(key = "ws-security.signature.properties", value = "stsKeystore.properties"),
@EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.stsbearer.STSBearerCallbackHandler")
public class SampleSTSBearer extends SecurityTokenServiceProvider
public SampleSTSBearer() throws Exception
super();
StaticSTSProperties props = new StaticSTSProperties();
props.setSignatureCryptoProperties("stsKeystore.properties");
props.setSignatureUsername("mystskey");
props.setCallbackHandlerClass(STSBearerCallbackHandler.class.getName());
props.setEncryptionCryptoProperties("stsKeystore.properties");
props.setEncryptionUsername("myservicekey");
props.setIssuer("DoubleItSTSIssuer");
List<ServiceMBean> services = new LinkedList<ServiceMBean>();
StaticService service = new StaticService();
service.setEndpoints(Arrays.asList(
"https://localhost:(\\d)*/jaxws-samples-wsse-policy-trust-bearer/BearerService",
"https://\\[::1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-bearer/BearerService",
"https://\\[0:0:0:0:0:0:0:1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-bearer/BearerService"
services.add(service);
TokenIssueOperation issueOperation = new TokenIssueOperation();
issueOperation.getTokenProviders().add(new SAMLTokenProvider());
issueOperation.setServices(services);
issueOperation.setStsProperties(props);
this.setIssueOperation(issueOperation);
STSBearerCallbackHandler
STSBearerCallbackHandler is a callback handler for the WSS4J Crypto API.
It is used to obtain the password for the private key in the keystore.
This class enables CXF to retrieve the password of the user name to use
for the message signature.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.stsbearer;
import org.jboss.wsf.stack.cxf.extensions.security.PasswordCallbackHandler;
import java.util.HashMap;
import java.util.Map;
public class STSBearerCallbackHandler extends PasswordCallbackHandler
public STSBearerCallbackHandler()
super(getInitMap());
private static Map<String, String> getInitMap()
Map<String, String> passwords = new HashMap<String, String>();
passwords.put("mystskey", "stskpass");
passwords.put("alice", "clarinet");
return passwords;
Crypto properties and keystore files
WSS4J’s Crypto implementation is loaded and configured via a Java
properties file that contains Crypto configuration data. The file
contains implementation-specific properties such as a keystore location,
password, default alias and the like. This application is using the
Merlin implementation. File stsKeystore.properties contains this
information.
File servicestore.jks, is a Java KeyStore (JKS) repository. It contains
self signed certificates for myservicekey and mystskey. Self signed
certificates are not appropriate for production use.
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=stsspass
org.apache.ws.security.crypto.merlin.keystore.file=stsstore.jks
When deployed on WildFly, this application requires access to the
JBossWs and CXF APIs provided in modules
org.jboss.ws.cxf.jbossws-cxf-client and org.apache.cxf. The Apache CXF
internals, org.apache.cxf.impl, are needed to build the STS
configuration in the SampleSTS constructor. The dependency statement
directs the server to provide them at deployment.
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.8.2
Created-By: 1.7.0_25-b15 (Oracle Corporation)
Dependencies: org.jboss.ws.cxf.jbossws-cxf-client,org.apache.cxf.impl
Web service requester
This section examines the crucial elements in calling a web service that
implements endpoint security as described in the SAML Bearer scenario.
The components that will be discussed are.
Web service requester Implementation
The ws-requester, the client, uses standard procedures for creating a
reference to the web service. To address the endpoint security
requirements, the web service’s "Request Context" is configured with the
information needed in message generation. In addition, the STSClient
that communicates with the STS is configured with similar values. Note
the key strings ending with a ".it" suffix. This suffix flags these
settings as belonging to the STSClient. The internal CXF code assigns
this information to the STSClient that is auto-generated for this
service call.
There is an alternate method of setting up the STSCLient. The user may
provide their own instance of the STSClient. The CXF code will use this
object and not auto-generate one. When providing the STSClient in this
way, the user must provide a org.apache.cxf.Bus for it and the
configuration keys must not have the ".it" suffix. This is used in the
ActAs and OnBehalfOf examples.
String serviceURL = "https://" + getServerHost() + ":8443/jaxws-samples-wsse-policy-trust-bearer/BearerService";
final QName serviceName = new QName
("http://www.jboss.org/jbossws/ws-extensions/bearerwssecuritypolicy", "BearerService");
Service service = Service.create(new URL(serviceURL + "?wsdl"), serviceName);
BearerIface proxy = (BearerIface) service.getPort(BearerIface.class);
Map<String, Object> ctx = ((BindingProvider)proxy).getRequestContext();
// set the security related configuration information for the service "request"
ctx.put(SecurityConstants.CALLBACK_HANDLER, new ClientCallbackHandler());
ctx.put(SecurityConstants.SIGNATURE_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.ENCRYPT_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myclientkey");
ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myservicekey");
//-- Configuration settings that will be transfered to the STSClient
// "alice" is the name provided for the WSS Username. Her password will
// be retreived from the ClientCallbackHander by the STSClient.
ctx.put(SecurityConstants.USERNAME + ".it", "alice");
ctx.put(SecurityConstants.CALLBACK_HANDLER + ".it", new ClientCallbackHandler());
ctx.put(SecurityConstants.ENCRYPT_PROPERTIES + ".it",
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.ENCRYPT_USERNAME + ".it", "mystskey");
ctx.put(SecurityConstants.STS_TOKEN_USERNAME + ".it", "myclientkey");
ctx.put(SecurityConstants.STS_TOKEN_PROPERTIES + ".it",
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO + ".it", "true");
proxy.sayHello();
ClientCallbackHandler
https://docs.jboss.org/author/display/JBWS/WS-Trust+and+STS#WS-TrustandSTS-ClientCallbackHandler
ClientCallbackHandler is a callback handler for the WSS4J Crypto API. It
is used to obtain the password for the private key in the keystore. This
class enables CXF to retrieve the password of the user name to use for
the message signature. Note that "alice" and her password have been
provided here. This information is not in the (JKS) keystore but
provided in the WildFly security domain. It was declared in file
jbossws-users.properties.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.shared;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
public class ClientCallbackHandler implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof WSPasswordCallback) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
if ("myclientkey".equals(pc.getIdentifier())) {
pc.setPassword("ckpass");
break;
} else if ("alice".equals(pc.getIdentifier())) {
pc.setPassword("clarinet");
break;
} else if ("bob".equals(pc.getIdentifier())) {
pc.setPassword("trombone");
break;
} else if ("myservicekey".equals(pc.getIdentifier())) { // rls test added for bearer test
pc.setPassword("skpass");
break;
Crypto properties and keystore files
WSS4J’s Crypto implementation is loaded and configured via a Java
properties file that contains Crypto configuration data. The file
contains implementation-specific properties such as a keystore location,
password, default alias and the like. This application is using the
Merlin implementation. File clientKeystore.properties contains this
information.
File clientstore.jks, is a Java KeyStore (JKS) repository. It contains
self signed certificates for myservicekey and mystskey. Self signed
certificates are not appropriate for production use.
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=cspass
org.apache.ws.security.crypto.merlin.keystore.alias=myclientkey
org.apache.ws.security.crypto.merlin.keystore.file=META-INF/clientstore.jks
SAML Holder-Of-Key Assertion Scenario
WS-Trust deals with managing software security tokens. A SAML assertion
is a type of security token. In the Holder-Of-Key method, the STS
creates a SAML token containing the client’s public key and signs the
SAML token with its private key. The client includes the SAML token and
signs the outgoing soap envelope to the web service with its private
key. The web service validates the SOAP message and the SAML token.
Implementation of this scenario has the following requirements.
SAML tokens with a Holder-Of-Key subject confirmation method must be
protected so the token can not be snooped. In most cases, a
Holder-Of-Key token combined with HTTPS is sufficient to prevent "a man
in the middle" getting possession of the token. This means a security
policy that uses a sp:TransportBinding and sp:HttpsToken.
A Holder-Of-Key token has no encryption or signing keys associated
with it, therefore a sp:IssuedToken of SymmetricKey or PublicKey keyType
should be used with a sp:SignedEndorsingSupportingTokens.
Web service provider WSDL
The web service provider is a contract-first endpoint. All the WS-trust
and security policies for it are declared in the WSDL,
HolderOfKeyService.wsdl. For this scenario a ws-requester is required to
present a SAML 2.0 token of SymmetricKey keyType, issued from a designed
STS. The address of the STS is provided in the WSDL. A transport binding
policy is used. The token is declared to be signed and endorsed,
sp:SignedEndorsingSupportingTokens. A detailed explanation of the
security settings are provided in the comments in the listing below.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions targetNamespace="http://www.jboss.org/jbossws/ws-extensions/holderofkeywssecuritypolicy"
name="HolderOfKeyService"
xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/holderofkeywssecuritypolicy"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsaws="http://www.w3.org/2005/08/addressing"
xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex"
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702"
xmlns:t="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
<types>
<xsd:schema>
<xsd:import namespace="http://www.jboss.org/jbossws/ws-extensions/holderofkeywssecuritypolicy"
schemaLocation="HolderOfKeyService_schema1.xsd"/>
</xsd:schema>
</types>
<message name="sayHello">
<part name="parameters" element="tns:sayHello"/>
</message>
<message name="sayHelloResponse">
<part name="parameters" element="tns:sayHelloResponse"/>
</message>
<portType name="HolderOfKeyIface">
<operation name="sayHello">
<input message="tns:sayHello"/>
<output message="tns:sayHelloResponse"/>
</operation>
</portType>
The wsp:PolicyReference binds the security requirments on all the endpoints.
The wsp:Policy wsu:Id="#TransportSAML2HolderOfKeyPolicy" element is defined later in this file.
<binding name="HolderOfKeyServicePortBinding" type="tns:HolderOfKeyIface
">
<wsp:PolicyReference URI="#TransportSAML2HolderOfKeyPolicy" />
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<operation name="sayHello">
<soap:operation soapAction=""/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
The soap:address has been defined to use JBoss's https port, 8443. This is
set in conjunction with the sp:TransportBinding policy for https.
<service name="HolderOfKeyService">
<port name="HolderOfKeyServicePort" binding="tns:HolderOfKeyServicePortBinding">
<soap:address location="https://@jboss.bind.address@:8443/jaxws-samples-wsse-policy-trust-holderofkey/HolderOfKeyService"/>
</port>
</service>
<wsp:Policy wsu:Id="TransportSAML2HolderOfKeyPolicy">
<wsp:ExactlyOne>
<wsp:All>
The wsam:Addressing element, indicates that the endpoints of this
web service MUST conform to the WS-Addressing specification. The
attribute wsp:Optional="false" enforces this assertion.
<wsam:Addressing wsp:Optional="false">
<wsp:Policy />
</wsam:Addressing>
The sp:TransportBinding element indicates that security is provided by the
message exchange transport medium, https. WS-Security policy specification
defines the sp:HttpsToken for use in exchanging messages transmitted over HTTPS.
<sp:TransportBinding
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:TransportToken>
<wsp:Policy>
<sp:HttpsToken>
<wsp:Policy/>
</sp:HttpsToken>
</wsp:Policy>
</sp:TransportToken>
The sp:AlgorithmSuite element, requires the TripleDes algorithm suite
be used in performing cryptographic operations.
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:TripleDes />
</wsp:Policy>
</sp:AlgorithmSuite>
The sp:Layout element, indicates the layout rules to apply when adding
items to the security header. The sp:Lax sub-element indicates items
are added to the security header in any order that conforms to
WSS: SOAP Message Security.
<sp:Layout>
<wsp:Policy>
<sp:Lax />
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp />
</wsp:Policy>
</sp:TransportBinding>
The sp:SignedEndorsingSupportingTokens, when transport level security level is
used there will be no message signature and the signature generated by the
supporting token will sign the Timestamp.
<sp:SignedEndorsingSupportingTokens
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
The sp:IssuedToken element asserts that a SAML 2.0 security token of type
Bearer is expected from the STS. The
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
attribute instructs the runtime to include the initiator's public key
with every message sent to the recipient.
The sp:RequestSecurityTokenTemplate element directs that all of the
children of this element will be copied directly into the body of the
RequestSecurityToken (RST) message that is sent to the STS when the
initiator asks the STS to issue a token.
<sp:IssuedToken
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<sp:RequestSecurityTokenTemplate>
<t:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0</t:TokenType>
KeyType of "SymmetricKey", the client must prove to the WS service that it
possesses a particular symmetric session key.
<t:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/SymmetricKey</t:KeyType>
</sp:RequestSecurityTokenTemplate>
<wsp:Policy>
<sp:RequireInternalReference />
</wsp:Policy>
The sp:Issuer element defines the STS's address and endpoint information
This information is used by the STSClient.
<sp:Issuer>
<wsaws:Address>http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-sts-holderofkey/SecurityTokenService</wsaws:Address>
<wsaws:Metadata
xmlns:wsdli="http://www.w3.org/2006/01/wsdl-instance"
wsdli:wsdlLocation="http://@jboss.bind.address@:8080/jaxws-samples-wsse-policy-trust-sts-holderofkey/SecurityTokenService?wsdl">
<wsaw:ServiceName
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:stsns="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
EndpointName="UT_Port">stsns:SecurityTokenService</wsaw:ServiceName>
</wsaws:Metadata>
</sp:Issuer>
</sp:IssuedToken>
</wsp:Policy>
</sp:SignedEndorsingSupportingTokens>
The sp:Wss11 element declares WSS: SOAP Message Security 1.1 options
to be supported by the STS. These particular elements generally refer
to how keys are referenced within the SOAP envelope. These are normally
handled by CXF.
<sp:Wss11>
<wsp:Policy>
<sp:MustSupportRefIssuerSerial />
<sp:MustSupportRefThumbprint />
<sp:MustSupportRefEncryptedKey />
</wsp:Policy>
</sp:Wss11>
The sp:Trust13 element declares controls for WS-Trust 1.3 options.
They are policy assertions related to exchanges specifically with
client and server challenges and entropy behaviors. Again these are
normally handled by CXF.
<sp:Trust13>
<wsp:Policy>
<sp:MustSupportIssuedTokens />
<sp:RequireClientEntropy />
<sp:RequireServerEntropy />
</wsp:Policy>
</sp:Trust13>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
</definitions>
SSL configuration
https://docs.jboss.org/author/display/JBWS/WS-Trust+and+STS#WS-TrustandSTS-SSLconfiguration
This web service is using https, therefore the JBoss server must be
configured to provide SSL support in the Web subsystem. There are 2
components to SSL configuration.
Follow the directions in the, " Using the pure Java implementation
supplied by JSSE" section in the link:#[SSL Setup
Guide|../../../../../../../../../../display/WFLY8/SSL+setup+guide|||\].
Here is an example of an SSL connector declaration.
<subsystem xmlns="urn:jboss:domain:web:1.4" default-virtual-server="default-host" native="false">
.....
<connector name="jbws-https-connector" protocol="HTTP/1.1" scheme="https" socket-binding="https" secure="true" enabled="true">
<ssl key-alias="tomcat" password="changeit" certificate-key-file="/myJbossHome/security/test.keystore" verify-client="false"/>
</connector>
Web service Interface
The web service provider interface class, HolderOfKeyIface, is a simple
straight forward web service definition.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.holderofkey;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/holderofkeywssecuritypolicy"
public interface HolderOfKeyIface {
@WebMethod
String sayHello();
Web service Implementation
The web service provider implementation class, HolderOfKeyImpl, is a
simple POJO. It uses the standard WebService annotation to define the
service endpoint. In addition there are two Apache CXF annotations,
EndpointProperties and EndpointProperty used for configuring the
endpoint for the CXF runtime. These annotations come from the
Apache WSS4J project, which provides a
Java implementation of the primary WS-Security standards for Web
Services. These annotations are programmatically adding properties to
the endpoint. With plain Apache CXF, these properties are often set via
the <jaxws:properties> element on the <jaxws:endpoint> element in the
Spring config; these annotations allow the properties to be configured
in the code.
WSS4J uses the Crypto interface to get keys and certificates for
signature creation/verification, as is asserted by the WSDL for this
service. The WSS4J configuration information being provided by
HolderOfKeyImpl is for Crypto’s Merlin implementation. More information
will be provided about this in the keystore section.
The first EndpointProperty statement in the listing disables ensurance
of compliance with the Basic Security Profile 1.1. The next
EndpointProperty statements declares the Java properties file that
contains the (Merlin) crypto configuration information. The last
EndpointProperty statement declares the STSHolderOfKeyCallbackHandler
implementation class. It is used to obtain the user’s password for the
certificates in the keystore file.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.holderofkey;
import org.apache.cxf.annotations.EndpointProperties;
import org.apache.cxf.annotations.EndpointProperty;
import javax.jws.WebService
;
@WebService
portName = "HolderOfKeyServicePort",
serviceName = "HolderOfKeyService",
wsdlLocation = "WEB-INF/wsdl/HolderOfKeyService.wsdl",
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/holderofkeywssecuritypolicy",
endpointInterface = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.holderofkey.HolderOfKeyIface"
@EndpointProperties(value = {
@EndpointProperty(key = "ws-security.is-bsp-compliant", value = "false"),
@EndpointProperty(key = "ws-security.signature.properties", value = "serviceKeystore.properties"),
@EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.holderofkey.HolderOfKeyCallbackHandler")
public class HolderOfKeyImpl implements HolderOfKeyIface
public String sayHello()
return "Holder-Of-Key WS-Trust Hello World!";
Crypto properties and keystore files
WSS4J’s Crypto implementation is loaded and configured via a Java
properties file that contains Crypto configuration data. The file
contains implementation-specific properties such as a keystore location,
password, default alias and the like. This application is using the
Merlin implementation. File serviceKeystore.properties contains this
information.
File servicestore.jks, is a Java KeyStore (JKS) repository. It contains
self signed certificates for myservicekey and mystskey. Self signed
certificates are not appropriate for production use.
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=sspass
org.apache.ws.security.crypto.merlin.keystore.alias=myservicekey
org.apache.ws.security.crypto.merlin.keystore.file=servicestore.jks
MANIFEST.MF
https://docs.jboss.org/author/display/JBWS/WS-Trust+and+STS#WS-TrustandSTS-MANIFEST.MF
When deployed on WildFly this application requires access to the JBossWs
and CXF APIs provided in module org.jboss.ws.cxf.jbossws-cxf-client. The
dependency statement directs the server to provide them at deployment.
Manifest-Version:1.0
Ant-Version: Apache Ant1.8.2
Created-By:1.7.0_25-b15 (Oracle Corporation)
Dependencies: org.jboss.ws.cxf.jbossws-cxf-client
Security Token Service
This section examines the crucial elements in providing the Security
Token Service functionality for providing a SAML Holder-Of-Key token.
The components that will be discussed are.
The STS requires a JBoss security domain be configured. The
jboss-web.xml descriptor declares a named security
domain,"JBossWS-trust-sts" to be used by this service for
authentication. This security domain requires two properties files and
the addition of a security-domain declaration in the JBoss server
configuration file.
For this scenario the domain needs to contain user alice, password
clarinet, and role friend. See the listings below for
jbossws-users.properties and jbossws-roles.properties. In addition the
following XML must be added to the JBoss security subsystem in the
server configuration file. Replace " SOME_PATH" with appropriate
information.
<security-domain name="JBossWS-trust-sts">
<authentication>
<login-module code="UsersRoles" flag="required">
<module-option name="usersProperties" value="/SOME_PATH/jbossws-users.properties"/>
<module-option name="unauthenticatedIdentity" value="anonymous"/>
<module-option name="rolesProperties" value="/SOME_PATH/jbossws-roles.properties"/>
</login-module>
</authentication>
</security-domain>
jboss-web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss-web PUBLIC"-//JBoss//DTD Web Application 2.4//EN" ">
<jboss-web>
<security-domain>java:/jaas/JBossWS-trust-sts</security-domain>
</jboss-web>
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions
targetNamespace="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
xmlns:tns="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
xmlns:wstrust="http://docs.oasis-open.org/ws-sx/ws-trust/200512/"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:wsap10="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata">
<wsdl:types>
<xs:schema elementFormDefault="qualified"
targetNamespace='http://docs.oasis-open.org/ws-sx/ws-trust/200512'>
<xs:element name='RequestSecurityToken'
type='wst:AbstractRequestSecurityTokenType'/>
<xs:element name='RequestSecurityTokenResponse'
type='wst:AbstractRequestSecurityTokenType'/>
<xs:complexType name='AbstractRequestSecurityTokenType'>
<xs:sequence>
<xs:any namespace='##any' processContents='lax' minOccurs='0'
maxOccurs='unbounded'/>
</xs:sequence>
<xs:attribute name='Context' type='xs:anyURI' use='optional'/>
<xs:anyAttribute namespace='##other' processContents='lax'/>
</xs:complexType>
<xs:element name='RequestSecurityTokenCollection'
type='wst:RequestSecurityTokenCollectionType'/>
<xs:complexType name='RequestSecurityTokenCollectionType'>
<xs:sequence>
<xs:element name='RequestSecurityToken'
type='wst:AbstractRequestSecurityTokenType' minOccurs='2'
maxOccurs='unbounded'/>
</xs:sequence>
</xs:complexType>
<xs:element name='RequestSecurityTokenResponseCollection'
type='wst:RequestSecurityTokenResponseCollectionType'/>
<xs:complexType name='RequestSecurityTokenResponseCollectionType'>
<xs:sequence>
<xs:element ref='wst:RequestSecurityTokenResponse' minOccurs='1'
maxOccurs='unbounded'/>
</xs:sequence>
<xs:anyAttribute namespace='##other' processContents='lax'/>
</xs:complexType>
</xs:schema>
</wsdl:types>
<!-- WS-Trust defines the following GEDs -->
<wsdl:message name="RequestSecurityTokenMsg">
<wsdl:part name="request"
element="wst:RequestSecurityToken"/>
</wsdl:message>
<wsdl:message name="RequestSecurityTokenResponseMsg">
<wsdl:part name="response"
element="wst:RequestSecurityTokenResponse"/>
</wsdl:message>
<wsdl:message name="RequestSecurityTokenCollectionMsg">
<wsdl:part name="requestCollection"
element="wst:RequestSecurityTokenCollection"/>
</wsdl:message>
<wsdl:message name="RequestSecurityTokenResponseCollectionMsg">
<wsdl:part name="responseCollection"
element="wst:RequestSecurityTokenResponseCollection"/>
</wsdl:message>
<!-- This portType an example of a Requestor (or other) endpoint that
Accepts SOAP-based challenges from a Security Token Service -->
<wsdl:portType name="WSSecurityRequestor">
<wsdl:operation name="Challenge">
<wsdl:input message="tns:RequestSecurityTokenResponseMsg"/>
<wsdl:output message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
</wsdl:portType>
<!-- This portType is an example of an STS supporting full protocol -->
<wsdl:portType name="STS">
<wsdl:operation name="Cancel">
<wsdl:input
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Cancel"
message="tns:RequestSecurityTokenMsg"/>
<wsdl:output
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/CancelFinal"
message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
<wsdl:operation name="Issue">
<wsdl:input
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue"
message="tns:RequestSecurityTokenMsg"/>
<wsdl:output
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTRC/IssueFinal"
message="tns:RequestSecurityTokenResponseCollectionMsg"/>
</wsdl:operation>
<wsdl:operation name="Renew">
<wsdl:input
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Renew"
message="tns:RequestSecurityTokenMsg"/>
<wsdl:output
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/RenewFinal"
message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
<wsdl:operation name="Validate">
<wsdl:input
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Validate"
message="tns:RequestSecurityTokenMsg"/>
<wsdl:output
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/ValidateFinal"
message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
<wsdl:operation name="KeyExchangeToken">
<wsdl:input
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/KET"
message="tns:RequestSecurityTokenMsg"/>
<wsdl:output
wsam:Action="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/KETFinal"
message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
<wsdl:operation name="RequestCollection">
<wsdl:input message="tns:RequestSecurityTokenCollectionMsg"/>
<wsdl:output message="tns:RequestSecurityTokenResponseCollectionMsg"/>
</wsdl:operation>
</wsdl:portType>
<!-- This portType is an example of an endpoint that accepts
Unsolicited RequestSecurityTokenResponse messages -->
<wsdl:portType name="SecurityTokenResponseService">
<wsdl:operation name="RequestSecurityTokenResponse">
<wsdl:input message="tns:RequestSecurityTokenResponseMsg"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="UT_Binding" type="wstrust:STS">
<wsp:PolicyReference URI="#UT_policy"/>
<soap:binding style="document"
transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="Issue">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue"/>
<wsdl:input>
<wsp:PolicyReference
URI="#Input_policy"/>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<wsp:PolicyReference
URI="#Output_policy"/>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Validate">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Validate"/>
<wsdl:input>
<wsp:PolicyReference
URI="#Input_policy"/>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<wsp:PolicyReference
URI="#Output_policy"/>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Cancel">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Cancel"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body
use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="Renew">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Renew"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="KeyExchangeToken">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/KeyExchangeToken"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="RequestCollection">
<soap:operation
soapAction="http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/RequestCollection"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="SecurityTokenService">
<wsdl:port name="UT_Port" binding="tns:UT_Binding">
<soap:address location="http://localhost:8080/SecurityTokenService/UT"/>
</wsdl:port>
</wsdl:service>
<wsp:Policy wsu:Id="UT_policy">
<wsp:ExactlyOne>
<wsp:All>
<wsap10:UsingAddressing/>
<sp:SymmetricBinding
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:ProtectionToken>
<wsp:Policy>
<sp:X509Token
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never">
<wsp:Policy>
<sp:RequireDerivedKeys/>
<sp:RequireThumbprintReference/>
<sp:WssX509V3Token10/>
</wsp:Policy>
</sp:X509Token>
</wsp:Policy>
</sp:ProtectionToken>
<sp:AlgorithmSuite>
<wsp:Policy>
<sp:Basic256/>
</wsp:Policy>
</sp:AlgorithmSuite>
<sp:Layout>
<wsp:Policy>
<sp:Lax/>
</wsp:Policy>
</sp:Layout>
<sp:IncludeTimestamp/>
<sp:EncryptSignature/>
<sp:OnlySignEntireHeadersAndBody/>
</wsp:Policy>
</sp:SymmetricBinding>
<sp:SignedSupportingTokens
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:UsernameToken
sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10/>
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SignedSupportingTokens>
<sp:Wss11
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:MustSupportRefKeyIdentifier/>
<sp:MustSupportRefIssuerSerial/>
<sp:MustSupportRefThumbprint/>
<sp:MustSupportRefEncryptedKey/>
</wsp:Policy>
</sp:Wss11>
<sp:Trust13
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:MustSupportIssuedTokens/>
<sp:RequireClientEntropy/>
<sp:RequireServerEntropy/>
</wsp:Policy>
</sp:Trust13>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="Input_policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:SignedParts
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<sp:Body/>
<sp:Header Name="To"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="From"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="FaultTo"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="ReplyTo"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="MessageID"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="RelatesTo"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="Action"
Namespace="http://www.w3.org/2005/08/addressing"/>
</sp:SignedParts>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
<wsp:Policy wsu:Id="Output_policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:SignedParts
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<sp:Body/>
<sp:Header Name="To"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="From"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="FaultTo"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="ReplyTo"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="MessageID"
Namespace="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="RelatesTo"
Namespace
="http://www.w3.org/2005/08/addressing"/>
<sp:Header Name="Action"
Namespace="http://www.w3.org/2005/08/addressing"/>
</sp:SignedParts>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
</wsdl:definitions>
STS’s implementation class
The Apache CXF’s STS, SecurityTokenServiceProvider, is a web service
provider that is compliant with the protocols and functionality defined
by the WS-Trust specification. It has a modular architecture. Many of
its components are configurable or replaceable and there are many
optional features that are enabled by implementing and configuring
plug-ins. Users can customize their own STS by extending from
SecurityTokenServiceProvider and overriding the default settings.
Extensive information about the CXF’s STS configurable and pluggable
components can be found
here.
This STS implementation class, SampleSTSHolderOfKey, is a POJO that
extends from SecurityTokenServiceProvider. Note that the class is
defined with a WebServiceProvider annotation and not a WebService
annotation. This annotation defines the service as a Provider-based
endpoint, meaning it supports a more messaging-oriented approach to Web
services. In particular, it signals that the exchanged messages will be
XML documents of some type. SecurityTokenServiceProvider is an
implementation of the javax.xml.ws.Provider interface. In comparison the
WebService annotation defines a (service endpoint interface) SEI-based
endpoint which supports message exchange via SOAP envelopes.
As was done in the HolderOfKeyImpl class, the WSS4J annotations
EndpointProperties and EndpointProperty are providing endpoint
configuration for the CXF runtime. The first EndpointProperty statements
declares the Java properties file that contains the (Merlin) crypto
configuration information. WSS4J reads this file and extra required
information for message handling. The last EndpointProperty statement
declares the STSHolderOfKeyCallbackHandler implementation class. It is
used to obtain the user’s password for the certificates in the keystore
file.
In this implementation we are customizing the operations of token
issuance and their static properties.
StaticSTSProperties is used to set select properties for configuring
resources in the STS. You may think this is a duplication of the
settings made with the WSS4J annotations. The values are the same but
the underlaying structures being set are different, thus this
information must be declared in both places.
The setIssuer setting is important because it uniquely identifies the
issuing STS. The issuer string is embedded in issued tokens and, when
validating tokens, the STS checks the issuer string value. Consequently,
it is important to use the issuer string in a consistent way, so that
the STS can recognize the tokens that it has issued.
The setEndpoints call allows the declaration of a set of allowed token
recipients by address. The addresses are specified as reg-ex patterns.
TokenIssueOperation has a modular structure. This allows custom
behaviors to be injected into the processing of messages. In this case
we are overriding the SecurityTokenServiceProvider’s default behavior
and performing SAML token processing. CXF provides an implementation of
a SAMLTokenProvider which we are using rather than writing our own.
Learn more about the SAMLTokenProvider
here.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.stsholderofkey;
import org.apache.cxf.annotations.EndpointProperties;
import org.apache.cxf.annotations.EndpointProperty;
import org.apache.cxf.sts.StaticSTSProperties;
import org.apache.cxf.sts.operation.TokenIssueOperation;
import org.apache.cxf.sts.service.ServiceMBean;
import org.apache.cxf.sts.service.StaticService;
import org.apache.cxf.sts.token.provider.SAMLTokenProvider;
import org.apache.cxf.ws.security.sts.provider.SecurityTokenServiceProvider;
import javax.xml.ws.WebServiceProvider;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
* User: rsearls
* Date: 3/14/14
@WebServiceProvider(serviceName = "SecurityTokenService",
portName = "UT_Port",
targetNamespace = "http://docs.oasis-open.org/ws-sx/ws-trust/200512/",
wsdlLocation = "WEB-INF/wsdl/holderofkey-ws-trust-1.4-service.wsdl")
//be sure to have dependency on org.apache.cxf module when on AS7, otherwise Apache CXF annotations are ignored
@EndpointProperties(value = {
@EndpointProperty(key = "ws-security.signature.properties", value = "stsKeystore.properties"),
@EndpointProperty(key = "ws-security.callback-handler", value = "org.jboss.test.ws.jaxws.samples.wsse.policy.trust.stsholderofkey.STSHolderOfKeyCallbackHandler")
public class SampleSTSHolderOfKey extends SecurityTokenServiceProvider
public SampleSTSHolderOfKey() throws Exception
super();
StaticSTSProperties props = new StaticSTSProperties();
props.setSignatureCryptoProperties("stsKeystore.properties");
props.setSignatureUsername("mystskey");
props.setCallbackHandlerClass(STSHolderOfKeyCallbackHandler.class.getName());
props.setEncryptionCryptoProperties("stsKeystore.properties");
props.setEncryptionUsername("myservicekey");
props.setIssuer("DoubleItSTSIssuer");
List<ServiceMBean> services = new LinkedList<ServiceMBean>();
StaticService service = new StaticService();
service.setEndpoints(Arrays.asList(
"https://localhost:(\\d)*/jaxws-samples-wsse-policy-trust-holderofkey/HolderOfKeyService",
"https://\\[::1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-holderofkey/HolderOfKeyService",
"https://\\[0:0:0:0:0:0:0:1\\]:(\\d)*/jaxws-samples-wsse-policy-trust-holderofkey/HolderOfKeyService"
services.add(service);
TokenIssueOperation issueOperation = new TokenIssueOperation();
issueOperation.getTokenProviders().add(new SAMLTokenProvider());
issueOperation.setServices(services);
issueOperation.setStsProperties(props);
this.setIssueOperation(issueOperation);
HolderOfKeyCallbackHandler
STSHolderOfKeyCallbackHandler is a callback handler for the WSS4J Crypto
API. It is used to obtain the password for the private key in the
keystore. This class enables CXF to retrieve the password of the user
name to use for the message signature.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.stsholderofkey;
import org.jboss.wsf.stack.cxf.extensions.security.PasswordCallbackHandler;
import java.util.HashMap;
import java.util.Map;
* User: rsearls
* Date: 3/19/14
public class STSHolderOfKeyCallbackHandler extends PasswordCallbackHandler
public STSHolderOfKeyCallbackHandler()
super(getInitMap());
private static Map<String, String> getInitMap()
Map<String, String> passwords = new HashMap<String, String>();
passwords.put("mystskey", "stskpass");
passwords.put("alice", "clarinet");
return passwords;
Crypto properties and keystore files
WSS4J’s Crypto implementation is loaded and configured via a Java
properties file that contains Crypto configuration data. The file
contains implementation-specific properties such as a keystore location,
password, default alias and the like. This application is using the
Merlin implementation. File stsKeystore.properties contains this
information.
File servicestore.jks, is a Java KeyStore (JKS) repository. It contains
self signed certificates for myservicekey and mystskey. Self signed
certificates are not appropriate for production use.
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=stsspass
org.apache.ws.security.crypto.merlin.keystore.file=stsstore.jks
When deployed on WildFly, this application requires access to the
JBossWs and CXF APIs provided in modules
org.jboss.ws.cxf.jbossws-cxf-client and org.apache.cxf. The Apache CXF
internals, org.apache.cxf.impl, are needed to build the STS
configuration in the SampleSTSHolderOfKey constructor. The dependency
statement directs the server to provide them at deployment.
Manifest-Version:1.0
Ant-Version: Apache Ant1.8.2
Created-By:1.7.0_25-b15 (Oracle Corporation)
Dependencies: org.jboss.ws.cxf.jbossws-cxf-client,org.apache.cxf.impl
Web service requester
This section examines the crucial elements in calling a web service that
implements endpoint security as described in the SAML Holder-Of-Key
scenario. The components that will be discussed are.
Web service requester Implementation
The ws-requester, the client, uses standard procedures for creating a
reference to the web service. To address the endpoint security
requirements, the web service’s "Request Context" is configured with the
information needed in message generation. In addition, the STSClient
that communicates with the STS is configured with similar values. Note
the key strings ending with a ".it" suffix. This suffix flags these
settings as belonging to the STSClient. The internal CXF code assigns
this information to the STSClient that is auto-generated for this
service call.
There is an alternate method of setting up the STSCLient. The user may
provide their own instance of the STSClient. The CXF code will use this
object and not auto-generate one. When providing the STSClient in this
way, the user must provide a org.apache.cxf.Bus for it and the
configuration keys must not have the ".it" suffix. This is used in the
ActAs and OnBehalfOf examples.
String serviceURL = "https://" + getServerHost() + ":8443/jaxws-samples-wsse-policy-trust-holderofkey/HolderOfKeyService";
final QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/holderofkeywssecuritypolicy", "HolderOfKeyService");
final URL wsdlURL = new URL(serviceURL + "?wsdl");
Service service = Service.create(wsdlURL, serviceName);
HolderOfKeyIface proxy = (HolderOfKeyIface) service.getPort(HolderOfKeyIface.class);
Map<String, Object> ctx = ((BindingProvider)proxy).getRequestContext();
// set the security related configuration information for the service "request"
ctx.put(SecurityConstants.CALLBACK_HANDLER, new ClientCallbackHandler());
ctx.put(SecurityConstants.SIGNATURE_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.ENCRYPT_PROPERTIES,
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.SIGNATURE_USERNAME, "myclientkey");
ctx.put(SecurityConstants.ENCRYPT_USERNAME, "myservicekey");
//-- Configuration settings that will be transfered to the STSClient
// "alice" is the name provided for the WSS Username. Her password will
// be retreived from the ClientCallbackHander by the STSClient.
ctx.put(SecurityConstants.USERNAME + ".it", "alice");
ctx.put(SecurityConstants.CALLBACK_HANDLER + ".it", new ClientCallbackHandler());
ctx.put(SecurityConstants.ENCRYPT_PROPERTIES + ".it",
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.ENCRYPT_USERNAME + ".it", "mystskey");
ctx.put(SecurityConstants.STS_TOKEN_USERNAME + ".it", "myclientkey");
ctx.put(SecurityConstants.STS_TOKEN_PROPERTIES + ".it",
Thread.currentThread().getContextClassLoader().getResource(
"META-INF/clientKeystore.properties"));
ctx.put(SecurityConstants.STS_TOKEN_USE_CERT_FOR_KEYINFO + ".it", "true");
proxy.sayHello();
ClientCallbackHandler
ClientCallbackHandler is a callback handler for the WSS4J Crypto API. It
is used to obtain the password for the private key in the keystore. This
class enables CXF to retrieve the password of the user name to use for
the message signature. Note that "alice" and her password have been
provided here. This information is not in the (JKS) keystore but
provided in the WildFly security domain. It was declared in file
jbossws-users.properties.
package org.jboss.test.ws.jaxws.samples.wsse.policy.trust.shared;
import java.io.IOException;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
public class ClientCallbackHandler implements CallbackHandler {
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof WSPasswordCallback) {
WSPasswordCallback pc = (WSPasswordCallback) callbacks[i];
if ("myclientkey".equals(pc.getIdentifier())) {
pc.setPassword("ckpass");
break;
} else if ("alice".equals(pc.getIdentifier())) {
pc.setPassword("clarinet");
break;
} else if ("bob".equals(pc.getIdentifier())) {
pc.setPassword("trombone");
break;
} else if ("myservicekey".equals(pc.getIdentifier())) { // rls test added for bearer test
pc.setPassword("skpass");
break;
Crypto properties and keystore files
WSS4J’s Crypto implementation is loaded and configured via a Java
properties file that contains Crypto configuration data. The file
contains implementation-specific properties such as a keystore location,
password, default alias and the like. This application is using the
Merlin implementation. File clientKeystore.properties contains this
information.
File clientstore.jks, is a Java KeyStore (JKS) repository. It contains
self signed certificates for myservicekey and mystskey. Self signed
certificates are not appropriate for production use.
org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin
org.apache.ws.security.crypto.merlin.keystore.type=jks
org.apache.ws.security.crypto.merlin.keystore.password=cspass
org.apache.ws.security.crypto.merlin.keystore.alias=myclientkey
org.apache.ws.security.crypto.merlin.keystore.file=META-INF/clientstore.jks
16.5.14. WS-Reliable Messaging
JBoss Web Services inherits full WS-Reliable Messaging capabilities from
the underlying Apache CXF implementation. At the time of writing, Apache
CXF provides support for the
WS-Reliable Messaging 1.0
(February 2005) version of the specification.
Enabling WS-Reliable Messaging
WS-Reliable Messaging is implemented internally in Apache CXF through a
set of interceptors that deal with the low level requirements of the
reliable messaging protocol. In order for enabling WS-Reliable
Messaging, users need to either:
The former approach relies on the Apache CXF WS-Policy engine and is the
only portable one. The other approaches are Apache CXF proprietary ones,
however they allow for fine-grained configuration of protocol aspects
that are not covered by the WS-Reliable Messaging Policy. More details
are available in the
Apache CXF
documentation.
Example
In this example we configure WS-Reliable Messaging endpoint and client
through the WS-Policy support.
Endpoint
We go with a contract-first approach, so we start by creating a proper
WSDL contract, containing the WS-Reliable Messaging and WS-Addressing
policies (the latter is a requirement of the former):
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="SimpleService" targetNamespace="http://www.jboss.org/jbossws/ws-extensions/wsrm"
xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/wsrm" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsp="http://www.w3.org/2006/07/ws-policy">
<wsdl:types>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://www.jboss.org/jbossws/ws-extensions/wsrm"
attributeFormDefault="unqualified" elementFormDefault="unqualified"
targetNamespace="http://www.jboss.org/jbossws/ws-extensions/wsrm">
<xsd:element name="ping" type="tns:ping"/>
<xsd:complexType name="ping">
<xsd:sequence/>
</xsd:complexType>
<xsd:element name="echo" type="tns:echo"/>
<xsd:complexType name="echo">
<xsd:sequence>
<xsd:element minOccurs="0" name="arg0" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="echoResponse" type="tns:echoResponse"/>
<xsd:complexType name="echoResponse">
<xsd:sequence>
<xsd:element minOccurs="0" name="return" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
</wsdl:types>
<wsdl:message name="echoResponse">
<wsdl:part name="parameters" element="tns:echoResponse">
</wsdl:part>
</wsdl:message>
<wsdl:message name="echo">
<wsdl:part name="parameters" element="tns:echo">
</wsdl:part>
</wsdl:message>
<wsdl:message name="ping">
<wsdl:part name="parameters" element="tns:ping">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="SimpleService">
<wsdl:operation name="ping">
<wsdl:input name="ping" message="tns:ping">
</wsdl:input>
</wsdl:operation>
<wsdl:operation
name="echo">
<wsdl:input name="echo" message="tns:echo">
</wsdl:input>
<wsdl:output name="echoResponse" message="tns:echoResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="SimpleServiceSoapBinding" type="tns:SimpleService">
<wsp:Policy>
<!-- WS-Addressing and basic WS-Reliable Messaging policy assertions -->
<wswa:UsingAddressing xmlns:wswa="http://www.w3.org/2006/05/addressing/wsdl"/>
<wsrmp:RMAssertion xmlns:wsrmp="http://schemas.xmlsoap.org/ws/2005/02/rm/policy"/>
<!-- --------------------------------------------------------------- -->
</wsp:Policy>
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="ping">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="ping">
<soap:body use="literal"/>
</wsdl:input>
</wsdl:operation>
<wsdl:operation name="echo">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="echo">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="echoResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="SimpleService">
<wsdl:port name="SimpleServicePort" binding="tns:SimpleServiceSoapBinding">
<soap:address location="http://localhost:8080/jaxws-samples-wsrm-api"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
Then we use the wsconsume tool to generate both standard Jakarta XML Web Services client
and endpoint.
We provide a basic Jakarta XML Web Services implementation for the endpoint, nothing
special in it:
package org.jboss.test.ws.jaxws.samples.wsrm.service;
import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebService;
@WebService
name = "SimpleService",
serviceName = "SimpleService",
wsdlLocation = "WEB-INF/wsdl/SimpleService.wsdl",
targetNamespace = "http://www.jboss.org/jbossws/ws-extensions/wsrm"
public class SimpleServiceImpl
@Oneway
@WebMethod
public void ping()
System.out.println("ping()");
@WebMethod
public String echo(String s)
System.out.println("echo(" + s + ")");
return s;
Finally we package the generated POJO endpoint together with a basic
web.xml the usual way and deploy to the application server. The
webservices stack automatically detects the policies and enables
WS-Reliable Messaging.
Client
The endpoint advertises his RM capabilities (and requirements) through
the published WSDL and the client is required to also enable WS-RM for
successfully exchanging messages with the server.
So a regular JAX WS client is enough if the user does not need to tune
any specific detail of the RM subsystem.
QName serviceName = new QName("http://www.jboss.org/jbossws/ws-extensions/wsrm", "SimpleService");
URL wsdlURL = new URL("http://localhost:8080/jaxws-samples-wsrm-api?wsdl");
Service service = Service.create(wsdlURL, serviceName);
proxy = (SimpleService)service.getPort(SimpleService.class);
proxy.echo("Hello World!");
Additional configuration
Fine-grained tuning of WS-Reliable Messaging engine requires setting up
proper RM features and attach them for instance to the client proxy.
Here is an example:
package org.jboss.test.ws.jaxws.samples.wsrm.client;
//...
import javax.xml.ws.Service;
import org.apache.cxf.ws.rm.feature.RMFeature;
import org.apache.cxf.ws.rm.manager.AcksPolicyType;
import org.apache.cxf.ws.rm.manager.DestinationPolicyType;
import org.jboss.test.ws.jaxws.samples.wsrm.generated.SimpleService;
// ...
Service service = Service.create(wsdlURL, serviceName);
RMFeature feature = new RMFeature();
RMAssertion rma = new RMAssertion();
RMAssertion.BaseRetransmissionInterval bri = new RMAssertion.BaseRetransmissionInterval();
bri.setMilliseconds(4000L);
rma.setBaseRetransmissionInterval(bri);
AcknowledgementInterval ai = new AcknowledgementInterval();
ai.setMilliseconds(2000L);
rma.setAcknowledgementInterval(ai);
feature.setRMAssertion(rma);
DestinationPolicyType dp = new DestinationPolicyType();
AcksPolicyType ap = new AcksPolicyType();
ap.setIntraMessageThreshold(0);
dp.setAcksPolicy(ap);
feature.setDestinationPolicy(dp);
SimpleService proxy = (SimpleService)service.getPort(SimpleService.class, feature);
proxy.echo("Hello World");
The same can of course be achieved by factoring the feature into a
custom pojo extending org.apache.cxf.ws.rm.feature.RMFeature and
setting the obtained property in a client configuration:
package org.jboss.test.ws.jaxws.samples.wsrm.client;
import org.apache.cxf.ws.rm.feature.RMFeature;
import org.apache.cxf.ws.rm.manager.AcksPolicyType;
import org.apache.cxf.ws.rm.manager.DestinationPolicyType;
import org.apache.cxf.ws.rmp.v200502.RMAssertion;
import org.apache.cxf.ws.rmp.v200502.RMAssertion.AcknowledgementInterval;
public class CustomRMFeature extends RMFeature
public CustomRMFeature() {
super();
RMAssertion rma = new RMAssertion();
RMAssertion.BaseRetransmissionInterval bri = new RMAssertion.BaseRetransmissionInterval();
bri.setMilliseconds(4000L);
rma.setBaseRetransmissionInterval(bri);
AcknowledgementInterval ai = new AcknowledgementInterval();
ai.setMilliseconds(2000L);
rma.setAcknowledgementInterval(ai);
super.setRMAssertion(rma);
DestinationPolicyType dp = new DestinationPolicyType();
AcksPolicyType ap = new AcksPolicyType();
ap.setIntraMessageThreshold(0);
dp.setAcksPolicy(ap);
super.setDestinationPolicy(dp);
<?xml version="1.0" encoding="UTF-8"?>
<jaxws-config xmlns="urn:jboss:jbossws-jaxws-config:4.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="urn:jboss:jbossws-jaxws-config:4.0 schema/jbossws-jaxws-config_4_0.xsd">
<client-config>
<config-name>Custom Client Config</config-name>
<property>
<property-name>cxf.features</property-name>
<property-value>org.jboss.test.ws.jaxws.samples.wsrm.client.CustomRMFeature</property-value>
</property>
</client-config>
</jaxws-config>
import org.jboss.ws.api.configuration.ClientConfigUtil;
import org.jboss.ws.api.configuration.ClientConfigurer;
//...
Service service = Service.create(wsdlURL, serviceName);
SimpleService proxy = (SimpleService)service.getPort(SimpleService.class);
ClientConfigurer configurer = ClientConfigUtil.resolveClientConfigurer();
configurer.setConfigProperties(proxy, "META-INF/jaxws-client-config.xml", "Custom Client Config");
proxy.echo("Hello World!");
16.5.15. SOAP over Jakarta Messaging
JBoss Web Services allows communication over the Jakarta Messaging transport. The
functionality comes from Apache CXF support for the
SOAP over Java Message Service 1.0
specification, which is aimed at a set of standards for interoperable
transport of SOAP messages over Jakarta Messaging.
On top of Apache CXF functionalities, the JBossWS integration allows
users to deploy WS archives containing both Jakarta Messaging and HTTP endpoints
the same way as they do for basic HTTP WS endpoints (in war
archives). The webservices layer of WildFly takes care of looking for
Jakarta Messaging enpdoints in the deployed archive and starts them delegating to
the Apache CXF core similarly as with HTTP endpoints.
Configuring SOAP over Jakarta Messaging
As per specification, the SOAP over Jakarta Messaging transport configuration is
controlled by proper elements and attributes in the binding and
service elements of the WSDL contract. So a Jakarta Messaging endpoint is usually
developed using a contract-first approach.
The Apache CXF
documentation covers all the details of the supported configurations.
The minimum configuration implies:
<wsdl:definitions name="HelloWorldService" targetNamespace="http://org.jboss.ws/jaxws/cxf/jms"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://org.jboss.ws/jaxws/cxf/jms"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soapjms="http://www.w3.org/2010/soapjms/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:binding name="HelloWorldServiceSoapBinding" type="tns:HelloWorld">
<soap:binding style="document" transport="http://www.w3.org/2010/soapjms/"/> <!-- 3 -->
<wsdl:operation name="echo">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="echo">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="echoResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HelloWorldService">
<soapjms:jndiConnectionFactoryName>java:/ConnectionFactory</soapjms:jndiConnectionFactoryName> <!-- 2 -->
<wsdl:port binding="tns:HelloWorldServiceSoapBinding" name="HelloWorldImplPort">
<soap:address location="jms:queue:testQueue"/> <!-- 1 -->
</wsdl:port>
</wsdl:service>
Apache CXF takes care of setting up the Jakarta Messaging transport for endpoint
implementations whose @WebService annotation points to a port declared
for Jakarta Messaging transport as explained above.
Jakarta Messaging endpoint only deployment
In this example we create a simple endpoint relying on SOAP over Jakarta Messaging
and deploy it as part of a jar archive.
The endpoint is created using wsconsume tool from a WSDL contract such
<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions name="HelloWorldService" targetNamespace="http://org.jboss.ws/jaxws/cxf/jms"
xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://org.jboss.ws/jaxws/cxf/jms"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soapjms="http://www.w3.org/2010/soapjms/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xs:schema elementFormDefault="unqualified" targetNamespace="http://org.jboss.ws/jaxws/cxf/jms" version="1.0" xmlns:tns="http://org.jboss.ws/jaxws/cxf/jms" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="echo" type="tns:echo"/>
<xs:element name="echoResponse" type="tns:echoResponse"/>
<xs:complexType name="echo">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="echoResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="echoResponse">
<wsdl:part element="tns:echoResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="echo">
<wsdl:part element="tns:echo" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="HelloWorld">
<wsdl:operation name="echo">
<wsdl:input message="tns:echo" name="echo">
</wsdl:input>
<wsdl:output message="tns:echoResponse" name="echoResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="HelloWorldServiceSoapBinding" type="tns:HelloWorld">
<soap:binding style="document" transport="http://www.w3.org/2010/soapjms/"/>
<wsdl:operation name="echo">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="echo">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="echoResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="
HelloWorldService">
<soapjms:jndiConnectionFactoryName>java:jms/RemoteConnectionFactory</soapjms:jndiConnectionFactoryName>
<soapjms:jndiInitialContextFactory>org.jboss.naming.remote.client.InitialContextFactory</soapjms:jndiInitialContextFactory>
<soapjms:jndiURL>http-remoting://myhost:8080</soapjms:jndiURL>
<wsdl:port binding="tns:HelloWorldServiceSoapBinding" name="HelloWorldImplPort">
<soap:address location="jms:queue:testQueue"/>
</wsdl:port>
</wsdl:service>
<wsdl:service name="HelloWorldServiceLocal">
<soapjms:jndiConnectionFactoryName>java:/ConnectionFactory</soapjms:jndiConnectionFactoryName>
<wsdl:port binding="tns:HelloWorldServiceSoapBinding" name="HelloWorldImplPort">
<soap:address location="jms:queue:testQueue"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
At the time of writing, java:/ConnectionFactory is the default
connection factory JNDI location on WildFly
For allowing remote JNDI lookup of the connection factory, a specific
service ( HelloWorldService) for remote clients is added to the WSDL.
The java:jms/RemoteConnectionFactory is the JNDI location of the same
connection factory mentioned above, except it’s exposed for remote
lookup. The soapjms:jndiInitialContextFactory and soap:jmsjndiURL
complete the remote connection configuration, specifying the initial
context factory class to use and the JNDI registry address.
portName = "HelloWorldImplPort",
serviceName = "HelloWorldServiceLocal",
wsdlLocation = "META-INF/wsdl/HelloWorldService.wsdl",
endpointInterface = "org.jboss.test.ws.jaxws.cxf.jms.HelloWorld",
targetNamespace = "http://org.jboss.ws/jaxws/cxf/jms"
public class HelloWorldImpl implements HelloWorld
public String echo(String input)
return input;
The endpoint implementation references the HelloWorldServiceLocal wsdl
service, so that the local JNDI connection factory location is used for
starting the endpoint on server side.
That’s pretty much all. We just need to package the generated service
endpoint interface, the endpoint implementation and the WSDL file in a
jar archive and deploy it:
alessio@inuyasha /dati/jbossws/stack/cxf/trunk $ jar -tvf ./modules/testsuite/cxf-tests/target/test-libs/jaxws-cxf-jms-only-deployment.jar
0 Thu Jun 23 15:18:44 CEST 2011 META-INF/
129 Thu Jun 23 15:18:42 CEST 2011 META-INF/MANIFEST.MF
0 Thu Jun 23 15:18:42 CEST 2011 org/
0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/
0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/
0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/
0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/
0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/cxf/
0 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/cxf/jms/
313 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/cxf/jms/HelloWorld.class
1173 Thu Jun 23 15:18:42 CEST 2011 org/jboss/test/ws/jaxws/cxf/jms/HelloWorldImpl.class
0 Thu Jun 23 15:18:40 CEST 2011 META-INF/wsdl/
3074 Thu Jun 23 15:18:40 CEST 2011 META-INF/wsdl/HelloWorldService.wsdl
URL wsdlUrl = ...
//start another bus to avoid affecting the one that could already be assigned to the current thread - optional but highly suggested
Bus bus = BusFactory.newInstance().createBus();
BusFactory.setThreadDefaultBus(bus);
QName serviceName = new QName("http://org.jboss.ws/jaxws/cxf/jms", "HelloWorldService");
Service service = Service.create(wsdlUrl, serviceName);
HelloWorld proxy = (HelloWorld) service.getPort(new QName("http://org.jboss.ws/jaxws/cxf/jms", "HelloWorldImplPort"), HelloWorld.class);
setupProxy(proxy);
proxy.echo("Hi");
finally
bus.shutdown(true);
The WSDL location URL needs to be retrieved in a custom way, depending
on the client application. Given the endpoint is Jakarta Messaging only, there’s no
automatically published WSDL contract.
in order for performing the remote invocation (which internally goes
through remote JNDI lookup of the connection factory), the calling user
credentials need to be set into the Apache CXF JMSConduit:
private void setupProxy(HelloWorld proxy) {
JMSConduit conduit = (JMSConduit)ClientProxy.getClient(proxy).getConduit();
JNDIConfiguration jndiConfig = conduit.getJmsConfig().getJndiConfig();
jndiConfig.setConnectionUserName("user");
jndiConfig.setConnectionPassword("password");
Properties props = conduit.getJmsConfig().getJndiTemplate().getEnvironment();
props.put(Context.SECURITY_PRINCIPAL, "user");
props.put(Context.SECURITY_CREDENTIALS, "password");
Have a look at the WildFly domain and messaging configuration for
finding out the actual security requirements. At the time of writing, a
user with guest role is required and that’s internally checked using
the other security domain.
Of course once the endpoint is exposed over Jakarta Messaging transport, any plain Jakarta Messaging
client can also be used to send messages to the webservice endpoint. You
can have a look at the SOAP over Jakarta Messaging spec details and code the client
similarly to
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
env.put(Context.PROVIDER_URL, "http-remoting://myhost:8080");
env.put(Context.SECURITY_PRINCIPAL, "user");
env.put(Context.SECURITY_CREDENTIALS, "password");
InitialContext context = new InitialContext(env);
QueueConnectionFactory connectionFactory = (QueueConnectionFactory)context.lookup("jms/RemoteConnectionFactory");
Queue reqQueue = (Queue)context.lookup("jms/queue/test");
Queue resQueue = (Queue)context.lookup("jms/queue/test");
QueueConnection con = connectionFactory.createQueueConnection("user", "password");
QueueSession session = con.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
QueueReceiver receiver = session.createReceiver(resQueue);
ResponseListener responseListener = new ResponseListener(); //a custom response listener...
receiver.setMessageListener(responseListener);
con.start();
TextMessage message = session.createTextMessage(reqMessage);
message.setJMSReplyTo(resQueue);
//setup SOAP-over-JMS properties...
message.setStringProperty("SOAPJMS_contentType", "text/xml");
message.setStringProperty("SOAPJMS_requestURI", "jms:queue:testQueue");
QueueSender sender = session.createSender(reqQueue);
sender.send(message);
sender.close();
Jakarta Messaging and HTTP endpoints deployment
In this example we create a deployment containing an endpoint that
serves over both HTTP and Jakarta Messaging transports.
We from a WSDL contract such as below (please note we’ve two binding /
portType for the same service):
<?xml version='1.0' encoding='UTF-8'?>
<wsdl:definitions name="HelloWorldService" targetNamespace="http://org.jboss.ws/jaxws/cxf/jms"
xmlns:ns1="http://schemas.xmlsoap.org/soap/http"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://org.jboss.ws/jaxws/cxf/jms"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:soapjms="http://www.w3.org/2010/soapjms/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<wsdl:types>
<xs:schema elementFormDefault="unqualified" targetNamespace="http://org.jboss.ws/jaxws/cxf/jms" version="1.0"
xmlns:tns="http://org.jboss.ws/jaxws/cxf/jms" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="echo" type="tns:echo"/>
<xs:element name="echoResponse" type="tns:echoResponse"/>
<xs:complexType name="echo">
<xs:sequence>
<xs:element minOccurs="0" name="arg0" type="xs:string"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="echoResponse">
<xs:sequence>
<xs:element minOccurs="0" name="return"
type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
</wsdl:types>
<wsdl:message name="echoResponse">
<wsdl:part element="tns:echoResponse" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:message name="echo">
<wsdl:part element="tns:echo" name="parameters">
</wsdl:part>
</wsdl:message>
<wsdl:portType name="HelloWorld">
<wsdl:operation name="echo">
<wsdl:input message="tns:echo" name="echo">
</wsdl:input>
<wsdl:output message="tns:echoResponse" name="echoResponse">
</wsdl:output>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="HelloWorldServiceSoapBinding" type="tns:HelloWorld">
<soap:binding style="document" transport="http://www.w3.org/2010/soapjms/"/>
<wsdl:operation name="echo">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="echo">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="echoResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="HttpHelloWorldServiceSoapBinding" type="tns:HelloWorld">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="echo">
<soap:operation soapAction="" style="document"/>
<wsdl:input name="echo">
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output name="echoResponse">
<soap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="HelloWorldService">
<soapjms:jndiConnectionFactoryName>java:jms/RemoteConnectionFactory</soapjms:jndiConnectionFactoryName>
<soapjms:jndiInitialContextFactory>org.jboss.naming.remote.client.InitialContextFactory</soapjms:jndiInitialContextFactory>
<soapjms:jndiURL>http-remoting://localhost:8080</soapjms:jndiURL>
<wsdl:port binding="tns:HelloWorldServiceSoapBinding" name="HelloWorldImplPort">
<soap:address location="jms:queue:testQueue"/>
</wsdl:port>
<wsdl:port binding="tns:HttpHelloWorldServiceSoapBinding" name="HttpHelloWorldImplPort">
<soap:address location="http://localhost:8080/jaxws-cxf-jms-http-deployment"/>
</wsdl:port>
</wsdl:service>
<wsdl:service name="HelloWorldServiceLocal">
<soapjms:jndiConnectionFactoryName>java:/ConnectionFactory</soapjms:jndiConnectionFactoryName>
<wsdl:port binding="tns:HelloWorldServiceSoapBinding" name="HelloWorldImplPort">
<soap:address location="jms:queue:testQueue"/>
</wsdl:port>
</wsdl:definitions>
The same considerations of the previous example regarding the Jakarta Messaging queue
and JNDI connection factory still apply.
Here we can implement the endpoint in multiple ways, either with a
common implementation class that’s extended by the Jakarta Messaging and HTTP ones, or
keep the two implementation classes independent and just have them
implement the same service endpoint interface:
package org.jboss.test.ws.jaxws.cxf.jms_http;
import javax.jws.WebService;
@WebService
portName = "HelloWorldImplPort",
serviceName = "HelloWorldServiceLocal",
wsdlLocation = "WEB-INF/wsdl/HelloWorldService.wsdl",
endpointInterface = "org.jboss.test.ws.jaxws.cxf.jms_http.HelloWorld",
targetNamespace = "http://org.jboss.ws/jaxws/cxf/jms"
public class HelloWorldImpl implements HelloWorld
public String echo(String input)
System.out.println("input: " + input);
return input;
portName = "HttpHelloWorldImplPort",
serviceName = "HelloWorldService",
wsdlLocation = "WEB-INF/wsdl/HelloWorldService.wsdl",
endpointInterface = "org.jboss.test.ws.jaxws.cxf.jms_http.HelloWorld",
targetNamespace = "http://org.jboss.ws/jaxws/cxf/jms"
public class HttpHelloWorldImpl implements HelloWorld
public String echo(String input)
System.out.println("input (http): " + input);
return "(http) " + input;
Both classes are packaged together the service endpoint interface and
the WSDL file in a war archive:
alessio@inuyasha /dati/jbossws/stack/cxf/trunk $ jar -tvf ./modules/testsuite/cxf-spring-tests/target/test-libs/jaxws-cxf-jms-http-deployment.war
0 Thu Jun 23 15:18:44 CEST 2011 META-INF/
129 Thu Jun 23 15:18:42 CEST 2011 META-INF/MANIFEST.MF
0 Thu Jun 23 15:18:44 CEST 2011 WEB-INF/
569 Thu Jun 23 15:18:40 CEST 2011 WEB-INF/web.xml
0 Thu Jun 23 15:18:44 CEST 2011 WEB-INF/classes/
0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/
0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/
0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/
0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/
0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/
0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/
0 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/jms_http/
318 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/jms_http/HelloWorld.class
1192 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/jms_http/HelloWorldImpl.class
1246 Thu Jun 23 15:18:42 CEST 2011 WEB-INF/classes/org/jboss/test/ws/jaxws/cxf/jms_http/HttpHelloWorldImpl.class
0 Thu Jun 23 15:18:40 CEST 2011 WEB-INF/wsdl/
3068 Thu Jun 23 15:18:40 CEST 2011 WEB-INF/wsdl/HelloWorldService.wsdl
A trivial web.xml descriptor is also included to trigger the HTTP
endpoint publish:
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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"
version="2.4">
<servlet>
<servlet-name>
EndpointServlet</servlet-name>
<servlet-class>org.jboss.test.ws.jaxws.cxf.jms_http.HttpHelloWorldImpl</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>EndpointServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Finally, the Jakarta XML Web Services client can ineract with both Jakarta Messaging and HTTP endpoints
as usual:
//start another bus to avoid affecting the one that could already be assigned to current thread - optional but highly suggested
Bus bus = BusFactory.newInstance().createBus();
BusFactory.setThreadDefaultBus(bus);
QName serviceName = new QName("http://org.jboss.ws/jaxws/cxf/jms", "HelloWorldService");
Service service = Service.create(wsdlUrl, serviceName);
//JMS test
HelloWorld proxy = (HelloWorld) service.getPort(new QName("http://org.jboss.ws/jaxws/cxf/jms", "HelloWorldImplPort"), HelloWorld.class);
setupProxy(proxy);
proxy.echo("Hi");
//HTTP test
HelloWorld httpProxy = (HelloWorld) service.getPort(new QName("http://org.jboss.ws/jaxws/cxf/jms", "HttpHelloWorldImplPort"), HelloWorld.class);
httpProxy.echo("Hi");
finally
bus.shutdown(true);
Use of Endpoint.publish() API
An alternative to deploying an archive containing Jakarta Messaging endpoints is in
starting them directly using the Jakarta XML Web Services Endpoint.publish(..) API.
That’s as easy as doing:
Object implementor = new HelloWorldImpl();
Endpoint ep = Endpoint.publish("jms:queue:testQueue", implementor);
//use or let others use the endpoint
finally
ep.stop();
where HelloWorldImpl is a POJO endpoint implementation referencing a
Jakarta Messaging port in a given WSDL contract, as explained in the previous
examples.
The main difference among the deployment approach is in the direct
control and responsibility over the endpoint lifecycle ( start/publish
and stop).
The HTTP Proxy related functionalities of JBoss Web Services are
provided by the Apache CXF http transport layer.
The suggested configuration mechanism when running JBoss Web Services is
explained below; for further information please refer to the
Apache
CXF documentation.
Configuration
The HTTP proxy configuration for a given Jakarta XML Web Services client can be set in the
following ways:
The latter is a client stub/port level configuration: the setup is
performed on the HTTPConduit object that’s part of the Apache CXF
Client abstraction.
import org.apache.cxf.configuration.security.ProxyAuthorizationPolicy;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.apache.cxf.transports.http.configuration.ProxyServerType;
Service service = Service.create(wsdlURL, new QName("http://org.jboss.ws/jaxws/cxf/httpproxy", "HelloWorldService"));
HelloWorld port = (HelloWorld) service.getPort(new QName("http://org.jboss.ws/jaxws/cxf/httpproxy", "HelloWorldImplPort"), HelloWorld.class);
Client client = ClientProxy.getClient(port);
HTTPConduit conduit = (HTTPConduit)client.getConduit();
ProxyAuthorizationPolicy policy = new ProxyAuthorizationPolicy();
policy.setAuthorizationType("Basic");
policy.setUserName(PROXY_USER);
policy.setPassword(PROXY_PWD);
conduit.setProxyAuthorization(policy);
port.echo("Foo");
The ProxyAuthorizationPolicy also allows for setting the authotization
type as well as the username / password to be used.
Speaking of authorization and authentication, please note that the JDK
already features the java.net.Authenticator facility, which is used
whenever opening a connection to a given URL requiring a http proxy.
Users might want to set a custom Authenticator for instance when needing
to read WSDL contracts before actually calling into the JBoss Web
Services / Apache CXF code; here is an example:
import java.net.Authenticator;
import java.net.PasswordAuthentication;
public class ProxyAuthenticator extends Authenticator
private String user, password;
public ProxyAuthenticator(String user, String password)
this.user = user;
this.password = password;
protected PasswordAuthentication getPasswordAuthentication()
return new PasswordAuthentication(user, password.toCharArray());
Authenticator.setDefault(new ProxyAuthenticator(PROXY_USER, PROXY_PWD));
Apache CXF includes support for Web Services Dynamic Discovery (
WS-Discovery),
which is a protocol to enable dynamic discovery of services available on
the local network. The protocol implies using a UDP based multicast
transport to announce new services and probe for existing services. A
managed mode where a discovery proxy is used to reduce the amount of
required multicast traffic is also covered by the protocol.
JBossWS integrates the WS-Discovery
functionalities provided
by Apache CXF into the application server.
Enabling WS-Discovery
Apache CXF enables WS-Discovery depending on the availability of its
runtime component; given that’s always shipped in the application
server, JBossWS integration requires using the
cxf.ws-discovery.enabled
property
usage for enabling WS-Discovery for a given deployment. By default
WS-Discovery is disabled on the application server. Below is an
example of jboss-webservices.xml descriptor to be used for enabling
WS-Discovery:
<webservices xmlns="http://www.jboss.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="1.2" xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee">
<property>
<name>cxf.ws-discovery.enabled</name>
<value>true</value>
</property>
</webservices>
By default, a WS-Discovery service endpoint (SOAP-over-UDP bound) will
be started the first time a WS-Discovery enabled deployment is processed
on the application server. Every ws endpoint belonging to WS-Discovery
enabled deployments will be automatically registered into such a
WS-Discovery service endpoint ( Hello messages). The service will
reply to Probe and Resolve messages received on UDP port 3702
(including multicast messages sent to IPv4 address 239.255.255.250,
as per
specification).
Endpoints will eventually be automatically unregistered using Bye
messages upon undeployment.
Probing services
Apache CXF comes with a WS-Discovery API that can be used to probe /
resolve services. When running in-container, a JBoss module
dependency to the org.apache.cxf.impl module is to
be set to have access to WS-Discovery client functionalities.
org.apache.cxf.ws.discovery.WSDiscoveryClient
class provides the probe and resolve methods which also accepts
filters on scopes. Users can rely on them for locating available
endpoints on the network. Please have a look at the JBossWS testsuite
which includes a
sample
on CXF WS-Discovery usage.
Apache CXF WS-Policy support
JBossWS policy support rely on the Apache CXF WS-Policy framework, which
is compliant with the
Web Services Policy
1.5 - Framework and
Web Services
Policy 1.5 - Attachment specifications.
Users can work with policies in different ways:
by adding policy assertions to wsdl contracts and letting the runtime
consume them and behave accordingly;
by specifying endpoint policy attachments using either CXF annotations
or features.
Of course users can also make direct use of the Apache CXF policy
framework,
defining custom
assertions, etc.
Finally, JBossWS provides some additional annotations for simplified
policy attachment.
Contract-first approach
WS-Policies can be attached and referenced in wsdl elements (the
specifications describe all possible alternatives). Apache CXF
automatically recognizes, reads and uses policies defined in the wsdl.
Users should hence develop endpoints using the contract-first
approach, that is explicitly providing the contract for their services.
Here is a excerpt taken from a wsdl including a WS-Addressing policy:
<wsdl:definitions name="Foo" targetNamespace="http://ws.jboss.org/foo"
<wsdl:service name="FooService">
<wsdl:port binding="tns:FooBinding" name="FooPort">
<soap:address location="http://localhost:80800/foo"/>
<wsp:Policy xmlns:wsp="http://www.w3.org/ns/ws-policy">
<wsam:Addressing xmlns:wsam="http://www.w3.org/2007/02/addressing/metadata">
<wsp:Policy/>
</wsam:Addressing>
</wsp:Policy>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
Of course, CXF also acts upon policies specified in wsdl documents
consumed on client side.
Code-first approach
For those preferring code-first (java-first) endpoint development,
Apache CXF comes with org.apache.cxf.annotations.Policy and
org.apache.cxf.annotations.Policies annotations to be used for
attaching policy fragments to the wsdl generated at deploy time.
Here is an example of a code-first endpoint including @Policy
annotation:
import javax.jws.WebService;
import org.apache.cxf.annotations.Policy;
@WebService(portName = "MyServicePort",
serviceName = "MyService",
name = "MyServiceIface",
targetNamespace = "http://www.jboss.org/jbossws/foo")
@Policy(placement = Policy.Placement.BINDING, uri = "
JavaFirstPolicy.xml")
public class MyServiceImpl {
public String sayHello() {
return "Hello World!";
The referenced descriptor is to be added to the deployment and will
include the policy to be attached; the attachment position in the
contracts is defined through the placement attribute. Here is a
descriptor example:
<?xml version="1.0" encoding="UTF-8" ?>
<wsp:Policy wsu:Id="MyPolicy" xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsp:ExactlyOne>
<wsp:All>
<sp:SupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:UsernameToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10/>
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SupportingTokens>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
Both approaches above require users to actually write their policies'
assertions; while this offer great flexibility and control of the actual
contract, providing the assertions might end up being quite a
challenging task for complex policies. For this reason, the JBossWS
integration provides policy sets, which are basically pre-defined
groups of policy assertions corresponding to well known / common needs.
Each set has a label allowing users to specify it in the
@org.jboss.ws.api.annotation.PolicySets annotation to have the policy
assertions for that set attached to the annotated endpoint. Multiple
labels can also be specified. Here is an example of the @PolicySets
annotation on a service endpoint interface:
import javax.jws.WebService;
import org.jboss.ws.api.annotation.PolicySets;
@WebService(name = "EndpointTwo", targetNamespace = "http://org.jboss.ws.jaxws.cxf/jbws3648")
@PolicySets({"WS-RM_Policy_spec_example", "WS-SP-EX223_WSS11_Anonymous_X509_Sign_Encrypt", "WS-Addressing"})
public interface EndpointTwo
String echo(String input);
The three sets specified in @PolicySets will cause the wsdl generated
for the endpoint having this interface to be enriched with some policy
assertions for WS-RM, WS-Security and WS-Addressing.
The labels' list of known sets is stored in the
META-INF/policies/org.jboss.wsf.stack.cxf.extensions.policy.PolicyAttachmentStore
file within the jbossws-cxf-client.jar (
org.jboss.ws.cxf:jbossws-cxf-client maven artifact). Actual policy
fragments for each set are also stored in the same artifact at
META-INF/policies/<set-label>-<attachment-position>.xml.
Here is a list of the available policy sets:
WS-SP-EX2121_SSL_UT_Supporting_Token
The group of policy assertions
used in the section 2.1.2.1 example of the WS-Security Policy Examples
1.0 specification
WS-SP-EX213_WSS10_UT_Mutual_Auth_X509_Sign_Encrypt
The group of policy
assertions used in the section 2.1.3 example of the WS-Security Policy
Examples 1.0 specification
WS-SP-EX214_WSS11_User_Name_Cert_Sign_Encrypt
The group of policy
assertions used in the section 2.1.4 example of the WS-Security Policy
Examples 1.0 specification
WS-SP-EX221_WSS10_Mutual_Auth_X509_Sign_Encrypt
The group of policy
assertions used in the section 2.2.1 example of the WS-Security Policy
Examples 1.0 specification
WS-SP-EX222_WSS10_Mutual_Auth_X509_Sign_Encrypt
The group of policy
assertions used in the section 2.2.2 example of the WS-Security Policy
Examples 1.0 specification
WS-SP-EX223_WSS11_Anonymous_X509_Sign_Encrypt
The group of policy
assertions used in the section 2.2.3 example of the WS-Security Policy
Examples 1.0 specification
WS-SP-EX224_WSS11_Mutual_Auth_X509_Sign_Encrypt
The group of policy
assertions used in the section 2.2.4 example of the WS-Security Policy
Examples 1.0 specification
AsymmetricBinding_X509v1_TripleDesRsa15_EncryptBeforeSigning_ProtectTokens
A WS-Security policy for asymmetric binding (encrypt before signing)
using X.509v1 tokens, 3DES + RSA 1.5 algorithms and with token
protections enabled
AsymmetricBinding_X509v1_GCM256OAEP_ProtectTokens
The same as before,
but using custom Apache CXF algorithm suite including GCM 256 + RSA OAEP
algorithms
Always verify the contents of the generated wsdl contract, as policy
sets are potentially subject to updates between JBossWS releases. This
is especially important when dealing with security related policies; the
provided sets are to be considered as convenient configuration options
only; users remain responsible for the policies in their contracts.
JBossWS supports the rewrite of the <soap:address> element of
endpoints published in WSDL contracts. This feature is useful for
controlling the server address that is advertised to clients for each
endpoint. The rewrite mechanism is configured at server level through a
set of elements in the webservices subsystem of the WildFly management
model. Please refer to the container documentation for details on the
options supported in the selected container version. Below is a list of
the elements available in the latest WildFly sources:
boolean
This boolean enables and disables the
address rewrite functionality.When modify-wsdl-address is set to true
and the content of <soap:address> is a valid URL, JBossWS will rewrite
the URL using the values of wsdl-host and wsdl-port or
wsdl-secure-port.When modify-wsdl-address is set to false and the
content of <soap:address> is a valid URL, JBossWS will not rewrite the
URL. The <soap:address> URL will be used.When the content of
<soap:address> is not a valid URL, JBossWS will rewrite it no matter
what the setting of modify-wsdl-address.If modify-wsdl-address is set to
true and wsdl-host is not defined or explicitly set to
'jbossws.undefined.host' the content of <soap:address> URL is use.
JBossWS uses the requester’s host when rewriting the <soap:address>When
modify-wsdl-address is not defined JBossWS uses a default value of true.
wsdl-host
string
The hostname / IP address to be used for rewriting
<soap:address>.If wsdl-host is set to jbossws.undefined.host, JBossWS
uses the requester’s host when rewriting the <soap:address>When
wsdl-host is not defined JBossWS uses a default value of
'jbossws.undefined.host'.
wsdl-port
Set this property to explicitly define the HTTP port
that will be used for rewriting the SOAP address.Otherwise the HTTP port
will be identified by querying the list of installed HTTP connectors.
wsdl-secure-port
Set this property to explicitly define the HTTPS
port that will be used for rewriting the SOAP address.Otherwise the
HTTPS port will be identified by querying the list of installed HTTPS
connectors.
wsdl-uri-scheme
string
This property explicitly sets the URI scheme
to use for rewriting <soap:address> . Valid values are http and https.
This configuration overrides scheme computed by processing the endpoint
(even if a transport guaranteeis specified). The provided values for
wsdl-port and wsdl-secure-port (or their default values) are used
depending on specified scheme.
wsdl-path-rewrite-rule
string
This string defines a SED substitution
command (e.g., 's/regexp/replacement/g') that JBossWS executes against
the path component of each <soap:address> URL published from the
server.When wsdl-path-rewrite-rule is not defined, JBossWS retains the
original path component of each <soap:address> URL.When
'modify-wsdl-address' is set to "false" this element is ignored.
Additionally, users can override the server level configuration by
requesting a specific rewrite behavior for a given endpoint deployment.
That is achieved by setting one of the following properties within a
jboss-webservices.xml descriptor:
<?xml version="1.1" encoding="UTF-8"?>
<webservices version="1.2"
xmlns="http://www.jboss.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee">
<property>
<name>wsdl.soapAddress.rewrite.wsdl-uri-scheme</name>
<value>https</value>
</property>
<property>
<name>wsdl.soapAddress.rewrite.wsdl-host</name>
<value>foo</value>
</property>
</webservices>
System property references
System property references wrapped within "@" characters are expanded
when found in WSDL attribute and element values. This allows for
instance including multiple WS-Policy declarations in the contract and
selecting the policy to use depending on a server wide system property;
here is an example:
<wsdl:definitions ...>
<wsdl:binding name="ServiceOneSoapBinding" type="tns:EndpointOne">
<wsp:PolicyReference URI="#@org.jboss.wsf.test.JBWS3628TestCase.policy@"/>
<wsdl:operation name="echo">
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="ServiceOne">
<wsdl:port binding="tns:ServiceOneSoapBinding" name="EndpointOnePort">
<soap:address location="http://localhost:8080/jaxws-cxf-jbws3628/ServiceOne"/>
</wsdl:port>
</wsdl:service>
<wsp:Policy xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy" wsu:Id="WS-RM_Policy">
<wsrmp:RMAssertion xmlns:wsrmp="http://schemas.xmlsoap.org/ws/2005/02/rm/policy">
</wsrmp:RMAssertion>
</wsp:Policy>
<wsp:Policy xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsp="http://www.w3.org/ns/ws-policy"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" wsu:Id="WS-Addressing_policy">
<wsam:Addressing>
<wsp:Policy/>
</wsam:Addressing>
</wsp:Policy>
</wsdl:definitions>
If the org.jboss.wsf.test.JBWS3628TestCase.policy system property is
defined and set to " WS-Addressing_policy ", WS-Addressing will be
enabled for the endpoint defined by the contract above.
16.6. JBoss Modules and WS applications
The JBoss Web Services functionalities are provided by a given set of
modules / libraries installed on WildFly, which are organized into JBoss
Modules modules. In particular the org.jboss.as.webservices.* and
org.jboss.ws.* modules belong to the JBossWS - WildFly integration.
Users should not need to change anything in them.
While users are of course allowed to provide their own modules for their
custom needs, below is a brief collection of suggestions and hints
around modules and webservices development on WildFly.
16.6.1. Setting module dependencies
On WildFly the user deployment classloader does not have any visibility
over JBoss internals; so for instance you can’t directly use JBossWS
implementation classes unless you explicitly set a dependency to the
corresponding module. As a consequence, users need to declare the module
dependencies they want to be added to their deployment.
The JBoss Web Services APIs are always available by default whenever the
webservices subsystem is available on AS7. So users just use them, no
need for explicit dependencies declaration for those modules.
Here above org.jboss.ws.cxf.jbossws-cxf-client and foo.bar are the
modules you want to set dependencies to; services tells the modules
framework that you want to also import META-INF/services/..
declarations from the dependency, while export exports the classes
from the module to any other module that might be depending on the
module implicitly created for your deployment.
When using annotations on your endpoints / handlers such as the Apache
CXF ones (@InInterceptor, @GZIP, …) remember to add the proper module
dependency in your manifest. Otherwise your annotations are not picked
up and added to the annotation index by WildFly, resulting in them being
completely and silently ignored.
Using Jakarta XML Binding
In order for successfully directly using Jakarta XML Binding contexts, etc. in your
client or endpoint running in-container, you need to properly setup a
Jakarta XML Binding implementation; that is performed setting the following dependency:
Dependencies: com.sun.xml.bind services export
In order for using Apache CXF APIs and implementation classes you need
to add a dependency to the org.apache.cxf (API) module and / or
org.apache.cxf.impl (implementation) module:
Dependencies: org.apache.cxf services
However, please note that would not come with any JBossWS-CXF
customizations nor additional extensions. For this reason, and generally
speaking for simplifying user configuration, a client side aggregation
module is available with all the WS dependencies users might need.
Client side WS aggregation module
Whenever you simply want to use all the JBoss Web Services
feature/functionalities, you can set a dependency to the convenient
client module.
Dependencies: org.jboss.ws.cxf.jbossws-cxf-client services
Please note the services option above: that’s strictly required in
order for you to get the JBossWS-CXF version of classes that are
retrieved using the Service API, the org.apache.cxf.Bus for
instance.
Be careful as issues because of misconfiguration here can be quite hard
to track down, because the Apache CXF behaviour would be sensibly
different.
The services option is almost always needed when declaring
dependencies on org.jboss.ws.cxf.jbossws-cxf-client and
org.apache.cxf modules. The reason for this is in it affecting the
loading of classes through the Service API, which is what is used to
wire most of the JBossWS components as well as all Apache CXF Bus
extensions.
Annotation scanning
The application server uses an annotation index for detecting Jakarta XML Web Services
endpoints in user deployments. When declaring WS endpoints whose class
belongs to a different module (for instance referring that in the
web.xml descriptor), be sure to have an annotations type dependency
in place. Without that, your endpoints would simply be ignored as they
won’t appear as annotated classes to the webservices subsystem.
Dependencies: org.foo annotations
Using jboss-deployment-descriptor.xml
In some circumstances, the convenient approach of setting module
dependencies in MANIFEST.MF might not work. An example is the need for
importing/exporting specific resources from a given module dependency.
Users should hence add a jboss-deployment-structure.xml descriptor to
their deployment and set module dependencies in it.
WildFly 26 introduced the EJB client API for managing remote EJB
invocations. The EJB client API works off EJBClientContext(s). An
EJBClientContext can potentially contain any number of EJB receivers. An
EJB receiver is a component which knows how to communicate with a server
which is capable of handling the EJB invocation. Typically EJB remote
applications can be classified into:
Depending on the kind of remote client, from an EJB client API point of
view, there can potentially be more than 1 EJBClientContext(s) within a
In case of standalone applications, typically a single EJBClientContext
(backed by any number of EJB receivers) exists. However this isn’t
mandatory. Certain standalone applications can potentially have more
than one EJBClientContext(s) and an EJB client context selector will be
responsible for returning the appropriate context.
In case of remote clients which run within another WildFly 26 instance,
each deployed application will have a corresponding EJB client context.
Whenever that application invokes on another EJB, the corresponding EJB
client context will be used for finding the right EJB receiver and
letting it handle the invocation.
17.1. Potential shortcomings of a single EJB client context
In the Overview section we briefly looked at the different types of
remote clients. Let’s focus on the standalone remote clients (the ones
that don’t run within another WildFly 26 instance) for some of the next
sections. Like mentioned earlier, typically a remote standalone client
has just one EJB client context backed by any number of EJB receivers.
Consider this example:
public class MyApplication {
public static void main(String args[]) {
final javax.naming.Context ctxOne = new javax.naming.InitialContext();
final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface");
beanOne.doSomething();
Now, we have seen in this other chapter
invocations from a remote client using JNDI that the JNDI lookups are
(typically) backed by jboss-ejb-client.properties file which is used to
setup the EJB client context and the EJB receivers. Let’s assume we have
a jboss-ejb-client.properties with the relevant receivers
configurations. These configurations include the security credentials
that will be used to create an EJB receiver which connects to the AS7
server. Now when the above code is invoked, the EJB client API looks for
the EJB client context to pick an EJB receiver, to pass on the EJB
invocation request. Since we just have a single EJB client context, that
context is used by the above code to invoke the bean.
Now let’s consider a case where the user application wants to invoke on
the bean more than once, but wants to connect to the WildFly 26 server
using different security credentials. Let’s take a look at the following
code:
public class MyApplication {
public static void main(String args[]) {
// let's say we want to use "foo" security credential while connecting to the AS7 server for invoking on this bean instance
final javax.naming.Context ctxOne = new javax.naming.InitialContext();
final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface");
beanOne.doSomething();
// let's say we want to use "bar" security credential while connecting to the AS7 server for invoking on this bean instance
final javax.naming.Context ctxTwo = new javax.naming.InitialContext();
final MyBeanInterface beanTwo = ctxTwo.lookup("ejb:app/module/distinct/bean!interface");
beanTwo.doSomething();
So we have the same application, which wants to connect to the same
server instance for invoking the EJB(s) hosted on that server, but wants
to use two different credentials while connecting to the server.
Remember, the client application has a single EJB client context which
can have atmost 1 EJB receiver for each server instance. Which
effectively means that the above code will end up using just one
credential to connect to the server. So there was no easy way to have
the above code working.
That was one of the use cases which prompted the
https://issues.redhat.com/browse/EJBCLIENT-34 feature request. The
proposal was to introduce a way, where you can have more control over
the EJB client contexts and their association with JNDI contexts which
are typically used for EJB invocations.
17.2. Scoped EJB client contexts
Developers familiar with earlier versions of JBoss AS would remember
that for invoking an EJB, you would typically create a JNDI context
passing it the PROVIDER_URL which would point to the target server. That
way any invocation is done on EJB proxies looked up using that JNDI
context, would end up on that server. If we look back at the example
above, we’ll realize that, we are ultimately aiming for a similar
functionality through https://issues.redhat.com/browse/EJBCLIENT-34. We
want the user applications to have more control over which EJB receiver
gets used for a specific invocation.
Before we introduced https://issues.redhat.com/browse/EJBCLIENT-34
feature, the EJB client context was typically scoped to the client
application. As part of https://issues.redhat.com/browse/EJBCLIENT-34 we
now allow the EJB client contexts to be scoped with the JNDI contexts.
Consider the following example:
public class MyApplication {
public static void main(String args[]) {
// let's say we want to use "foo" security credential while connecting to the AS7 server for invoking on this bean instance
final Properties ejbClientContextPropsOne = getPropsForEJBClientContextOne():
final javax.naming.Context ctxOne = new javax.naming.InitialContext(ejbClientContextPropsOne);
final MyBeanInterface beanOne = ctxOne.lookup("ejb:app/module/distinct/bean!interface");
beanOne.doSomething();
closeContext(ctxOne); // read on the entire article to understand more about closing scoped EJB client contexts
// let's say we want to use "bar" security credential while connecting to the AS7 server for invoking on this bean instance
final Properties ejbClientContextPropsTwo = getPropsForEJBClientContextTwo():
final javax.naming.Context ctxTwo = new javax.naming.InitialContext(ejbClientContextPropsTwo);
final MyBeanInterface beanTwo = ctxTwo.lookup("ejb:app/module/distinct/bean!interface");
beanTwo.doSomething();
closeContext(ctxTwo); // read on the entire article to understand more about closing scoped EJB client contexts
Notice any difference between this code and the earlier one? We now
create and pass EJB client context specific properties to the JNDI
context. So what do the EJB client context properties look like? The
properties are the same that you would pass through the
jboss-ejb-client.properties file, except for one additional property
which is required to scope the EJB client context to the JNDI context.
The name of the property is:
org.jboss.ejb.client.scoped.context
which is expected to have a value true. This property lets the EJB
client API know that it has to created an EJB client context (backed by
EJB receiver(s)) and that created context is then scoped/visible to only
that JNDI context which created it. Lookup and invocation on any EJB
proxies looked up using this JNDI context will only know of the EJB
client context associated with this JNDI context. This effectively means
that the other JNDI contexts which the application uses to lookup and
invoke on EJBs will not know about the other scoped EJB client
contexts at all.
JNDI contexts which aren’t scoped to a EJB client context (for example,
not passing the org.jboss.ejb.client.scoped.context property) will
fallback to the default behaviour of using the "current" EJB client
context which typically is the one tied to the entire application.
This scoping of the EJB client context helps the user applications to
have more control over which JNDI context "talks to" which server and
connects to that server in "what way". This gives the user applications
the flexibility that was associated with the JNP based JNDI invocations
prior to WildFly 26 versions.
IMPORTANT: It is very important to remember that scoped EJB client
contexts which are scoped to the JNDI contexts are NOT fire and forget
kind of contexts. What that means is the application program which is
using these contexts is solely responsible for managing their lifecycle
and the application itself is responsible for closing the context at the
right moment. After closing the context the proxies which are bound to
this context are no longer valid and any invocation will throw an
Exception. Not closing the context will end in resource problems as the
underlying physical connection will stay open.
17.3. Lifecycle management of scoped EJB client contexts
Like you saw in the previous sections, in case of scoped EJB client
contexts, the EJB client context is tied to the JNDI context. It’s very
important to understand how the lifecycle of the EJB client context
works in such cases. Especially since any EJB client context is almost
always backed by connections to the server. Not managing the EJB client
context lifecycle correctly can lead to connection leaks in some cases.
When you create a scoped EJB client context, the EJB client context
connects to the server(s) listed in the JNDI properties. An internal
implementation detail of this logic includes the ability of the EJB
client context to cache connections based on certain internal algorithm
it uses. The algorithm itself isn’t publicly documented (yet) since the
chances of it changing or even removal shouldn’t really affect the
client application and instead it’s supposed to be transparent to the
client application.
The connections thus created for an EJB client context are kept open as
long as the EJB client context is open. This allows the EJB client
context to be usable for EJB invocations. The connections associated
with the EJB client context are closed when the EJB client context
itself is closed.
The connections that were manually added by the application to the EJB
client context are not managed by the EJB client context. i.e. they
won’t be opened (obviously) nor closed by the EJB client API when the
EJB client context is closed.
17.3.1. How to close EJB client contexts?
The answer to that is simple. Use the close() method on the appropriate
EJB client context.
17.3.2. How to close scoped EJB client contexts?
The answer is the same, use the close() method on the EJB client
context. But the real question is how do you get the relevant scoped EJB
client context which is associated with a JNDI context. Before we get to
that, it’s important to understand how the ejb: JNDI namespace that’s
used for EJB lookups and how the JNDI context (typically the
InitialContext that you see in the client code) are related. The JNDI
API provided by Java language allows "URL context factory" to be
registered in the JNDI framework (see this for details
http://docs.oracle.com/javase/jndi/tutorial/provider/url/factory.html).
Like that documentation states, the URL context factory can be used to
resolve URL strings during JNDI lookup. That’s what the ejb: prefix is
when you do a remote EJB lookup. The ejb: URL string is backed by a URL
context factory.
Internally, when a lookup happens for a ejb: URL string, a relevant
javax.naming.Context is created for that ejb: lookup. Let’s see some
code for better understanding:
// JNDI context "A"
Context jndiCtx = new InitialContext(props);
// Now let's lookup a EJB
MyBean bean = jndiCtx.lookup("ejb:app/module/distinct/bean!interface");
So we first create a JNDI context and then use it to lookup an EJB. The
bean lookup using the ejb: JNDI name, although, is just one statement,
involves a few more things under the hood. What’s actually happening
when you lookup that string is that a separate javax.naming.Context gets
created for the ejb: URL string. This new javax.naming.Context is then
used to lookup the rest of the string in that JNDI name.
Let’s break up that one line into multiple statements to understand
better:
// Remember, the ejb: is backed by a URL context factory which returns a Context for the ejb: URL (that's why it's called a context factory)
final Context ejbNamingContext = (Context) jndiCtx.lookup("ejb:");
// Use the returned EJB naming context to lookup the rest of the JNDI string for EJB
final MyBean bean = ejbNamingContext.lookup("app/module/distinct/bean!interface");
As you see above, we split up that single statement into a couple of
statements for explaining the details better. So as you can see when the
ejb: URL string is parsed in a JNDI name, it gets hold of a
javax.naming.Context instance. This instance is different from the one
which was used to do the lookup (jndiCtx in this example). This is an
important detail to understand (for reasons explained later). Now this
returned instance is used to lookup the rest of the JNDI string
("app/module/distinct/bean!interface"), which then returns the EJB
proxy. Irrespective of whether the lookup is done in a single statement
or multiple parts, the code works the same. i.e. an instance of
javax.naming.Context gets created for the ejb: URL string.
So why am I explaining all this when the section is titled
"How to close scoped EJB client contexts"? The reason is because
client applications dealing with scoped EJB client contexts which are
associated with a JNDI context would expect the following code to close
the associated EJB client context, but will be surprised that it won’t:
final Properties props = new Properties();
// mark it for scoped EJB client context
props.put("org.jboss.ejb.client.scoped.context","true");
// add other properties
props.put(....);
Context jndiCtx = new InitialContext(props);
try {
final MyBean bean = jndiCtx.lookup("ejb:app/module/distinct/bean!interface");
bean.doSomething();
} finally {
jndiCtx.close();
Applications expect that the call to jndiCtx.close() will effectively
close the EJB client context associated with the JNDI context. That
doesn’t happen because as explained previously, the javax.naming.Context
backing the ejb: URL string is a different instance than the one the
code is closing. The JNDI implementation in Java, only just closes the
context on which the close was called. As a result, the other
javax.naming.Context that backs the ejb: URL string is still not closed,
which effectively means that the scoped EJB client context is not closed
too which then ultimately means that the connection to the server(s) in
the EJB client context are not closed too.
So now let’s see how this can be done properly. We know that the ejb:
URL string lookup returns us a javax.naming.Context. All we have to do
is keep a reference to this instance and close it when we are done with
the EJB invocations. So here’s how it’s going to look:
final Properties props = new Properties();
// mark it for scoped EJB client context
props.put("org.jboss.ejb.client.scoped.context","true");
// add other properties
props.put(....);
Context jndiCtx = new InitialContext(props);
Context ejbRootNamingContext = (Context) jndiCtx.lookup("ejb:");
try {
final MyBean bean = ejbRootNamingContext.lookup("app/module/distinct/bean!interface"); // the rest of the EJB jndi string
bean.doSomething();
} finally {
try {
// close the EJB naming JNDI context
ejbRootNamingContext.close();
} catch (Throwable t) {
// log and ignore
try {
// also close our other JNDI context since we are done with it too
jndiCtx.close();
} catch (Throwable t) {
// log and ignore
As you see, we changed the code to first do a lookup on just the "ejb:"
string to get hold of the EJB naming context and then used that
ejbRootNamingContext instance to lookup the rest of the EJB JNDI name to
get hold of the EJB proxy. Then when it was time to close the context,
we closed the ejbRootNamingContext (as well as the other JNDI context).
Closing the ejbRootNamingContext ensures that the scoped EJB client
context associated with that JNDI context is closed too. Effectively,
this closes the connection(s) to the server(s) within that EJB client
context.
Can that code be simplified a bit?
If you are using that JNDI context only for EJB invocations, then yes
you can get rid of some instances and code from the above code. You can
change that code to:
final Properties props = new Properties();
// mark it for scoped EJB client context
props.put("org.jboss.ejb.client.scoped.context","true");
// add other properties
props.put(....);
Context ejbRootNamingContext = (Context) new InitialContext(props).lookup("ejb:");
try {
final MyBean bean = ejbRootNamingContext.lookup("app/module/distinct/bean!interface"); // the rest of the EJB jndi string
bean.doSomething();
} finally {
try {
// close the EJB naming JNDI context
ejbRootNamingContext.close();
} catch (Throwable t) {
// log and ignore
Notice that we no longer hold a reference to 2 JNDI contexts and instead
just keep track of the ejbRootNamingContext which is actually the root
JNDI context for our "ejb:" URL string. Of course, this means that you
can only use this context for EJB lookups or any other EJB related JNDI
lookups. So it depends on your application and how it’s coded.
17.3.3. Can’t the scoped EJB client context be automatically closed by the
EJB client API when the JNDI context is no longer in scope (i.e. on GC)?
That’s one of the common questions that gets asked. No, the EJB client
API can’t take that decision. i.e. it cannot automatically go ahead and
close the scoped EJB client context by itself when the associated JNDI
context is eligible for GC. The reason is simple as illustrated by the
following code:
void doEJBInvocation() {
final MyBean bean = lookupEJB();
bean.doSomething();
bean.doSomeOtherThing();
... // do some other work
bean.keepDoingSomething();
MyBean lookupEJB() {
final Properties props = new Properties();
// mark it for scoped EJB client context
props.put("org.jboss.ejb.client.scoped.context","true");
// add other properties
props.put(....);
Context ejbRootNamingContext = (Context) new InitialContext(props).lookup("ejb:");
final MyBean bean = ejbRootNamingContext.lookup("app/module/distinct/bean!interface"); // rest of the EJB jndi string
return bean;
As you can see, the doEJBInvocation() method first calls a lookupEJB()
method which does a lookup of the bean using a JNDI context and then
returns the bean (proxy). The doEJBInvocation() then uses that returned
proxy and keeps doing the invocations on the bean. As you might have
noticed, the JNDI context that was used for lookup (i.e. the
ejbRootNamingContext) is eligible for GC. If the EJB client API had
closed the scoped EJB client context associated with that JNDI context,
when that JNDI context was garbage collected, then the subsequent EJB
invocations on the returned EJB (proxy) would start failing in
doEJBInvocation() since the EJB client context is no longer available.
That’s the reason why the EJB client API doesn’t automatically close the
EJB client context.
This chapter explains how to invoke EJBs from a remote client by using
the JNDI API to first lookup the bean proxy and then invoke on that
proxy.
Before getting into the details, we would like the users to know that we
have introduced a new EJB client API, which is a WildFly-specific API
and allows invocation on remote EJBs. This client API isn’t based on
JNDI. So remote clients need not rely on JNDI API to invoke on EJBs. A
separate document covering the EJB remote client API will be made
available. For now, you can refer to the javadocs of the EJB client
project at http://docs.jboss.org/ejbclient/. In this document, we’ll
just concentrate on the traditional JNDI based invocation on EJBs. So
let’s get started:
18.1. Deploying your EJBs on the server side:
As a first step, you’ll have to deploy your application containing the
EJBs on the WildFly server. If you want those EJBs to be remotely
invocable, then you’ll have to expose at least one remote view for that
bean. In this example, let’s consider a simple Calculator stateless bean
which exposes a RemoteCalculator remote business interface. We’ll also
have a simple stateful CounterBean which exposes a RemoteCounter remote
business interface. Here’s the code:
package org.jboss.as.quickstarts.ejb.remote.stateless;
* @author Jaikiran Pai
public interface RemoteCalculator {
int add(int a, int b);
int subtract(int a, int b);
@Stateless
@Remote(RemoteCalculator.class)
public class CalculatorBean implements RemoteCalculator {
@Override
public int add(int a, int b) {
return a + b;
@Override
public int subtract(int a, int b) {
return a - b;
@Stateful
@Remote(RemoteCounter.class)
public class CounterBean implements RemoteCounter {
private int count = 0;
@Override
public void increment() {
this.count++;
@Override
public void decrement() {
this.count--;
@Override
public int getCount() {
return this.count;
Let’s package this in a jar (how you package it in a jar is out of scope
of this chapter) named "jboss-as-ejb-remote-app.jar" and deploy it to
the server. Make sure that your deployment has been processed
successfully and there aren’t any errors.
18.2. Writing a remote client application for accessing and invoking the
EJBs deployed on the server
The next step is to write an application which will invoke the EJBs that
you deployed on the server. In WildFly, you can either choose to use the
WildFly specific EJB client API to do the invocation or use JNDI to
lookup a proxy for your bean and invoke on that returned proxy. In this
chapter we will concentrate on the JNDI lookup and invocation and will
leave the EJB client API for a separate chapter.
So let’s take a look at what the client code looks like for looking up
the JNDI proxy and invoking on it. Here’s the entire client code which
invokes on a stateless bean:
package org.jboss.as.quickstarts.ejb.remote.client;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import java.security.Security;
import
java.util.Hashtable;
import org.jboss.as.quickstarts.ejb.remote.stateful.CounterBean;
import org.jboss.as.quickstarts.ejb.remote.stateful.RemoteCounter;
import org.jboss.as.quickstarts.ejb.remote.stateless.CalculatorBean;
import org.jboss.as.quickstarts.ejb.remote.stateless.RemoteCalculator;
import org.jboss.sasl.JBossSaslProvider;
* A sample program which acts a remote client for a EJB deployed on WildFly 10 server.
* This program shows how to lookup stateful and stateless beans via JNDI and then invoke on them
* @author Jaikiran Pai
public class RemoteEJBClient {
public static void main(String[] args) throws Exception {
// Invoke a stateless bean
invokeStatelessBean();
// Invoke a stateful bean
invokeStatefulBean();
* Looks up a stateless bean and invokes on it
* @throws NamingException
private static void invokeStatelessBean() throws NamingException {
// Let's lookup the remote stateless calculator
final RemoteCalculator statelessRemoteCalculator = lookupRemoteStatelessCalculator();
System.out.println("Obtained a remote stateless calculator for invocation");
// invoke on the remote calculator
int a = 204;
int b = 340;
System.out.println("Adding " + a + " and " + b + " via the remote stateless calculator deployed on the server");
int sum = statelessRemoteCalculator.add(a, b);
System.out.println("Remote calculator returned sum = " + sum);
if (sum != a + b) {
throw new RuntimeException("Remote stateless calculator returned an incorrect sum " + sum + " ,expected sum was " + (a + b));
// try one more invocation, this time for subtraction
int num1 = 3434;
int num2 = 2332;
System.out.println("Subtracting " + num2 + " from " + num1 + " via the remote stateless calculator deployed on the server");
int difference = statelessRemoteCalculator.subtract(num1, num2);
System.out.println("Remote calculator returned difference = " + difference);
if (difference != num1 - num2) {
throw new RuntimeException("Remote stateless calculator returned an incorrect difference " + difference + " ,expected difference was " + (num1 - num2));
* Looks up a stateful bean and invokes on it
* @throws NamingException
private static void invokeStatefulBean() throws NamingException {
// Let's lookup the remote stateful counter
final RemoteCounter statefulRemoteCounter = lookupRemoteStatefulCounter();
System.out.println("Obtained a remote stateful counter for invocation");
// invoke on the remote counter bean
final int NUM_TIMES = 20;
System.out.println("Counter will now be incremented " + NUM_TIMES + " times");
for (int i = 0; i < NUM_TIMES; i++) {
System.out.println("Incrementing counter");
statefulRemoteCounter.increment();
System.out.println("Count after increment is " + statefulRemoteCounter.getCount());
// now decrementing
System.out.println("Counter will now be decremented " + NUM_TIMES + " times");
for (int i = NUM_TIMES; i > 0; i--) {
System.out.println("Decrementing counter");
statefulRemoteCounter.decrement();
System.out.println("Count after decrement is " + statefulRemoteCounter.getCount());
* Looks up and returns the proxy to remote stateless calculator bean
* @return
* @throws NamingException
private static RemoteCalculator lookupRemoteStatelessCalculator() throws NamingException {
final Hashtable jndiProperties = new Hashtable();
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
final Context context = new InitialContext(jndiProperties);
// The app name is the application name of the deployed EJBs. This is typically the ear name
// without the .ear suffix. However, the application name could be overridden in the application.xml of the
// EJB deployment on the server.
// Since we haven't deployed the application as a .ear, the app name for us will be an empty string
final String appName = "";
// This is the module name of the deployed EJBs on the server. This is typically the jar name of the
// EJB deployment, without the .jar suffix, but can be overridden via the ejb-jar.xml
// In this example, we have deployed the EJBs in a jboss-as-ejb-remote-app.jar, so the module name is
// jboss-as-ejb-remote-app
final String moduleName = "jboss-as-ejb-remote-app";
// AS7 allows each deployment to have an (optional) distinct name. We haven't specified a distinct name for
// our EJB deployment, so this is an empty string
final String distinctName = "";
// The EJB name which by default is the simple class name of the bean implementation class
final String beanName = CalculatorBean.class.getSimpleName();
// the remote view fully qualified class name
final String viewClassName = RemoteCalculator.class.getName();
// let's do the lookup
return (RemoteCalculator) context.lookup("ejb:" + appName + "/" + moduleName + "/" + distinctName + "/" + beanName + "!" + viewClassName);
* Looks up and returns the proxy to remote stateful counter bean
* @return
* @throws NamingException
private static RemoteCounter lookupRemoteStatefulCounter() throws NamingException {
final Hashtable jndiProperties = new Hashtable();
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
final Context context = new InitialContext(jndiProperties);
// The app name is the application name of the deployed EJBs. This is typically the ear name
// without the .ear suffix. However, the application name could be overridden in the application.xml of the
// EJB deployment on the server.
// Since we haven't deployed the application as a .ear, the app name for us will be an empty string
final String appName = "";
// This is the module name of the deployed EJBs on the server. This is typically the jar name of the
// EJB deployment, without the .jar suffix, but can be overridden via the ejb-jar.xml
// In this example, we have deployed the EJBs in a jboss-as-ejb-remote-app.jar, so the module name is
// jboss-as-ejb-remote-app
final String moduleName = "jboss-as-ejb-remote-app";
// AS7 allows each deployment to have an (optional) distinct name. We haven't specified a distinct name for
// our EJB deployment, so this is an empty string
final String distinctName = "";
// The EJB name which by default is the simple class name of the bean implementation class
final String beanName = CounterBean.class.getSimpleName();
// the remote view fully qualified class name
final String viewClassName = RemoteCounter.class.getName();
// let's do the lookup (notice the ?stateful string as the last part of the jndi name for stateful bean lookup)
return (RemoteCounter) context.lookup("ejb:" + appName + "/" + moduleName + "/" + distinctName + "/" + beanName + "!" + viewClassName + "?stateful");
The code has some comments which will help you understand each of those
lines. But we’ll explain here in more detail what the code does. As a
first step in the client code, we’ll do a lookup of the EJB using a JNDI
name. In AS7, for remote access to EJBs, you use the ejb: namespace with
the following syntax:
For stateless beans:
ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-interface>
For stateful beans:
ejb:<app-name>/<module-name>/<distinct-name>/<bean-name>!<fully-qualified-classname-of-the-remote-interface>?stateful
The ejb: namespace identifies it as a EJB lookup and is a constant (i.e.
doesn’t change) for doing EJB lookups. The rest of the parts in the jndi
name are as follows:
app-name : This is the name of the .ear (without the .ear suffix) that
you have deployed on the server and contains your EJBs.
Jakarta EE allows you to override the application name, to a name of
your choice by setting it in the application.xml. If the deployment uses
uses such an override then the app-name used in the JNDI name should
match that name.
EJBs can also be deployed in a .war or a plain .jar (like we did in
step 1). In such cases where the deployment isn’t an .ear file, then the
app-name must be an empty string, while doing the lookup.
module-name : This is the name of the .jar (without the .jar suffix)
that you have deployed on the server and the contains your EJBs. If the
EJBs are deployed in a .war then the module name is the .war name
(without the .war suffix).
Jakarta EE allows you to override the module name, by setting it in the
ejb-jar.xml/web.xml of your deployment. If the deployment uses such an
override then the module-name used in the JNDI name should match that
name.
Module name part cannot be an empty string in the JNDI name
distinct-name : This is a WildFly-specific name which can be
optionally assigned to the deployments that are deployed on the server.
More about the purpose and usage of this will be explained in a separate
chapter. If a deployment doesn’t use distinct-name then, use an empty
string in the JNDI name, for distinct-name
bean-name : This is the name of the bean for which you are doing the
lookup. The bean name is typically the unqualified classname of the bean
implementation class, but can be overriden through either ejb-jar.xml or
via annotations. The bean name part cannot be an empty string in the
JNDI name.
fully-qualified-classname-of-the-remote-interface : This is the fully
qualified class name of the interface for which you are doing the
lookup. The interface should be one of the remote interfaces exposed by
the bean on the server. The fully qualified class name part cannot be an
empty string in the JNDI name.
For stateful beans, the JNDI name expects an additional "?stateful" to
be appended after the fully qualified interface name part. This is
because for stateful beans, a new session gets created on JNDI lookup
and the EJB client API implementation doesn’t contact the server during
the JNDI lookup to know what kind of a bean the JNDI name represents
(we’ll come to this in a while). So the JNDI name itself is expected to
indicate that the client is looking up a stateful bean, so that an
appropriate session can be created.
Now that we know the syntax, let’s see our code and check what JNDI name
it uses. Since our stateless EJB named CalculatorBean is deployed in a
jboss-as-ejb-remote-app.jar (without any ear) and since we are looking
up the org.jboss.as.quickstarts.ejb.remote.stateless.RemoteCalculator
remote interface, our JNDI name will be:
ejb:/jboss-as-ejb-remote-app//CalculatorBean!org.jboss.as.quickstarts.ejb.remote.stateless.RemoteCalculator
That’s what the lookupRemoteStatelessCalculator() method in the above
client code uses.
For the stateful EJB named CounterBean which is deployed in hte same
jboss-as-ejb-remote-app.jar and which exposes the
org.jboss.as.quickstarts.ejb.remote.stateful.RemoteCounter, the JNDI
name will be:
ejb:/jboss-as-ejb-remote-app//CounterBean!org.jboss.as.quickstarts.ejb.remote.stateful.RemoteCounter?stateful
That’s what the lookupRemoteStatefulCounter() method in the above client
code uses.
Now that we know of the JNDI name, let’s take a look at the following
piece of code in the lookupRemoteStatelessCalculator():
final Hashtable jndiProperties = new Hashtable();
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
final Context context = new InitialContext(jndiProperties);
Here we are creating a JNDI InitialContext object by passing it some
JNDI properties. The Context.URL_PKG_PREFIXES is set to
org.jboss.ejb.client.naming. This is necessary because we should let the
JNDI API know what handles the ejb: namespace that we use in our JNDI
names for lookup. The "org.jboss.ejb.client.naming" has a
URLContextFactory implementation which will be used by the JNDI APIs to
parse and return an object for ejb: namespace lookups. You can either
pass these properties to the constructor of the InitialContext class or
have a jndi.properites file in the classpath of the client application,
which (atleast) contains the following property:
java.naming.factory.url.pkgs=org.jboss.ejb.client.naming
So at this point, we have setup the InitialContext and also have the
JNDI name ready to do the lookup. You can now do the lookup and the
appropriate proxy which will be castable to the remote interface that
you used as the fully qualified class name in the JNDI name, will be
returned. Some of you might be wondering, how the JNDI implementation
knew which server address to look, for your deployed EJBs. The answer is
in AS7, the proxies returned via JNDI name lookup for ejb: namespace do
not connect to the server unless an invocation on those proxies is done.
Now let’s get to the point where we invoke on this returned proxy:
// Let's lookup the remote stateless calculator
final RemoteCalculator statelessRemoteCalculator = lookupRemoteStatelessCalculator();
System.out.println("Obtained a remote stateless calculator for invocation");
// invoke on the remote calculator
int a = 204;
int b = 340;
System.out.println("Adding " + a + " and " + b + " via the remote stateless calculator deployed on the server");
int sum = statelessRemoteCalculator.add(a, b);
We can see here that the proxy returned after the lookup is used to
invoke the add(…) method of the bean. It’s at this point that the JNDI
implementation (which is backed by the EJB client API) needs to know the
server details. So let’s now get to the important part of setting up the
EJB client context properties.
18.3. Setting up EJB client context properties
A EJB client context is a context which contains contextual information
for carrying out remote invocations on EJBs. This is a WildFly-specific
API. The EJB client context can be associated with multiple EJB
receivers. Each EJB receiver is capable of handling invocations on
different EJBs. For example, an EJB receiver "Foo" might be able to
handle invocation on a bean identified by
app-A/module-A/distinctinctName-A/Bar!RemoteBar, whereas a EJB receiver
named "Blah" might be able to handle invocation on a bean identified by
app-B/module-B/distinctName-B/BeanB!RemoteBean. Each such EJB receiver
knows about what set of EJBs it can handle and each of the EJB receiver
knows which server target to use for handling the invocations on the
bean. For example, if you have a AS7 server at 10.20.30.40 IP address
which has its remoting port opened at 4447 and if that’s the server on
which you deployed that CalculatorBean, then you can setup a EJB
receiver which knows its target address is 10.20.30.40:4447. Such an EJB
receiver will be capable enough to communicate to the server via the
JBoss specific EJB remote client protocol (details of which will be
explained in-depth in a separate chapter).
Now that we know what a EJB client context is and what a EJB receiver
is, let’s see how we can setup a client context with 1 EJB receiver
which can connect to 10.20.30.40 IP address at port 4447. That EJB
client context will then be used (internally) by the JNDI implementation
to handle invocations on the bean proxy.
The client will have to place a jboss-ejb-client.properties file in the
classpath of the application. The jboss-ejb-client.properties can
contain the following properties:
endpoint.name=client-endpoint
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=10.20.30.40
remote.connection.default.port = 8080
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.connection.default.username=appuser
remote.connection.default.password=apppassword
The above properties file is just an example. The actual file that was
used for this sample program is available here for reference
jboss-ejb-client.properties
First the endpoint.name property. We mentioned earlier that the EJB
receivers will communicate with the server for EJB invocations.
Internally, they use JBoss Remoting project to carry out the
communication. The endpoint.name property represents the name that will
be used to create the client side of the enpdoint. The endpoint.name
property is optional and if not specified in the
jboss-ejb-client.properties file, it will default to
"config-based-ejb-client-endpoint" name.
Next is the remote.connectionprovider.create.options.<….> properties:
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
The "remote.connectionprovider.create.options." property prefix can be
used to pass the options that will be used while create the connection
provider which will handle the "remote:" protocol. In this example we
use the "remote.connectionprovider.create.options." property prefix to
pass the "org.xnio.Options.SSL_ENABLED" property value as false. That
property will then be used during the connection provider creation.
Similarly other properties can be passed too, just append it to the
"remote.connectionprovider.create.options." prefix
Next we’ll see:
remote.connections=default
This is where you define the connections that you want to setup for
communication with the remote server. The "remote.connections" property
uses a comma separated value of connection "names". The connection names
are just logical and are used grouping together the connection
configuration properties later on in the properties file. The example
above sets up a single remote connection named "default". There can be
more than one connections that are configured. For example:
remote.connections=one, two
Here we are listing 2 connections named "one" and "two". Ultimately,
each of the connections will map to a EJB receiver. So if you have 2
connections, that will setup 2 EJB receivers that will be added to the
EJB client context. Each of these connections will be configured with
the connection specific properties as follows:
remote.connection.default.host=10.20.30.40
remote.connection.default.port = 8080
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
As you can see we are using the "remote.connection.<connection-name>."
prefix for specifying the connection specific property. The connection
name here is "default" and we are setting the "host" property of that
connection to point to 10.20.30.40. Similarly we set the "port" for that
connection to 4447.
By default WildFly uses 8080 as the remoting port. The EJB client API
uses the http port, with the http-upgrade functionality, for
communicating with the server for remote invocations, so that’s the port
we use in our client programs (unless the server is configured for some
other http port)
remote.connection.default.username=appuser
remote.connection.default.password=apppassword
The given user/password must be set by using the command bin/add-user.sh
(or.bat).
The user and password must be set because the security-realm is enabled
for the subsystem remoting (see standalone*.xml or domain.xml) by
default.
If you do not need the security for remoting you might remove the
attribute security-realm in the configuration.
security-realm is enabled by default.
We then use the "remote.connection.<connection-name>.connect.options."
property prefix to setup options that will be used during the connection
creation.
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=one, two
remote.connection.one.host=localhost
remote.connection.one.port=6999
remote.connection.one.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
remote.connection.two.host=localhost
remote.connection.two.port=7999
remote.connection.two.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
As you can see we setup 2 connections "one" and "two" which both point
to "localhost" as the "host" but different ports. Each of these
connections will internally be used to create the EJB receivers in the
EJB client context.
So that’s how the jboss-ejb-client.properties file can be setup and
placed in the classpath.
The EJB client code will by default look for jboss-ejb-client.properties
in the classpath. However, you can specify a different file of your
choice by setting the "jboss.ejb.client.properties.file.path" system
property which points to a properties file on your filesystem,
containing the client context configurations. An example for that would
"-Djboss.ejb.client.properties.file.path=/home/me/my-client/custom-jboss-ejb-client.properties"
A jboss-client jar is shipped in the distribution. It’s available at
WILDFLY_HOME/bin/client/jboss-client.jar. Place this jar in the
classpath of your client application.
If you are using Maven to build the client application, then please
follow the instructions in the WILDFLY_HOME/bin/client/README.txt to add
this jar as a Maven dependency.
Summary
In the above examples, we saw what it takes to invoke a EJB from a
remote client. To summarize:
Has a jboss-ejb-client.properties in its classpath to setup the
server connection information
Either has a jndi.properties to specify the
java.naming.factory.url.pkgs property or passes that as a property to
the InitialContext constructor
Setup the client classpath to include the jboss-client jar that’s
required for remote invocation of the EJBs. The location of the jar is
mentioned above. You’ll also need to have your application’s bean
interface jars and other jars that are required by your application, in
the client classpath
The purpose of this chapter is to demonstrate how to lookup and invoke
on EJBs deployed on an WildFly server instance from another WildFly
server instance. This is different from invoking the EJBs
a remote standalone client
Let’s call the server, from which the invocation happens to the EJB, as
"Client Server" and the server on which the bean is deployed as the
"Destination Server".
19.1. Application packaging
In this example, we’ll consider a EJB which is packaged in a myejb.jar
which is within a myapp.ear. Here’s how it would look like:
myapp.ear
|---- myejb.jar
| |
| |---- <org.myapp.ejb.*> // EJB classes
Note that packaging itself isn’t really important in the context of this
article. You can deploy the EJBs in any standard way (.ear, .war or
.jar).
@Override
public String greet(String user) {
return "Hello " + user + ", have a pleasant day!";
WildFly 26 is secure by default. What this means is that no communication
can happen with an WildFly instance from a remote client (irrespective
of whether it is a standalone client or another server instance) without
passing the appropriate credentials. Remember that in this example, our
"client server" will be communicating with the "destination server". So
in order to allow this communication to happen successfully, we’ll have
to configure user credentials which we will be using during this
communication. So let’s start with the necessary configurations for
this.
19.4. Configuring a user on the "Destination Server"
As a first step we’ll configure a user on the destination server who
will be allowed to access the destination server. We create the user
using the add-user script that’s available in the JBOSS_HOME/bin
folder. In this example, we’ll be configuring a Application User named
ejb and with a password test in the ApplicationRealm. Running the
add-user script is an interactive process and you will see
questions/output as follows:
add-user
jpai@jpai-laptop:bin$ ./add-user.sh
What type of user do you wish to add?
a) Management User (mgmt-users.properties)
b) Application User (application-users.properties)
(a): b
Enter the details of the new user to add.
Realm (ApplicationRealm) :
Username : ejb
Password :
Re-enter Password :
What roles do
you want this user to belong to? (Please enter a comma separated list, or leave blank for none)\[ \]:
About to add user 'ejb' for realm 'ApplicationRealm'
Is this correct yes/no? yes
Added user 'ejb' to file '/jboss-as-7.1.1.Final/standalone/configuration/application-users.properties'
Added user 'ejb' to file '/jboss-as-7.1.1.Final/domain/configuration/application-users.properties'
Added user 'ejb' with roles to file '/jboss-as-7.1.1.Final/standalone/configuration/application-roles.properties'
Added user 'ejb' with roles to file '/jboss-as-7.1.1.Final/domain/configuration/application-roles.properties'
As you can see in the output above we have now configured a user on the
destination server who’ll be allowed to access this server. We’ll use
this user credentials later on in the client server for communicating
with this server. The important bits to remember are the user we have
created in this example is ejb and the password is test.
19.5. Start the "Destination Server"
As a next step towards running this example, we’ll start the
"Destination Server". In this example, we’ll use the standalone server
and use the standalone-full.xml configuration. The startup command
will look like:
./standalone.sh -server-config=standalone-full.xml
Ensure that the server has started without any errors.
It’s very important to note that if you are starting both the server
instances on the same machine, then each of those server instances
must have a unique jboss.node.name system property. You can do that
by passing an appropriate value for -Djboss.node.name system property
to the startup script:
19.6. Deploying the application
The application ( myapp.ear in our case) will be deployed to
"Destination Server". The process of deploying the application is out of
scope of this chapter. You can either use the Command Line Interface or
the Admin console or any IDE or manually copy it to
JBOSS_HOME/standalone/deployments folder (for standalone server). Just
ensure that the application has been deployed successfully.
So far, we have built a EJB application and deployed it on the
"Destination Server". Now let’s move to the "Client Server" which acts
as the client for the deployed EJBs on the "Destination Server".
19.7. Configuring the "Client Server" to point to the EJB remoting connector
on the "Destination Server"
As a first step on the "Client Server", we need to let the server know
about the "Destination Server"'s EJB remoting connector, over which it
can communicate during the EJB invocations. To do that, we’ll have to
add a " remote-outbound-connection" to the remoting subsystem on the
"Client Server". The " remote-outbound-connection" configuration
indicates that a outbound connection will be created to a remote server
instance from that server. The " remote-outbound-connection" will be
backed by a " outbound-socket-binding" which will point to a remote
host and a remote port (of the "Destination Server"). So let’s see how
we create these configurations.
19.8. Start the "Client Server"
In this example, we’ll start the "Client Server" on the same machine as
the "Destination Server". We have copied the entire server installation
to a different folder and while starting the "Client Server" we’ll use a
port-offset (of 100 in this example) to avoid port conflicts:
./standalone.sh -server-config=standalone-full.xml -Djboss.socket.binding.port-offset=100
19.9. Create a security realm on the client server
Remember that we need to communicate with a secure destination server.
In order to do that the client server has to pass the user credentials
to the destination server. Earlier we created a user on the destination
server who’ll be allowed to communicate with that server. Now on the
"client server" we’ll create a security-realm which will be used to pass
the user information.
In this example we’ll use a security realm which stores a Base64 encoded
password and then passes on that credentials when asked for. Earlier we
created a user named ejb and password test. So our first task here
would be to create the base64 encoded version of the password test.
You can use any utility which generates you a base64 version for a
string. I used this online site which
generates the base64 encoded string. So for the test password, the
base64 encoded version is dGVzdA==
While generating the base64 encoded string make sure that you don’t have
any trailing or leading spaces for the original password. That can lead
to incorrect encoded versions being generated.
Now that we have generated that base64 encoded password, let’s use in
the in the security realm that we are going to configure on the "client
server". I’ll first shutdown the client server and edit the
standalone-full.xml file to add the following in the <management>
section
Now let’s create a " security-realm" for the base64 encoded password.
/core-service=management/security-realm=ejb-security-realm:add()
/core-service=management/security-realm=ejb-security-realm/server-identity=secret:add(value=dGVzdA==)
Notice that the CLI show the message "process-state" ⇒
"reload-required", so you have to restart the server before you can use
this change.
upon successful invocation of this command, the following configuration
will be created in the management section:
standalone-full.xml
<management>
<security-realms>
<security-realm name="ejb-security-realm">
<server-identities>
<secret value="dGVzdA=="/>
</server-identities>
</security-realm>
</security-realms>
As you can see I have created a security realm named
"ejb-security-realm" (you can name it anything) with the base64 encoded
password. So that completes the security realm configuration for the
client server. Now let’s move on to the next step.
19.10. Create a outbound-socket-binding on the "Client Server"
Let’s first create a outbound-socket-binding which points the
"Destination Server"'s host and port. We’ll use the CLI to create this
configuration:
/socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=remote-ejb:add(host=localhost, port=8080)
The above command will create a outbound-socket-binding named "
remote-ejb" (we can name it anything) which points to "localhost" as
the host and port 8080 as the destination port. Note that the host
information should match the host/IP of the "Destination Server" (in
this example we are running on the same machine so we use "localhost")
and the port information should match the http-remoting connector port
used by the EJB subsystem (by default it’s 8080). When this command is
run successfully, we’ll see that the standalone-full.xml (the file which
we used to start the server) was updated with the following
outbound-socket-binding in the socket-binding-group:
<socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
<outbound-socket-binding name="remote-ejb">
<remote-destination host="localhost" port="8080"/>
</outbound-socket-binding>
</socket-binding-group>
19.11. Create a "remote-outbound-connection" which uses this newly created
"outbound-socket-binding"
Now let’s create a " remote-outbound-connection" which will use the
newly created outbound-socket-binding (pointing to the EJB remoting
connector of the "Destination Server"). We’ll continue to use the CLI to
create this configuration:
/subsystem=remoting/remote-outbound-connection=remote-ejb-connection:add(outbound-socket-binding-ref=remote-ejb, protocol=http-remoting, security-realm=ejb-security-realm, username=ejb)
The above command creates a remote-outbound-connection, named "
remote-ejb-connection" (we can name it anything), in the remoting
subsystem and uses the previously created " remote-ejb"
outbound-socket-binding (notice the outbound-socket-binding-ref in that
command) with the http-remoting protocol. Furthermore, we also set the
security-realm attribute to point to the security-realm that we created
in the previous step. Also notice that we have set the username
attribute to use the user name who is allowed to communicate with the
destination server.
What this step does is, it creates a outbound connection, on the client
server, to the remote destination server and sets up the username to the
user who allowed to communicate with that destination server and also
sets up the security-realm to a pre-configured security-realm capable of
passing along the user credentials (in this case the password). This way
when a connection has to be established from the client server to the
destination server, the connection creation logic will have the
necessary security credentials to pass along and setup a successful
secured connection.
Now let’s run the following two operations to set some default
connection creation options for the outbound connection:
/subsystem=remoting/remote-outbound-connection=remote-ejb-connection/property=SASL_POLICY_NOANONYMOUS:add(value=false)
<outbound-connections>
<remote-outbound-connection name="remote-ejb-connection" outbound-socket-binding-ref="remote-ejb" protocol="http-remoting" security-realm="ejb-security-realm" username="ejb">
<properties>
<property name="SASL_POLICY_NOANONYMOUS" value="false"/>
<property name="SSL_ENABLED" value="false"/>
</properties>
</remote-outbound-connection>
</outbound-connections>
</subsystem>
From a server configuration point of view, that’s all we need on the
"Client Server". Our next step is to deploy an application on the
"Client Server" which will invoke on the bean deployed on the
"Destination Server".
19.12. Packaging the client application on the "Client Server"
Like on the "Destination Server", we’ll use .ear packaging for the
client application too. But like previously mentioned, that’s not
mandatory. You can even use a .war or .jar deployments. Here’s how our
client application packaging will look like:
client-app.ear
|--- META-INF
| |
| |--- jboss-ejb-client.xml
|--- web.war
| |
| |--- WEB-INF/classes
| | |
| | |---- <org.myapp.FooServlet> // classes in the web app
In the client application we’ll use a servlet which invokes on the bean
deployed on the "Destination Server". We can even invoke the bean on the
"Destination Server" from a EJB on the "Client Server". The code remains
the same (JNDI lookup, followed by invocation on the proxy). The
important part to notice in this client application is the file
jboss-ejb-client.xml which is packaged in the META-INF folder of a top
level deployment (in this case our client-app.ear). This
jboss-ejb-client.xml contains the EJB client configurations which will
be used during the EJB invocations for finding the appropriate
destinations (also known as, EJB receivers). The contents of the
jboss-ejb-client.xml are explained next.
If your application is deployed as a top level .war deployment, then the
jboss-ejb-client.xml is expected to be placed in .war/WEB-INF/ folder
(i.e. the same location where you place any web.xml file).
<client-context>
<ejb-receivers>
<remoting-ejb-receiver outbound-connection-ref="remote-ejb-connection"/>
</ejb-receivers>
</client-context>
</jboss-ejb-client>
You’ll notice that we have configured the EJB client context (for this
application) to use a remoting-ejb-receiver which points to our earlier
created " remote-outbound-connection" named "
remote-ejb-connection". This links the EJB client context to use the "
remote-ejb-connection" which ultimately points to the EJB remoting
connector on the "Destination Server".
19.14. Deploy the client application
Let’s deploy the client application on the "Client Server". The process
of deploying the application is out of scope, of this chapter. You can
use either the CLI or the admin console or a IDE or deploy manually to
JBOSS_HOME/standalone/deployments folder. Just ensure that the
application is deployed successfully.
19.15. Client code invoking the bean
We mentioned that we’ll be using a servlet to invoke on the bean, but
the code to invoke the bean isn’t servlet specific and can be used in
other components (like EJB) too. So let’s see how it looks like:
import javax.naming.Context;
import java.util.Hashtable;
import javax.naming.InitialContext;
public void invokeOnBean() {
try {
final Hashtable props = new Hashtable();
// setup the ejb: namespace URL factory
props.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
// create the InitialContext
final Context context = new javax.naming.InitialContext(props);
// Lookup the Greeter bean using the ejb: namespace syntax which is explained here https://docs.jboss.org/author/display/AS71/EJB+invocations+from+a+remote+client+using+JNDI
final Greeter bean = (Greeter) context.lookup("ejb:" + "myapp" + "/" + "myejb" + "/" + "" + "/" + "GreeterBean" + "!" + org.myapp.ejb.Greeter.class.getName());
// invoke on the bean
final String greeting = bean.greet("Tom");
System.out.println("Received greeting: " + greeting);
} catch (Exception e) {
throw new RuntimeException(e);
That’s it! The above code will invoke on the bean deployed on the
"Destination Server" and return the result.
20. Remote Jakarta Enterprise Beans invocations via JNDI - Jakarta Enterprise Beans client API or wildfly-naming-client project
20.1. Purpose
WildFly provides EJB client API project as well as wildfly-naming-client project(https://github.com/wildfly/wildfly-naming-client)
for invoking on remote objects exposed via JNDI. This article explains
which approach to use when and what the differences and scope of each of
these projects is.
20.2. Overview
Now that we know that for remote client JNDI communication with WildFly
requires wildfly-naming-client project, let’s quickly see what the code
looks like.
20.2.1. Client code relying on jndi.properties in classpath
// Create an InitialContext using the javax.naming.* API
InitialContext ctx = new InitialContext();
Blah blah = (Blah) ctx.lookup("foo:blah");
As you can see, there’s not much here in terms of code. We first create
a InitialContext which as per the API will look for a jndi.properties in
the classpath of the application. We’ll see what our jndi.properties looks like, later.
Once the InitialContext is created, we just use it to do a lookup on a
JNDI name which we know is bound on the server side. We’ll come back to
the details of this lookup string in a while.
Let’s now see what the jndi.properties in our client classpath looks
like:
java.naming.factory.initial=org.wildfly.naming.client.WildFlyInitialContextFactory
java.naming.provider.url=remote+http://localhost:8080
Those two properties are important for wildfly-naming-client project to be
used for communicating with the WildFly server. The first property tells
the JNDI API which initial context factory to use. In this case we are
pointing it to the WildFlyInitailContextFactory class supplied by the
wildfly-naming-client project. The other property is the PROVIDER_URL.
which is remote+http:// for wildfly-naming-client. The rest
of the PROVIDER_URL part is the server hostname or IP and the port on
which the remoting connector is exposed on the server side. By default
the http-remoting connector port in WildFly 26 is 8080. That’s what we
have used in our example. The hostname we have used is localhost but
that should point to the server IP or hostname where the server is
running.
So we saw how to setup the JNDI properties in the jndi.properties file.
The JNDI API also allows you to pass these properties to the constructor
of the InitialContext class (please check the javadoc of that class for
more details). Let’s quickly see what the code would look like:
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
props.put(Context.PROVIDER_URL, "remote+https://localhost:8080");
InitialContext ctx = new InitialContext(props);
Blah blah = (Blah) ctx.lookup("foo:blah");
That’s it! You can see that the values that we pass to those properties
are the same as what we did via the jndi.properties. It’s upto the
client application to decide which approach they want to follow.
20.2.2. Using the wildfly-config.xml
The wildfly-config.xml can be used to specify a static configuration for a
standalone client to use. Below is a simple example specifying plaintext
username / password and the remote server connection (host/port).
The wildfly-config.xml goes in the META-INF directory.
<configuration>
<authentication-client xmlns="urn:elytron:1.0.1">
<authentication-rules>
<rule use-configuration="default"/>
</authentication-rules>
<authentication-configurations>
<configuration name="default">
<set-user-name name="ranabir"/>
<credentials>
<clear-password password="redhat1!"/>
</credentials>
</configuration>
</authentication-configurations>
</authentication-client>
<jboss-ejb-client xmlns="urn:jboss:wildfly-client-ejb:3.0">
<connections>
<connection uri="remote+http://127.0.0.1:8080" />
</connections>
</jboss-ejb-client>
</configuration>
Using the wildfly-config.xml, the InitialContext creation does not specify
the host/port/user/pass since it is already defined in the wildfly-config.xml.
The EJB lookup
public static Context getInitialContext(String host, Integer port, String username, String password) throws NamingException {
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
return new InitialContext(props);
public void callRemoteEjb() {
HelloRemote remote = getInitialContext(host, port, user, pass).lookup("ejb:helloWorld/helloWorld-ejb/HelloWorldSLSB!org.jboss.examples.ejb.HelloRemote");
remote.helloWorld();
20.2.3. How does wildfly naming client work
We have so far had an overview of how the client code looks like when
using the wildfly-naming-client project. Let’s now have a brief look at how the wildfly-naming-client project
internally establishes the communication with the server and allows JNDI
operations from the client side.
When the client code creates an InitialContext backed by the
org.wildfly.naming.client.WildFlyInitialContextFactory class, the
org.wildfly.naming.client.WildFlyInitialContextFactory internally looks
for the PROVIDER_URL (and other) properties that are applicable for that
context ( doesn’t matter whether it comes from the jndi.properties
file or whether passed explicitly to the constructor of the
InitialContext). Once it identifies the server and port to connect to,
the wildfly-naming-client project internally sets up a connection using the
remoting APIs with the remoting connector which is exposed on that
port.
It has also increased its support for security configurations.
Every service including the http+remote connector (which
listens by default on port 8080), is secured.
This means that when trying to do JNDI operations like a
lookup, the client has to pass appropriate user credentials. In our
examples so far we haven’t passed any username/pass or any other
credentials while creating the InitialContext. That was just to keep the
examples simple. But let’s now check and use one
of the ways how we pass the user credentials. Let’s at the moment just
assume that the remoting connector on port 8080 is accessible to a user
named " `ranabir`" whose password is expected to be " `redhat1!`".
void doLookup() {
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
props.put(Context.PROVIDER_URL, "remote+https://localhost:8080");
// provide an username
props.put(Context.SECURITY_PRINCIPAL, "ranabir");
// provide a password
props.put(Context.SECURITY_CREDENTIALS, "redhat1!");
// create a context passing these properties
InitialContext ctx = new InitialContext(props);
Blah blah = (Blah) ctx.lookup("foo:blah");
The code is similar to our previous example, except that we now have
added 2 additional properties that are passed to the InitialContext
constructor. The first is Context.SECURITY_PRINCIPAL
which passes the username (ranabir in this case)
and the second is Context.SECURITY_CREDENTIALS
which passes the password (redhat1! in this case). Of course the same
properties can be configured in the jndi.properties file (read the
javadoc of the Context class for appropriate properties to be used in
the jndi.properties) and as well as in wildfly-config.xml.
This is one way of passing the security credentials for JNDI communication with WildFly.
There are some other ways to do this too.
Moreover In order to manage Lookup High Availability, you can provide a list of remote servers
that will be checked for the Initial Lookup of the remote+http call. Here is the updated
PROVIDER_URL format, supposing you were to contact two servers located at localhost:8080 and localhost:8180
props.put(Context.PROVIDER_URL, "remote+http://localhost:8080,remote+http://localhost:8180");
20.2.4. JNDI operations allowed using wildfly-naming-client project
So far we have mainly concentrated on how the naming context is created
and what it internally does when an instance is created. Let’s now take
this one step further and see what kind of operations are allowed for a
JNDI context backed by the wildfly-naming-client project.
The JNDI Context has various methods that
are exposed for JNDI operations. One important thing to note in case of
wildfly-naming-client project is that, the project’s scope is to allow a client
to communicate with the JNDI backend exposed by the server. As such, the
wildfly-naming-client project does not support many of the methods that are
exposed by the javax.naming.Context class. The wildfly-naming-client project
only supports the read-only kind of methods (like the lookup() method)
and does not support any write kind of methods (like the bind() method).
The client applications are expected to use the wildfly-naming-client project
mainly for lookups of JNDI objects. Neither WildFly nor wildfly-naming-client
project allows writing/binding to the JNDI server from a remote
application.
20.2.5. Pre-requisites of remotely accessible JNDI objects
On the server side, the JNDI can contain numerous objects that are bound
to it. However, not all of those are exposed remotely. The two
conditions that are to be satisfied by the objects bound to JNDI, to be
remotely accessible are:
1) Such objects should be bound under the java:jboss/exported/
namespace. For example, java:jboss/exported/foo/bar
2) Objects bound to the java:jboss/exported/ namespace are expected to
be serializable. This allows the objects to be sent over the wire to the
remote clients
Both these conditions are important and are required for the objects to
be remotely accessible via JNDI.
20.2.6. JNDI lookup strings for remote clients backed by the wildfly-naming-client project
In our examples, so far, we have been consistently using " `foo/bar`" as
the JNDI name to lookup from a remote client using the wildfly-naming-client
project. There’s a bit more to understand about the JNDI name and how it
maps to the JNDI name that’s bound on the server side.
First of all, the JNDI names used while using the wildfly-naming-client project
are always relative to the java:jboss/exported/ namespace. So in our
examples, we are using " `foo/bar`" JNDI name for the lookup, that
actually is (internally) " `java:jboss/exported/foo/bar`". The
wildfly-naming-client project expects it to always be relative to the "
`java:jboss/exported/`" namespace. Once connected with the server side,
the wildfly-naming-client project will lookup for "foo/bar" JNDI name under the
" `java:jboss/exported/`" namespace of the server.
Note: Since the JNDI name that you use on the client side is always
relative to java:jboss/exported namespace, you shouldn’t be prefixing
the java:jboss/exported/ string to the JNDI name. For example, if you
use the following JNDI name:
The wildfly-naming-client implementation perhaps should be smart enough to strip
off the java:jboss/exported/ namespace prefix if supplied. But let’s not
go into that here.
20.2.7. How does wildfly-naming-client project implementation transfer the JNDI
objects to the clients
When a lookup is done on a JNDI string, the wildfly-naming-client implementation
internally uses the connection to the remoting connector (which it has
established based on the properties that were passed to the
InitialContext) to communicate with the server. On the server side, the
implementation then looks for the JNDI name under the
java:jboss/exported/ namespace. Assuming that the JNDI name is
available, under that namespace, the wildfly-naming-client implementation then
passes over the object bound at that address to the client. This is
where the requirement about the JNDI object being serializable comes
into picture. wildfly-naming-client project internally uses jboss-marshalling
project to marshal the JNDI object over to the client. On the client
side the wildfly-naming-client implementation then unmarshalles the object and
returns it to the client application.
So literally, each lookup backed by the wildfly-naming-client project entails a
server side communication/interaction and then marshalling/unmarshalling
of the object graph. This is very important to remember. We’ll come back
to this later, to see why this is important when it comes to using EJB
client API project for doing EJB lookups ( EJB
invocations from a remote client using JNDI) as against using
wildfly-naming-client project for doing the same thing.
20.2.8. Few more things
Unlike the previous jboss-remote-naming project, the connection to
the peer is not requested. Until an operation is performed on the connection,
and all consumers of the same remote URL will share a connection.
The connection lifecycle is independent of any Context instances which reference it.
Multiple services can be looked up via the same context. To register providers,
implement the org.wildfly.naming.client.NamingProvider interface and register the
implementation using the approach described in the java.util.ServiceLoader documentation.
Note: One important thing is that jndi.properties should not be packaged in an
app running in Wildfly though you can put it in a standalone java app.
The reason is, when you run in Wildfly , if you do new InitialContext() and
you have jndi.properties in your classpath, it will read those settings
and change the default configuration for the whole wildfly JVM.
That pretty much covers whatever is important to know, in the
wildfly-naming-client project, for a typical client application.
This simple JNDI/naming client library abstracts away some of the
pain of JNDI by providing the following features:
Federation support, Class loader based provider extensibility,
A replacement implementation of the jboss-remote-naming protocol,
Abstract context implementations for supporting relative contexts and
federation in custom naming providers.
Those of you who don’t have client applications doing remote EJB
invocations, can just skip the rest of this article if you aren’t
interested in those details.
20.4. Remote EJB invocations backed by the wildfly-naming-client project
In previous sections of this article we saw that whatever is exposed in
the java:jboss/exported/ namespace is accessible remotely to the client
applications under the relative JNDI name. Some of you might already
have started thinking about exposing remote views of EJBs under that
namespace.
It’s important to note that WildFly server side already by default
exposes the remote views of a EJB under the java:jboss/exported/
namespace (although it isn’t logged in the server logs). So assuming
your server side application has the following stateless bean:
package org.myapp.ejb;
@Stateless
@Remote(Foo.class)
public class FooBean implements Foo {
public String sayBar() {
return "Baaaaaaaar";
Then the " Foo`" remote view is exposed under the
`java:jboss/exported/ namespace under the following JNDI name scheme
(which is similar to that mandated by Jakarta Enterprise Beans 3.2 spec for java:global/
namespace)
app-name/module-name/bean-name!bean-interface
where,
app-name = the name of the .ear (without the .ear suffix) or the
application name configured via application.xml deployment descriptor.
If the application isn’t packaged in a .ear then there will be no
app-name part to the JNDI string.
module-name = the name of the .jar or .war (without the .jar/.war
suffix) in which the bean is deployed or the module-name configured in
web.xml/ejb-jar.xml of the deployment. The module name is mandatory part
in the JNDI string.
bean-name = the name of the bean which by default is the simple name
of the bean implementation class. Of course it can be overridden either
by using the "name" attribute of the bean definining annotation
(@Stateless(name="blah") in this case) or even the ejb-jar.xml
deployment descriptor.
bean-interface = the fully qualified class name of the interface being
exposed by the bean.
So in our example above, let’s assume the bean is packaged in a
myejbmodule.jar which is within a myapp.ear. So the JNDI name for the
Foo remote view under the java:jboss/exported/ namespace would be:
java:jboss/exported/myapp/myejbmodule/FooBean!org.myapp.ejb.Foo
That’s where WildFly will automatically expose the remote views of the
EJBs under the java:jboss/exported/ namespace, in addition to the
java:global/ java:app/ java:module/ namespaces mandated by the EJB 3.1
spec.
So the next logical question would be, are these remote views of EJBs
accessible and invokable using the wildfly-naming-client project on the client
application. The answer is yes! Let’s quickly see the client code for
invoking our FooBean. Again, let’s just use " `ranabir`" and " `redhat1!`"
as username/password for connecting to the remoting connector.
void doBeanLookup() {
Properties props = new Properties();
props.put(Context.INITIAL_CONTEXT_FACTORY, "org.wildfly.naming.client.WildFlyInitialContextFactory");
props.put(Context.PROVIDER_URL,"remote+https://localhost:8080");
// username
props.put(Context.SECURITY_PRINCIPAL, "ranabir");
// password
props.put(Context.SECURITY_CREDENTIALS, "redhat1!");
// This is an important property to set if you want to do EJB invocations via the wildfly-naming-client project
props.put("wildfly.naming.client.ejb.context", true);
// create a context passing these properties
InitialContext ctx = new InitialContext(props);
// lookup the bean Foo
beanRemoteInterface = (Foo) ctx.lookup("myapp/myejbmodule/FooBean!org.myapp.ejb.Foo");
String bar = beanRemoteInterface.sayBar();
System.out.println("Remote Foo bean returned " + bar);
ctx.close();
// after this point the beanRemoteInterface is not longer valid!
As you can see, most of the code is similar to what we have been seeing
so far for setting up a JNDI context backed by the wildfly-naming-client
project. The only parts that change are:
\1) An additional " `wildfly.naming.client.ejb.context`" property that is
added to the properties passed to the InitialContext constructor.
2) The JNDI name used for the lookup
3) And subsequently the invocation on the bean interface returned by the
lookup.
Let’s see what the " wildfly.naming.client.ejb.context`" does. In
WildFly, remote access/invocations on EJBs is facilitated by the JBoss
specific EJB client API, which is a project on its own
https://github.com/wildfly/jboss-ejb-client. So no matter, what
mechanism you use (wildfly-naming-client or core EJB client API), the
invocations are ultimately routed through the EJB client API project. In
this case too, the wildfly-naming-client internally uses EJB client API to
handle EJB invocations. From a EJB client API project perspective, for
successful communication with the server, the project expects a
`EJBClientContext backed by (atleast one) EJBReceiver(s). The
EJBReceiver is responsible for handling the EJB invocations. One type
of a EJBReceiver is a RemotingConnectionEJBReceiver which internally
uses jboss-remoting project to communicate with the remote server to
handle the EJB invocations. Such a EJBReceiver expects a connection
backed by the jboss-remoting project. Of course to be able to connect to
the server, such a EJBReceiver would have to know the server address,
port, security credentials and other similar parameters. If you were
using the core EJB client API, then you would have configured all these
properties via the jboss-ejb-client.properties or via programatic API
usage as explained here EJB invocations from a remote
client using JNDI. But in the example above, we are using wildfly-naming-client
project and are not directly interacting with the EJB client API
project.
If you look closely at what’s being passed, via the JNDI properties, to
the wildfly-naming-client project and if you remember the details that we
explained in a previous section about how the wildfly-naming-client project
establishes a connection to the remote server, you’ll realize that these
properties are indeed the same as what the
RemotingConnectionEJBReceiver would expect to be able to establish the
connection to the server. Now this is where the "
wildfly.naming.client.ejb.context`" property comes into picture. When
this is set to true and passed to the InitialContext creation (either
via jndi.properties or via the constructor of that class), the
wildfly-naming-client project internally will do whatever is necessary to setup
a `EJBClientContext, containing a RemotingConnectionEJBReceiver which
is created using the same remoting connection that is created by and
being used by wildfly-naming-client project for its own JNDI communication
usage. So effectively, the InitialContext creation via the wildfly-naming-client
project has now internally triggered the creation of a
EJBClientContext containing a EJBReceiver capable of handling the
EJB invocations (remember, no remote EJB invocations are possible
without the presence of a EJBClientContext containing a EJBReceiver
which can handle the EJB).
So we now know the importance of the "
wildfly.naming.client.ejb.context`" property and its usage. Let’s move on
the next part in that code, the JNDI name. Notice that we have used the
JNDI name relative to the `java:jboss/exported/ namespace while doing
the lookup. And since we know that the Foo view is exposed on that JNDI
name, we cast the returned object back to the Foo interface. Remember
that we earlier explained how each lookup via wildfly-naming-client triggers a
server side communication and a marshalling/unmarshalling process. This
applies for EJB views too. In fact, the wildfly-naming-client project has no
clue (since that’s not in the scope of that project to know) whether
it’s an EJB or some random object.
Once the unmarshalled object is returned (which actually is a proxy to
the bean), the rest is straightforward, we just invoke on that returned
object. Now since the wildfly-naming-client implementation has done the
necessary setup for the EJBClientContext (due to the presence of "
wildfly.naming.client.ejb.context`" property), the invocation on that
proxy will internally use the `EJBClientContext (the proxy is smart
enough to do that) to interact with the server and return back the
result. We won’t go into the details of how the EJB client API handles
the communication/invocation.
Long story short, using the wildfly-naming-client project for doing remote EJB
invocations against WildFly is possible!
20.5. Why use the EJB client API approach then?
I can guess that some of you might already question why/when would one
use the EJB client API style lookups as explained in the
EJB invocations from a remote client using JNDI
article instead of just using (what appears to be a simpler)
wildfly-naming-client style lookups.
Before we answer that, let’s understand a bit about the EJB client
project. The EJB client project was implemented keeping in mind various
optimizations and features that would be possible for handling remote
invocations. One such optimization was to avoid doing unnecessary server
side communication(s) which would typically involve network calls,
marshalling/unmarshalling etc… The easiest place where this
optimization can be applied, is to the EJB lookup. Consider the
following code (let’s ignore how the context is created):
ctx.lookup("foo/bar");
Now foo/bar JNDI name could potentially point to any type of object
on the server side. The jndi name itself won’t have the type/semantic
information of the object bound to that name on the server side. If the
context was setup using the wildfly-naming-client project (like we have seen
earlier in our examples), then the only way for wildfly-naming-client to return
an object for that lookup operation is to communicate with the server
and marshal/unmarshal the object bound on the server side. And that’s
exactly what it does (remember, we explained this earlier).
20.5.1. Is the lookup optimization applicable for all bean types?
In the previous section we have mentioned that the lookup
optimization by the EJB client API project happens for stateless beans.
This kind of optimization is not possible for stateful beans because
in case of stateful beans, a lookup is expected to create a session for
that stateful bean and for session creation we do have to communicate
with the server since the server is responsible for creating that
session.
That’s exactly why the EJB client API project expects the JNDI name
lookup string for stateful beans to include the " `?stateful`" string at
the end of the JNDI name:
context.lookup("ejb:myapp/myejbmodule//StatefulBean!org.myapp.ejb.Counter?stateful
");
Notice the use of `"?stateful`" in that JNDI name. See
EJB invocations from a remote client using JNDI for
more details about such lookup.
The presence of " ?stateful`" in the JNDI name lookup string is a
directive to the EJB client API to let it know that a stateful bean is
being looked up and it’s necessary to communicate with the server and
create a session during that lookup. Though `?stateful is optional now.
So as you can see, we have managed to optimize certain operations by
using the EJB client API for EJB lookup/invocation as against using the
wildfly-naming-client project. There are other EJB client API implementation
details (and probably more might be added) which are superior when it is
used for remote EJB invocations in client applications as against
wildfly-naming-client project which doesn’t have the intelligence to carry out
such optimizations for EJB invocations. That’s why the wildfly-naming-client
project for remote EJB invocations is considered " deprecated
". Note that if you want to use wildfly-naming-client for looking up and
invoking on non-EJB remote objects then you are free to do so. In fact,
that’s why that project has been provided. You can even use the
wildfly-naming-client project for EJB invocations (like we just saw), if you are
fine with not wanting the optimizations that the EJB client API can do
for you or if you have other restrictions that force you to use that
project.
The H2 database library provided by WildFly’s com.h2database.h2 module includes an implementation of the Jakarta Servlet HttpServlet interface (org.h2.server.web.WebServlet) that can be used to expose the H2 project’s web console tool. WildFly users could utilize this servlet in their own application by adding a dependency on the com.h2database.h2 module to their application’s deployment and then adding the servlet to the application’s web.xml. This is certainly not recommended for production use — no use of H2 is recommended in production applications — but developers may find use of the H2 console useful.
WildFly has historically integrated H2 in such a way that this is possible. But beginning with the 26 release, this no longer works out of the box, as the com.h2database.h2 module no longer has out-of-the-box access to the module that exposes the Jakarta Servlet API. As of late 2021 the H2 project does not provide a variant of their WebServlet that will work on EE 9 or later, so in preparation for WildFly’s move to EE 10, out of the box support for this integration has been removed.
However it is possible for users of an EE 8 WildFly to restore this integration by adding a JBoss Modules module named javax.servlet.api.h2 to their server installation. The steps to do this are as follows:
<?xml version="1.0" encoding="UTF-8"?>
<module-alias xmlns="urn:jboss:module:1.9"
name="javax.servlet.api:h2"
target-name="javax.servlet.api"/>
The com.h2database.h2 module optionally depends on the javax.servlet.api.h2 module, so if one exists and it exposes the Jakarta Servlet API, then H2 WebServlet integration will work.
This will work so long as the javax.servlet.api module provides the EE 8 Jakarta Servlet API. As of the 26 release that is the case with standard WildFly but is not the case with WildFly Preview. H2 Web Console integration will not work with WildFly Preview.
If in the future a version of the H2 database library becomes available that supports an EE 9 or later version of Jakarta Servlet, out of the box support for use of the H2 WebServlet may be restored.
22.1. Example Applications Migrated from Previous Releases
The applications in this section were written for a previous version of
the server but have been modified to run on WildFly or JBoss AS 7,
which was based on the same core architecture WildFly uses. Changes were made
to resolve issues that arose during deployment and runtime or to fix
problems with application behaviour. Each example below documents the
changes that were made to get the application to run successfully.
22.1.1. Seam 2 DVD Store example
For details on how to migrate this demo application, see
2 DVD Store example on JBoss AS 7 on Marek Novotny’s Blog.
22.1.2. Seam 2 Booking example
For details on how to migrate this demo application, see
2 Booking example on JBoss AS 7 on Marek Novotny’s Blog.
22.1.3. jBPM-Console application
Kris Verlaenen migrated this application from JBoss AS 5 to JBoss AS 7. For
details about this migration, see jBPM5 on JBoss AS 7 on his Kris’s Blog.
22.1.4. Order application used for performance testing
Andy Miller migrated this application from JBoss AS 5 to JBoss AS 7. For details
about this migration, see Order Application Migration from EAP5.1 to JBoss AS 7.
22.1.5. Migrate example application
A step by step work through of issues, and their solutions, that might
crop up when migrating applications to WildFly 26. See the following
github project for details.
22.2. Example Applications Based on EE6
Applications in this section were designed and written specifically to
use the features and functions of EE6.
Quickstarts: A number of quickstart applications were written to
demonstrate Jakarta EE and a few additional technologies. They provide
small, specific, working examples that can be used as a reference for
your own project. For more information about the quickstarts, see
Get Started Developing Applications
Andy Miller ported an example Order application that was used for
performance testing from EAP 5.1 to JBoss AS 7. These are the notes he
made during the migration process.
23.1. Overview of the application
The application is relatively simple. it contains three servlets, some
stateless session beans, a stateful session bean, and some entities.
In addition to application code changes, modifications were made to the
way the EAR was packaged. This is because WildFly removed support of
some proprietary features that were available in EAP 5.1.
23.2. Summary of changes
23.2.1. Code Changes
Modify JNDI lookup code
Since this application was first written for EAP 4.2/4.3, which did not
support EJB reference injection, the servlets were using pre-EE 5
methods for looking up stateless and stateful session bean interfaces.
While migrating to WildFly, it seemed a good time to change the code to
use the @EJB annotation, although this was not a required change.
The real difference is in the lookup name. WildFly only supports the new
EE 6 portable JNDI names rather than the old EAR structure based names.
The JNDI lookup code changed as follows:
Example of code in the EAP 5.1 version:
try {
context = new InitialContext();
distributionCenterManager = (DistributionCenterManager) context.lookup("OrderManagerApp/DistributionCenterManagerBean/local");
} catch(Exception lookupError) {
throw new ServletException("Couldn't find DistributionCenterManager bean", lookupError);
try {
customerManager = (CustomerManager) context.lookup("OrderManagerApp/CustomerManagerBean/local");
} catch(Exception lookupError) {
throw new ServletException("Couldn't find CustomerManager bean", lookupError);
try {
productManager = (ProductManager) context.lookup("OrderManagerApp/ProductManagerBean/local");
} catch(Exception lookupError) {
throw new ServletException("Couldn't find the ProductManager bean", lookupError);
Example of how this is now coded in WildFly:
@EJB(lookup="java:app/OrderManagerEJB/DistributionCenterManagerBean!services.ejb.DistributionCenterManager")
private DistributionCenterManager distributionCenterManager;
@EJB(lookup="java:app/OrderManagerEJB/CustomerManagerBean!services.ejb.CustomerManager")
private CustomerManager customerManager;
@EJB(lookup="java:app/OrderManagerEJB/ProductManagerBean!services.ejb.ProductManager")
private ProductManager productManager;
In addition to the change to injection, which was supported in EAP
5.1.0, the lookup name changed from:
OrderManagerApp/DistributionCenterManagerBean/local
java:app/OrderManagerEJB/DistributionCenterManagerBean!services.ejb.DistributionCenterManager
All the other beans were changed in a similar manner. They are now based
on the portable JNDI names described in EE 6.
23.2.2. Modify logging code
The next major change was to logging within the application. The old
version was using the commons logging infrastructure and Log4J that is
bundled in the application server. Rather than bundling third-party
logging, the application was modified to use the new WildFly Logging
infrastructure.
The code changes themselves are rather trivial, as this example
illustrates:
Old JBoss Commons Logging/Log4J:
private static Log log = LogFactory.getLog(CustomerManagerBean.class);
New WildFly Logging
private static Logger logger = Logger.getLogger(CustomerManagerBean.class.toString());
Old JBoss Commons Logging/Log4J:
if(log.isTraceEnabled()) {
log.trace("Just flushed " + batchSize + " rows to the database.");
log.trace("Total rows flushed is " + (i+1));
New WildFly Logging:
if(logger.isLoggable(Level.TRACE)) {
logger.log(Level.TRACE, "Just flushed " + batchSize + " rows to the database.");
logger.log(Level.TRACE, "Total rows flushed is " + (i+1));
In addition to the code changes made to use the new AS7 JBoss log
manager module, you must add this dependency to the MANIFEST.MF file
as follows:
Manifest-Version: 1.0
Dependencies: org.jboss.logmanager
23.2.3. Modify the code to use Infinispan for 2nd level cache
Jboss Cache has been replaced by Infinispan for 2nd level cache. This
requires modification of the persistence.xml file.
This is what the file looked like in EAP 5.1:
<properties>
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.jbc2.JndiMultiplexedJBossCacheRegionFactory"/>
<property name="hibernate.cache.region.jbc2.cachefactory" value="java:CacheManager"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.use_query_cache" value="false"/>
<property name="hibernate.cache.use_minimal_puts" value="true"/>
<property name="hibernate.cache.region.jbc2.cfg.entity" value="mvcc-entity"/>
<property name="hibernate.cache.region_prefix" value="services"/>
</properties>
This is how it was modified to use Infinispan for the same
configuration:
<properties>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.use_minimal_puts" value="true"/>
</properties>
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
Most of the properties are removed since they will default to the
correct values for the second level cache. See
"Using
the Infinispan second level cache" for more details.
That was the extent of the code changes required to migrate the
application to AS7.
23.2.4. EAR Packaging Changes
Due to modular class loading changes, the structure of the existing EAR
failed to deploy successfully in WildFly.
The old structure of the EAR was as follows:
$ jar tf OrderManagerApp.ear
META-INF/MANIFEST.MF
META-INF/application.xml
OrderManagerWeb.war
OrderManagerEntities.jar
OrderManagerEJB.jar
META-INF/
In this structure, the entities and the persistence.xml were in one
jar file, OrderManagerEntities.jar, and the stateless and stateful
session beans were in another jar file, OrderManagerEJB.jar. This did
not work due to modular class loading changes in WildFly. There are a
couple of ways to resolve this issue:
Since there is no longer an OrderManagerEntities.jar file, the
applcation.xml file was modified to remove the entry.
An entry was added to the MANIFEST.MF file in the
OrderManagerWeb.war to resolve another class loading issue resulting
from the modification to use EJB reference injection in the servlets.
Manifest-Version: 1.0
Dependencies: org.jboss.logmanager
Class-Path: OrderManagerEJB.jar
The Class-Path entry tells the application to look in the
OrderManagerEJB.jar file for the injected beans.
23.2.5. Summary
Although the existing EAR structure could have worked with additional
modifications to the MANIFEST.MF file, this approach seemed more
appealing because it simplified the structure while maintaining the web
tier in its own WAR.
The source files for both versions is attached so you can view the
changes that were made to the application.
This document details the main points that need to be considered by
Spring developers that wish to develop new applications or to migrate
existing applications to be run into WildFly 26.
24.1. Dependencies and Modularity
WildFly 26 has a modular class loading strategy, different from previous
versions of JBoss AS, which enforces a better class loading isolation
between deployments and the application server itself. A detailed
description can be found in the documentation dedicated to
class
loading in WildFly 26.
This reduces significantly the risk of running into a class loading
conflict and allows applications to package their own dependencies if
they choose to do so. This makes it easier for Spring applications that
package their own dependencies - such as logging frameworks or
persistence providers to run on WildFly 26.
At the same time, this does not mean that duplications and conflicts
cannot exist on the classpath. Some module dependencies are implicit,
depending on the type of deployment as shown
here.
24.2. Persistence usage guide
Depending on the strategy being used, Spring applications can be:
24.3. Native Spring/Hibernate applications
Applications that use the Hibernate API directly with Spring (i.e.
through either one of LocalSessionFactoryBean or
AnnotationSessionFactoryBean) may need to use a different version of Hibernate than is provided by WildFly.
24.4. Jakarta Persistence based applications
Spring applications using Jakarta Persistence may choose between:
24.4.1. Using server-deployed persistence units
Applications that use a server-deployed persistence unit must observe
the typical Jakarta EE rules in what concerns dependency management, i.e.
the javax.persistence classes and persistence provider (Hibernate) are
contained in modules which are added automatically by the application
when the persistence unit is deployed.
In order to use the server-deployed persistence units from within
Spring, either the persistence context or the persistence unit need to
be registered in JNDI via web.xml as follows:
<persistence-context-ref>
<persistence-context-ref-name>persistence/petclinic-em</persistence-unit-ref-name>
<persistence-unit-name>petclinic</persistence-unit-name>
</persistence-context-ref>
or, respectively:
<persistence-unit-ref>
<persistence-unit-ref-name>persistence/petclinic-emf</persistence-unit-ref-name>
<persistence-unit-name>petclinic</persistence-unit-name>
</persistence-unit-ref>
When doing so, the persistence context or persistence unit are available
to be looked up in JNDI, as follows:
<jee:jndi-lookup id="entityManager" jndi-name="java:comp/env/persistence/petclinic-em"
expected-type="javax.persistence.EntityManager"/>
<jee:jndi-lookup id="entityManagerFactory" jndi-name="java:comp/env/persistence/petclinic-emf"
expected-type="javax.persistence.EntityManagerFactory"/>
JNDI binding
24.4.2. Using Spring-managed persistence units
Spring applications running in WildFly 26 may also create persistence
units on their own, using the LocalContainerEntityManagerFactoryBean.
This is what these applications need to consider:
Placement of the persistence unit definitions
When the application server encounters a deployment that has a file
named META-INF/persistence.xml (or, for that matter,
WEB-INF/classes/META-INF/persistence.xml), it will attempt to create a
persistence unit based on what is provided in the file. In most cases,
such definition files are not compliant with the Jakarta EE requirements,
mostly because required elements such as the datasource of the
persistence unit are supposed to be provided by the Spring context
definitions, which will fail the deployment of the persistence unit, and
consequently of the entire deployment.
Spring applications can easily avoid this type of conflict, by using a
feature of the LocalContainerEntityManagerFactoryBean which is designed
for this purpose. Persistence unit definition files can exist in other
locations than META-INF/persistence.xml and the location can be
indicated through the persistenceXmlLocation property of the factory
bean class.
Assuming that the persistence unit is in the
META-INF/jpa-persistence.xml, the corresponding definition can be:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceXmlLocation" value="classpath*:META-INF/jpa-persistence.xml"/>
<!-- other definitions -->
</bean>
24.4.3. Managing dependencies
To ensure that Spring applications can use a specific version of Hibernate ORM, exclude the WildFly provided Hibernate ORM version,
instruct the server to exclude the module from
the deployment’s list of dependencies. In order to do so, include a
META-INF/jboss-deployment-structure.xml or, for web applications,
WEB-INF/jboss-deployment-structure.xml with the following content:
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.0">
<deployment>
<exclusions>
<module name="org.hibernate"/>
</exclusions>
</deployment>
</jboss-deployment-structure>
25.1. About this Document
The purpose of this guide is to document changes that are needed to successfully run and deploy JBoss AS 7 applications on WildFly 10. It provides information on to resolve deployment and runtime problems and how to prevent changes in application behavior. This is the first step in moving to the new platform. Once the application is successfully deployed and running on the new platform, plans can be made to upgrade individual components to use the new functions and features of WildFly.
25.2. Overview of WildFly
The list of WildFly new functionality is extensive, being the most relevant, with respect to server and application migrations:
Jakarta EE - WildFly is a certified implementation of Jakarta EE, meeting both the Web and the Full Platform, and already includes support for the latest iterations of CDI (1.2) and Web Sockets (1.1).
Undertow - A new cutting-edge web server in WildFly, designed for maximum throughput and scalability, including environments with over a million connections. And the latest web technologies, such as the new HTTP/2 standard, are already onboard.
Apache ActiveMQ Artemis - WildFly’s new Jakarta Messaging broker. Based on an code donation from HornetQ, this Apache subproject provides outstanding performance based on a proven non-blocking architecture.
IronJacamar 1.2 - The latest IronJacamar provides a stable and feature rich Jakarta Connectors & Datasources support.
JBossWS 5 - The fifth generation of JBossWS, a major leap forward, brings new features and performances improvements to WildFly Web Services
RESTEasy - WildFly which goes beyond the standard Jakarta EE REST APIs (Jakarta RESTful Web Services 2.1), by also providing a number of useful extensions, such as JSON Web Encryption, Jackson, Yaml, Jakarta JSON Processing, and reactive facilities.
OpenJDK ORB - WildFly switched the IIOP implementation from JacORB, to a downstream branch of the OpenJDK Orb, leading to better interoperability with the JVM ORB and the Jakarta EE RI.
Feature Rich Clustering - Clustering support was heavily refactored in WildFly, and includes several APIs for applications
Port Reduction - By utilizing HTTP upgrade, WildFly has moved nearly all of its protocols to be multiplexed over just two HTTP ports: a management port (9990), and an application port (8080).
Enhanced Logging - The management API now supports the ability to list and view the available log files on a server, or even define custom formatters other than the default pattern formatter. Deployment’s logging setup is also greatly enhanced.
Jakarta XML RPC - Jakarta XML Web Services offer a much more accurate and complete solution
JSR-88 - With very little adoption, the more complete deployment APIs provided by vendors are preferred
25.3. Server Migration
Migrating a JBoss AS 7 server to WildFly consists of migrating custom configuration files, and some persisted data that may exist.
25.3.1. JacORB Subsystem
WildFly ORB support is provided by the JDK itself, instead of relying on JacORB. A subsystem configuration migration is required.
JacORB Subsystem Configuration
The extension’s module org.jboss.as.jacorb is replaced by module org.wildfly.iiop-openjdk, while the subsystem configuration namespace urn:jboss:domain:jacorb:2.0 is replaced by
urn:jboss:domain:iiop-openjdk:1.0.
The XML configuration of the new subsystem accepts only a subset of the legacy elements and attributes. Consider the following example of the JacORB subsystem configuration, containing all valid elements and attributes:
<subsystem xmlns="urn:jboss:domain:jacorb:1.3">
<orb name="JBoss" print-version="off" use-imr="off" use-bom="off" cache-typecodes="off"
cache-poa-names="off" giop-minor-version ="2" socket-binding="jacorb" ssl-socket-binding="jacorb-ssl">
<connection retries="5" retry-interval="500" client-timeout="0" server-timeout="0"
max-server-connections="500" max-managed-buf-size="24" outbuf-size="
2048"
outbuf-cache-timeout="-1"/>
<initializers security="off" transactions="spec"/>
<poa monitoring="off" queue-wait="on" queue-min="10" queue-max="100">
<request-processors pool-size="10" max-threads="32"/>
<naming root-context="JBoss/Naming/root" export-corbaloc="on"/>
<interop sun="on" comet="off" iona="off" chunk-custom-rmi-valuetypes="on"
lax-boolean-encoding="off" indirection-encoding-disable="off" strict-check-on-tc-creation="off"/>
<security support-ssl="off" add-component-via-interceptor="on" client-supports="MutualAuth"
client-requires="None" server-supports="MutualAuth" server-requires="None"/>
<properties>
<property name="some_property" value="some_value"/>
</properties>
</subsystem>
Properties that are not supported and have to be removed:
<orb/>: client-timeout, max-managed-buf-size, max-server-connections, outbuf-cache-timeout, outbuf-size, connection-retries, retry-interval, name,server-timeout
<poa/>: queue-min, queue-max, pool-size, max-threads
Properties X cannot be emulated using OpenJDK ORB and are not supported
This warning means that mentioned properties are not supported and won’t be included in the new subsystem configuration. As a result of that admin must be aware that any behavior implied by those properties would be nonexistent. Admin has to check whether subsystem is able to operate
correctly without that behavior on the new server.Unsupported properties: cache-poa-names, cache-typecodes, chunk-custom-rmi-valuetypes, client-timeout, comet, indirection-encoding-disable, iona, lax-boolean-encoding, max-managed-buf-size, max-server-connections, max-threads, outbuf-cache-timeout, outbuf-size, queue-max, queue-min, poa-monitoring, print-version, retries, retry-interval, queue-wait, server-timeout, strict-check-on-tc-creation, use-bom, use-imr.
The properties X use expressions. Configuration properties that are used to resolve those expressions should be transformed manually to the new iiop-openjdk subsystem format.
Admin has to transform all the configuration files to work correctly with the jacorb subsystem. For example, jacorb has a property giop-minor-version whereas iiop-openjdk uses the property giop-version. Let’s suppose we use 1 minor version in jacorb and have it configured in standalone.conf file as system variable: -Diiop-giop-minor-version=1. Admin is responsible for changing this variable to 1.1 after the migration to make sure that the new subsystem will work correctly.
25.3.2. JBoss Web Subsystem
JBoss Web is replaced by Undertow in WildFly, which means that the legacy subsystem configuration should be migrated to WildFly’s Undertow subsystem configuration.
JBoss Web Subsystem Configuration
The extension’s module org.jboss.as.web is replaced by module org.wildfly.extension.undertow, while the subsystem configuration namespace urn:jboss:domain:web: is replaced by
urn:jboss:domain:undertow:.
The XML configuration of the new subsystem is relatively different. Consider the following example of the JBoss Web subsystem configuration, containing all valid elements and attributes:
<?xml version="1.0" encoding="UTF-8"?>
<subsystem xmlns="urn:jboss:domain:web:2.2" default-virtual-server="default-host" native="true" default-session-timeout="30" instance-id="foo">
<configuration>
<static-resources listings="true"
sendfile="1000"
file-encoding="utf-8"
read-only="true"
webdav="false"
secret="secret"
max-depth="5"
disabled="false"
<jsp-configuration development="true"
disabled="false"
keep-generated="true"
trim-spaces="true"
tag-pooling="true"
mapped-file="true"
check-interval="20"
modification-test-interval="1000"
recompile-on-fail="true"
smap="true"
dump-smap="true"
generate-strings-as-char-arrays="true"
error-on-use-bean-invalid-class-attribute="true"
scratch-dir="/some/dir"
source-vm="1.7"
target-vm="1.7"
java-encoding="utf-8"
x-powered-by="true"
display-source-fragment="true" />
<mime-mapping name="ogx" value="application/ogg" />
<welcome-file>titi</welcome-file>
</configuration>
<connector name="http" scheme="http"
protocol="HTTP/1.1"
socket-binding="http"
enabled="true"
enable-lookups="false"
proxy-binding="reverse-proxy"
max-post-size="2097153"
max-save-post-size="512"
redirect-binding="https"
max-connections="300"
secure="false"
executor="some-executor"
<connector name="https" scheme="https" protocol="HTTP/1.1" secure="true" socket-binding="https">
<ssl certificate-key-file="${file-base}/server.keystore"
ca-certificate-file="${file-base}/jsse.keystore"
key-alias="test"
password="changeit"
cipher-suite="SSL_RSA_WITH_3DES_EDE_CBC_SHA"
protocol="SSLv3"
verify-client="true"
verify-depth="3"
certificate-file="certificate-file.ext"
ca-revocation-url="https://example.org/some/url"
ca-certificate-password="changeit"
keystore-type="JKS"
truststore-type="JKS"
session-cache-size="512"
session-timeout="3000"
ssl-protocol="RFC4279"
</connector>
<connector name="http-vs" scheme="http" protocol="HTTP/1.1" socket-binding="http" >
<virtual-server name="vs1" />
<virtual-server name="vs2" />
</connector>
<virtual-server name="default-host" enable-welcome-root="true" default-web-module="foo.war">
<alias name="localhost" />
<alias name="example.com" />
<access-log resolve-hosts="true" extended="true" pattern="extended" prefix="prefix" rotate="true" >
<directory relative-to="jboss.server.base.dir" path="toto" />
</access-log>
<rewrite name="myrewrite" pattern="^/helloworld(.*)" substitution="/helloworld/test.jsp" flags="L" />
<rewrite name="with-conditions" pattern="^/helloworld(.*)" substitution="/helloworld/test.jsp" flags="L" >
<condition name="https" pattern="off" test="%{HTTPS}" flags="NC"/>
<condition name="user" test="%{USER}" pattern="toto" flags="NC"/>
<condition name="no-flags" test="%{USER}" pattern="toto"/>
</rewrite>
<sso reauthenticate="true" domain="myDomain" cache-name="myCache"
cache-container="cache-container" http-only="true"/>
</virtual-server>
<virtual-server name="vs1" />
<virtual-server name="vs2" />
<valve name="myvalve" module="org.jboss.some.module" class-name="org.jboss.some.class" enabled="true">
<param param-name="param-name" param-value="some-value"/>
</valve>
<valve name="accessLog" module="org.jboss.as.web" class-name="org.apache.catalina.valves.AccessLogValve">
<param param-name="prefix" param-value="myapp_access_log." />
<param param-name="suffix" param-value=".log" />
<param param-name="rotatable" param-value="true" />
<param param-name="fileDateFormat" param-value="yyyy-MM-dd" />
<param param-name="pattern" param-value="common" />
<param param-name="directory" param-value="${jboss.server.log.dir}" />
<param param-name="resolveHosts" param-value="false"/>
<param param-name="conditionIf" param-value="log-enabled"/>
</valve>
<valve
name="request-dumper" module="org.jboss.as.web" class-name="org.apache.catalina.valves.RequestDumperValve"/>
<valve name="remote-addr" module="org.jboss.as.web" class-name="org.apache.catalina.valves.RemoteAddrValve">
<param param-name="allow" param-value="127.0.0.1,127.0.0.2" />
<param param-name="deny" param-value="192.168.1.20" />
</valve>
<valve name="crawler" class-name="org.apache.catalina.valves.CrawlerSessionManagerValve" module="org.jboss.as.web" >
<param param-name="sessionInactiveInterval" param-value="1" />
<param param-name="crawlerUserAgents" param-value="Google" />
</valve>
<valve name="proxy" class-name="org.apache.catalina.valves.RemoteIpValve" module="org.jboss.as.web" >
<param param-name="internalProxies" param-value="192\.168\.0\.10|192\.168\.0\.11" />
<param param-name="remoteIpHeader" param-value="x-forwarded-for" />
<param param-name="proxiesHeader" param-value="x-forwarded-by" />
<param param-name="trustedProxies" param-value="proxy1|proxy2" />
</valve>
</subsystem>
It is possible to do a migration of the legacy subsystem configuration and related persisted data by invoking the legacy subsystem’s migrate operation, using the management CLI:
/subsystem=web:migrate
There is also a describe-migration operation that returns a list of all the management operations that are performed to migrate from the legacy subsystem to the new one:
/subsystem=web:describe-migration
Both migrate and describe-migration will also display a list of migration-warnings if there are some resource or attributes that can not be migrated automatically. The following is a list of these warnings:
Could not migrate resource X
This warning means that mentioned resource configuration is not supported and won’t be included in the new subsystem configuration. As a result of that admin must be aware that any behavior implied by those resources would be nonexistent. Admin has to check whether subsystem is able to operate correctly without that behavior on the new server.
Could not migrate attribute X from resource Y.
This warning means that mentioned resource configuration property is not supported and won’t be included in the new subsystem configuration. As a result of that admin must be aware that any behavior implied by those properties would be nonexistent. Admin has to check whether subsystem is
able to operate correctly without that behavior on the new server.
Could not migrate SSL connector as no SSL config is defined
Could not migrate verify-client attribute %s to the Undertow equivalent
Could not migrate verify-client expression %s
Could not migrate valve X
This warning means that mentioned valve configuration is not supported and won’t be included in the new subsystem configuration. As a result of that admin must be aware that any behavior implied by those resources would be nonexistent. Admin has to check whether subsystem is able to operate correctly without that behavior on the new server. This warning may happen for:
org.apache.catalina.valves.RemoteAddrValve : must have at least one
allowed or denied value.
org.apache.catalina.valves.RemoteHostValve : must have at least one
allowed or denied value.
org.apache.catalina.authenticator.BasicAuthenticator
org.apache.catalina.authenticator.DigestAuthenticator
org.apache.catalina.authenticator.FormAuthenticator
org.apache.catalina.authenticator.SSLAuthenticator
org.apache.catalina.authenticator.SpnegoAuthenticator
custom valves
Could not migrate attribute X from valve Y
This warning means that mentioned valve configuration property is not supported and won’t be included in the new subsystem configuration. As a result of that admin must be aware that any behavior implied by those properties would be nonexistent. Admin has to check whether subsystem is
able to operate correctly without that behavior on the new server. This warning may happen for :
org.apache.catalina.valves.AccessLogValve : if you use the following parameters resolveHosts, fileDateFormat, renameOnRotate,
encoding, locale, requestAttributesEnabled, buffered.
org.apache.catalina.valves.ExtendedAccessLogValve : if you use the following parameters resolveHosts, fileDateFormat, renameOnRotate, encoding, locale, requestAttributesEnabled, buffered.
org.apache.catalina.valves.RemoteIpValve:
if remoteIpHeader is defined and isn’t set to "x-forwarded-for".
if protocolHeader is defined and isn’t set to "x-forwarded-proto".
if you use the following parameters httpServerPort and httpsServerPort .
Also, note that Undertow doesn’t support JBoss Web valves, but some of these may be migrated to Undertow handlers, and JBoss Web subsystem’s migrate operation do that too.
Here is a list of those valves and their corresponding Undertow handler:
org.apache.catalina.valves.AccessLogValve
io.undertow.server.handlers.accesslog.AccessLogHandler
org.apache.catalina.valves.ExtendedAccessLogValve
io.undertow.server.handlers.accesslog.AccessLogHandler
org.apache.catalina.valves.RequestDumperValve
io.undertow.server.handlers.RequestDumpingHandler
org.apache.catalina.valves.RewriteValve
io.undertow.server.handlers.SetAttributeHandler
org.apache.catalina.valves.RemoteHostValve
io.undertow.server.handlers.AccessControlListHandler
org.apache.catalina.valves.RemoteAddrValve
io.undertow.server.handlers.IPAddressAccessControlHandler
org.apache.catalina.valves.RemoteIpValve
io.undertow.server.handlers.ProxyPeerAddressHandler
org.apache.catalina.valves.StuckThreadDetectionValve
io.undertow.server.handlers.StuckThreadDetectionHandler
org.apache.catalina.valves.CrawlerSessionManagerValve
io.undertow.servlet.handlers.CrawlerSessionManagerHandler
The org.apache.catalina.valves.JDBCAccessLogValve can’t be automatically migrated to io.undertow.server.handlers.JDBCLogHandler as the expectations differ.
The migration can be done manually though :
Create a datasource pointing to the database where the log entries are going to be stored
Add an expression-filter definition with the following expression: "jdbc-access-log(datasource='datasource-jndi-name")
<valve name="jdbc" module="org.jboss.as.web" class-name="org.apache.catalina.valves.JDBCAccessLogValve">
<param param-name="driverName" param-value="com.mysql.jdbc.Driver" />
<param param-name="connectionName" param-value="root" />
<param param-name="connectionPassword" param-value="password" />
<param param-name="connectionURL" param-value="jdbc:mysql://localhost:3306/wildfly?zeroDateTimeBehavior=convertToNull" />
<param param-name="format" param-value="combined" />
</valve>
should become:
<subsystem xmlns="urn:jboss:domain:datasources:1.2">
<datasources>
<datasource jndi-name="java:jboss/datasources/accessLogDS" pool-name="ccessLogDS" enabled="true" use-java-context="true">
<connection-url>jdbc:mysql://localhost:3306/wildfly?zeroDateTimeBehavior=convertToNull</connection-url>
<driver>mysql</driver>
<security>
<user-name>root</user-name>
<password>password</password>
</security>
</datasource>
<drivers>
<driver name="mysql" module="com.mysql">
<driver-class>com.mysql.jdbc.Driver</driver-class>
</driver>
</drivers>
</datasources>
</subsystem>
<subsystem xmlns="urn:jboss:domain:undertow:3.1" default-virtual-host="default-virtual-host" default-servlet-container="myContainer"
default-server="some-server" instance-id
="some-id" statistics-enabled="true">
<server name="some-server" default-host="other-host" servlet-container="myContainer">
<host name="other-host" alias="www.mysite.com, ${prop.value:default-alias}" default-web-module="something.war" disable-console-redirect="true">
<location name="/" handler="welcome-content" />
<filter-ref name="jdbc-access"/>
</host>
</server>
<filters>
<expression-filter name="jdbc-access" expression="jdbc-access-log(datasource='java:jboss/datasources/accessLogDS')" />
</filters>
</subsystem>
Note that any custom valve won’t be migrated at all and will just be removed from the configuration.
Also the authentication related valves are to be replaced by Undertow authentication mechanisms, and this have to be done manually.
WebSockets
In JBoss AS 7, to use WebSockets, you had to configure the 'http' connector in the web subsystem of the server configuration file to use the NIO2 protocol. The following is an example of the management CLI command to configure WebSockets in the previous releases.
/subsystem=web/connector=http/:write-attribute(name=protocol,value=org.apache.coyote.http11.Http11NioProtocol)
WebSockets are a requirement of the Jakarta EE specification and the default configuration is included in WildFly. More complex WebSocket configuration is done in the servlet-container of the undertow subsystem of the server configuration file.
You no longer need to configure the server for default WebSocket support.
25.3.3. Messaging Subsystem
WildFly JMS support is provided by ActiveMQ Artemis, instead of HornetQ. It’s possible to do a migration of the legacy subsystem configuration, and related persisted data.
Messaging Subsystem Configuration
The extension’s module org.jboss.as.messaging is replaced by module org.wildfly.extension.messaging-activemq, while the subsystem configuration namespace urn:jboss:domain:messaging:3.0 is replaced by urn:jboss:domain:messaging-activemq:1.0.
Management model
In most cases, an effort was made to keep resource and attribute names as similar as possible to those used in previous releases. The following table lists some of the changes.
The management operations invoked on the new messaging-subsystem starts with /subsystem=messaging-activemq/server=X while the legacy messaging subsystem was at /subsystem=messaging/hornetq-server=X.
In case the legacy subsystem configuration is available, such configuration may be migrated to the new subsystem by invoking its migrate operation, using the management CLI:
/subsystem=messaging:migrate
There is also a describe-migration operation that returns a list of all the management operations that are performed to migrate from the legacy subsystem to the new one:
/subsystem=messaging:describe-migration
Both migrate and describe-migration will also display a list of migration-warnings if there are some resource or attributes that can not be migrated automatically. The following is a list of these warnings:
Can not migrate attribute local-bind-address from resource X. Use instead the socket-attribute to configure this broadcast-group.
Can not migrate attribute local-bind-port from resource X. Use instead the socket-binding attribute to configure this broadcast-group.
Can not migrate attribute group-address from resource X. Use instead the socket-binding attribute to configure this broadcast-group.
Can not migrate attribute group-port from resource X. Use instead the socket-binding attribute to configure this broadcast-group.
Broadcast-group resources no longer accept local-bind-address, local-bind-port, group-address, group-port attributes. It only accepts a socket-binding. The warning notifies that resource X has an unsupported attribute. The user will have to set the socket-binding attribute on the resource and ensures it corresponds to a defined socket-binding
resource.
Classes providing the %s are discarded during the migration. To use them in the new messaging-activemq subsystem, you will have to extend the Artemis-based Interceptor.
Messaging interceptors support is significantly different in WildFly 10, any interceptors configured in the legacy subsystem are discarded during migration. See the Messaging Interceptors section to learn how to migrate legacy Messaging interceptors.
Can not migrate the HA configuration of X. Its shared-store and backup attributes holds expressions and it is not possible to determine unambiguously how to create the corresponding ha-policy for the messaging-activemq server.
If the hornetq-server X’s shared-store or backup attributes hold an expression, such as ${xxx}, then it’s not possible to determine the actual ha-policy of the migrated server. In that case, we discard it and the user will have to add the correct ha-policy afterwards. The ha-policy is a single resource underneath the messaging-activemq server resource.
Can not migrate attribute local-bind-address from resource X. Use instead the socket-binding attribute to configure this discovery-group.Can not migrate attribute local-bind-port from resource X. Use instead the socket-binding attribute to configure this discovery-group.
Can not migrate attribute group-address from resource X. Use instead the socket-binding attribute to configure this discovery-group.
Can not migrate attribute group-port from resource X. Use instead the socket-binding attribute to configure this discovery-group.
The discovery-group resources no longer accept local-bind-address, local-bind-port, group-address, group-port attributes. It only accepts a socket-binding. The warning notifies that resource X has an unsupported attribute.
The user will have to set the socket-binding attribute on the resource and ensures it corresponds to a defined socket-binding resource.
Can not create a legacy-connection-factory based on connection-factory X. It uses a HornetQ in-vm connector that is not compatible with Artemis in-vm connector
Legacy subsystem’s remote connection-factory resources are migrated into legacy-connection-factory resources, to allow old EAP6 clients to connect to EAP7. However a connection-factory using in-vm will not be migrated, because a in-vm client will be based on EAP7, not EAP 6. In other words, legacy-connection-factory are created only when the CF is using remote connectors, and this warning notifies about in-vm connection-factory X not migrated.
Can not migrate attribute X from resource Y. The attribute uses an expression that can be resolved differently depending on system properties. After migration, this attribute must be added back with an actual value instead of the expression.
This warning appears when the migration logic needs to know the concrete value of attribute X during migration, but instead such value includes an expression that’s can’t be resolved, so the actual value can not be determined, and the attribute is discarded. It happens in several cases,
for instance:
cluster-connection forward-when-no-consumers. This boolean attribute has been replaced by the message-load-balancing-type attribute (which is an enum of OFF, STRICT, ON_DEMAND)
broadcast-group and discovery-group’s jgroups-stack and jgroups-channel attributes. They reference other resources and we no longer accept expressions for them.
Can not migrate attribute X from resource Y. This attribute is not supported by the new messaging-activemq subsystem.
Some attributes are no longer supported in the new messaging-activemq subsystem and are simply discarded:
XML Configuration
The XML configuration has changed significantly with the new messaging-activemq subsystem to provide a XML scheme more consistent with other WildFly subsystems.
It is not advised to change the XML configuration of the legacy messaging subsystem to conform to the new messaging-activemq subsystem. Instead, invoke the legacy subsystem migrate operation. This operation will write the XML configuration of the new messaging-activemq subsystem as a part of its execution.
Messaging Interceptors
Messaging Interceptors are significantly different in WildFly 10, requiring both code and configuration changes by the user. In concrete the interceptor base Java class is now org.apache.artemis.activemq.api.core.interceptor.Interceptor, and the user interceptor implementation classes may now be loaded by any server module. Note that prior to WildFly 10 the interceptor classes could only be installed by adding these to the HornetQ module, thus requiring the user to change such module XML descriptor, its module.xml.
With respect to the server XML configuration, the user must now specify the module to load its interceptors in the new messaging-activemq subsystem XML config, e.g:
<subsystem xmlns="urn:jboss:domain:messaging-activemq:1.0">
<server name="default">
<incoming-interceptors>
<class name="org.foo.incoming.myInterceptor" module="org.foo" />
<class name="org.bar.incoming.myOtherInterceptor" module="org.bar" />
</incoming-interceptors>
<outgoing-interceptors>
<class name="org.foo.outgoing.myInterceptor" module="org.foo" />
<class name="org.bar.outgoing.myOtherInterceptor" module="org.bar" />
</outgoing-interceptors>
</server>
</subsystem>
<jms-queue name="testQueue">
<entry name="queue/test"/>
<entry name="java:jboss/exported/jms/queue/test"/>
</jms-queue>
</jms-destinations>
In WildFly, the Jakarta Messaging destination queue is configured in the default server of the messaging-activemq subsystem.
<jms-queue name="testQueue" entries="queue/test java:jboss/exported/jms/queue/test"/>
25.4. Application Migration
Before you migrate your application, you should be aware that some features that were available in previous releases are now deprecated or missing.
25.4.1. EJBs
CMP Entity EJBs
Container-Managed Persistence entity beans support is optional in Jakarta EE, and WildFly does not provide support for these.
CMP entity beans are defined in the ejb-jar.xml descriptor, in concrete an entity bean is CMP only if the <entity/> child element named persistence-type is included and has a value of Container. An example:
<?xml version="1.1" encoding="UTF-8"?>
<ejb-jar xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
version="3.1">
<enterprise-beans>
<entity>
<ejb-name>SimpleBMP</ejb-name>
<local-home>org.jboss.as.test.integration.ejb.entity.bmp.BMPLocalHome</local-home>
<local>org.jboss.as.test.integration.ejb.entity.bmp.BMPLocalInterface</local>
<ejb-class>org.jboss.as.test.integration.ejb.entity.bmp.SimpleBMPBean</ejb-class>
<persistence-type>Container</persistence-type>
<prim-key-class>java.lang.Integer</prim-key-class>
<reentrant>true</reentrant>
</entity>
</enterprise-beans>
</ejb-jar>
CMP entity beans should be replaced by Jakarta Persistence entities.
EJB Client
Default Remote Connection Port
The default remote connection port has changed from 4447 to 8080.
In JBoss AS 7, the jboss-ejb-client.properties file looked similar to
the following:
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=localhost
remote.connection.default.port=4447
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
In WildFly, the properties file looks like this:
remote.connectionprovider.create.options.org.xnio.Options.SSL_ENABLED=false
remote.connections=default
remote.connection.default.host=localhost
remote.connection.default.port=8080
remote.connection.default.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=false
If a client application uses the EJB client library from JBoss AS 7 and wants to connect to WildFly 10 server, the server must be configured to expose a remoting connector on a port other than 8080. The client must then connect using that newly configured connector.
A client application that uses the EJB client library from WildFly 10 and wants to connect to a JBoss AS 7 server must be aware that the server instance does not use the http-remoting connector and instead uses a remoting connector. This is achieved by defining a new client-side connection property.
remote.connection.default.protocol=remote
Proprietary Jakarta Messaging Resource Definitions
The proprietary XML descriptors, previously used to setup Jakarta Messaging resources, are deprecated in WildFly. Jakarta EE (section EE.5.18) standardized such functionality.
The deprecated descriptors are files bundled in the application package, which name ends with -jms.xml. Their namespace has been changed to urn:jboss:messaging-activemq-deployment:1.0.
External Jakarta Messaging Clients
Jakarta Messaging Resources are remotely looked up using JNDI, and looking up resources in a WildFly 10 server may require changes in the application code, see #Remote JNDI Clients section for further information.
25.4.3. Jakarta Persistence (and Hibernate)
Applications That Plan to Use Hibernate ORM 5.3.x
WildFly ships with Hibernate ORM 5.3.x and those libraries are implicitly added to the application classpath when a persistence.xml is detected during deployment. If your application uses Jakarta Persistence, it will default to using the Hibernate ORM 5.3.x libraries.
Hibernate ORM 5.3.x introduces:
Applications that currently use Hibernate ORM 4.0 - 4.3
You should migrate to Hibernate ORM 5.3.x.
For information about the changes implemented between Hibernate 4 and Hibernate 5, see
https://github.com/hibernate/hibernate-orm/blob/master/migration-guide.adoc
Applications that currently use Hibernate 3
You should migrate to Hibernate ORM 5.3.x.
JBoss Web Valves
Undertow does not support the JBoss Web Valve functionality. This can be replaced by Undertow Handlers. See the Undertow Handler Authors Guide for more information.
List of valves that were provided with JBoss Web, together with a corresponding Undertow handler, is provided above, in the section on the JBoss Web subsystem.
JBoss Web Valves are specified in the proprietary jboss-web.xml descriptor, through <valve /> element(s). These can be replaced using the <http-handler /> element(s). For example:
<jboss-web>
<valve>
<class-name>org.apache.catalina.valves.RequestDumperValve</class-name>
<module>org.jboss.as.web</module>
</valve>
</jboss-web>
can be replaced by
<jboss-web>
<http-handler>
<class-name>io.undertow.server.handlers.RequestDumpingHandler</class-name>
<module>io.undertow.core</module>
</http-handler>
</jboss-web>
CXF Spring Webservices
The setup of web service’s endpoints and clients, through a Spring XML descriptor, driving a CXF bus creation, is no longer supported in WildFly.
Any application containing a jbossws-cxf.xml must migrate all functionality specified in such XML descriptor, mostly already supported by the Jakarta XML Web Services specification, included in Jakarta EE. It is still possible to rely on direct Apache CXF API usage, loosing the Jakarta EE portability of the application, for instance when specific Apache CXF functionalities are needed. See the Apache CXF Integration document for further information.
Jakarta XML RPC
Jakarta XML RPC is an API for building Web services and clients that used remote procedure calls (RPC) and XML, which was deprecated in Jakarta EE, and is no longer supported by WildFly.
Jakarta XML RPC Web Services may be identified by the presence of the XML descriptor named webservices.xml, containing a <webservice-description/> element that includes a child element named <jaxrpc-mapping-file/>. An example:
<webservices 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://www.ibm.com/webservices/xsd/j2ee_web_services_1_1.xsd" version="1.1">
<webservice-description>
<webservice-description-name>HelloService</webservice-description-name>
<wsdl-file>WEB-INF/wsdl/HelloService.wsdl</wsdl-file>
<jaxrpc-mapping-file>WEB-INF/mapping.xml</jaxrpc-mapping-file>
<port-component>
<port-component-name>Hello</port-component-name>
<wsdl-port>HelloPort</wsdl-port>
<service-endpoint-interface>org.jboss.chap12.hello.Hello</service-endpoint-interface>
<service-impl-bean>
<servlet-link>HelloWorldServlet</servlet-link>
</service-impl-bean>
</port-component>
</webservice-description>
</webservices>
Applications using Jakarta XML RPC should be migrated to use Jakarta XML Web Services, the current Jakarta EE standard web service framework.
Jakarta RESTful Web Services 2.1
Jakarta RESTful Web Services 2.1: The Java API for RESTful Web Services specification is located at https://jakarta.ee/specifications/restful-ws/2.1/
Some changes to the MessageBodyWriter interface may represent a backward incompatible change with respect to JAX-RS 1.X.
Be sure to define a @Produces or @Consumes for your endpoints. Failure to do so may result in an error similar to the following.
org.jboss.resteasy.core.NoMessageBodyWriterFoundFailure: Could not find MessageBodyWriter for response object of type: <OBJECT> of media type: <CONTENT_TYPE>
REST Client API
Some REST Client API classes and methods are deprecated or removed, for example: org.jboss.resteasy.client.ClientRequest and org.jboss.resteasy.client.ClientResponse have been removed. Instead, use
javax.ws.rs.client.Client and javax.ws.rs.core.Response. See the resteasy-jaxrs-client quickstart for an example of an external Jakarta RESTful Web Services RESTEasy client that interacts with a Jakarta RESTful Web Services.
HA Singleton
JBoss AS 7 introduced singleton services - a mechanism for installing an service such that it would only start on one node in the cluster at a time, a HA Singleton. Such mechanism required usage of a private WildFly Clustering API, designed around the class org.jboss.as.clustering.singleton.SingletonService, and was documented in detail at
https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/6.4/html/Development_Guide/Implement_an_HA_Singleton.html, and while not difficult to implement, the installation process suffered from a couple shortcomings:
Installing multiple singleton services within a single deployment caused the deployer to hang.
Installing a singleton service required the user to specify several private module dependencies in /META-INF/MANIFEST.MF
WildFly 10 introduces a new public API for building such services, which significantly simplifies the process, and solves the issues found in the legacy solution. The WildFly 10 Quickstart application named cluster-ha-singleton examples a HA Singleton implementation using the new API, and may be found at https://github.com/jboss-developer/jboss-eap-quickstarts/tree/7.0.x-develop/cluster-ha-singleton
Stateful Session EJB Clustering
WildFly 10 no longer requires Stateful Session EJBs to use the org.jboss.ejb3.annotation.Clustered annotation to enable clustering behavior. By default, if the server is started using an HA profile, the state of your SFSBs will be replicated automatically. Disabling this behavior is achievable on a per-EJB basis, by annotating your bean using @Stateful(passivationCapable=false), which is new to the EJB 3.2 specification; or globally through the configuration of the EJB3 subsystem, in the server configuration.
Note that the @Clustered annotation, if used by an application, is simply ignored, the application deployment will not fail.
Web Session Clustering
WildFly 10 introduces a new web session clustering implementation, replacing the one found in JBoss AS 7, which has been around for ages (since JBoss AS 3.2!), and was tightly coupled to the legacy JBoss Web subsystem source code. The most relevant changes in the new implementation are:
Introduction of a proper session manager SPI, and an Infinispan implementation of it, decoupled from the web subsystem implementation
Sessions are implemented as a facade over one or more cache entries, which means that the container’s session manager itself does not retain a separate reference to each HttpSession
Pessimistic locking of cache entries effectively ensures that only a single client on a single node ever accesses a given session at any given time
Usage of cache entry grouping, instead of atomic maps, to ensure that multiple cache entries belonging to the same session are co-located.
Session operations within a request only ever use a single batch/transaction. This results in fewer RPCs per request.
Support for write-through cache stores, as well as passivation-only cache stores.
With respect to applications, the new web session clustering implementation deprecates/reinterprets much of the related configuration, which is included in JBoss’s proprietary web application
XML descriptor, jboss-web.xml:
<max-active-sessions/>
Previously, session creation would fail if an additional session would cause the number of active sessions to exceed the value specified by <max-active-sessions/>.
In the new implementation, <max-active-sessions/> is used to enable session passivation. If session creation would cause the number of active sessions to exceed <max-active-sessions/>, then the oldest session known to the session manager will passivate to make room for the new session.
<passivation-config/>
This configuration element and its sub-elements are no longer used in WildFly.
<use-session-passivation/>
Previously, passivation was enabled via this attribute, yet in the new implementation, passivation is enabled by specifying a non-negative value for <max-active-sessions/>.
<passivation-min-idle-time/>
Previously, sessions needed to be active for at least a specific amount of time before becoming a candidate for passivation. This could cause session creation to fail, even when passivation was enabled.
The new implementation does not support this logic and thus avoids this DoS vulnerability.
<passivation-max-idle-time/>
Previously, a session would be passivated after it was idle for a specific amount of time.
The new implementation does not support eager passivation - only lazy passivation. Sessions are only passivated when necessary to comply with <max-active-sessions/>.
<replication-config/>
The new implementation deprecates a number of sub-elements.
<replication-trigger/>
Previously, session attributes could be treated as either mutable or immutable depending on the values specified by <replication-trigger/>:
SET treated all attributes as immutable, requiring a separate HttpSession.setAttribute(…) to indicate that the value changed.
SET_AND_GET treated all session attributes as mutable.
SET_AND_NON_PRIMITIVE_GET recognized a small set of types, for example strings and boxed primitives, as immutable, and assumed that any other attribute was mutable.
The new implementation replaces this configuration option with a single, robust strategy. Session attributes are assumed to be mutable unless one of the following is true:
The value is a known immutable value:
java.util.Collections.EMPTY_LIST, EMPTY_MAP, EMPTY_SET
<use-jk/>
Previously, the instance-id of the node handling a given request was appended to the jsessionid, for use by load balancers such as mod_jk, mod_proxy_balancer, mod_cluster, etc., depending on the value specified for <use-jk/>. In the new implementation, the instance-id, if defined, is always appended to the jsessionid.
<max-unreplicated-interval/>
Previously, this configuration option was an optimization that would prevent the replicate of a session’s timestamp if no session attribute was changed. While this sounds nice, in practice it doesn’t prevent any RPCs, since session access requires cache transaction RPCs regardless of
whether any session attributes changed. In the new implementation, the timestamp of a session is replicated on every request. This prevents stale session meta data following failover.
<snapshot-mode/>
Previously, one could configure <snapshot-mode/> as INSTANT or INTERVAL. Infinispan’s replication queue renders this configuration option obsolete.
<snapshot-interval/>
Only relevant for <snapshot-mode>INTERVAL</snapshot-mode>. See above.
<session-notification-policy/>
Previously, the value defined by this attribute defined a policy for triggering session events. In the new implementation, this behavior is spec-driven and not configurable.
Remote JNDI Clients
WildFly 10’s default JNDI Provider URL has changed, which means that external applications, using JNDI to lookup remote resources, for instance an EJB or a Jakarta Messaging Queue, may need to change the value for the JNDI InitialContext environment’s property named
java.naming.provider.url. The default URL scheme is now
http-remoting, and the default URL port is now 8080.
As an example, considering the application server host is localhost,
then clients previously accessing WildFly 10 would use
java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
java.naming.provider.url=remote://localhost:4447
while clients now accessing WildFly should use instead
java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
java.naming.provider.url=http-remoting://localhost:8080
JSR-88
The specification which aimed to standardize deployment tasks got very little adoption, due to much more "feature rich" proprietary solutions already included in every vendor application server. It was no surprise that JSR-88 support was dropped from Jakarta EE, and WildFly followed that and dropped support too.
A JSR-88 deployment plan is identified by a XML descriptor named deployment-plan.xml, bundled in a zip/jar archive.
Module Dependencies
Applications defining dependencies to WildFly modules, through the application’s package MANIFEST.MF or jboss-deployment-structure.xml, may be referencing missing modules. When migrating an application, relying on such functionality, the presence of the referenced modules should be validated in advance.
The purpose of this guide is to document the application changes that
are needed to successfully run and deploy WebLogic applications on
WildFly.
26.1.1. About this Guide
The purpose of this document is to guide you through the planning
process and migration of fairly simple and standard Oracle WebLogic
applications to WildFly. O
The purpose of this guide is to document the application changes that
are needed to successfully run and deploy WebLogic applications on
WildFly.
27.1.1. About this Guide
The purpose of this document is to guide you through the planning
process and migration of fairly simple and standard Oracle WebLogic
applications to WildFly.