42 Extensibility Using Java Callouts and POJOs

This chapter describes how to extend the capabilities of Oracle Service Bus by invoking custom Java code from within proxy services. Oracle Service Bus supports a Java exit mechanism through a Java callout action that lets you call out to a Plain Old Java Object (POJO).

Static methods can be accessed from any POJO. The POJO and its parameters are visible in the Oracle Service Bus Administration Console or Oracle Service Bus plug-ins for Eclipse at design time; the parameters can be mapped to message context variables.

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.

For information about configuring a Java callout to a POJO, see Section 21.20, "Adding Java Callout Actions."

42.1 Usage Guidelines

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

  • Custom validation—Examples of custom validation include validation against a DTD, or doing cross-field semantic validation in Java.

  • Custom transformation—Examples of custom transformations can include converting a binary document to base64Binary, or vice versa, or using a custom Java transformation class.

  • Custom authentication and authorization—Examples of custom authentication and authorizations include scenarios in which a custom token in a message must be authenticated and authorized. However, the authenticated user's identity cannot be propagated by Oracle Service Bus to the services or POJOs subsequently invoked by the proxy service.

  • 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.

  • Binary data access—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.

  • Create a Java object and store it in the pipeline.

  • Pass 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. See Section 39.3.5, "Java Content in the body Variable" for more information about storing and passing Java objects in the pipeline.

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

  • When you already have an EJB implementation; although the JEJB transport lets you invoke EJBs with EJB calls through Oracle Service Bus, letting you leverage Oracle 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 management of, and connectivity 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.

42.2 Working with Streaming Content

You can work with streaming content using Java callouts, both to pass binary-content as an input argument to callout methods and to accept streaming content results from Java callout methods.

42.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. Oracle 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. 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()

If the input argument is not a DataSource, Oracle Service Bus converts the argument to a byte[] array.

42.2.2 Streaming Content Results from a Java Callout

You can get streaming content results from a Java callout method. Oracle 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 Oracle Service Bus pipeline 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.

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

42.3 Best Practices

POJOs are registered as JAR resources in Oracle Service Bus. For information about JAR resources, see Chapter 8, "JARs."

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 made use of are best included in the system classpath. If you make a change to the system classpath, you must reboot the server.

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

A single POJO can be invoked by one or more proxy services. All the threads in the proxy services invoke the same POJO. Therefore, 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 proxy services. Any finer-grained concurrency (for example, to control access to a DB 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.