Skip navigation
2010

Martin Hynar's Blog

November 2010 Previous month Next month

Recently I came accross JBPM and wanted to start with some small application. It was easy to download latest JBPM package, install it and import examples into Eclipse to check the stuff. However, I am more to Maven so I wanted to create the application based with this setup. The following paragraphs are the way how to build mavenized standalone application that makes use of JBPM.

 

Step 1: Make simple Maven project

First of all, create simple maven application:

 

mvn -DgroupId=com.example -DartifactId=jbpm-standalone -Dversion=1.0 archetype:generate

 

Select archetype named maven-archetype-quickstart and confirm creation. By this you'll get basic maven project. Now, import the project as the existing Maven project into Eclipse and see what's in. Pretty simple and boring so far

 

 

Step 2: Update dependencies

To make use of JBPM in this maven project, we need to import dependencies that are necessary for the application to compile and run. I finished with the bellow listed POM.

 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>jbpm-standalone</artifactId>
    <version>1.0</version>
    <packaging>jar</packaging>

    <name>jbpm-standalone</name>
    <url>http://jboss.org</url>

    <properties>
        <repository.jboss.org.url>http://repository.jboss.org/maven2</repository.jboss.org.url>
        <repository.maven2.dev.java.net.url>http://download.java.net/maven/2/</repository.maven2.dev.java.net.url>
        <repository.ibiblio.url>http://www.ibiblio.org/maven2</repository.ibiblio.url>
        <repository.jboss.org.nexus.url>https://repository.jboss.org/nexus/content/groups/public-jboss</repository.jboss.org.nexus.url>

        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <jdk.debug>true</jdk.debug>
        <jdk.optimize>false</jdk.optimize>
        <jdk.source>1.6</jdk.source>
        <jdk.target>1.6</jdk.target>

        <version.jbpm>4.4</version.jbpm>
        <version.xerces>2.9.1</version.xerces>
        <version.javax.mail>1.4.1</version.javax.mail>
        <version.slf4j>1.6.1</version.slf4j>
        <version.testng>5.14</version.testng>
        <version.javassist>3.4.GA</version.javassist>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.jbpm.jbpm4</groupId>
            <artifactId>jbpm-api</artifactId>
            <version>${version.jbpm}</version>
        </dependency>
        <dependency>
            <groupId>org.jbpm.jbpm4</groupId>
            <artifactId>jbpm-jpdl</artifactId>
            <version>${version.jbpm}</version>
        </dependency>
        <dependency>
            <groupId>org.jbpm.jbpm4</groupId>
            <artifactId>jbpm-pvm</artifactId>
            <version>${version.jbpm}</version>
        </dependency>
        <dependency>
            <groupId>org.jbpm.jbpm4</groupId>
            <artifactId>jbpm-log</artifactId>
            <version>${version.jbpm}</version>
        </dependency>
        <dependency>
            <groupId>org.jbpm.jbpm4</groupId>
            <artifactId>jbpm-db</artifactId>
            <version>${version.jbpm}</version>
        </dependency>
        <dependency>
            <groupId>xerces</groupId>
            <artifactId>xercesImpl</artifactId>
            <version>${version.xerces}</version>
        </dependency>
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>${version.javax.mail}</version>
        </dependency>
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>${version.testng}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${version.slf4j}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jdk14</artifactId>
            <version>${version.slf4j}</version>
        </dependency>
        <dependency>
            <groupId>javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>${version.javassist}</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.1</version>
                <configuration>
                    <source>${jdk.source}</source>
                    <target>${jdk.target}</target>
                    <encoding>utf-8</encoding>
                    <debug>${jdk.debug}</debug>
                    <optimize>${jdk.optimize}</optimize>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.1</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <mainClass>com.example.App</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>repository.jboss.org</id>
            <name>JBoss Repository</name>
            <url>${repository.jboss.org.url}</url>
            <layout>default</layout>
        </repository>
        <repository>
            <id>repository.maven2.dev.java.net</id>
            <name>Java.net Repository for Maven</name>
            <url>${repository.maven2.dev.java.net.url}</url>
            <layout>default</layout>
        </repository>
        <repository>
            <id>repository.ibiblio</id>
            <name>IBiblio Maven Repository</name>
            <url>${repository.ibiblio.url}</url>
            <layout>default</layout>
        </repository>
    </repositories>
</project> 

 

Step 3: Define simple workflow

For JBPM to run, you must have some process to execute. It is not any super extensive super complex process, but is just sufficient for the starter app. Bellow are the steps of the process:

  1. Starts ...
  2. Greets ...
  3. Waits in intermediate state ...
  4. Prints that it is ending ...
  5. Ends.

 

... and the the process file itself ...

 

 

<?xml version="1.0" encoding="UTF-8"?> 
<process name="Activity" xmlns="http://jbpm.org/4.4/jpdl"> 
    <start g="27,51,80,40">
        <transition g="-18,4" name="greet" to="say hello" />
    </start>
 
    <java class="com.example.OutputActivity" g="141,49,87,50" method="say"
        name="say hello">
        <arg>
            <string value="Hello!" />
        </arg>
        <transition g="-28,4" name="wait for end" to="proceed to end" />
    </java>

    <state g="312,47,92,52" name="proceed to end">
        <transition g="-23,4" name="to ending" to="ending" />
    </state>

    <java class="com.example.OutputActivity" g="465,48,92,52" method="say"
        name="ending">
        <arg>
            <string value="Bye!" />
        </arg>
        <transition g="-19,4" name="to end" to="end" />
    </java>

    <end g="619,51,80,40" name="end" />

</process>

 

The class that is executed by the process is shipped in the archive attached to this post. It is very simple...

 

Step 4: Configuration files

 

Each JBPM-enabled application needs specific configuration file in the classpath. By default, the name is jbpm.cfg.xml, and we will keep this name as well. The goal of this configuration file is to set up various services that JBPM makes use of during execution. The details of this configuration are described in JBPM configuration. Here we focus only on setting up the basic app. So, the configuration file, that gets placed into src/main/resources looks like this:

 

<?xml version="1.0" encoding="UTF-8"?>
<jbpm-configuration>
 
 <import resource="jbpm.default.cfg.xml" />
 <import resource="jbpm.tx.hibernate.cfg.xml" />
 <import resource="jbpm.jpdl.cfg.xml" />
 <import resource="jbpm.identity.cfg.xml" />
 
</jbpm-configuration>

 

In addition, as JBPM makes use of Hibernate to offload data about processes into database, so the hibernate configuration is necessary as well. Here, we stick to the simplest possible configuration - put data into HSQLDB. For this we need file jbpm.hibernate.cfg.xml in the same location mentioned above. The contents is the following:

 

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
          "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
          "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
  <session-factory>
  
     <property name="hibernate.dialect">org.hibernate.dialect.HSQLDialect</property>
     <property name="hibernate.connection.driver_class">org.hsqldb.jdbcDriver</property>
     <property name="hibernate.connection.url">jdbc:hsqldb:mem:.</property>
     <property name="hibernate.connection.username">sa</property>
     <property name="hibernate.connection.password"></property>
     <property name="hibernate.hbm2ddl.auto">create-drop</property>
     <property name="hibernate.format_sql">true</property>
     
     <mapping resource="jbpm.repository.hbm.xml" />
     <mapping resource="jbpm.execution.hbm.xml" />
     <mapping resource="jbpm.history.hbm.xml" />
     <mapping resource="jbpm.task.hbm.xml" />
     <mapping resource="jbpm.identity.hbm.xml" />
     
  </session-factory>
</hibernate-configuration>

 

Step 5: The code

The last step is to setup process engine, command it to execute and go through states. This is done in couple simple steps, that are listed and explained in the code snippet bellow:

 

package com.example;
 
import org.jbpm.api.Configuration;
import org.jbpm.api.Execution;
import org.jbpm.api.ExecutionService;
import org.jbpm.api.ProcessEngine;
import org.jbpm.api.ProcessInstance;
import org.jbpm.api.RepositoryService;
 
public class App {
 
    public static void main(String[] args) {
        /*
         * Create process engine from configuration. Here the configuration
         * object represents default jbpm.cfg.xml file.
         */
        ProcessEngine processEngine = Configuration.getProcessEngine();
 
        /*
         * Repository service provides access to resources that are known to
         * JBPM, including loaded processes.
         */
        RepositoryService repositoryService = processEngine.getRepositoryService();
 
        /*
         * Execution service is the handle to execute state transitions in
         * process engine.
         */
        ExecutionService executionService = processEngine.getExecutionService();
 
        /*
         * Now query repository service for the process that we have created and
         * deploy it.
         */
        repositoryService.createDeployment().addResourceFromClasspath(
                "process.jpdl.xml").deploy();
 
        /*
         * Start the process instance with the name of the process. By starting
         * process, new ProcessInstance object is created. In our example flow,
         * after the initial state, there is 'java' state that gets executed and
         * the transition continues to next 'state' where it waits to be
         * commanded.
         */
        ProcessInstance processInstance = executionService
                .startProcessInstanceByKey("Activity");
 
        /*
         * We are now in 'state' named 'proceed to end'. We must ask process
         * instance to find active execution in this particular state.
         */
        Execution execution = processInstance
                .findActiveExecutionIn("proceed to end");
 
        /*
         * Finally we ask execution service to signal that transition from the
         * state shall occur. By this, the rest of the process is executed and
         * the flow ends.
         */
        processInstance = executionService.signalExecutionById(execution
                .getId());
    }
} 

 

Step 6: Package and execute

At this point, it is possible to compile the whole application and execute it. You can do that by running following command (it both build the app and executes it). Note that some of the files were not listed here, but are included in the takeaway archive bellow this post..

 

mvn clean package exec:java

 

exec:java is execution of the exec-maven-plugin that runs com.example.App class. If you change the name of the main class, you shall change also the configuration of the plugin in pom.xml.

 

After executing it, you shall see lots of listing of various messages with text Hello! and Bye! at the very end. If you see that, you've succeeded!

 

Takeaway

Attached to this post, there is archive with project just described. It is ready Eclipse project that you just need to execute. In addition, there is pair of launchers, one that build the application and one that executes it. You will find them in the "Run configurations".

Recently, I had used http://en.wikipedia.org/wiki/Composite_pattern in one of my projects. That pattern was used on model objects that had to be persisted in database. And, not suprisingly, JPA with Hibernate as a persistence provider was used. The problem does not make any headache in its simple form, but might become complex ... (as everything in real life). Nevertheless, I decided to publish my solution to help others and give sort of baseline ...

 


Background

To refresh memory with Composite pattern: We have 3 classes (I say classes as it is not possible to map interfaces so far). First serves as a common base (interface) and gives common business methods. Second is a node in the composite structure that could contain other nodes. Third is a leaf that only implements business methods.

 

Persistable implementation

In the solution, there are couple of classes, let's start with AbstractComposite that represents common base. The code is listed bellow:

 

@Entity(name = AbstractComposite.TABLE_NAME)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "compositeType", discriminatorType = DiscriminatorType.STRING)
@NamedQueries(value = { @NamedQuery(name = "GetCompositeById", query = "from "
        + AbstractComposite.TABLE_NAME + " where id = :id") })
public abstract class AbstractComposite implements Serializable {

    private static final long serialVersionUID = 7139007918101901734L;
    public static final String TABLE_NAME = "Composite_Table";

    @Id
    @GeneratedValue
    @TableGenerator(name = "Id_Generator", table = "Sequence_Table", pkColumnName = "name", valueColumnName = "count", pkColumnValue = "Composite_Sequence")
    private Long id;

    public AbstractComposite() {
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }

    // Whatever business operations come here
}

 

As the Composite structure indicates, we must cope with inheritance hierarchy. In this case, I have chosen SINGLE_TABLE because I have only small number of leaf types. In fact, each leaf type add at least one column to the table. If you are going to have rather extensive leaf types, then joining will avoid very very sparse single table.

 

Then, there is also defined DiscriminatorColumn and its type. Each leaf is the supposed to define unique DiscriminatorValue.

 

So far, so good. Let us check leaf implementation. I have created leaf that holds String value, called CompositeString. The code is listed bellow:

 

@Entity
@DiscriminatorValue("string")
public class CompositeString extends AbstractComposite {
    private static final long serialVersionUID = -7787631697361429875L;

    @Column(name = "stringValue")
    private String value;

    public CompositeString() {
    }

    public CompositeString(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return String.format("CompositeString [value = %s]", value);
    }
}

 

As I mentioned above, DiscriminatorValue defines value that will distinguish this composite type from the others. In addition, I have defined attribute value which has defined ut database name to stringValue. By redefinition of the database name using ColumnName annotation, you can have same attribute in all leaf types (check in the attached archive).

 

The last but probably the most complex is the intermediate node that must hold references to other contained nodes or leaves. Let's check the code:

 

@Entity()
@DiscriminatorValue("multi")
public class CompositeMultiValue extends AbstractComposite {

    private static final long serialVersionUID = -4324727276077386614L;

    @OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
    @JoinTable(name = "Composite_Multi_Values_Table", joinColumns = @JoinColumn(name = "compositeId"), inverseJoinColumns = @JoinColumn(name = "multiValueId"))
    private Set<AbstractComposite> values;

    public CompositeMultiValue() {
    }

    public CompositeMultiValue(Set<AbstractComposite> values) {
        this.values = values;
    }

    public void setValues(Set<AbstractComposite> values) {
        this.values = values;
    }

    public Set<AbstractComposite> getValues() {
        return values;
    }

    @Override
    public String toString() {
        return String.format("ComparableMultiValue [values=%s]", values);
    }
}

 

This entity again defines value that discriminates it from others. Then, it contains set of contained nodes and leaves. These are linked to the actual "multivalue" using reference table with two columns, both holding a identifier from Composite_Table. First identifier is parent (compositeId) and the second is child (multiValueId). In this particular case, the cascading is set to ALL and fetching is EAGER, but in specific cases, this might be changed appropriatelly.

 

Note on storage of enums

By default (at least in Oracle), enum values are stored as RAW values. You can observe this if you change database connection parameters to Oracle in src/test/resources/META-INF/persistence.xml. The result type for column enumValue is RAW. If you desire to store your enums as String values, you need to follow guidelines in here: http://community.jboss.org/wiki/Java5EnumUserType.

Takeaway

The codes used in this post are shipped in the attached archive as mavenized Eclipse project. There is also simple test that demonstrates usage of the composite structure.Of interrest might be after running the test also location target/hibernate3/sql/schema.ddl that contains DDL scripts for creating the database structures. Enjoy!

This post describes a way to authenticate and authorize users in EAR application. This is nothing special but there could be some problems with respect to the requirements in place. So the tasks that shall be solved are the following:

 

  • User can access system via WUI by logging in.
  • Other applications can access system via WebService. Account used for accessing must be in specific role.
  • User credentials are in database.
  • Seam (version 2) is used.
  • Custom security domain is defined.
  • Authentication and authorization is covered by JAAS

 


Custom security domain definition

To define new security domain, there is a dedicated configuration file in JBoss, located in $JBOSS_HOME/server/$SERVER_PROFILE/conf/login-config.xml. It is possible to define new security domain there or in separate configuration file that gets then shipped together with EAR. We use here the second alternative. So the configuration file, named e.g. custom-login-config-beans.xml is then packed in EAR's META-INF folder.

 

<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
    <application-policy xmlns="urn:jboss:security-beans:1.0" name="CustomSecurityDomain">
         <authentication>
            <login-module code="com.example.CustomAuthModule"
                flag="required">
                <module-option name="entityManagerFactoryJndiName">java:/EntityManagerFactory</module-option>
                <module-option name="unauthenticatedIdentity">anonymous</module-option>
                <module-option name="hashAlgorithm">md5</module-option>
            </login-module>
        </authentication>
    </application-policy>
</deployment>

 

This configuration file defines new security domain - CustomSecurityDomain. It declares the class that provides facilities for this security domain - com.example.CustomAuthModule. It declares JNDI name of the EntityManagerFactory that provides persistence facilty used to get credentials. It declares identity of unauthenticated user (some resources might be accessible also for unauthenticated users). And it declares hash algorithm used to disable access to plain passwords.

 

The com.example.CustomAuthModule itself

To implement custom authentication module that could access user credentials in dozens of exotic places, we need a class that inherits from org.jboss.security.auth.spi.AbstractServerLoginModule. In this particular case, as we have credentials in database, we will inherit from org.jboss.security.auth.spi.UsernamePasswordLoginModule which is more appropriate. The code of CustomAuthModule is the following:

 

 

package com.example;

import java.security.Principal;
import java.security.acl.Group;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.security.auth.Subject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginException;

import com.example.UserAccount;
import com.example.UserRole;

import org.jboss.security.SimpleGroup;
import org.jboss.security.auth.spi.UsernamePasswordLoginModule;

public class AuthModule extends UsernamePasswordLoginModule { 
    private EntityManager entityManager;
    private static final String EMF_JNDI_CONFIG_KEY = "entityManagerFactoryJndiName";

    /**
     * Initialize this LoginModule.
     */
    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
        super.initialize(subject, callbackHandler, sharedState, options);
        InitialContext ctx;
        try {
            ctx = new InitialContext();
            // Get the name of EMF JNDI
            String jndiEntityManagerFactory = options.get(EMF_JNDI_CONFIG_KEY).toString();
            EntityManagerFactory factory = null;
            factory = (EntityManagerFactory) ctx.lookup(jndiEntityManagerFactory);
            entityManager = factory.createEntityManager();
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected String getUsersPassword() throws LoginException {
        String username = getIdentity().getName();
        UserAccount user = (UserAccount) entityManager.createQuery("from UserAccount where username = :username").setParameter("username", username)
                .getSingleResult();
        return user.getPasswordHash();
    }

    @Override
    protected Group[] getRoleSets() throws LoginException {
        HashMap<String, Group> groupMap = new HashMap<String, Group>();
 
        String username = getIdentity().getName();
        UserAccount user = (UserAccount) entityManager.createQuery("from UserAccount where username = :username").setParameter("username", username)
                .getSingleResult();
 
        String defaultGroupName = "Roles";
        Group defaultGroup = new SimpleGroup(defaultGroupName);
        setsMap.put(defaultGroupName, defaultGroup);

        List<UserRole> roles = user.getRoles();
        for (UserRole userRole : roles) {
            Principal p;
            try {
                p = createIdentity(userRole.getRolename());
                defaultGroup.addMember(p);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
 
        Group[] roleSets = new Group[setsMap.size()];
        groupMap.values().toArray(roleSets);
        return roleSets;
    }
}

 

We need to override three methods

  • initialize - which initiates the authentication module to the JAAS infrastructure and populates reference to the entity manager via entity manager factory declared in configuration. Note, that to make use of entity manager you have to provide appropriate configuration that is not part of this post.
  • getUsersPassword - retrieves user password from database using name that is available via getIdentity method.
  • getRoleSets - gets roles of the user. This example considers only one level of roles (meaning that role cannot contain group of other roles).

 

The classes UserAccount and UserRole are simple entity classes that consist of rolename in case of UserRole and username, passwordHash and list of type UserRole in case of UserAccount.

Enable new authentication module in Seam

To allow Seam to make use of above defined custom authentication module, we need to make certain configuration in components.xml that is shippen in WEB module of the EAR. The necessary configuration is the following.

 

 

<?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
    xmlns:security="http://jboss.com/products/seam/security"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.2.xsd http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.2.xsd">
    <!-- other configuration -->
    <security:identity jaas-config-name="CustomSecurityDomain" />
</components>

 

This informs Seam to use JAAS as an authentication mechanism and names security domain that shall be used for this purpose.

Login page

The login page is very simple and uses embedded Seam Identity. The code is self-explanatory.

 

<h:form id="loginForm">  
    <div>
        <h:outputLabel for="username">Username</h:outputLabel>
        <h:inputText id="username" value="#{credentials.username}"/>
    </div>
    <div>
        <h:outputLabel for="password">Password</h:outputLabel>
        <h:inputSecret id="password" value="#{credentials.password}"/>
    </div>
    <div>
        <h:commandButton id="submit" value="Login" action="#{identity.login}"/>
    </div>
</h:form>

 

To refresh memory. #{credentials.username} is EL reference to Seam component named credentials and its property username. (The same applies to #{credentials.password}). #{identity.login} is EL execution of method login on Seam built-in component identity.

 

WebService

Finally, we wanted to have a WebService that will share the same authentication mechanism as WUI. With help of annotations, this is quite straightforward and no extensive configuration is needed. Here is example WebService that will make use of CustomSecurityDomain and will allow to execute methods only users from group external. The code again does not require any closer explanation as the annotations are more than self-descriptive.

 

 

package com.example;

import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;

import org.jboss.ejb3.annotation.SecurityDomain;
import org.jboss.wsf.spi.annotation.WebContext;

@WebContext(contextRoot = "/webservices", urlPattern = "/TestService", secureWSDLAccess = false)
@SecurityDomain(value = "CustomSecurityDomain")
@RolesAllowed("external")
@SOAPBinding(style = SOAPBinding.Style.DOCUMENT, use = SOAPBinding.Use.LITERAL, parameterStyle = SOAPBinding.ParameterStyle.WRAPPED)
@WebService(name = "TestService", targetNamespace = "urn:com:example", endpointInterface = "com.example.TestService")
@Stateless
public class TestServiceSession implements TestService {
    // WebMethods from TestService interface that gets generated from WSDL
}

 

By this you are able to reuse authentication and authorization facility in both web user interface and also in backend webservices. By providing different implementation of security domain, you can change the source of your user credentials.