cancel
Showing results for 
Search instead for 
Did you mean: 

Getting all fields in a Java Delegate

jorell
Champ in-the-making
Champ in-the-making
Hello
We are trying use activiti as a webservice orchestration tool. We're trying to design a generic delegate in which we pass in a list of variables which can be used as parameters in the external service call within the delegate. If we use activiti:field elements to specify the the webservice parameters then it requires us to know the field names before hand (since the Expression object need to be named after the activiti:field names).
Is there a way from within the delegate to get all the activiti:field elements for the given task. This would allow us to code a generic delegate that can be used across our different process definitions.

As a side not: We also tried using IO Specification but it seems to not be supported yet in activiti delegates.
Thanks in advance for any help!
5 REPLIES 5

trolltop
Champ in-the-making
Champ in-the-making
Hiya Jorell,
We are attempting the same sort of usage: generic Web Service delegate to handle calling out to a Service; parameterise the Web Service operation via expressions and property injection. I have hit a small wall however with regards to property injection (using Spring).

I have defined the following:
[application.properties]

## – ABC Webservices
abc.webservice.service.soa.wsdl = "http://some.address.ours:9999/wisd/Services/SOA/this_service.wsdl"
abc.webservice.service.soa.class = ours.address.some.workflow.service.CDRSOAService
abc.webservice.service.soa.port.class = ours.address.some.workflow.service.CDRSOAServicePort
abc.webservice.service.soa.operation.collection = cdrSOACollectionOperation
abc.webservice.service.soa.operation.classification = cdrSOAClassificationOperation


## – Database connection parameters
db.connection.driver = org.h2.Driver
db.connection.url = jdbc:h2:mem:activiti;DB_CLOSE_DELAY=1000
db.connection.username = sa
db.connection.password =

[loader.bpmn]

    <serviceTask id="abcServiceCall" name="Request Load" activiti:class=ours.address.some.workflow.WebServiceDelegate">
      <extensionElements>
        <activiti:field name="wsdl">
          <activiti:expression>${abc.webservice.service.soa.wsdl}</activiti:expression>
        </activiti:field>
        <activiti:field name="serviceClass">
          <activiti:expression>${abc.webservice.service.soa.class}</activiti:expression>
        </activiti:field>
        <activiti:field name="portClass">
          <activiti:expression>${abc.webservice.service.soa.port.class}</activiti:expression>
        </activiti:field>
        <activiti:field name="operation">
          <activiti:expression>${abc.webservice.service.soa.operation.collection}</activiti:expression>
        </activiti:field>
        <activiti:field name="parameters">
          <activiti:expression>${jobName},${invocationId},${fileLocation},${collectionStartDate}</activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>

… and then in the Delegate
[WebServiceDelegate.java]

public class WebServiceDelegate implements JavaDelegate {


    private Expression          wsdl;
    private Expression          serviceClass;
    private Expression          portClass;
    private Expression          operation;
    private Expression          parameters;
    private Expression          returnValue;
    private Expression          interceptor;

    @Override
    public void execute(DelegateExecution execution) {
        LOG.info("Processing web service request: operation[" + operation.getValue(execution) + "]");

        // .. parameters for operation
        List<String> params = new ArrayList<>();
        if (null != parameters) {
            if (parameters.getValue(execution) instanceof String) {
                extractStringParameters(execution, params);
            }
        }

        String wsdlString = (String) wsdl.getValue(execution);
        try {

            // .. client connection
            LOG.info("Trying URL: " + wsdlString);
            URL wsdlURL = new URL(wsdlString);

            // .. get Web Service access point and invocation port via Reflection
            Class<?> serviceClazz = Class.forName((String) serviceClass.getValue(execution));
            Object service;
            // try {
            service = serviceClazz.getConstructor(new Class[] { URL.class, String.class }).newInstance(wsdlURL, SERVICE_NAME);

            Object port = getServicePort(serviceClazz, service, (String) portClass.getValue(execution));

            // .. setup endpoint (if required) and invoke service
            if ((null != interceptor) && (null != interceptor.getValue(execution))) {
                ClientProxy.getClient(port).getEndpoint().getOutInterceptors().add((Interceptor) interceptor.getValue(execution));
            }

            String operationName = (String) operation.getValue(execution);
            LOG.info("Requested operation: " + operationName);


Our general approach is: for each web service operation required to support our process applications, specify a service task using property file definitions, and parameterise BPMN service task using these property definitions, and provide one generic delegate to handle all/most "web service" Service Tasks.

This may help what you are trying to achieve … however … the small wall!

I can get Spring to load the property definitions, but I cannot get them injected into the BPMN process definitions for Activiti to recognise. The stack trace I get is:

org.activiti.engine.ActivitiException: Unknown property used in expression: ${abc.webservice.service.soa.operation.collection}
at org.activiti.engine.impl.el.JuelExpression.getValue(JuelExpression.java:53)
at au.edu.ncver.cdr.domain.workflow.AbcServiceDelegate.execute(WebServiceDelegate.java:39)
at org.activiti.engine.impl.delegate.JavaDelegateInvocation.invoke(JavaDelegateInvocation.java:34)
at org.activiti.engine.impl.delegate.DelegateInvocation.proceed(DelegateInvocation.java:37)
at org.activiti.engine.impl.delegate.DefaultDelegateInterceptor.handleInvocation(DefaultDelegateInterceptor.java:25)
at org.activiti.engine.impl.bpmn.behavior.ServiceTaskJavaDelegateActivityBehavior.execute(ServiceTaskJavaDelegateActivityBehavior.java:49)
at org.activiti.engine.impl.bpmn.behavior.ServiceTaskJavaDelegateActivityBehavior.execute(ServiceTaskJavaDelegateActivityBehavior.java:40)
at org.activiti.engine.impl.bpmn.helper.ClassDelegate.execute(ClassDelegate.java:116)
at org.activiti.engine.impl.pvm.runtime.AtomicOperationActivityExecute.execute(AtomicOperationActivityExecute.java:44)
at org.activiti.engine.impl.interceptor.CommandContext.performOperation(CommandContext.java:87)
at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:532)
at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:527)
at org.activiti.engine.impl.pvm.runtime.AtomicOperationTransitionNotifyListenerStart.eventNotificationsCompleted(AtomicOperationTransitionNotifyListenerStart.java:52)
at org.activiti.engine.impl.pvm.runtime.AbstractEventAtomicOperation.execute(AbstractEventAtomicOperation.java:56)
at org.activiti.engine.impl.interceptor.CommandContext.performOperation(CommandContext.java:87)
at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperationSync(ExecutionEntity.java:532)
at org.activiti.engine.impl.persistence.entity.ExecutionEntity.performOperation(ExecutionEntity.java:527)

Other info:
  • using JUnit 4 (@RunWith(SpringJUnit4ClassRunner.class)
  • process deployed using @Deployment(resources = { "application.properties", "workflow/loader.bpmn" })
Any ideas??

jbarrez
Star Contributor
Star Contributor
So do I understand you correctly that you want the values of the properties file to be injected  into the delegate class?

I don't know if that's supported. Either way, a quick-hack could be to create a wrapper bean around it and reference that bean in the xml

trolltop
Champ in-the-making
Champ in-the-making
Thanks Joram,
I figured (after a bit of hair-pulling) that my approach was not possible. Will give the wrapper bean idea a go.

Cheers

trolltop
Champ in-the-making
Champ in-the-making
FYI …

Following Joram's advice, I have managed to get over the small wall with Spring and the generic WebService approach mentioned earlier.
My steps:
* the generic JavaDelegate class for handling all of our web service class as described above.
* a property-value based configuration bean - properties set by Spring from application.properties as above
[java]
@Getter
public class CollectionServiceConfiguration implements AbcServiceConfiguration {

    @Autowired(required = true)
    @Value("${abc.webservice.service.soa.wsdl}")
    private String wsdl;
   
    @Autowired(required = true)
    @Value("${abc.webservice.service.soa.class}")
    private String serviceClass;
   
    @Autowired(required = true)
    @Value("${abc.webservice.service.soa.port.class}")
    private String servicePortClass;
   
    @Autowired(required = true)
    @Value("${abc.webservice.service.soa.operation.collection}")
    private String serviceOperation;

}
[/java]
* bean definition in the Spring application context file:

    <bean id="collectionServiceConfiguration" class="ours.address.some.workflow.service.CollectionServiceConfiguration"/>
* and the service definition

    <serviceTask id="abcServiceCall" name="Request Load" activiti:class="ours.address.some.workflow.AbcServiceDelegate">
      <extensionElements>
        <activiti:field name="wsdl">
          <activiti:expression>#{collectionServiceConfiguration.wsdl}</activiti:expression>
        </activiti:field>
        <activiti:field name="serviceClass">
          <activiti:expression>#{collectionServiceConfiguration.serviceClass}</activiti:expression>
        </activiti:field>
        <activiti:field name="portClass">
          <activiti:expression>#{collectionServiceConfiguration.servicePortClass}</activiti:expression>
        </activiti:field>
        <activiti:field name="operation">
          <activiti:expression>#{collectionServiceConfiguration.serviceOperation}</activiti:expression>
        </activiti:field>
        <activiti:field name="parameters">
          <activiti:expression>${jobName},${invocationId},${fileLocation},${collectionStartDate}</activiti:expression>
        </activiti:field>
      </extensionElements>
    </serviceTask>

All hooked up and loads fine in JUnit - gotta be happy with that Smiley Happy

Hope this helps,

jbarrez
Star Contributor
Star Contributor
Awesome. Thanks for posting back your solution!