Class FrankSender

All Implemented Interfaces:
HasPhysicalDestination, IConfigurable, IConfigurationAware, INamedObject, IScopeProvider, ISender, ISenderWithParameters, IWithParameters, IThreadCreator, org.springframework.beans.factory.Aware, org.springframework.context.ApplicationContextAware

@Forward(name="*", description="Exit code") @Category(BASIC) public class FrankSender extends AbstractSenderWithParameters implements HasPhysicalDestination, IThreadCreator
Sender to send a message to another Frank! Adapter, or an external program running in the same JVM as the Frank!Framework.

Sends a message to another Frank!Framework-adapter in the same Frank!Framework instance, or an external program running in the same JVM as the Frank!Framework. If the callee exits with an <Exit/> that has state PipeLine.ExitState.ERROR, an error is considered to happen in the caller which means that the exception forward is followed if it is present.

Returns exit.code as forward name to the SenderPipe, provided that exit.code can be parsed as integer. For example, if the called adapter has an exit state with code 2, then the SenderPipe supports a forward with name 2 that is followed when the called adapter exits with the mentioned exit. This does not work if the code is for example c2.

A FrankSender makes a call to either an Adapter or an external program by setting the scope. By default the scope is ADAPTER.

Configuration of the Adapter to be called

A call to another Adapter in the same Frank!Framework instance is preferably made using the combination of a FrankSender configured with the name of the adapter.

Configuring FrankSender and Adapter

  • Define a SenderPipe with a FrankSender
  • Set the attribute target to targetAdapterName
  • If the adapter is in another Configuration deployed in the same Frank!Framework instance, then set target to targetConfigurationName/targetAdapterName (note the slash-separator between Configuration name and Adapter name).
In the Adapter to be called:
  • The adapter does not need to have a dedicated receiver configured to be called from a FrankSender.
  • The adapter will run in the same transaction as the calling adapter.
  • If the called adapter does not to run in its own transaction, set the transaction attributes on the PipeLine attribute of this adapter or on the SenderPipe that contains this FrankSender.

Configuring FrankSender with FrankListener

  • Define a SenderPipe with a FrankSender
  • In the target adapter, define a Receiver with a FrankListener
  • Give a unique name to the listener: FrankListener.setName(String). If the name is not set, the name of the Adapter will be used.
  • Set the setScope(Scope) to LISTENER and the setTarget(String) to the listener name as per previous point
  • If the listener is in a different configuration, prefix the listener name with the name of the configuration and a slash (/) as separator between configuration and listener name

Configuring FrankSender and Remote Application

NB: Please make sure that the IbisServiceDispatcher-1.4.jar or newer is present on the class path of the server. For more information, see:

  • Define a SenderPipe with a FrankSender
  • Set the attribute scope to either JVM for a Java application, or to DLL for code loaded from a DLL
  • Set the attribute target to the service-name the other application used to register itself

In the other application:

  • Implement the interface nl.nn.adapterframework.dispatcher.RequestProcessor from the IbisServiceDispatcher library
  • Register the instance with the nl.nn.adapterframework.dispatcher.DispatcherManager obtained via the nl.nn.adapterframework.dispatcher.DispatcherManagerFactory
  • See the implementation code of the JavaListener in the Frank!Framework for an example

See also the repository of the IbisServiceDispatcher: https://github.com/frankframework/servicedispatcher

Using FrankSender to call an adapter from Larva tests

You can configure a FrankSender in Larva property files to use the FrankSender to invoke an adapter to test. When doing this, keep the following in mind:

  • If you leave the default scope as ADAPTER, then the target property needs to have both configuration name and adapter name, separated by a / character
  • When scope is left as default, the receiver and JavaListener are skipped and no transaction is started unless it is set on the adapter's PipeLine
  • If you do need a transaction and the adapter has a JavaListener that has JavaListener.setServiceName(String) defined, you can use the FrankSender with scope JVM and set the target attribute to the serviceName attribute of the JavaListener.

Migrating Existing Configurations

When one adapter (named A) needs to call another adapter (named B) like a subroutine, you will usually have an IbisLocalSender or an IbisJavaSender in adapter A, and a JavaListener in adapter B.

NB: For the example it is assumed that all adapters are defined in the same configuration.

Example of Existing Configuration

The existing configuration might look like this in the calling adapter:

 <module>
     <adapter name="Adapter A">
         <receiver name="Adapter A Receiver">
             <listener name="Adapter A Listener"
                 className="org.frankframework..." etc/>
         </receiver>
  	   <pipeline firstPipe="...">
  	       <pipe name="send" className="org.frankframework.pipes.SenderPipe">
  	           <sender className="org.frankframework.senders.IbisJavaSender"
  	               serviceName="service-Adapter-B" />
                 <forward name="success" path="..." />
  	       </pipe>
         </pipeline>
     </adapter>
 </module>
 
Or like using the modern XML XSD and an IbisLocalSender instead:

 <Module>
     <Adapter name="Adapter A">
         <Receiver name="Adapter A Receiver">
             ... Listener setup and other configuration
         </Receiver>
         <Pipeline>
             <SenderPipe name="send">
                 <IbisLocalSender name="call Adapter B"
                     javaListener="Adapter B Listener"/>
                 <Forward name="success" path="EXIT" />
             </SenderPipe>
         </Pipeline>
     </Adapter>
 </Module>
 
In the receiving adapter B the listener would have been configured like this:

 <Module>
     <Adapter name="adapter B">
         <Receiver name="Receiver B">
             <JavaListener name="Adapter B Listener" serviceName="service-Adapter-B"/>
         </Receiver>
         <Pipeline>
             ...
         </Pipeline>
     </Adapter>
 </Module>
 

Rewritten Example Configuration With FrankSender

This example shows the most simple way of using the FrankSender to call another adapter with least amount of overhead.

 <Module>
     <Adapter name="Adapter A">
         <Receiver name="Adapter A Receiver">
             ... Listener setup and other configuration
         </Receiver>
         <Pipeline>
             <SenderPipe name="send">
                 <!-- when scope="ADAPTER", then target is directly the name of the adapter you want to call -->
                 <FrankSender name="call Adapter C"
                     scope="ADAPTER"
                     target="adapter B"
                 />
                 <Forward name="success" path="EXIT" />
             </SenderPipe>
         </Pipeline>
     </Adapter>
     <Adapter name="adapter B">
         <!-- No receiver needed for FrankSender in this scenario -->
         <Pipeline>
             ... Exits, Pipes etc
         </Pipeline>
     </Adapter>
 </Module>
 

Rewritten Example Configuration With FrankSender and FrankListener

This example shows why you might want to call the other adapter via the FrankListener. This adds a bit more overhead to the call of the sub-adapter for the extra error-handling done by the target receiver.

 <Module>
    <Adapter name="Adapter A">
        <Receiver name="Adapter A Receiver">
         ... Listener setup and other configuration
 		  </Receiver>
 		  <Pipeline>
            <SenderPipe name="send">
                <!-- when scope="LISTENER", then target is directly the name of the FrankListener in the adapter you want to call -->
                <FrankSender
                    scope="LISTENER"
                    target="Adapter B Listener"/>
                <Forward name="success" path="EXIT" />
            </SenderPipe>
        </Pipeline>
     </Adapter>
     <Adapter name="adapter B">
         <!-- Messages will only be sent to the error storage if:
             - The target receiver is not transactional, and has maxTries="0", or
             - The target receiver is transaction, and the Sender is set up to retry sending on error
             For internal adapters, sending / receiving with retries might not make sense so the example does not show that.
         -->
         <Receiver name="Receiver B" maxRetries="0" transactionAttribute="NotSupported">
             <!-- Listener name is optional, defaults to Adapter name -->
             <FrankListener name="Adapter B Listener"/>
                 <!-- This adapter now has an error storage -- without Receiver and FrankListener the sub-adapter couldn't have that -->
             <JdbcErrorStorage slotId="Adapter B - Errors" />
         </Receiver>
         <!-- If transactions are required, set transaction-attribute on the Pipeline -->
         <Pipeline transactionAttribute="RequiresNew">
             ... Exits, Pipes etc
         </Pipeline>
    </Adapter>
 </Module>
 
Parameters
All parameters except scope and target are copied to the PipeLineSession of the adapter called.
Specific parameters
code Determine scope dynamically at runtime. If the parameter value is empty, fall back to the scope configured via the attribute, or the default scope ADAPTER., target Determine target dynamically at runtime. If the parameter value is empty, fall back to the target configured via the attribute.
  • Field Details

  • Constructor Details

    • FrankSender

      public FrankSender()
  • Method Details

    • configure

      public void configure() throws ConfigurationException
      Description copied from interface: ISender
      configure() is called once at startup of the framework in the configure method of the owner of this sender. Purpose of this method is to check whether the static configuration of the sender is correct. As much as possible class-instantiating should take place in the configure() or open() method, to improve performance.
      Specified by:
      configure in interface IConfigurable
      Specified by:
      configure in interface ISender
      Overrides:
      configure in class AbstractSenderWithParameters
      Throws:
      ConfigurationException
    • getPhysicalDestinationName

      public String getPhysicalDestinationName()
      Specified by:
      getPhysicalDestinationName in interface HasPhysicalDestination
    • getDomain

      public String getDomain()
      Specified by:
      getDomain in interface HasPhysicalDestination
    • sendMessage

      @Nonnull public SenderResult sendMessage(@Nonnull Message message, @Nonnull PipeLineSession session) throws SenderException, TimeoutException
      Description copied from interface: ISender
      Send a message to some destination (as configured in the Sender object). This method may only be called after the configure() method is called.

      The following table shows the difference between synchronous and a-synchronous senders:

       synchronousa-synchronous
      ISender.isSynchronous() returnstruefalse
      return value of sendMessage() isthe reply-messagethe messageId of the message sent
      the correlationID specified with sendMessage()may be ignoredis sent with the message
      a {link TimeOutException}may be thrown if a timeout occurs waiting for a replyshould not be expected

      Multiple objects may try to call this method at the same time, from different threads. Implementations of this method should therefore be thread-safe, or synchronized.

      Specified by:
      sendMessage in interface ISender
      Throws:
      SenderException
      TimeoutException
    • setSynchronous

      public void setSynchronous(boolean b)
      Synchronous or Asynchronous execution of the call to other adapter or system.
      Set to false to make the call asynchronously. This means that the current adapter continues with the next pipeline and the result of the sub-adapter that was called, or other system that was called, is ignored. Instead, the input message will be returned as the result message.
      Default value
      true
    • setScope

      public void setScope(FrankSender.Scope scope)
      FrankSender.Scope decides if the FrankSender calls another adapter, or another Java program running in the same JVM.
      It is possible to set this via a parameter. If the parameter is defined but the value at runtime is empty, then the value set via this attribute will be used as default.
      Parameters:
      scope - Either ADAPTER, JVM or DLL.
      Default value
      ADAPTER
    • setTarget

      public void setTarget(String target)
      Target: service-name of service in other application that should be called, or name of adapter to be called. If the adapter is in another configuration, prefix the adapter name with the name of that configuration and a slash ("/").
      It is possible to set a target at runtime via a parameter.
      If a parameter with name "target" exists but has no value, then the target configured via the attribute will be used as a default.
      Parameters:
      target - Name of the target, adapter or registered service.
    • setReturnedSessionKeys

      public void setReturnedSessionKeys(String string)
      Comma separated list of keys of session variables that will be returned to caller, for correct results as well as for erroneous results. The set of available sessionKeys to be returned might be limited by the returnedSessionKeys attribute of the corresponding JavaListener.
    • getScope

      public FrankSender.Scope getScope()
    • getTarget

      public String getTarget()
    • getReturnedSessionKeys

      public String getReturnedSessionKeys()
    • isSynchronous

      public boolean isSynchronous()
      Description copied from interface: ISender
      When true, the result of sendMessage is the reply of the request.
      Specified by:
      isSynchronous in interface ISender
    • setIsolatedServiceCaller

      public void setIsolatedServiceCaller(IsolatedServiceCaller isolatedServiceCaller)
    • setThreadLifeCycleEventListener

      public void setThreadLifeCycleEventListener(ThreadLifeCycleEventListener<Object> threadLifeCycleEventListener)
      Specified by:
      setThreadLifeCycleEventListener in interface IThreadCreator
    • setAdapterManager

      public void setAdapterManager(AdapterManager adapterManager)
    • setIbisManager

      public void setIbisManager(IbisManager ibisManager)