2
1
0

Hi,

I'm trying to create a scheduled service (ServiceA) that calls another existing service (ServiceB), but I receive the above error when I run the Unit Test for ServiceA.

I am using the following code to call the existing service:

String result = new ServiceInvoker().setServiceName("ServiceB").setClientCode("client").invoke()

The method signature for ServiceB is the standard signature:

Object invoke(SvcDef svcDef, HttpServletRequest request, User user, Map params)

In ServiceB the 'Map params' is not used, so I feel that invoke(), rather than invoke(params) would work when I want to call ServiceB from ServiceA.

 

Is there something else required when invoking one service from another?

 

Thanks

  1. Radoslav Ivanov

    Hi Mark

    Basically, "invoke()" delegates to "invoke(Collections.emptyMap())".

    Can you please send the stack trace (and the code)?

    Cheers
    Rado

  2. Mark Murray

    Hi Rado,

    here is the code for the ScheduledService:

    import com.avoka.tm.svc.PropertyBuilder
    import com.avoka.tm.svc.ServiceInvoker
    import com.avoka.tm.util.Path
    import com.avoka.tm.util.XmlDoc
    import com.avoka.tm.vo.SvcDef
    import org.w3c.dom.Node
    /**
     * This Fluent Scheduled Service calls CBS method 'get-beneficiaries' to update the Organisation Property 'CRA Recipients'.
     */
    class RefreshCRARecipients {
        /*
         * Perform scheduled job
         *
         * returns: string message to be logged to the server log as an INFO message
         */
        String invoke(SvcDef svcDef) {
            String craXml = new ServiceInvoker().setServiceName("CBS - getBeneficiaries").setClientCode("BBA").invoke()
            // use com.avoka.tm.util.Path, as XmlSlurper is not type-safe
            Path xmlPath = new Path(craXml)
            // format the beneficiaries as dataValue1|displayValue1,dataValue2|displayValue2, etc
            String beneficiaries = xmlPath.paths("//Beneficiary").collect { beneficiary ->
                (String) beneficiary.val("//ID") + "|" + beneficiary.val("//Description")
            }.join(",")
            // set the Organisation Property
            new PropertyBuilder().setName("CRA Recipients").setValue(beneficiaries).setClientCode("BBA").build()
            /* @out message A server log INFO message to verify that this service was invoked */
            return 'Refresh CRA Recipients - Service invoked'
        }
    }

    and this is the code for the existing service:

    import com.avoka.tm.svc.ServiceInvoker
    import com.avoka.tm.vo.SvcDef
    import com.avoka.tm.vo.User
    import javax.servlet.http.HttpServletRequest
    /**
     * This Groovy Service provides the results of the get-beneficiaries call on CBS.
     * Result is returned as xml.
     */
    class CBSgetBeneficiaries {
        /*
         * Perform a groovy service invocation
         *
         * return: the result object
         */
        Object invoke(SvcDef svcDef, HttpServletRequest request, User user, Map params) {
            Map svcParams = [:]
            svcParams.put("soapAction", svcDef.paramsMap.soapAction)
            svcParams.put("soapEnvelope", svcDef.paramsMap.soapEnvelope)
            /* @out result The output of the CBS call as xml. */
            return new ServiceInvoker().setServiceName("Util - Call CBS").setClientCode("BBA").invoke(svcParams)
        }
    }

    and here is the stack trace when I run the Unit Test:

    java.lang.IllegalArgumentException: could not resolve method invoke() for args: []
    	com.avoka.core.groovy.runtime.SecureStaticGroovyTask.call(SecureStaticGroovyTask.java:213)
    	com.avoka.core.groovy.runtime.GroovyScriptRuntime.executeScript(GroovyScriptRuntime.java:241)
    	com.avoka.core.groovy.runtime.GroovyScriptRuntime.executeScript(GroovyScriptRuntime.java:85)
    	com.avoka.fc.core.service.fluent.AbstractFluentService.invoke(AbstractFluentService.java:130)
    	com.avoka.tm.svc.ServiceInvoker.invoke(ServiceInvoker.java:205)
    	com.avoka.tm.svc.ServiceInvoker.invoke(ServiceInvoker.java:213)
    	RefreshCRARecipients.invoke(script14937913919941265006972.groovy:20)
    	sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

     

    Thanks

CommentAdd your comment...

3 answers

  1.  
    1
    0
    -1

    Hi Rado, Malcolm,

    Thanks for the information; now I have a better understanding of the signature and the invoke() requirements.

    However, I am still having some issues - I find I am passing the wrong svcDef, or still missing something about how to pick the right svcDef in the right place.

     

    ServiceB has two parameters that I wish to access in the ServiceB.invoke() method - they are the soapAction and the soapEnvelope. They are accessed via the code:

    Map args = [
            "soapAction"  : svcDef.paramsMap.soapAction,
            "soapEnvelope": svcDef.paramsMap.soapEnvelope
    ]

    In this context the svcDef is the ServiceB svcDef.

    But if I call ServiceB from Service A, I need to pass a svcDef and use the code Rado has suggested.

    Then the svcDef that is passed to ServiceB is the ServiceA svcDef. That would mean that the above code to get the ServiceB paramsMap entries will fail because the svcDef passed to the ServiceB.invoke() method is for ServiceA and not ServiceB.

     

    So, I'm wondering if my technique to get ServiceB.paramsMap entries is correct, or do I need to define a ServiceB.svcDef within ServiceA and pass that as the svcDef param to the ServiceInvoker().invoke(params) call?

    Sorry, hope that is not too confusing; bit hard to explain.

    Thanks

    1. Radoslav Ivanov

      Hi Mark

      If I got the service chain correctly, we have - A->B->C and you want to pass soap action and envelop params from A's svcDef.paramsMap to C.

      I would suggest looking at GroovyServiceInvoker in that case:

      ServiceA:
      Map args = [
              "soapAction"  : svcDef.paramsMap.soapAction,
              "soapEnvelope": svcDef.paramsMap.soapEnvelope
      ]
      
      
      String result = new GroovyServiceInvoker().setServiceName("ServiceB").setClientCode("client").invoke(args)
       
       
      ServiceB:
      Map args = [
              "soapAction"  : params.soapAction,
              "soapEnvelope": params.soapEnvelope
      ]
      
      return new GroovyServiceInvoker().setServiceName("ServiceC").setClientCode("client").invoke(args) 

       

      Cheers
      Rado

       

    2. Mark Murray

      Hi Rado,

      Success. It works!

      The scenario is almost right. I have the following:

      there are three services in the chain; A -> B -> C

      svcA has no serviceParameters. It is a scheduled service that calls svcB without params.

      svcB has svcDef.paramsMap parameters (soapAction and soapEnvelope) that are passed to svcC when it is called by scvB.

      svcC has svcConn definition and also params which are passed from svcB.

       

      I studied the GroovyServiceInvoker() and it is what I needed. So, I end up with the following:

      ServiceA:
      String craXml = new GroovyServiceInvoker().setServiceName("CBS - getBeneficiaries").setClientCode("BBA").invoke()
       
      ServiceB:
      Map<String, Object> args = [:]
      
      args.soapAction = svcDef.paramsMap.soapAction
      args.soapEnvelope = svcDef.paramsMap.soapEnvelope
      
      return new GroovyServiceInvoker().setServiceName("Util - Call CBS").setClientCode("BBA").invoke(args)
      
      
      ServiceC:
      String soapAction = params.soapAction
      String soapEnvelope = params.soapEnvelope
      // process using above values

       

      Thanks for your help.

      Regards

    CommentAdd your comment...
  2.  
    1
    0
    -1

    Hi Mark

    The Map in question is not just the last "params" argument of ServiceB, but a Map of all the arguments. In your case it should be:

    Map params = [ 
                "svcDef": svcDef, 
                "request": null, 
                "user": null, 
                "params": null
            ] 
     
    String craXml = new ServiceInvoker().setServiceName("CBS - getBeneficiaries").setClientCode("BBA").invoke(params)        
     

     

    If you do not pass arguments correctly, as a result you will get an exception that it cannot resolve method.

     

    Please let me know if you have questions.

     

    Cheers
    Rado

      CommentAdd your comment...
    1.  
      1
      0
      -1

      If you are calling another service via invoke you need to provide the parameters to support that services invoke signature.

      For your example this signature is:

      invoke(SvcDef svcDef, HttpServletRequest request, User user, Map params)

      You will need to invoke the method with the parameters:

      • svcDef
      • request
      • user
      • params

      Please see the service unit tests code, as they are all using this pattern.

      regards Malcolm

        CommentAdd your comment...