Developing custom MBeans to manage J2EE Applications (Part III)

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

In Part I 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 Part II we saw:

  • How to add localized descriptions to our MBean, MBean attributes, MBean operations and MBean operation parameters.
  • How to specify meaningful name to our MBean operation parameters.
  • We also touched on future enhancements that will simplify how we can implement localized MBeans.

In this third and last part, we will re-write our MBean to simplify how we added localized descriptions. To do so we will take advantage of the functionality we already described in part II and that is now part of WebLogic 10.3.3.0.

We will show how to take advantage of WebLogic's localization support to localize our MBeans based on the client's Locale independently of the server's Locale. Each client will see MBean descriptions localized based on his/her own Locale. We will show how to achieve this using JConsole, and also using a sample programmatic JMX Java client.

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

Providing custom descriptions take II

In part II we localized our MBean descriptions by extending the StandardMBean class and overriding its many getDescription methods. WebLogic 10.3.3.0 similarly to JDK 7 can automatically localize MBean descriptions as long as those are specified according to the following conventions:

Descriptions resource bundle keys are named according to:

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

We already followed the above conventions when creating our resource bundle in part II, and our default resource bundle class with English descriptions looks like:

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"} 
        };
     }
 }

We have now also added a resource bundle with French localized descriptions:

package blog.wls.jmx.appmbean;
 
import java.util.ListResourceBundle;
 
public class MBeanDescriptions_fr extends ListResourceBundle {
     protected Object[][] getContents() {
         return new Object[][] {
             {"PropertyConfigMXBean.mbean", 
              "Manage proprietes sauvegarde dans un fichier disque."},  
             {"PropertyConfigMXBean.attribute.Properties", 
              "Proprietes associee avec l'application en cour d'execution"},
             {"PropertyConfigMXBean.operation.setProperty", 
              "Construit une nouvelle proprietee, ou change la valeur d'une proprietee existante."},
             {"PropertyConfigMXBean.operation.setProperty.key", 
              "Nom de la propriete dont la valeur est change."},
             {"PropertyConfigMXBean.operation.setProperty.value", 
              "Nouvelle valeur"},
             {"PropertyConfigMXBean.operation.getProperty", 
              "Retourne la valeur d'une propriete existante."}, 
             {"PropertyConfigMXBean.operation.getProperty.key", 
              "Nom de la propriete a retrouver."} 
        };
     }
 }

So now we can just remove the many getDescriptions methods from our MBean code, and have a much cleaner:

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 javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.MBeanRegistration;
import javax.management.StandardMBean;
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 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() {}
 
    protected String getParameterName(MBeanOperationInfo op,
                                      MBeanParameterInfo param,
                                      int sequence) {
        return operationsParamNames_.get(op.getName())[sequence];
    } 
 
}

The only reason we are still extending the StandardMBean class, is to override the default values for our operations parameters name. If this isn't a concern, then one could just write the following code:

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.Properties;
 
import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.MBeanRegistration;
import javax.management.StandardMBean;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
 
public class PropertyConfig implements
    PropertyConfigMXBean, MBeanRegistration {
 
    private String relativePath_ = null; 
 
    private Properties props_ = null;
 
    private File resource_ = null;
 
    public PropertyConfig(String relativePath) throws Exception {
        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() {}
}

Note: The above would also require changing the operations parameters name in the resource bundle classes. For instance: PropertyConfigMXBean.operation.setProperty.key would become: PropertyConfigMXBean.operation.setProperty.p0

Client based localization

When accessing our MBean using JConsole started with the following command line:

jconsole -J-Djava.class.path=$JAVA_HOME/lib/jconsole.jar:$JAVA_HOME/lib/tools.jar: $WL_HOME/server/lib/wljmxclient.jar -J-Djmx.remote.protocol.provider.pkgs=weblogic.management.remote -debug

We see that our MBean descriptions are localized according to the WebLogic's server Locale. English in this case:

app_mbean_part2_jconsole1.JPG

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

Now if we specify the client's Locale as part of the JConsole command line as follow:

jconsole -J-Djava.class.path=$JAVA_HOME/lib/jconsole.jar:$JAVA_HOME/lib/tools.jar: $WL_HOME/server/lib/wljmxclient.jar -J-Djmx.remote.protocol.provider.pkgs=weblogic.management.remote -J-Dweblogic.management.remote.locale=fr-FR -debug

We see that our MBean descriptions are now localized according to the specified client's Locale. French in this case:

app_mbean_part3_jconsole1.JPG

We use the weblogic.management.remote.locale system property to specify the Locale that should be associated with the cient's JMX connections. The value is composed of the client's language code and its country code separated by the - character. The country code is not required, and can be omitted. For instance: -Dweblogic.management.remote.locale=fr

We can also specify the client's Locale using a programmatic client as demonstrated below:

package blog.wls.jmx.appmbean.client;
 
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.MBeanInfo;
 
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.JMXConnectorFactory;
     
import java.util.Hashtable;
import java.util.Set;
import java.util.Locale;
 
public class JMXClient {
 
    public static void main(String[] args) throws Exception {
 
        JMXConnector jmxCon = null;
 
        try {
 
            JMXServiceURL serviceUrl = 
               new JMXServiceURL(
        "service:jmx:iiop://127.0.0.1:7001/jndi/weblogic.management.mbeanservers.runtime");          
 
            System.out.println("Connecting to: " + serviceUrl);
 
            // properties associated with the connection
            Hashtable env = new Hashtable();
 
            env.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES, 
                         "weblogic.management.remote");
 
            String[] credentials = new String[2];
            credentials[0] = "weblogic";
            credentials[1] = "weblogic";
            env.put(JMXConnector.CREDENTIALS, credentials);
 
             // specifies the client's Locale 
            env.put("weblogic.management.remote.locale", Locale.FRENCH);
 
            jmxCon = JMXConnectorFactory.newJMXConnector(serviceUrl, env);
            jmxCon.connect();
 
            MBeanServerConnection con = jmxCon.getMBeanServerConnection();
 
            Set mbeans = 
                con.queryNames(
                    new ObjectName(
                      "blog.wls.jmx.appmbean:name=myAppProperties,type=PropertyConfig,*"),
                      null);
 
            for (ObjectName mbeanName : mbeans) {
                System.out.println("\n\nMBEAN: " + mbeanName);
 
                MBeanInfo minfo = con.getMBeanInfo(mbeanName);
 
                System.out.println("MBean Description: "+minfo.getDescription());
                System.out.println("\n");
            }
        }
        finally {
            // release the connection
            if (jmxCon != null)
                jmxCon.close();
        }
    }
}

The above client code is part of the zip file associated with this blog, and can be run using the provided client.sh script. The resulting output is shown below:


$ ./client.sh
Connecting to: service:jmx:iiop://127.0.0.1:7001/jndi/weblogic.management.mbeanservers.runtime
 
MBEAN: blog.wls.jmx.appmbean:type=PropertyConfig,name=myAppProperties
MBean Description: Manage proprietes sauvegarde dans un fichier disque.
 
$ 

Miscellaneous

Using Description annotation to specify MBean descriptions

Earlier we have seen how to name our MBean descriptions resource keys, so that WebLogic 10.3.3.0 automatically uses them to localize our MBean. In some cases we might want to implicitly specify the resource key, and resource bundle. For instance when operations are overloaded, and the operation name is no longer sufficient to uniquely identify a single operation. In this case we can use the Description annotation provided by WebLogic as follow:

   import weblogic.management.utils.Description;
 
   @Description(resourceKey="myapp.resources.TestMXBean.description",
                resourceBundleBaseName="myapp.resources.MBeanResources")
   public interface TestMXBean {
 
      @Description(resourceKey="myapp.resources.TestMXBean.threshold.description",
                   resourceBundleBaseName="myapp.resources.MBeanResources"  )
      public int getthreshold();
 
      @Description(resourceKey="myapp.resources.TestMXBean.reset.description",
                   resourceBundleBaseName="myapp.resources.MBeanResources")
      public int reset(
          @Description(resourceKey="myapp.resources.TestMXBean.reset.id.description",
                       resourceBundleBaseName="myapp.resources.MBeanResources",
                       displayNameKey=
                       "myapp.resources.TestMXBean.reset.id.displayName.description")
          int id);
  }

The Description annotation should be applied to the MBean interface. It can be used to specify MBean, MBean attributes, MBean operations, and MBean operation parameters descriptions as demonstrated above.

Retrieving the Locale associated with a JMX operation from the MBean code

There are several cases where it is necessary to retrieve the Locale associated with a JMX call from the MBean implementation. For instance this can be useful when localizing exception messages. This can be done as follow:

   import weblogic.management.mbeanservers.JMXContextUtil;
 
   ......
   // some MBean method implementation
   public String setProperty(String key, String value) throws IOException {
 
       Locale callersLocale = JMXContextUtil.getLocale();    
 
       // use callersLocale to localize Exception messages or
       // potentially some return values such a Date ....
   }

Conclusion

With this last part we conclude our three part series on how to write MBeans to manage J2EE applications. We are far from having exhausted this particular topic, but we have gone a long way and are now capable to take advantage of the latest functionality provided by WebLogic's application server to write user friendly MBeans.

Comments:

Enjoying the JMX articles. The one I keep waiting for is an example of how to get your custom MBeans to show up and be manageable in the weblogic admin console. Can't seem to find any examples anywhere in docs nor on the web. I think that would be a great topic for your next JMX post.

Posted by Padraic on March 23, 2010 at 10:42 PM PDT #

Thanks for the kind words, I will keep your idea in mind for a future topic.

Posted by philippe Le Mouel on March 23, 2010 at 11:38 PM PDT #

Hello, could you touch on how to secure the MBean? I have followed your example and I'm able to alter the config settings while the JVM is running. However, I really need to secure access to the MBean before this can move into a prod environment. Thanks

Posted by Don on April 27, 2010 at 11:03 PM PDT #

Hi philippe, I have extends StandardEmitterMBean as you suggested in the (Part II) blog in the reply of my question. I wrote my custom MBean who emits AttributeChange notification. The MBean code is as follows: --------- Interface --------- public interface MyCustomMBean { public String getSomething(); public void setSomething(String str) throws IOException; } -------------------- Implementation class --------------------- public class MyCustomMBeanImpl extends StandardEmitterMBean implements MyCustomMBean { String something= "hello"; private static final MBeanNotificationInfo info = new MBeanNotificationInfo( new String[] {javax.management.AttributeChangeNotification.ATTRIBUTE_CHANGE }, AttributeChangeNotification.class.getName(), "Indicates that an attribute was changed" ); protected MyCustomMBeanImpl() { super(MyCustomMBean.class, new NotificationBroadcasterSupport(info)); } public String getSomething() { return something; } public void setSomething(String str)throws IOException { String oldValue = null; something = str; if(oldValue == null) { oldValue = getSomething(); }else { oldValue = str; } AttributeChangeNotification acn = new AttributeChangeNotification(this, 0, System.currentTimeMillis(), "Atrribute something got changed", "something", "String", oldValue, str); sendNotification(acn); } ----------------------- MBean Registration class ------------------------ I've registered this MBean with ApplicationLifeCycleListener public class RegisterCustomMBean extends ApplicationLifecycleListener { // register the mbean when application starts. public void postStart(weblogic.application.ApplicationLifecycleEvent p1) { try { ObjectName mymbean = new ObjectName("mycustommbean:Name=MyCustomMBean,Type=MyCustomMBean"); String classname = "weblogic.MyCustomMBean"; InitialContext ctx = new InitialContext(); MBeanServer server = (MBeanServer)ctx.lookup("java:comp/jmx/runtime"); MyCustomMBean mbean = new MyCustomMBeanImpl(); server.registerMBean(mbean, mymbean); } catch (Exception e) { e.printStackTrace(); } } // unregister the mbean when the application stops. public void preStop(weblogic.application.ApplicationLifecycleEvent p1) { InitialContext ctx; try { ObjectName mymbean = new ObjectName("mycustommbean:Name=MyCustomMBean,Type=MyCustomMBean"); ctx = new InitialContext(); MBeanServer server = (MBeanServer)ctx.lookup("java:comp/jmx/runtime"); server.unregisterMBean(mymbean); } catch (Exception e) { e.printStackTrace(); } } My MBean got registered successfully but I'm not able to listen notification from client program. My client program keeps waiting forever but does not receive notification. ------------------- Client Program ------------------ public class MyQueryCustomMBean { private static JMXConnector jmxConnector; private static MBeanServerConnection mbsc; private static void queryCustom() throws IOException { Set mbeans = mbsc.queryNames(null, null); for (ObjectName objectName : mbeans) { //if(objectName.toString().indexOf("mycustommbean") >= 0) { if(objectName.toString().startsWith("mycustommbean")) { System.out.println("Mbean Name = "+objectName.toString()); listenChangeNotification(objectName); } } } private static void listenChangeNotification(ObjectName mbName) { MyAttributeChangeListenerImpl listener = new MyAttributeChangeListenerImpl(); try { mbsc.addNotificationListener(mbName, listener, null, null); System.out.println("\n[myListener]: Listener registered with " +mbName); synchronized(listener) { System.out.println("Waiting for notification..."); listener.wait(); } } catch (InstanceNotFoundException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); }catch(Exception ex) { ex.printStackTrace(); } } public static void initRemoteConnection() throws IOException { JMXServiceURL serviceURL = new JMXServiceURL("service:jmx:t3://localhost:7005/jndi/weblogic.management.mbeanservers.domainruntime"); System.out.println("Connecting to : " +serviceURL); HashMap env = new HashMap(); env.put(JMXConnectorFactory.PROTOCOL_PROVIDER_PACKAGES, "weblogic.management.remote"); env.put(javax.naming.Context.SECURITY_PRINCIPAL, "weblogic"); env.put(javax.naming.Context.SECURITY_CREDENTIALS, "welcome123"); jmxConnector = JMXConnectorFactory.connect(serviceURL, env); mbsc = jmxConnector.getMBeanServerConnection(); } public static void main(String[] args) { try { initRemoteConnection(); queryCustom(); } catch (IOException e) { e.printStackTrace(); } } } ----------------------------- Output of Client this Program ----------------------------- Connecting to : service:jmx:t3://localhost:7005/jndi/weblogic.management.mbeanservers.domainruntime Mbean Name = mycustommbean:Name=MyCustomMBean,Location=MS3,Type=MyCustomMBean [myListener]: Listener registered with mycustommbean:Name=MyCustomMBean,Location=MS3,Type=MyCustomMBean Waiting for notification... What is way to listen notifications from already registered Custom MBeans ? Is there any specific serviceURL for connecting Custom MBean ? Am I missing something here ? Please guide. Thanks

Posted by Jyotisekhar on June 30, 2010 at 05:10 AM PDT #

The modification you made to the MBean code seems right, and I verified that it is working as expected for me. What I would do is verify that the custom MBean properly emits notification. You can use JConsole to do that. Just connect using JConsole see the following blog for more info: http://blogs.oracle.com/WebLogicServer/2009/10/managing_weblogic_servers_with.html under the custom MBean "Notifications" tab you should be able to register a listener for the MBean, and after that all notifications emitted by that MBean will be recorded and displayed. This is how I tested that my suggested changed worked. Philippe

Posted by philippe Le Mouel on June 30, 2010 at 06:06 AM PDT #

It seems that everything changes in the world of fashion quickly in all new things. Imitation Designer Handbags, purses, jewelry and even forms that have never seen before, and they are looking both had a more cordial reception. Increasingly, new ideas take shape, and although the traditional gold and silver are still there, everything is now possible, from wood to stone, ceramics, plastics, including glass and in almost everything that could be good, fashion.

Posted by Imitation Designer Handbags on October 27, 2010 at 05:08 AM PDT #

Great blog here! Also your web site loads up very fast! What host are you using? Can I get your affiliate link to your host? I wish my web site loaded up as quickly as yours lol

Posted by frostwire on February 02, 2011 at 07:51 AM PST #

Nice to be visiting your blog again, it has been months for me. Well this article that i've been waited for so long. I need this article to complete my assignment in the college, and it has same topic with your article. Thanks, great share.

Posted by designer evening dresses on February 28, 2011 at 12:26 PM PST #

Hello. Great job. I did not expect this on a Wednesday. This is a great story. Thanks!

Posted by horror movie dvds on March 03, 2011 at 02:04 PM PST #

Thanks for such a great post and the review, I am totally impressed! Keep stuff like this coming.

Posted by Rado Watches on March 03, 2011 at 03:04 PM PST #

Not a unhealthy post in the least!

Posted by Gregory Despain on March 03, 2011 at 03:38 PM PST #

Pretty section of content. I just stumbled upon your site and in accession capital to assert that I acquire in fact enjoyed account your blog posts. Any way I will be subscribing to your augment and even I achievement you access consistently quickly.

Posted by Car Care on March 04, 2011 at 06:42 AM PST #

I like this post. I found out something new about headache today.

Posted by Migraine Zanzibarios on March 07, 2011 at 12:19 PM PST #

You completed various fine points there. I did a search on the topic and found most folks will agree with your blog.

Posted by zero friction marketing on March 07, 2011 at 06:59 PM PST #

*Aw, this was a really nice post. In idea I would like to put in writing like this additionally – taking time and actual effort to make a very good article… but what can I say… I procrastinate alot and by no means seem to get something done.

Posted by read more on March 13, 2011 at 08:06 AM PDT #

Hi, do you have a facebook profile?

Posted by sacsamainvente on April 01, 2011 at 05:57 PM PDT #

What I love about this breakdown of Pujols swing is the guy is talking about his back foot slightly lifting off the ground which gives so much power and makes your hips stay closed for those outside pitches. Your back foot and lower half is the most underrated part of your swing and is usually forgotten as your progress through the high school and college levels. Remember the basics and fundamentals should never be forgotten as you progress through your baseball careers.

Posted by website design on April 14, 2011 at 09:07 AM PDT #

Nice post! You truly have a wonderful way of writing which I find captivating! I will definitely be bookmarking you and returning to your blog. In fact, your post reminded me about a strange thing that happened to me the other day. I’ll tell you about that later…

Posted by Leanne Shulthess on April 17, 2011 at 08:56 AM PDT #

Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away.

Posted by Kelsey Wheeley on April 26, 2011 at 05:15 AM PDT #

great effor from u

Posted by Free games downloads on August 13, 2011 at 02:43 AM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

The official blog for Oracle WebLogic Server fans and followers!

Stay Connected

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
5
6
7
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today