What is a Custom Work Context ?
The idea behind out-of-band context propagation in Weblogic server was to allow application developers to send information from a client to an application as an extra payload on the underlying transport protocol. Typical transport protocols that are supported for context propagation include Threads, JMS queues, SOAP, RMI etc. The use cases of context propagation are applications that need to carry information outside the application rather than inside the application itself. Diagnostics seems like a classic example that can ride on workcontext to propagate monitoring information from client to server and vice versa. For a quick refresher to work context propagation, please refer to latest Weblogic documentation. A few important APIs in the workcontext framework are -
1. weblogic.workarea.WorkContextMap - The main context propagation interface to tie applications with data and propagate that information via application requests. WorkContextMap is part of the client/application's JNDI environment. The map can be accessed through JNDI lookup by the name "java:comp/WorkContextMap".
2. weblogic.workarea.WorkContext - Developers could use this interface to marshal and unmarshal the user data that is passed along with the application. With the interface came a set of four implementing classes that could marshall 8 bit Ascii, Long, String and Serializable contexts.
Applications that started replying on this framework created custom contexts either by extending WorkContext or Serializable interface. We found some interesting use cases as these custom contexts were developed and put in use. The following sections document some of these use cases.
Using the correct classloader to load a custom WorkContext
Initially, the custom contexts were bundled as a part of the server through the Weblogic System classpath. As a result, the Context classloader (Weblogic System ClassLoader) for the Weblogic server could resolve these custom classes while unmarshalling the Work Context object. This scheme fell short of the fact that an application may not choose to bundle its custom context as a part of the System Loader in Weblogic. A specific application may choose to bundle the custom context as a part of the application (EAR or WAR) rather than the System classpath approach. Hence, instead of using SystemLoader.loadClass(classname) to resolve the custom context, the Server started relying on the Thread Context classloader to load the custom context. The Thread Context classloader is set correctly to the application loader if the unmarshalling of the context had to happen in an application loader scope.
Inter-operating custom work contexts across different Weblogic server versions
As Weblogic server got integrated into the Oracle Fusion Middleware Framework (11g), the context propagation framework faced a challenging problem with inter-operating custom work contexts created in 11g release with older versions of the Weblogic server. Once a newly created custom context passed from a newer version of Weblogic server to an older version, the Weblogic server had to convert the byte array into a work context object while de-serializing the context in the older server. An older version of the server would not know about newer work context object types. This resulted in ClassNotFoundExceptions.
The solution to this problem was easy. The application provider had to wrap their custom objects in a SerializableWorkContext. In effect, the SerializableWorkContext wraps a Java Serializable object (for example a custom Work Context) such that on de-serializing the work context, the server does not hit the ClassNotFoundExceptions. When the work context provider is present in the newer version of the server, it would firstly get the SerializableWorkContext from the map and then get the wrapped serializable custom object. The custom context provider knows if it can handle the new context. Hence, the serialization of the custom object gets controlled by the application provider rather than the work context framework.
Here is a snippet of code that creates a Serializable work context and puts it in the work context map for the client .
1. WorkContext serializedCustomContext = PrimitiveContextFactory.create(Serializable customContext)
2. Put the serialized custom context inside the workcontext map -
WorkContextMap map =
WorkContextHelper.getWorkContextHelper().getWorkContextMap();
map.put(MYAPP.CUSTOM_CONTEXT,serializedCustomContext);
On the receiving end, the custom context would be retrieved as -
1. WorkContextMap map =
WorkContextHelper.getWorkContextHelper().getWorkContextMap();
2. (SerializableWorkContext)WorkContext customContext = (SerializableWorkContext)map.get(MYAPP.CUSTOM_CONTEXT);
3. MyContext myCtx = (MyContext)((SerializableWorkContext)customContext).get();
Unless the application provider turned around and calls step (3) or customContext.getSerializable(), the custom object would not be converted from a byte array to an object instance. Hence, the workcontext framework would simply unmarshal the wrapped serialized object containing the payload instead of de-serializing the custom object itself. This mechanism shifts the control of de-serializing to the application provider who now has more control on when to convert the byte array into the custom work context.
If the application provider on a specific Weblogic server version cannot de-serialize the custom context, it would pass the wrapped object to a different server who may be able to read it fully. The mechanism is limited to Serializable work context types. Serialized work contexts come with a performance overhead. Hence an application provider should carefully evaluate the pros and cons of using a Serialized work context type.
So far so good. Now came the side effect of when to serialize while sending a custom context from a client.
When to serialize the custom work context before sending it from the client
There is a hidden side-effect to SerializedWorkContexts other than performance. A serialized work context is serialized the moment it is created. For example, if the client wraps its custom context in a Serializable context -
WorkContext customContext = PrimitiveContextFactory.create(Serializable context);
The custom work context is serialized at the point of creation (the create call). This is often a drawback to the application provider who may not have all the information it needs at the creation time to stuff in the custom context. Once the context is created, its too late to change it. Hence we needed a better mechanism to delay the serialization of the custom context. This allows the Serializable context data to be updated even after it is put in the WorkContextMap. From 11g release, the framework provided a new API to create a mutable Serializable object that in effect delayed the serialization of the work context -
WorkContext customContext = PrimitiveContextFactory.createMutable(Serializable context);
The mutable work context vastly improved the time window when clients could change the contents of the work context. Diagnostic frameworks often use this feature since they do not have all the information they need in the work context when creating it in the first place. However, they do have all the required information just before the request is send over the wire from the client to the server. This feature gave developers the opportunity to change the workcontext repeatedly before it was put on the wire.
We haven't talked about the inter-operability case with mutable work contexts. The Weblogic server itself needs to distinguish from a mutable (or delayed serialized work context) from the plain serialized work context. Hence the mutable flag or property had to be carried with the new type of work context so that the Server could take an appropriate action when it came across a mutable or non mutable Serializable work context.
When is a Work Context is really available to the application ?
It would be desirable for a work context send from a client to a server to be available to the application the moment the request is intercepted. However, the availability of the workcontext in the application is dependent on the client server communication protocol. For example, when a Web Services request is send over HTTP, the SOAP request is not unmarshalled by the server unless the protocol specific interceptors (or handlers who know what to do with the payload) have read the whole SOAP message. The interceptors don't fire at the first point of request interception. If applications have J2EE filters or security related policy checks that are done before the SOAP request is unmarshalled, the goodies in the workcontext payload are hidden to these consumers unless the SOAP envelope is fully unmarshalled later. This is a known limitation with how work contexts interact with the different protocols.
Unless the request is completely unmarshalled, a call to the WorkContextMap to fetch an already known work context will return null -
WorkContext context =
WorkContextHelper.getWorkContextHelper().getWorkContextMap().get(MYAPP.MY_CONTEXT);
It is therefore incumbent on frameworks who ride on Weblogic work contexts not to access the map during this window (intercept request --> unmarshall request). Though this window is small, its possible some piece of application code would like get hold of the incoming work context at the very first request interception point. Without that, they may face the dilemma of having to reconcile a work context created during this window and the real work context that reveals itself later. Such reconciliations are tough and come with even more serious inter-operability side effects. Hence, its recommended that the workcontext is only accessed from the map after the request is fully unmarshalled.
In Conclusion
Custom work contexts (mutable or un-mutable) is a handy feature for applications who intend to send extra data outside the application scope. The Weblogic server tries to hide all the underlying gory details of marshalling and unmarshalling the context associated with various protocols from the end user. The consumer simply needs to remember the "key" or "id" to the stuffed work context in the incoming request and pull the work context out of the map. Used wisely, this feature can save man hours with applications providers who intend to drive data outside their scope. The workcontext framework has also evolved over time to learn and adapt with changing needs of the application provider. Once used primarily as an internal Weblogic server feature, workcontext is now full integrated and deployed in applications running on Oracle Fusion Middleware 11g framework.