24 Using Java Callouts and POJOs

This chapter describes how to extend the capabilities of Service Bus by invoking custom Java code from within pipelines and split-joins. Service Bus pipelines and split-joins each have a Java callout action that allows you to call a Plain Old Java Object (POJO) external to the pipeline or split-join.

This chapter includes the following sections:

For information about configuring a Java callout to a POJO, see Adding Java Callout Actions in the Console.

24.1 Introduction to Java Callouts

The Java callout action lets you access the methods in a Java archive (JAR) file to add processing logic to your pipelines and split-joins.

When you configure the callout, you can specify arguments for the method and you can optionally specify a service account for security. The parameters can be mapped to message context variables.Static methods can be accessed from any POJO.

You can also use Java callouts to create Java objects to store in the pipeline and to pass Java objects as parameters to other Java callouts.

Tip:

JSON-typed variables are passed in to java.lang.String-typed arguments with their stringified representation.

24.1.1 Java Callout Usage Guidelines

The scenarios in which you can use Java callouts in Service Bus include the following:

  • Performing custom validations, such as validating against a DTD, or doing cross-field semantic validation in Java.

  • Performing custom transformations, such as converting a binary document to base64Binary (or vice versa) or using a custom Java transformation class.

  • Performing custom authentication and authorizations. Examples include scenarios in which a custom token in a message needs to be authenticated and authorized. However, the authenticated user's identity cannot be propagated by Service Bus to the services or POJOs subsequently invoked by the pipeline or split-join.

  • Performing lookups for message enrichment. For example, a file or Java table can be used to look up any piece of data that can enrich a message.

  • Accessing binary data. You can use a Java callout to a POJO to sniff the first few bytes of a binary document to deduce the MFL type. The MFL type returned is used for a subsequent NonXML-to-XML transformation using the MFL Transform action.

  • Implementing custom routing rules or rules engines.

  • Creating a Java object and storing it in the pipeline.

  • Passing a Java object as a parameter to another Java callout.

  • Invoking a remote EJB operation or service with a POJO using a JEJB proxy service.

The input and return types for Java callouts are not restricted. For more information about storing and passing Java objects in the pipeline, see Java Content in the Body Variable.

24.1.2 Java Callouts or EJBs

Enterprise JavaBeans (EJBs) also provide a Java exit mechanism. The use of EJBs is recommended over Java callouts in the following cases:

  • When you already have an EJB implementation. The JEJB transport lets you invoke EJBs with EJB calls through Service Bus, letting you leverage Service Bus functionality such as message routing, UDDI integration, alerts, per-operation monitoring, reporting, and result caching.

  • When you require read access to a JDBC database. Although POJOs can be used for this purpose, EJBs were specifically designed for this and provide better support for managing and connecting to JDBC resources.

  • When you require write access to a JDBC database or other J2EE transactional resource. EJBs were specifically designed for transactional business logic and they provide better support for proper handling of failures. However, transaction and security context propagation is supported with POJOs, and the JEJB transport provides error handling in transaction contexts.

For outbound messaging, Oracle recommends that you write a custom transport instead of using POJOs or EJBs.

24.2 Working with Streaming Content

You can work with streaming content using Java callouts.

Streaming content can pass binary-content as an input argument to callout methods and accept streaming content results from Java callout methods.

24.2.1 Passing Streaming Content to a Java Callout

You can pass binary-content as an input argument to a Java callout method in a streaming fashion. Service Bus handles this by checking the Java type of the input argument. If the argument is of type javax.activation.DataSource, the system creates a wrapper DataSource object and gets the InputStream from the corresponding source by invoking the Source.getInputStream() method. You can call this method as many times as you need in your Java callout code.

In addition, the getContentType() method returns the application/octet-stream unless the binary content is a paged MIME attachment, in which case the value of the Content-Type header of the corresponding MIME part is used, if present.

Similarly, the getName() method returns the string value of the binary-content reference attribute unless the binary content is a paged MIME attachment, in which case the value of the Content-ID header of the corresponding MIME part is used, if available. The getOutputStream() method throws the UnsupportedOperationException, as required.

After completing, the result is passed to the Java callout method argument. Note that to properly interpret the binary octets in the input stream, the Java callout method might also require the value of the Content-Transfer-Encoding header (for example, to determine whether the encoding is binary, 7bit, 8bit, and so on). You can pass this parameter as a separate argument, as shown in the following:

$attachments/*:attachment[1]/*:Content-Transfer-Encoding/text()

Note that if the input argument is not a DataSource, Service Bus converts the argument to a byte[] array.

24.2.2 Streaming Content Results from a Java Callout

You can get streaming content results from a Java callout method. Service Bus handles this by checking the Java type of the result and then adding the new source to the source repository, setting the appropriate context variable value to the corresponding ctx:binary-content XML element.

Note:

To return the contents of a file from a Java callout method, you can use an instance of javax.activation.FileDataSource.

Whenever the pipeline or split-joins needs the binary contents of the source, it looks up the DataSource object corresponding to the ctx:binary-content element in the repository and invokes the DataSource.getInputStream() method to retrieve the binary octets.

Note that the getInputStream() method might be called multiple times during message processing, for example to accommodate outbound message retries in the transport layer.

24.3 Best Practices for Java Callouts and POJOs

In general, Oracle recommends that the JARs are small and simple; any large bodies of code that a JAR invokes or large frameworks that are used are best included in the system classpath. If you make a change to the system classpath, you must reboot the server.

POJOs are registered as JAR resources in Service Bus. For information about JAR resources, see Working with JAR Files.

Oracle recommends that you put dependent and overlapping classes in the same JAR resource. If they are naturally distinct, put them in different JAR resources. Any change to a JAR resource causes all the services that reference it to be redeployed. This can be time consuming for your Service Bus system. The same class can be located in multiple JAR resources without causing conflicts. The JAR files are dynamically class loaded when they are first referenced.

A single POJO can be invoked by one or more services. All the threads in the service invoke the same POJO, so the POJO must be thread safe. A class or method on a POJO can be synchronized, in which case it serializes access across all threads in all of the invoking services. Any finer-grained concurrency (for example, to control access to a database read results cache and implement stale cache entry handling) must be implemented by the POJO code.

It is generally a bad practice for POJOs to create threads.