« October 2009 | Main

November 2009 Archives

November 2, 2009

Understanding Custom Context propagation

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.

November 9, 2009

Stateful JAX-WS with Coherence*Web


We describe a stateful JAX-WS web service with high availability provided by Coherence*Web [1]. Deployed to two separate WebLogic managed servers, the web service stores its state in http session cookies. Coherence*Web distributes and synchronizes http session data between both managed servers. As a result, two endpoints with synchronized web service state provide high availability. With one endpoint down, another could continue to serve the service request with current web service state.

Note that WebLogic clustering can also provide highly available http sessions. However, we want to demonstrate Coherence*Web as an alternative to clustering for high availability. Therefore, we do not use clustering here. Instead, we use Coherence*Web.

Setting up the stateful web service with Coherence*Web involves the following steps.

  • start Coherence*Web cache server 
  • create and configure managed servers
    • deploy Coherence*Web as a shared library to the managed servers
  • create and deploy the web service
    • stores state in http session
    • instrument the web service using Coherence*Web
    • deploy the web service to both managed servers
Start Coherence*Web Cache Server

Start Coherence*Web cache server using the command below [2].

java -server -Xms512m -Xmx512m 
-cp coherence/lib/coherence.jar:coherence/lib/coherence-web-spi.war
-Dtangosol.coherence.management.remote=true
-Dtangosol.coherence.cacheconfig=WEB-INF/classes/session-cache-config.xml
-Dtangosol.coherence.session.localstorage=true
com.tangosol.net.DefaultCacheServer

Create and Configure Managed Servers

Copy coherence/lib/coherence.jar [2] to $DOMAIN_HOME/lib.

Start WebLogic AdminServer using

$DOMAIN_HOME/bin/startWebLogic.sh

Create two managed servers Server-2 at port 7012 and Server-3 at port 7013 using the admin console. Start both servers using the commands below.

$DOMAIN_HOME/bin/startManagedWebLogic.sh Server-2
$DOMAIN_HOME/bin/startManagedWebLogic.sh Server-3

Deploy coherence\lib\coherence-web-api.war [2] to the servers as a shared library. 

Create and Deploy the Web Service

Install Oracle Enterprise Pack for Eclipse (OEPE) on Eclipse Galileo [4]. In Eclipse, create a web service using the following WSDL and implementation.

<definitions
 xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
 xmlns:tns="http://oracle.ws.demo/"
 xmlns:xsd="http://www.w3.org/2001/XMLSchema"
 xmlns="http://schemas.xmlsoap.org/wsdl/"
 targetNamespace="http://oracle.ws.demo/"
 name="StatefulWSService">
<types>
<xsd:schema>
<xsd:import namespace="http://oracle.ws.demo/"
 schemaLocation="http://localhost:7001/Stateful_WS_Coherence/StatefulWSService?xsd=1" />
</xsd:schema>
</types>
<message name="addItem">
<part name="parameters" element="tns:addItem" />
</message>
<message name="addItemResponse">
<part name="parameters" element="tns:addItemResponse" />
</message>
<portType name="StatefulWS">
<operation name="addItem">
<input message="tns:addItem" />
<output message="tns:addItemResponse" />
</operation>
</portType>
<binding name="StatefulWSPortBinding" type="tns:StatefulWS">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document" />
<operation name="addItem">
<soap:operation soapAction="" />
<input>
<soap:body use="literal" />
</input>
<output>
<soap:body use="literal" />
</output>
</operation>
</binding>
<service name="StatefulWSService">
<port name="StatefulWSPort" binding="tns:StatefulWSPortBinding">
<soap:address
 location="http://localhost:7001/Stateful_WS_Coherence/StatefulWSService" />
</port>
</service>
</definitions>

StatefulWS.java and Item.java

package demo.ws.oracle;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.*;
import javax.xml.ws.handler.MessageContext;
import javax.jws.*;
import javax.annotation.Resource;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*
@WebService
public class StatefulWS {
@Resource
WebServiceContext ctx;
@WebMethod()
public int addItem(String name) {
HttpServletRequest req = (HttpServletRequest)
ctx.getMessageContext().get(MessageContext.SERVLET_REQUEST);
HttpSession session = req.getSession(true);
if (session == null)
throw new WebServiceException("No HTTP Session found");
System.out.println("httpsession: id="+session.getId());

//Get the cart object from the HttpSession
List<Item> cart = (List<Item>)session.getAttribute("myCart");
if (cart == null)
{ cart = new ArrayList(); }
// Add the item to the cart
cart.add(new Item(name));
// Save the updated cart in the HTTPSession
session.setAttribute("myCart", cart);
// return the number of items in the stateful cart
return cart.size();
}
}
package demo.ws.oracle;
public class Item implements java.io.Serializable {
public String name;public Item(String name){
this.name = name;
}
}

Add coherence-web-api library into weblogic.xml [2].

<weblogic-web-app
 xmlns="http://xmlns.oracle.com/weblogic/weblogic-web-app"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://xmlns.oracle.com/weblogic/weblogic-web-app
 http://xmlns.oracle.com/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd"
>
<weblogic-version>10.3.1</weblogic-version>
<context-root>Stateful_WS_Coherence</context-root>
<library-ref>
<library-name>coherence-web-spi</library-name>
<specification-version>1.0.0.0</specification-version>
<implementation-version>1.0.0.0</implementation-version>
<exact-match>false</exact-match>
</library-ref>
</weblogic-web-app>

Deploy the web service to both managed servers.

Test 

Run the test.

package demo.ws;
import demo.ws.oracle.*;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;

public class StatefulWSClient {
public static void main(String[] args) throws Exception {
StatefulWSService service =
new
StatefulWSService(Thread.currentThread().getContextClassLoader()
.getResource("META-INF/wsdls/StatefulWSService.wsdl")
 , new QName("http://oracle.ws.demo/", "StatefulWSService"));
StatefulWS port = service.getStatefulWSPort();

((BindingProvider)port).getRequestContext()
.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true);

((BindingProvider)port).getRequestContext()
.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"http://stacd15.us.oracle.com:7012/Stateful_WS_Coherence/StatefulWSService");

System.out.println("calling " +
((BindingProvider)port).getRequestContext()
.get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY));
for (int j=0; j<5; j++) {
try {
int response = port.addItem("abc");
System.out.println("Got " + response);
Thread.currentThread().sleep(3000);
} catch (Exception ex) {}
}

((BindingProvider)port).getRequestContext()
.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
"http://stacd15.us.oracle.com:7013/Stateful_WS_Coherence/StatefulWSService");
System.out.println("calling " +
((BindingProvider)port).getRequestContext()
.get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY));
for (int j=0; j<5; j++) {
try {
int response = port.addItem("abc");
System.out.println("Got " + response);
Thread.currentThread().sleep(3000);
} catch (Exception ex) {}
}
}
}

The test invokes the web service five times for each endpoint.  The output shows that the returned count increases, indicating synchronized web service state across both endpoints.

calling http://stacd15.us.oracle.com:7012/Stateful_WS_Coherence/StatefulWSService
Got 1
Got 2
Got 3
Got 4
Got 5
calling http://stacd15.us.oracle.com:7013/Stateful_WS_Coherence/StatefulWSService
Got 6
Got 7
Got 8
Got 9
Got 10

Both managed servers output the same http session id's.

httpsession: id=tS1DNs2XTJZuQNhNfsl4fvpuj0Qd2ZnmTmHFKtKlUX4EytukR9V0!-280396885!1257829391923
httpsession: id=tS1DNs2XTJZuQNhNfsl4fvpuj0Qd2ZnmTmHFKtKlUX4EytukR9V0!-280396885!1257829391923
httpsession: id=tS1DNs2XTJZuQNhNfs14fvpuj0Qd2ZnmTmHFKtKlUX4EytukR9V0!-280396885!1257829391923
httpsession: id=tS1DNs2XTJZuQNhNfsl4fvpuj0Qd2ZnmTmHFKtKlUX4EytukR9V0!-280396885!1257829391923

Reference
  1. Coherence*Web and WebLogic Server
  2. Installing Coherence*Web on the WebLogic Server 10.3
  3. Stateful JAX-WS
  4. Oracle Enterprise Pack for Eclipse

Continue reading "Stateful JAX-WS with Coherence*Web " »

November 10, 2009

Developing custom MBeans to manage J2EE Applications (Part II)

This is the second part in a series of blogs, that demonstrate how to add management capability to your own application using JMX MBeans.

In Part I we did the bulk of the work. We saw:

  • How to implement a custom MBean to manage configuration associated with an application.
  • How to package the resulting code and configuration as part of the application's ear file.
  • How to register MBeans upon application startup, and unregistered them upon application stop (or undeployment).
  • How to use generic JMX clients such as JConsole to browse and edit our application's MBean.

In this second part, we will add descriptions to our:

  • MBean
  • MBean attributes
  • MBean operations
  • MBean operation parameters

We saw using JConsole, that default descriptions were generated for all of the above. However those default descriptions didn't provide any meaningful information to our MBean users. We will replace those default descriptions with custom descriptions that can be used by us human to understand the functionality provided by our MBean.

We will also add localization support to our MBean. Our goal is to ensure that our MBean's meta-data is localized based on the WebLogic Server it is deployed to. So if that server uses a French Locale, then we'd expect our MBeans descriptions to be French and not English.

The complete code sample and associated build files for part II are available as a zip file. The code has been tested against WebLogic Server 10.3.1 and JDK6. To build and deploy our sample application, please follow the instruction provided in Part I, as they also apply to part II's code and associated zip file.

Providing custom descriptions

In order to provide custom description we need to modify our MBean implementation to extend the StandardMBean class. Despite its name that class is used to implement both Standard and MXBeans.

We override the StandardMBean class many getDescription methods to provide our own custom descriptions. We use resources bundles to store our descriptions to ensure that those can be properly localized. The updated code for our MBean implementation is included below:

package blog.wls.jmx.appmbean;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.File;

import java.net.URL;

import java.util.Map;
import java.util.HashMap;
import java.util.Properties;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.MBeanRegistration;
import javax.management.StandardMBean;
import javax.management.MBeanInfo;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;

public class PropertyConfig extends StandardMBean implements
    PropertyConfigMXBean, MBeanRegistration {

    private String relativePath_ = null; 

    private Properties props_ = null;

    private File resource_ = null;

    private ResourceBundle resourceBundle_ = null;

    private static Map operationsParamNames_ = null;

    static {
        operationsParamNames_ = new HashMap();
        operationsParamNames_.put("setProperty", new String[] {"key", "value"});
        operationsParamNames_.put("getProperty", new String[] {"key"});
    }

    public PropertyConfig(String relativePath) throws Exception {
        super(PropertyConfigMXBean.class , true);
        props_ = new Properties();
        relativePath_ = relativePath;
    }

    public String setProperty(String key,
                              String value) throws IOException {

        String oldValue = null;

        if (value == null) {
            oldValue = String.class.cast(props_.remove(key));
        } else {
            oldValue = String.class.cast(props_.setProperty(key, value));      
        }

        save();
        return oldValue;
    }

    public String getProperty(String key) {
        return props_.getProperty(key);
    }

    public Map getProperties() {
        return (Map) props_;
    }

    private void load() throws IOException {
        
        InputStream is = new FileInputStream(resource_);
        try {
            props_.load(is);
        }
        finally {
            is.close();
        }
    } 

    private void save() throws IOException {
  
        OutputStream os = new FileOutputStream(resource_);

        try {
            props_.store(os, null);
        }
        finally {
            os.close();
        }
    }

    public ObjectName preRegister(MBeanServer server, ObjectName name)
        throws Exception {
       
        // MBean must be registered from an application thread
        // to have access to the application ClassLoader
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        URL resourceUrl = cl.getResource(relativePath_);
        resource_ = new File(resourceUrl.toURI());

        load();     

        return name;
    }

    public void postRegister(Boolean registrationDone) { }

    public void preDeregister() throws Exception {}

    public void postDeregister() {}

    private synchronized ResourceBundle getResourceBundle() {
   
        if ( resourceBundle_ == null ) {
            resourceBundle_ = 
                PropertyResourceBundle.getBundle(
                    "blog.wls.jmx.appmbean.MBeanDescriptions");
        }
       
        return resourceBundle_;
    } 

    protected String getDescription(MBeanAttributeInfo info) { 
        return getResourceBundle().getString("PropertyConfigMXBean.attribute." + 
            info.getName() ); 
    } 

    protected String getDescription(MBeanOperationInfo info) { 
        return getResourceBundle().getString("PropertyConfigMXBean.operation." + 
            info.getName() ); 
    }   

    protected String getDescription(MBeanInfo info) {
        return getResourceBundle().getString("PropertyConfigMXBean.mbean");
    }

    protected String getDescription(MBeanOperationInfo op,
                                    MBeanParameterInfo param,
                                    int sequence) {
        return getResourceBundle().getString(
            "PropertyConfigMXBean.operation." + 
            op.getName() + "." + 
            operationsParamNames_.get(op.getName())[sequence] ); 
    }

    protected String getParameterName(MBeanOperationInfo op,
                                      MBeanParameterInfo param,
                                      int sequence) {
        return operationsParamNames_.get(op.getName())[sequence];
    } 

}

Our default resource bundle class, that contains our English descriptions contains the following code:

package blog.wls.jmx.appmbean;

import java.util.ListResourceBundle;

public class MBeanDescriptions extends ListResourceBundle {
     protected Object[][] getContents() {
         return new Object[][] {
             {"PropertyConfigMXBean.mbean", 
              "MBean used to manage persistent application properties"},  
             {"PropertyConfigMXBean.attribute.Properties", 
              "Properties associated with the running application"},
             {"PropertyConfigMXBean.operation.setProperty", 
              "Create a new property, or change the value of an existing property"},
             {"PropertyConfigMXBean.operation.setProperty.key", 
              "Name that identify the property to set."},
             {"PropertyConfigMXBean.operation.setProperty.value", 
              "Value for the property being set"},
             {"PropertyConfigMXBean.operation.getProperty", 
              "Get the value for an existing property"}, 
             {"PropertyConfigMXBean.operation.getProperty.key", 
              "Name that identify the property to be retrieved"} 
        };
     }
 }

Our MBean is quite simple, and only exposes one attribute and two operations. This helps keep our resource bundle class quite small. For real world example that file will be much bigger. Note: We didn't override the getDescription method associated with our MBean constructor, to keep our sample small as this doesn't add much value to our discussion.

To add support for other languages, we only need to implement the corresponding resource bundle class. MBeanDescriptions_fr for French, and translate the description appropriately.

One interesting thing to note, is the name we used for our resource bundle keys. We didn't use arbitrary values, but we made sure we followed the following convention:

  • MBean description: <MBeanInterfaceClass>.mbean
  • MBean attribute description: <MBeanInterfaceClass>.attribute.<AttributeName>
  • MBean operation description: <MBeanInterfaceClass>.operation.<OperationName>
  • MBean operation parameter description: <MBeanInterfaceClass>.operation.<OperationName>.<ParameterName>
  • MBean constructor description: <MBeanInterfaceClass>.constructor.<ConstructorName>
  • MBean constructor parameter description: <MBeanInterfaceClass>.constructor.<ConstructorName>.<ParameterName>
We also purposely named our resource bundle class MBeanDescriptions and included it as part of the same package as our MBean. The above convention is used by the JDK 7 to localize MBean descriptions without requiring us to extend the StandardMBean class and override its many getDescription methods. Unfortunately JDK 6 doesn't support built-in JMX localization, so we have to write the above code. However we can anticipate the JDK 7 functionality (and possible early support by WebLogic) by using the above convention when specifying our MBean resource bundle and associated resource keys.

You might have noticed the following code in our updated MBean implementatrion:

    protected String getParameterName(MBeanOperationInfo op,
                                      MBeanParameterInfo param,
                                      int sequence) {
        return operationsParamNames_.get(op.getName())[sequence];
    } 
This is used to provide customized name to our operation parameters in place of the default generated 'po', 'p1', ... , 'pn' values.

The result of our hard work can be seen in the following JConsole screen shot:

app_mbean_part2_jconsole1.JPG

Consult Part I for information on how to use JConsole to browse/edit our MBean.

What's next?

What if our application is deployed to a WebLogic server running with an English Locale, and our management client wants to use a French Locale. Currently as things stand the Localization is performed based on the server Locale, and not based on the client's Locale. In the last part of this blog series, we will see how to associate a Locale with our JMX client connection, and we will update our MBean code to support client based localization.

About November 2009

This page contains all entries posted to WebLogic Server in November 2009. They are listed from oldest to newest.

October 2009 is the previous archive.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type and Oracle