WebServicesImplementation
Page Info Get as PDF

Web-Services Implementation#

In EmForge-0.22 we implemented new web-services for working with BPM information in EmForge (tasks, workflows and so on). Major idea was to implement one service, used in both - GUI (as standard Java class) and External Tools (via Web-Service wrappers). CXF allowed us ealily do it.

Based tutorials#

First of all want to say "thank you!" to authors of followed tutorials:

These tutorials helped us very much - and, actually, here we will only discuss some special issues like:

  • using Maven;
  • Integration with Acegi Security.
  • testing web-service with unit-tests

All other implementation got from these tutorials.

Using Maven#

Since EmForge used maven as build-system we needed to add dependencies for project to include cxf into project.

First of all - need to add incubator repository:


<repositories>
    <!-- Apache repository required for CXF -->
    <repository>
        <id>apache-snapshots</id>
        <name>Apache SNAPSHOT Repository</name>
        <url>http://people.apache.org/repo/m2-snapshot-repository/</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>apache-incubating</id>
        <name>Apache Incubating Repository</name>
        <url>http://people.apache.org/repo/m2-incubating-repository/</url>
    </repository>
		
    <!-- for jaxb-impl -->
    <repository>
        <id>java.net</id>
        <url>http://download.java.net/maven/1/</url>
        <layout>legacy</layout>
    </repository>		
</repositories>

Second - add dependencies. In our case project splitted into 2 subprojects:

  1. emforge-api - declared service interface and Transfer Objects used in this interface
  2. emforge-web - EmForge with Web-Services implementation.

Actually, emforge-api declared java-interfaces and POJOs, so, in most cases no any extra dependencies is required, but - since we are using followed annotations:

we need to add dependency to ws-metadata (these annotations are included into J2SE 6.0, so, if you are using only Java 6+ - it is not required)


<dependency>
    <groupId>org.apache.geronimo.specs</groupId>
    <artifactId>geronimo-ws-metadata_2.0_spec</artifactId>
    <version>1.1.1</version>
</dependency>

Plus, in our POJO we are using @XmlTransient annotation, so, jaxb-api is required (EmForge used jaxb for data-binding, not aegis like used tutorials)


<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.0</version>
</dependency>	

At implementation side we used followed versions:

  • cxf-2.0.5 (latest at this moment - 2.0.4 had compatibility bug with Spring 2.5.2)
  • spring-2.5.2
  • acegi-security-1.0.6 (we do not switched to spring-security yet)
  • xalan 2.7.0 - please, be careful! In our project was used xalan 2.6.0 - and we met some very strange errors. Switched to xalan 2.7.0 resolved these problems.
  • XmlSchema-1.3.2.emforge - it is version of XmlSchema from trunk. The thing is XmlSchema 1.3.2 has bug, produced error https://issues.apache.org/jira/browse/CXF-1388 This bug is already fixed in XmlSchema trunk, but still not released. So, we build own version from trunk and placed into own maven repository

So, to use these dependencies you need add emforge repository (to get fixed version of XmlSchema):


<repository>
    <id>emforge.org</id>
    <url>http://svn.emforge.org/svn/emforge/mvnrepo</url>
</repository>

and add followed dependencies:


<dependency>
    <groupId>xalan</groupId>
    <artifactId>xalan</artifactId>
    <version>2.7.0</version>
</dependency>

<dependency>
    <groupId>org.apache.ws.commons.schema</groupId>
    <artifactId>XmlSchema</artifactId>
    <version>1.3.2.emforge</version>
</dependency>

<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-frontend-jaxws</artifactId>
    <version>2.0.5</version>

    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </exclusion>
				
        <!-- Seems we do not need all these geromino staff for now -->
        <exclusion>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-activation_1.1_spec</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-annotation_1.0_spec</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-javamail_1.4_spec</artifactId>
        </exclusion>
        <exclusion>
            <groupId>geronimo-spec</groupId>
            <artifactId>geronimo-spec-jta</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-transports-http</artifactId>
    <version>2.0.5</version>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </exclusion>
				
        <!-- Seems we do not need all these geromino staff for now -->
        <exclusion>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-activation_1.1_spec</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-annotation_1.0_spec</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-javamail_1.4_spec</artifactId>
        </exclusion>
        <exclusion>
            <groupId>geronimo-spec</groupId>
            <artifactId>geronimo-spec-jta</artifactId>
        </exclusion>
    </exclusions>
</dependency>
		
<dependency>
    <groupId>org.apache.cxf</groupId>
    <artifactId>cxf-rt-ws-security</artifactId>
    <version>2.0.5</version>

    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </exclusion>
				
        <!-- Seems we do not need all these geromino staff for now -->
        <exclusion>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-activation_1.1_spec</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-annotation_1.0_spec</artifactId>
        </exclusion>
        <exclusion>
            <groupId>org.apache.geronimo.specs</groupId>
            <artifactId>geronimo-javamail_1.4_spec</artifactId>
        </exclusion>
        <exclusion>
            <groupId>geronimo-spec</groupId>
            <artifactId>geronimo-spec-jta</artifactId>
        </exclusion>
    </exclusions>
</dependency>

(We excluded different geronimo-specs dependencies seems we did not need them in our case).

Integration with acegi#

For integrating with Acegi we needed to implement own CallbackHandler performed authentication with using Acegi Authentication Manager:


else if (pc.getUsage() == WSPasswordCallback.USERNAME_TOKEN_UNKNOWN) {
    // performs Authentication
    try {
        Authentication authentication = new UsernamePasswordAuthenticationToken(pc.getIdentifer(), pc.getPassword());
        authentication = authenticationManager.authenticate(authentication);
        SecurityContextHolder.getContext().setAuthentication(authentication);
    } catch (AuthenticationException ex) {
        throw new IOException("password incorrect for user: " + pc.getIdentifer());
    }
        	
    log.debug("user logged in via web-service: " + pc.getIdentifer());
    pc.setPassword(pc.getPassword());
}

Declare this callback as bean in spring context:


<bean id="wsPasswordCallback" class="ru.emdev.EmForge.security.WsCallbackHandler">
    <property name="authenticationManager" ref="authenticationManager"/>
</bean>

And add it into wss4j configuration:


<jaxws:endpoint id="bpmWsService"
    implementorClass="org.emforge.jbpm.BpmServiceImpl"
    implementor="#bpmService"
    address="/org.emforge.BpmService">
    <jaxws:inInterceptors>
        <bean class="org.apache.cxf.binding.soap.saaj.SAAJInInterceptor"/>
        <ref bean="wss4jInConfiguration"/>
    </jaxws:inInterceptors>        
</jaxws:endpoint>
    
<bean id="wss4jInConfiguration" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor">
    <property name="properties">
        <map>
            <entry key="action" value="UsernameToken Timestamp Signature"/>
            <entry key="passwordType" value="PasswordText"/>
            <entry key="passwordCallbackRef" value-ref="wsPasswordCallback"/>      
            <entry key="signaturePropFile" value="server_sign.properties"/>      
        </map>
    </property>
</bean>

Quite easy!

Testing Web-Services with JUnit#

To test web-services we wrote JUnit-Test. Since this test required server to be run, we cannot include it into emforge-web project (unit-test will be called during build, then no any server run yet).

So, we created new project - emforge-service-tests, depended from emforge-api (since it will use BpmService interface and Transfer Objects).

Added dependencies to CXF like in emforge-web project and specified BpmService as ws-client in spring-context:


<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-2.5.xsd">
	
    <!-- Declaring Client -->
    <bean id="bpmServiceClient" class="org.emforge.BpmService" factory-bean="bpmClientFactory" factory-method="create"/>
	
    <bean id="bpmClientFactory" class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
        <property name="serviceClass" value="org.emforge.BpmService"/>
        <property name="address" value="${application.path}/ws/org.emforge.BpmService"/>
        <property name="outInterceptors">
            <list>
                <bean class="org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor" />
                <ref bean="wss4jOutConfiguration" />
            </list>
        </property>
    </bean>

    <bean id="wss4jOutConfiguration" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor">
        <property name="properties">
            <map>
                <entry key="action" value="UsernameToken Timestamp Signature"/>
                <entry key="user" value="${user.name}" />
                <entry key="passwordType" value="PasswordText" />
                <entry key="passwordCallbackRef" value-ref="passwordCallback" />
                <entry key="signaturePropFile" value="client_sign.properties"/>
            </map>
        </property>	
    </bean>
	
    <bean id="passwordCallback" class="org.emforge.test.PasswordCallbackHandler">
        <property name="password" value="${user.password}"/>
    </bean>
</beans>

${application.path}, ${user.name} and ${user.password} - properties defined path to the server, username and password we should use for login.

Now, we can write Unit-Test:


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:/META-INF/emforge-ws-config-test.xml",
                                 "classpath:/META-INF/emforge-ws-test.xml"})
public class BpmServiceTest {
    private final Log log = LogFactory.getLog(getClass());
	
    @Autowired
    private BpmService bpmServiceClient;

    @Test
    public void testSomething() {
    }
}

object, inplemented BPMService and worked with server via web-services will be initialized and injected into test by Spring, so you can use it for testing different methods.

Only one note - unit-tests, included into emforge-web always worked with clean database - they initialize database, do everything in transaction, so, they do not depend from any previously created data and do not pass any changes into really used database.

This tests are worked with server - so, they depend from data, already exist in the database, and all changes done by tests will come into database. So, be careful with this way of testing!

In future, we have plans to run emforge with jetty:run target in maven and run unit-tests against this internally run server (we can configure it to run with using memory-based HSQL). In this case these unit-tests will work as unit-tests should work: do not depend from external software and current data in database, do not leave any changes there.

Then it will be done - new tutorial will be written.

Last Modified by Sergey Zakusov 1 year ago
Comments (0)
Login to add comment