Administering WebSphere Using JMX

Although administering WebSphere is best done using the Wsadmin API (with scripting languages like Jython or Jacl), there may be situations when this is not a good option. One example is when your application is already using Java and calling out to an external program (wsadmin.sh) would make the application run slower or make your integration cumbersome.

If your application is already using Java, naturally, it’s best that you also use Java to administer WebSphere. And the recommended way of doing this is through the use of the JMX API for Websphere MBeans. Below is an example illustrating how to use Websphere’s JMX API to retrieve memory information of an application server JVM. The code below is composed of two classes (WASAdminClient.java,  JavaVirtualMachine.java) and a property file (soap_client.properties).

The key to the use of Websphere’s JMX API is to obtain a JMX client connection to your WebSphere’s Application Server and then use this connection to access WebSphere’s JMX MBean Interfaces.

The first step in the process is to establish a JMX client connection. This is the goal of the WASAdminClient class. This is achieved by obtaining the AdminClientFactory.createAdminClient(Properties props) object. The Properties object passed to the method contains the credentials and trustore information required to connect to your Websphere Application Server. The credentials are obtained from the soap_client.properties file while the trustore information is obtained from the files generated by Websphere.

WASAdminClient.java would have been simpler if security is not enabled. If security is not enabled in Websphere, credentials and trustore information won’t be needed to obtain the AdminClient object. However, in a real production setup, this is rarely done so I’d like to also show this process. This is probably the most crucial information you would want to hear. Using WebSphere’s JMX MBeans Interface is straightforward, however, getting the WAS AdminClient object with security enabled is what stumped a lot of people. As you can see in the code below, obtaining the adminclient is not as simple as passing the username and password. It also requires the ssl trustStore and keyStore files from a WebSphere installation. The other thing is that you just can’t get these files out of nowhere. They need to be validly generated from a WebSphere installation. Note: The WebSphere installation doesn’t need to be the server where you want to connect.

The WASAdminClient class has two options to obtain the username and password information. These can be obtained from the soap_client.properties file or from Websphere’s CONNECTOR_SOAP_CONFIG file.

Once you have an AdminClient object connection, you can now use this object to access WebSphere’s JMX MBean Interfaces. The JavaVirtualMachine class below demonstrates how to access some memory information of an application server like heap size, maximum memory, etc.

I have also included below a JUnit test case for the JavaVirtualMachine class. Several test cases are not implemented. I have only provided a couple just to show examples on how to use the two Java classes.

Alvin Abad


"WASAdminClient.java"
package admin.service;

import java.util.Properties;
import java.util.ResourceBundle;

import com.ibm.websphere.management.AdminClient;
import com.ibm.websphere.management.AdminClientFactory;
import com.ibm.websphere.management.exception.ConnectorException;

/**
 * Websphere JMX AdminClient Service
 * @author Alvin Abad
 *
 */
public class WASAdminClient {
	private String hostname = "localhost";    // default
	private String port = "8879";             // default

	private String username;
	private String password;
	private String connector_security_enabled;
	private String connector_soap_config;
	private String ssl_trustStore;
	private String ssl_keyStore;
	private String ssl_trustStorePassword;
	private String ssl_keyStorePassword;

	private ResourceBundle soapClient;
	private String soap_client_properties;

	private AdminClient adminClient;

	public WASAdminClient() throws ConnectorException {
		soap_client_properties = "soap_client";
	}

	public WASAdminClient(String soap_client_properties) throws ConnectorException {
		this.soap_client_properties = soap_client_properties;
	}

	public AdminClient getAdminClient() {
		return adminClient;
	}

	public AdminClient create() throws ConnectorException {
		getResourceBundle(soap_client_properties);

		Properties props = new Properties();
		props.setProperty(AdminClient.CONNECTOR_TYPE, AdminClient.CONNECTOR_TYPE_SOAP);
		props.setProperty(AdminClient.CONNECTOR_HOST, hostname);
		props.setProperty(AdminClient.CONNECTOR_PORT, port);
		props.setProperty(AdminClient.CACHE_DISABLED, "false");

		if (connector_security_enabled == "false") {
			adminClient = AdminClientFactory.createAdminClient(props);
			return adminClient;
		}

		props.setProperty(AdminClient.CONNECTOR_SECURITY_ENABLED, "true");
		props.setProperty(AdminClient.CONNECTOR_AUTO_ACCEPT_SIGNER, "true");
		props.setProperty("javax.net.ssl.trustStore", ssl_trustStore);
		props.setProperty("javax.net.ssl.keyStore", ssl_keyStore);
		props.setProperty("javax.net.ssl.trustStorePassword", ssl_trustStorePassword);
		props.setProperty("javax.net.ssl.keyStorePassword", ssl_keyStorePassword);

		// Use username and password or soap.client.props file
		if (username == null || password == null) {
			props.setProperty(AdminClient.CONNECTOR_SOAP_CONFIG, connector_soap_config);
		} else {
			props.setProperty(AdminClient.USERNAME, username);
			props.setProperty(AdminClient.PASSWORD, password);
		}

		adminClient = AdminClientFactory.createAdminClient(props);

		return adminClient;
	}

	public ResourceBundle getResourceBundle(String properties) {
		soapClient = ResourceBundle.getBundle(properties);

		hostname = soapClient.getString("hostname");
		port = soapClient.getString("port");
		connector_security_enabled = soapClient.getString("connector_security_enabled");
		ssl_trustStore = soapClient.getString("ssl_trustStore");
		ssl_keyStore = soapClient.getString("ssl_keyStore");
		ssl_trustStorePassword = soapClient.getString("ssl_trustStorePassword");
		ssl_keyStorePassword = soapClient.getString("ssl_keyStorePassword");

		if (soapClient.containsKey("connector_soap_config")) {
		    connector_soap_config = soapClient.getString("connector_soap_config");
		}

		if (soapClient.containsKey("username")) {
		    username = soapClient.getString("username");
		}

		if (soapClient.containsKey("password")) {
			password = soapClient.getString("password");
		}

		if (soapClient.containsKey("hostname")) {
			hostname = soapClient.getString("hostname");
		}

		if (soapClient.containsKey("port")) {
			port = soapClient.getString("port");
		}

		return soapClient;
	}

}

"JavaVirtualMachine.java"

package admin.service;

import java.util.Set;

import javax.management.AttributeNotFoundException;
import javax.management.InstanceNotFoundException;
import javax.management.MBeanException;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;

import com.ibm.websphere.management.AdminClient;
import com.ibm.websphere.management.exception.ConnectorException;

/**
 * Websphere JMX JavaVirtualMachine Service
 * @author Alvin Abad
 *
 */
public class JavaVirtualMachine {
	private String heapSize;
	private String maxMemory;
	private Integer maxHeapDumpsOnDisk;
	private String freeMemory;

	private ObjectName mBean;
	private Set mBeans;
	private AdminClient adminClient;

	public JavaVirtualMachine() {
	}

	public JavaVirtualMachine(AdminClient adminClient) {
		this.adminClient = adminClient;
	}

	public AdminClient getAdminClient() {
		return adminClient;
	}

	public void setAdminClient(AdminClient adminClient) {
		this.adminClient = adminClient;
	}

	public Set queryMBeans() throws MalformedObjectNameException,
                                         NullPointerException, ConnectorException {
		String query = "WebSphere:type=JVM,*";
		ObjectName queryName = new ObjectName(query);
		mBeans = adminClient.queryNames(queryName, null);

		return mBeans;
	}

	public Set getMBeans() {
		return mBeans;
	}

	public void setMBeans(Set mBeans) {
		this.mBeans = mBeans;
	}

	public ObjectName getMBean() {
		return mBean;
	}

	public void setMBean(ObjectName mBean) {
		this.mBean = mBean;
	}

	public String getHeapSize(ObjectName mBean) throws AttributeNotFoundException,
	                                   InstanceNotFoundException,
	                                   MBeanException, ReflectionException,
	                                   ConnectorException {
		heapSize = (String) adminClient.getAttribute(mBean, "heapSize");
		return heapSize;
	}

	public String getMaxMemory(ObjectName mBean) throws AttributeNotFoundException,
                                        InstanceNotFoundException, MBeanException,
                                        ReflectionException, ConnectorException {
		maxMemory = (String) adminClient.getAttribute(mBean, "maxMemory");
		return maxMemory;
	}

	public Integer getMaxHeapDumpsOnDisk(ObjectName mBean) throws AttributeNotFoundException,
                                       InstanceNotFoundException, MBeanException,
                                       ReflectionException, ConnectorException {
		maxHeapDumpsOnDisk = (Integer) adminClient.getAttribute(mBean, "maxHeapDumpsOnDisk");
		return maxHeapDumpsOnDisk;
	}

	public String getFreeMemory(ObjectName mBean) throws AttributeNotFoundException,
                                        InstanceNotFoundException, MBeanException,
                                        ReflectionException, ConnectorException {
		freeMemory = (String) adminClient.getAttribute(mBean, "freeMemory");
		return freeMemory;
	}

}

"soap_client.properties"

# Optional parameters
username = wasuser
password = secret
hostname = localhost
port = 8879

connector_security_enabled = true
#connector_soap_config = build/soap.client.props
ssl_trustStore = /usr/IBM/WebSphere/profiles/my_dmgr/etc/DummyClientTrustFile.jks
ssl_keyStore = /usr/IBM/WebSphere/profiles/my_dmgr/etc/DummyClientKeyFile.jks

ssl_trustStorePassword = WebAS
ssl_keyStorePassword = WebAS

"JavaVirtualMachineTest.java"

package admin.service;

import static org.junit.Assert.*;

import java.util.Set;

import javax.management.MalformedObjectNameException;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.ibm.websphere.management.AdminClient;
import com.ibm.websphere.management.exception.ConnectorException;

/**
 * JavaVirtualMachine Test Case
 * @author Alvin Abad
 */
public class JavaVirtualMachineTest {
	AdminClient adminClient;
	JavaVirtualMachine jvm;

	@Before
	public void setUp() throws Exception {
		WASAdminClient wasAdminClient = new WASAdminClient();
		adminClient = wasAdminClient.create();
	}

	@After
	public void tearDown() throws Exception {
	}

	@Test
	public void testJavaVirtualMachine() {
		jvm = new JavaVirtualMachine();
		assertNull(jvm.getAdminClient());
	}

	@Test
	public void testJavaVirtualMachineAdminClient() {
		jvm = new JavaVirtualMachine(adminClient);
		assertNotNull(jvm.getAdminClient());
		assertTrue(jvm.getAdminClient() instanceof AdminClient);
	}

	@Test
	public void testGetAdminClient() {
		jvm = new JavaVirtualMachine(adminClient);
		assertEquals(jvm.getAdminClient(), adminClient);
	}

	@Test
	public void testSetAdminClient() {
		jvm = new JavaVirtualMachine();
		jvm.setAdminClient(adminClient);
		assertEquals(jvm.getAdminClient(), adminClient);
	}

	@Test
	public void testQueryMBeans() throws MalformedObjectNameException,
                                   NullPointerException, ConnectorException {
		jvm = new JavaVirtualMachine();
		Set mBeans = jvm.queryMBeans();
		assertTrue(mBeans.size() > 0);
	}

	@Test
	public void testGetMBeans() {
		fail("Not yet implemented");
	}

	@Test
	public void testSetMBeans() {
		fail("Not yet implemented");
	}

	@Test
	public void testGetMBean() {
		fail("Not yet implemented");
	}

	@Test
	public void testSetMBean() {
		fail("Not yet implemented");
	}

	@Test
	public void testGetHeapSize() {
		fail("Not yet implemented");
	}

	@Test
	public void testGetMaxMemory() {
		fail("Not yet implemented");
	}

	@Test
	public void testGetMaxHeapDumpsOnDisk() {
		fail("Not yet implemented");
	}

	@Test
	public void testGetFreeMemory() {
		fail("Not yet implemented");
	}

}

Advertisements

A Simple Cross-Domain Ajax

In spite of the power of the XMLHttpRequest API, its usage is limited by the “same-origin” policy. What this means is that the hostname of the url you are sending the XMLHttpRequest cannot be different from the hostname of the web server.

Understandably, the reason behind this is security. However, there are legitimate reasons why you would need to call out to other domains, for example, you have a site with multiple domains or sub-domains and you would need to call XMLHttpRequest to these different domains.

One way to get around this issue is to let the server-side, on behalf of the client browser, perform the HTTP request to the external site. This functionality is best explained here. But this doesn’t sound like the true Ajax we imagined because we wanted to use the same idea of XMLHttpRequest where you can call out a URL from the browser, with the only difference that it can be a different domain.

So how does one perform an Ajax request using a different domain?

Below is a simple example of a cross-domain Ajax:

<html>
<head>
<script type="text/javascript">
    function xss_ajax(url) {
        var script_id = null;
        var script = document.createElement('script');
        script.setAttribute('type', 'text/javascript');
        script.setAttribute('src', url);
        script.setAttribute('id', 'script_id');

        script_id = document.getElementById('script_id');
        if(script_id){
            document.getElementsByTagName('head')[0].removeChild(script_id);
        }

        // Insert <script> into DOM
        document.getElementsByTagName('head')[0].appendChild(script);
    }

    function callback(data) {
        var txt = '';
        for(var key in data) {
            txt += key + " = " + data[key];
            txt += "\n";
        }
        alert(txt);
    }

    var url = "http://alvin-samplejson.appspot.com/callback_json.php";

</script>
<title>Simple Cross Domain Ajax</title>
</head>
<body>
<h1>Simple Cross Domain Ajax</h1>
<button onclick="xss_ajax(url);">Get Data</button>
</body>
</html>

The JavaScript code above has two functions: xss_ajax() and callback(). The first function, xss_ajax(), performs the HTTP request given a URL. This function mimics the XMLHttpRequest’s open() and send() methods combined. The URL passed to it is not limited by the same-origin policy, thus, it can be any domain name. The second function, callback(), is a function that processes the data returned by the HTTP request performed by xss_ajax().

In the HTML body, I have included a button element to demonstrate a call to the xss_ajax() function.

To try out this example, simply save this code into a file, load it into your browser, e.g., file:///C:/simpleCrossDomainAjax.html, and hit the “Get Data” button.

As you will see, even if the origin is a local file, it can still perform an HTTP request to a different domain and be able to return a response. (http://alvin-samplejson.appspot.com/callback_json.php)

One thing that is different from the XMLHttpRequest API is that the response of our http request cannot be a true JSON object (or XML). Instead, it needs to be constructed with a string-like function call, where its parameter will be the true format of the object you wish to return.

Viewing the sample URL:
http://alvin-samplejson.appspot.com/callback_json.php

the response of the PHP page will look like this:

callback({"firstname":"John","lastname":"Smith","email":"john.smith@johnsmith.com"});

Below is the PHP code that is used for the HTTP request:

<?php
$obj = array();
$obj['firstname'] = "John";
$obj['lastname'] = "Smith";
$obj['email'] = "john.smith@johnsmith.com";

$response = "callback(" . json_encode($obj) . ");";
print $response;

/**
  Browser prints this out as:
  callback({"firstname":"John", "lastname":"Smith", "email":"john.smith@johnsmith.com"});
**/
?>

The “callback” function string as returned by the HTTP request triggers the call to the callback() function.

The magic behind all this is in the usage of the <script> DOM element. As we all know, a web browser can load a JavaScript source file from a different domain. For example:

<script type="text/javascript"
  src="http://external-domain.com/myjavascript.js"></script>

What this code is doing is performing an HTTP request to external-domain.com, fetching the file myjavascript.js, and loading its content.

If this can be done to a javascript file, then it can be done as well to a regular HTML file, or to any type of HTTP requests, e.g. cgi, php, jsp, etc. For example:

<script type="text/javascript" src="http://external-domain.com/mypage.html"></script>
<script type="text/javascript" src="http://external-domain.com/mypage.cgi"></script>
<script type="text/javascript" src="http://external-domain.com/mypage.php"></script>
<script type="text/javascript" src="http://external-domain.com/mypage.xml"></script>

Based on this principle, in order to mimic an XMLHttpRequest kind of call, all we need to do is programmatically create a script element at runtime every time the browser needs to perform an HTTP request. Line numbers 6 to 9 in the xss_ajax() function above performs this dynamic creation of the script element. Line number 17 is when the script element is inserted into the DOM that will trigger the actual execution of the script tag.

Since the script element tag expects a JavaScript code, the response of the URL specified must be executable like a JavaScript code. That is why our PHP code above returns a “callback()” string instead of the raw JSON object. When the script-tag gets created, the browser will see this function-call-like string and will execute it.

The example above only performs a simple HTTP request without any parameters. What if we need to send request parameters? Since the script-tag follows the same principle of making an HTTP GET request, passing request parameters would be the same thing as adding the query-strings to the URL, For example:

<script type="text/javascript"
  src="http://otherdomain.com/mypage.php?name=JOHN&password=secret"></script>

Therefore, to make our xss_ajax() support request parameters, all we need to do is simply add the request parameters to the URL.

Alvin Abad

Extending JavaScript’s String Object

If you are the kind of programmer who works on multiple projects using different languages at the same time, I bet you sometimes can’t help mix things together and subconsciously expect that a method/function you commonly use in one language is also available in the other. And when this happens, you suddenly pause and realize that the function you are about to type doesn’t exist in the language you are currently writing. What could be worse is when you cannot instantly remember the syntax of the method you want to use and you have to scramble and look up the API documentation. The interruption can be annoying. Wouldn’t it be nice if you can simply use the same method that you are always familiar with in the other language?

The good news is that when it comes to JavaScript, this language is so flexible and powerful it can easily fix this problem.

For example in Java, there is this method s.trim() that you call on a string object s to remove any leading and trailing whitespaces. Unfortunately in JavaScript, this method is not available. Of course you can always write a separate function to implement this method, for example, mytrim(s). But wouldn’t it be nice if you can use it the same way you use it in Java? This way, you wouldn’t have to consciously remember about your mystrim(s) function and just let your fingers do the thinking and type it like the way you always do: s.trim().

Fortunately, JavaScript is a very powerful object-oriented programming language. You can easily extend the String object to add a method. This is accomplished by creating a function implementing the functionality you desire and attaching it to String.prototype. Below is a sample implementation of the trim() method.

String.prototype.trim = function () {
    return this.replace(/^\s+|\s+$/g, "");
};

After loading the code above, you can now use s.trim(); where s is the string object.

Below is a sample html page where you can try out this feature. Create this html file and load it into your browser.

<html>
<head>
<title>Extending String object</title>
<script type="text/javascript">
    String.prototype.trim = function () {
        return this.replace(/^\s+|\s+$/g, "");
    };

    var mystring = "       hello, world        ";
    document.writeln("
<pre>");
    document.writeln("Has leading and trailing spaces: [" + mystring + "]");
    document.writeln("Spaces removed: [" + mystring.trim() + "]");
    document.writeln("</pre>
");
</script>
</head>
<body>
</body>
</html>

You should see the output like this:

Has leading and trailing spaces: [       hello, world        ]
Spaces removed: [hello, world]

If you are coming from Python and are used to the upper() method to convert characters to uppercase, you can implement the same method in JavaScript. JavaScript already has this method but it is called toUpperCase(). But if you prefer the Python way, so that you can just let your fingers do the thinking, the technique to accomplish this is the same:

String.prototype.upper = function () {
    return this.toUpperCase();
};

As you can see above, we didn’t have to write our own implementation. We simply use JavaScript’s built-in toUpperCase() function to duplicate it.

Extending JavaScript’s String object is not for everyone. This may not be a good idea to some people because this can cause confusion if one is not aware that an extension has been introduced. Some people would rather prefer implementing the functionality using a separate namespace, e.g., MyStringUtil.trim(s), instead of extending the string object. This makes it clear that an external API is being used.

Five Easy Steps to Learn JsUnit

If you already know JUnit or PyUnit, it shouldn’t take you more than ten minutes to learn how to use JsUnit. JsUnit is a Unit Testing Framework for Javascript. It is a JavaScript version of JUnit.

Below are five easy steps you can follow to quickly learn JsUnit.

Step 1. Download JsUnit zip file and extract it to a local directory.

http://jsunit.net/
http://downloads.sourceforge.net/jsunit/

Step 2. Create a sample function that you will use to test using JsUnit.

For example, let’s create a function that adds two numbers. Save this code into a file and name it “myAdd.js”.

// myAdd.js
function add(x, y) {
    return (x+y);
}

Step 3. Create an HTML file and name it “testMyAdd.html”.

In the HEAD section of this file, include the JsUnit source code and your myAdd.js file. In the BODY section, write your testcase functions as shown below. Similar to JUnit and PyUnit, the names of testcase functions must begin with “test”.

“testAdd.html”

<html>
<head>
    <title>Test Add Function</title>

    <script type="text/javascript" src="junit/jsUnitCore.js"></script>
    <script type="text/javascript" src="myAdd.js"></script>

</head>
<body>
<script type="text/javascript">
    function setUp() {
        // fixture setup before running a testcase
    }

    function tearDown() {
        // anything to cleanup after running a testcase
    }

    function testAdd() {
        assertEquals("", 3, add(2, 1));
    }

    function testAdd2() {
        assertNotEquals("", 0, add(2, 1));
    }
</script>
</body>
</html>

Step 4. Using a browser, locate the file “jsunit/testRunner.html” and load it.

Step 5. On the testRunner.html page, enter the testpage URL (testAdd.html) and click on the RUN button.

When JsUnit loads the testpage, it will automatically find all testcase functions and run them in sequence.

That’s it! You now have a working JsUnit testcase. Of course, JsUnit has more to offer than this simple example. But this should get you started as you explore more features of JsUnit. See online doc for more details: http://jsunit.net/documentation/index.html