/*
* Copyright (C) The MX4J Contributors.
* All rights reserved.
*
* This software is distributed under the terms of the MX4J License version 1.0.
* See the terms of the MX4J License in the documentation provided with this software.
*/
package test.javax.management.loading;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.Iterator;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.ReflectionException;
import javax.management.loading.MLet;
import javax.management.loading.PrivateMLet;
import mx4j.MX4JSystemKeys;
import mx4j.loading.MLetParseException;
import mx4j.loading.MLetParser;
import mx4j.loading.MLetTag;
import mx4j.server.DefaultClassLoaderRepository;
import test.MX4JTestCase;
/**
* @version $Revision: 1.19 $
*/
public class MLetTest extends MX4JTestCase
{
public MLetTest(String s)
{
super(s);
}
public void testInvalidMLetFileParsing() throws Exception
{
MLetParser parser = new MLetParser();
String content = null;
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
content = "";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
content = " ";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
content = " <!--";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
content = " -->";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
content = "<!-- -->\n<!-- ->";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
content = "<!- -->";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
// Invalid, missing attributes
content = "<!-- -->\n<MLET/>";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
// Invalid, missing archive attribute
content = "<MLET CODE = \" test.mx4j.MBeanNormal\"/>";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
// Invalid, missing archive attribute
content = "\n<MLET CODE=\" test.mx4j.MBeanNormal\">\n</MLET>";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
// Invalid, missing archive attribute
content = "\n<MLET OBJECT=\"mx4j-mbean.ser\">\n</MLET>";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
// Invalid, missing code or object attribute
content = "<MLET ARCHIVE = \"..\\lib\"></MLET>";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
// Invalid, either code or object attribute must be present
content = "<MLET CODE=\"test.mx4j.MBeanNormal\" OBJECT=\"mx4j-mbean.ser\" ARCHIVE = \"..\\lib\"></MLET>";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
// Invalid arg tag
// content = "<MLET CODE=\"test.mx4j.MBeanNormal\" ARCHIVE = \"..\\lib\">\n<></MLET>";
// try {parser.parse(content); fail();}
// catch (MLetParseException x) {}
// Invalid arg tag
content = "<MLET CODE=\"test.mx4j.MBeanNormal\" ARCHIVE = \"..\\lib\">\n<ARG></MLET>";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
// Invalid arg tag
content = "<MLET CODE=\"test.mx4j.MBeanNormal\" ARCHIVE = \"..\\lib\">\n<ARG type=\"int\"></MLET>";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
// Invalid arg tag
content = "<MLET CODE=\"test.mx4j.MBeanNormal\" ARCHIVE = \"..\\lib\">\n<ARG value=\"int\"></MLET>";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
// Invalid name tag
content = "<MLET CODE=\"test.mx4j.MBeanNormal\" ARCHIVE = \"..\\lib\" name>\n</MLET>";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
// Invalid name tag
content = "<MLET CODE=\"test.mx4j.MBeanNormal\" ARCHIVE = \"..\\lib\" name=>\n</MLET>";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
// Invalid version tag
content = "<MLET CODE=\"test.mx4j.MBeanNormal\" ARCHIVE = \"..\\lib\" version>\n</MLET>";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
// Invalid version tag
content = "<MLET CODE=\"test.mx4j.MBeanNormal\" ARCHIVE = \"..\\lib\" version=>\n</MLET>";
try
{
parser.parse(content);
fail();
}
catch (MLetParseException x)
{
}
}
public void testValidMLetFileParsing() throws Exception
{
MLetParser parser = new MLetParser();
String content = "<MLET CODE=\"test.mx4j.MBeanNormal\" ARCHIVE=\"mx4j-tests.jar\" NAME=\":name=MLetTest1\"/>";
parser.parse(content);
content = "<MLET CODE=\"test.mx4j.MBeanNormal\" ARCHIVE=\"mx4j-tests.jar\" NAME=\":name=MLetTest2\">\n\t<ARG TYPE=\"int\" VALUE=\"5\">\n</MLET>\n";
parser.parse(content);
content = "<MLET CODE=\"test.mx4j.MBeanNormal\" ARCHIVE=\"mx4j-tests.jar\" CODEBASE=\"dist\\test\\\" NAME=\":name=MLetTest3\">\n\t<ARG TYPE=\"int\" VALUE=\"5\"/>\n</MLET>\n";
parser.parse(content);
}
public void testCompleteMLetFileParsing() throws Exception
{
MLetParser parser = new MLetParser();
StringBuffer content = new StringBuffer();
content.append("<!-- Comment -->");
content.append("<MLET \n");
content.append(" Code=\"mx4j.tools.naming.NamingService\"\n");
content.append(" archive = \" ../lib \"\n");
content.append(" CodeBase= \"http://localhost:8080/download\"\n");
content.append(" NAME=\":name=test\"\n");
content.append(" Version=\"1\">\n");
content.append(" <!-- Comment -->");
content.append(" <ARG \n");
content.append(" Type=\"boolean\"\n");
content.append(" VALUE = \"true\">\n");
content.append(" <!-- Comment -->");
content.append(" <ARG \n");
content.append(" TYPE = \"boolean\"\n");
content.append(" value=\"true\"/>\n");
content.append("</MLet>");
parser.parse(content.toString());
}
public void testCodebaseForGetMBeansFromURL() throws Exception
{
Class cls = Simple.class;
String className = cls.getName();
URL url = cls.getProtectionDomain().getCodeSource().getLocation();
String urlString = url.toExternalForm();
int index = urlString.lastIndexOf('/') + 1;
String jar = urlString.substring(index);
String codebase = ".";
String content = "<MLET CODE=\"" + className + "\" NAME=\":name=test\" ARCHIVE=\"" + jar + "\" CODEBASE=\"" + codebase + "\"/>";
MLetParser parser = new MLetParser();
List tags = parser.parse(content);
MLetTag tag = (MLetTag)tags.get(0);
URL mletFileURL = new URL("http://mx4j.sourceforge.net/mlets/mbeans.mlet");
URL codebaseURL = tag.normalizeCodeBase(mletFileURL);
assertEquals(codebaseURL.toExternalForm(), "http://mx4j.sourceforge.net/mlets/");
content = "<MLET CODE=\"" + className + "\" NAME=\":name=test\" ARCHIVE=\"" + jar + "\"/>";
tags = parser.parse(content);
tag = (MLetTag)tags.get(0);
codebaseURL = tag.normalizeCodeBase(mletFileURL);
assertEquals(codebaseURL.toExternalForm(), "http://mx4j.sourceforge.net/mlets/");
codebase = "../lib";
content = "<MLET CODE=\"" + className + "\" NAME=\":name=test\" ARCHIVE=\"" + jar + "\" CODEBASE=\"" + codebase + "\"/>";
tags = parser.parse(content);
tag = (MLetTag)tags.get(0);
codebaseURL = tag.normalizeCodeBase(mletFileURL);
assertEquals(codebaseURL.toExternalForm(), "http://mx4j.sourceforge.net/lib/");
codebase = "ftp://mx4j.sourceforge.net/mbeans";
content = "<MLET CODE=\"" + className + "\" NAME=\":name=test\" ARCHIVE=\"" + jar + "\" CODEBASE=\"" + codebase + "\"/>";
tags = parser.parse(content);
tag = (MLetTag)tags.get(0);
codebaseURL = tag.normalizeCodeBase(mletFileURL);
assertEquals(codebaseURL.toExternalForm(), codebase + "/");
}
public void testGetMBeansFromURL() throws Exception
{
Class cls = Simple.class;
String className = cls.getName();
URL url = cls.getProtectionDomain().getCodeSource().getLocation();
String urlString = url.toExternalForm();
int index = urlString.lastIndexOf('/') + 1;
String jar = urlString.substring(index);
String codebase = urlString.substring(0, index);
// Write an MLet file
File mletFile = File.createTempFile("mlet", null);
mletFile.deleteOnExit();
FileOutputStream fos = new FileOutputStream(mletFile);
String content = "<MLET CODE=\"" + className + "\" NAME=\":name=test\" ARCHIVE=\"" + jar + "\" CODEBASE=\"" + codebase + "\"/>";
fos.write(content.getBytes());
fos.close();
System.setProperty(MX4JSystemKeys.MX4J_MBEANSERVER_CLASSLOADER_REPOSITORY, CLRWithOnlyMLets.class.getName());
MBeanServer server = newMBeanServer();
ObjectName mletName = new ObjectName(":loader=mlet1");
MLet mlet = new MLet();
server.registerMBean(mlet, mletName);
Set mbeans = mlet.getMBeansFromURL(mletFile.toURL());
if (mbeans.size() != 1) fail("Loaded wrong number of MBeans");
ObjectInstance instance = (ObjectInstance)mbeans.iterator().next();
if (!instance.getClassName().equals(className)) fail("Loaded a different MBean");
}
public void testGetMBeansFromURLWithNoName() throws Exception
{
Class cls = SimpleRegistration.class;
String className = cls.getName();
URL url = cls.getProtectionDomain().getCodeSource().getLocation();
String urlString = url.toExternalForm();
int index = urlString.lastIndexOf('/') + 1;
String jar = urlString.substring(index);
String codebase = urlString.substring(0, index);
// Write an MLet file
File mletFile = File.createTempFile("mletnoname", null);
mletFile.deleteOnExit();
FileOutputStream fos = new FileOutputStream(mletFile);
String content = "<MLET CODE=\"" + className + "\" ARCHIVE=\"" + jar + "\" CODEBASE=\"" + codebase + "\"/>";
fos.write(content.getBytes());
fos.close();
System.setProperty(MX4JSystemKeys.MX4J_MBEANSERVER_CLASSLOADER_REPOSITORY, CLRWithOnlyMLets.class.getName());
MBeanServer server = newMBeanServer();
ObjectName mletName = new ObjectName(":loader=mlet1");
MLet mlet = new MLet();
server.registerMBean(mlet, mletName);
Set mbeans = mlet.getMBeansFromURL(mletFile.toURL());
if (mbeans.size() != 1) fail("Loaded wrong number of MBeans");
ObjectInstance instance = (ObjectInstance)mbeans.iterator().next();
if (!instance.getClassName().equals(className)) fail("Loaded a different MBean");
}
public void testMLetDelegatesToCLR() throws Exception
{
mletDelegationToCLR(true);
}
public void testMLetDoesNotDelegateToCLR() throws Exception
{
try
{
mletDelegationToCLR(false);
fail("MLet does not delegate, cannot load the class");
}
catch (ReflectionException ignored)
{
}
}
public void mletDelegationToCLR(boolean delegates) throws Exception
{
System.setProperty(MX4JSystemKeys.MX4J_MBEANSERVER_CLASSLOADER_REPOSITORY, CLRWithOnlyMLets.class.getName());
MBeanServer server = newMBeanServer();
ObjectName loaderName = new ObjectName("Loader", "id", "0");
ObjectName mletName = new ObjectName("Loader", "id", "1");
ObjectName mbeanName = new ObjectName("MBean", "id", "0");
Class cls = Simple.class;
URL url = cls.getProtectionDomain().getCodeSource().getLocation();
String className = cls.getName();
MLet loader = new MLet(new URL[]{url}, ClassLoader.getSystemClassLoader().getParent());
server.registerMBean(loader, loaderName);
MLet mlet = new MLet(new URL[0], ClassLoader.getSystemClassLoader().getParent(), delegates);
// Be sure the MLet cannot load the class
try
{
mlet.loadClass(className);
fail("MLet should not be able to load the class");
}
catch (ClassNotFoundException ignored)
{
}
server.registerMBean(mlet, mletName);
// Try to create the MBean
server.createMBean(className, mbeanName, mletName);
}
public void testSingleMLetLoadClass() throws Exception
{
System.setProperty(MX4JSystemKeys.MX4J_MBEANSERVER_CLASSLOADER_REPOSITORY, CLRWithOnlyMLets.class.getName());
MBeanServer server = newMBeanServer();
ObjectName loaderName = new ObjectName("Loader", "id", "0");
ObjectName mbeanName = new ObjectName("MBean", "id", "0");
Class cls = Simple.class;
URL url = cls.getProtectionDomain().getCodeSource().getLocation();
String className = cls.getName();
MLet mlet = new MLet(new URL[]{url}, ClassLoader.getSystemClassLoader().getParent());
server.registerMBean(mlet, loaderName);
server.createMBean(className, mbeanName, loaderName);
}
public void testManyMLetLoadClass() throws Exception
{
int loaderCount = 100;
int mbeanCount = 200;
long elapsed = manyMLetLoadClass(loaderCount, mbeanCount, Simple.class.getName(), false);
System.out.println("Loading " + mbeanCount + " MBeans with " + loaderCount + " MLets took " + elapsed + " ms, average is " + (elapsed / mbeanCount));
// Assume registering a valid MBean will take no more than 500 ms
if (elapsed > mbeanCount * 500) fail("Test took too much time, probably a problem in MLet loading");
}
public void testMultiMLetLoadNonExistingClass() throws Exception
{
int loaderCount = 100;
int mbeanCount = 200;
long elapsed = manyMLetLoadClass(loaderCount, mbeanCount, "dummy", true);
System.out.println("Loading " + mbeanCount + " non-existing MBeans with " + loaderCount + " MLets took " + elapsed + " ms, average is " + (elapsed / mbeanCount));
// We're looking for a non-existing class, so we have to ask to all classloaders in the CLR before the chosen MLet
// The time we spend looking for the class is roughly linear with the number of classloaders in the queried,
// that in average is loaderCount / 2 and at most loaderCount:
// elapsed = mbeanCount * loaderCount * k
// Assume that k > 5 ms is too bad performance
if (elapsed > mbeanCount * loaderCount * 5) fail("Test took too much time, probably a problem in MLet loading");
}
private long manyMLetLoadClass(int loaderCount, int mbeanCount, String className, boolean ignoreExceptionOnCreation) throws Exception
{
ObjectName[] loaders = new ObjectName[loaderCount];
ObjectName[] mbeans = new ObjectName[mbeanCount];
MBeanServer server = newMBeanServer();
URL url = getClass().getProtectionDomain().getCodeSource().getLocation();
// Register some MLet
for (int i = 0; i < loaderCount; ++i)
{
loaders[i] = new ObjectName("Loader", "id", String.valueOf(i));
MLet mlet = new MLet(new URL[]{url}, ClassLoader.getSystemClassLoader().getParent());
server.registerMBean(mlet, loaders[i]);
}
long start = System.currentTimeMillis();
Random random = new Random(start);
for (int i = 0; i < mbeanCount; ++i)
{
mbeans[i] = new ObjectName("MBean", "id", String.valueOf(i));
// Choose an MLet to load the MBean
int id = random.nextInt(loaderCount);
ObjectName loader = loaders[id];
if (ignoreExceptionOnCreation)
{
try
{
server.createMBean(className, mbeans[i], loader);
}
catch (ReflectionException ignored)
{
}
}
else
{
server.createMBean(className, mbeans[i], loader);
}
}
long end = System.currentTimeMillis();
return end - start;
}
public void testChildMLetRegisteredBeforeParentMLet() throws Exception
{
// A test to be sure the MLet implementation does not recurse infinitely when loading classes.
URL url = getClass().getProtectionDomain().getCodeSource().getLocation();
MLet parent = new MLet(new URL[]{url}, ClassLoader.getSystemClassLoader().getParent());
MLet child = new MLet(new URL[0], parent);
ObjectName parentName = new ObjectName("MLet", "type", "parent");
ObjectName childName = new ObjectName("MLet", "type", "child");
MBeanServer server = newMBeanServer();
// First register the child, then the parent.
server.registerMBean(child, childName);
server.registerMBean(parent, parentName);
// Now in the CLR there are: SystemCL, childMLet, parentMLet.
// If I ask someting to parentMLet that it cannot find, it will delegate to the CLR,
// and when it comes to the childMLet, the delegation mechanism will ask again to the parentMLet.
// If we're not smart in the MLet implementation this ends up in an infinite loop.
try
{
parent.loadClass("dummy");
fail("Class does not exist");
}
catch (ClassNotFoundException ignored)
{
}
}
public void testPrivateMLetNotAddedToCLR() throws Exception
{
Class cls = Simple.class;
URL url = cls.getProtectionDomain().getCodeSource().getLocation();
PrivateMLet mlet = new PrivateMLet(new URL[]{url}, ClassLoader.getSystemClassLoader().getParent(), true);
ObjectName mletName = new ObjectName(":MLet=Private");
ObjectName mbeanName = new ObjectName(":MBean=Simple");
System.setProperty(MX4JSystemKeys.MX4J_MBEANSERVER_CLASSLOADER_REPOSITORY, CLRWithOnlyMLets.class.getName());
MBeanServer server = newMBeanServer();
// The private MLet should not be registered in the CLR
server.registerMBean(mlet, mletName);
try
{
// Ask the CLR to load the class
server.createMBean(cls.getName(), mbeanName);
fail("Class cannot be found by the CLR");
}
catch (ReflectionException ignored)
{
}
}
public void testFindLibrary() throws Exception
{
MLet mlet = new MLet();
Method method = mlet.getClass().getDeclaredMethod("findLibrary", new Class[]{String.class});
method.setAccessible(true);
String result = (String)method.invoke(mlet, new Object[]{"stat"});
if (result != null) fail("MLet can load non-existing libraries");
}
public void testFindLibraryWithLibraryDirectoryInClassPath() throws Exception
{
// Prepare the library
String library = "mlet";
String libraryName = System.mapLibraryName(library);
File libraryFile = new File(libraryName);
FileOutputStream fos = new FileOutputStream(libraryFile);
fos.write("library".getBytes());
fos.close();
assertTrue(libraryFile.exists());
assertTrue(libraryFile.length() > 0);
MLet mlet = new MLet(new URL[]{libraryFile.getCanonicalFile().getParentFile().toURL()});
Method method = mlet.getClass().getDeclaredMethod("findLibrary", new Class[]{String.class});
method.setAccessible(true);
String result = (String)method.invoke(mlet, new Object[]{library});
assertNotNull(result);
assertTrue(libraryFile.exists());
assertTrue(libraryFile.length() > 0);
}
public void testFindLibraryWithLibraryDirectoryNotInClassPath() throws Exception
{
// Prepare the library
String library = "mlet";
String libraryName = System.mapLibraryName(library);
File libraryFile = new File(libraryName);
FileOutputStream fos = new FileOutputStream(libraryFile);
fos.write("library".getBytes());
fos.close();
assertTrue(libraryFile.exists());
assertTrue(libraryFile.length() > 0);
MLet mlet = new MLet(new URL[]{libraryFile.getCanonicalFile().getParentFile().toURL()});
// Set the library directory to some temp directory
File temp = File.createTempFile("abc", null);
temp.deleteOnExit();
mlet.setLibraryDirectory(temp.getCanonicalFile().getParentFile().getCanonicalPath());
Method method = mlet.getClass().getDeclaredMethod("findLibrary", new Class[]{String.class});
method.setAccessible(true);
String result = (String)method.invoke(mlet, new Object[]{library});
assertNotNull(result);
assertTrue(libraryFile.exists());
assertTrue(libraryFile.length() > 0);
File tempLibrary = new File(mlet.getLibraryDirectory(), libraryName);
assertTrue(tempLibrary.exists());
assertTrue(tempLibrary.length() > 0);
}
public void testDefaultMletName() throws Exception {
MBeanServer mbServer = newMBeanServer();
MLet mlet = new MLet();
ObjectName mletName = new ObjectName(mbServer.getDefaultDomain(), "type", "MLet");
assertFalse(mbServer.isRegistered(mletName));
assertEquals(mletName, mbServer.registerMBean(mlet, null).getObjectName());
assertTrue(mbServer.isRegistered(mletName));
}
public interface SimpleMBean
{
}
public static class Simple implements SimpleMBean
{
}
public static class SimpleRegistration extends Simple implements MBeanRegistration
{
public ObjectName preRegister(MBeanServer server, ObjectName name) throws Exception
{
return ObjectName.getInstance(":name=simple");
}
public void postRegister(Boolean registrationDone)
{
}
public void preDeregister() throws Exception
{
}
public void postDeregister()
{
}
}
public static class CLRWithOnlyMLets extends DefaultClassLoaderRepository
{
protected void addClassLoader(ClassLoader cl)
{
if (cl == null) return;
if (cl.getClass() == MLet.class || cl.getClass() == PrivateMLet.class) super.addClassLoader(cl);
}
}
}