6e86513c3a
Update for files that have been modified starting July 2008 Reviewed-by: ohair, tbell
657 lines
22 KiB
Java
657 lines
22 KiB
Java
/*
|
|
* Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
|
* have any questions.
|
|
*/
|
|
|
|
/*
|
|
* @test %M% %I%
|
|
* @bug 6323980
|
|
* @summary Test resource injection via @Resource
|
|
* @author Eamonn McManus
|
|
* @run main/othervm -ea ResourceInjectionTest
|
|
*/
|
|
|
|
import java.io.File;
|
|
import java.io.PrintWriter;
|
|
import java.io.Serializable;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.lang.reflect.InvocationTargetException;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Modifier;
|
|
import java.util.Arrays;
|
|
import javax.annotation.Resource;
|
|
import javax.management.Attribute;
|
|
import javax.management.AttributeList;
|
|
import javax.management.AttributeNotFoundException;
|
|
import javax.management.DynamicMBean;
|
|
import javax.management.InstanceNotFoundException;
|
|
import javax.management.MBean;
|
|
import javax.management.MBeanException;
|
|
import javax.management.MBeanInfo;
|
|
import javax.management.MBeanRegistrationException;
|
|
import javax.management.MBeanServer;
|
|
import javax.management.MBeanServerFactory;
|
|
import javax.management.MXBean;
|
|
import javax.management.MalformedObjectNameException;
|
|
import javax.management.ManagedAttribute;
|
|
import javax.management.ManagedOperation;
|
|
import javax.management.NotCompliantMBeanException;
|
|
import javax.management.Notification;
|
|
import javax.management.NotificationEmitter;
|
|
import javax.management.NotificationListener;
|
|
import javax.management.ObjectName;
|
|
import javax.management.ReflectionException;
|
|
import javax.management.SendNotification;
|
|
import javax.management.StandardEmitterMBean;
|
|
import javax.management.StandardMBean;
|
|
import javax.management.openmbean.MXBeanMappingFactory;
|
|
|
|
public class ResourceInjectionTest {
|
|
private static MBeanServer mbs;
|
|
private static final ObjectName objectName;
|
|
static {
|
|
try {
|
|
objectName = new ObjectName("test:type=Test");
|
|
} catch (MalformedObjectNameException e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
/* This is somewhat nasty. In the current state of affairs, a
|
|
* StandardEmitterMBean can only get the
|
|
* MBeanServer to rewrite the source of a Notification from
|
|
* the originating object's reference to its ObjectName IF
|
|
* StandardEmitterMBean.getResource() returns a reference to the
|
|
* wrapped object. By default it doesn't, and you need to specify
|
|
* the option below to make it do so. We may hope that this is
|
|
* obscure enough for users to run into it rarely if ever.
|
|
*/
|
|
private static final StandardMBean.Options withWrappedVisible;
|
|
private static final StandardMBean.Options withWrappedVisibleMX;
|
|
static {
|
|
withWrappedVisible = new StandardMBean.Options();
|
|
withWrappedVisible.setWrappedObjectVisible(true);
|
|
withWrappedVisibleMX = withWrappedVisible.clone();
|
|
withWrappedVisibleMX.setMXBeanMappingFactory(MXBeanMappingFactory.DEFAULT);
|
|
}
|
|
|
|
@Retention(RetentionPolicy.RUNTIME)
|
|
private static @interface ExpectException {
|
|
Class<? extends Exception> value();
|
|
}
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
if (!ResourceInjectionTest.class.desiredAssertionStatus())
|
|
throw new Exception("Test must be run with -ea");
|
|
|
|
File policyFile = File.createTempFile("jmxperms", ".policy");
|
|
policyFile.deleteOnExit();
|
|
PrintWriter pw = new PrintWriter(policyFile);
|
|
pw.println("grant {");
|
|
pw.println(" permission javax.management.MBeanPermission \"*\", \"*\";");
|
|
pw.println(" permission javax.management.MBeanServerPermission \"*\";");
|
|
pw.println(" permission javax.management.MBeanTrustPermission \"*\";");
|
|
pw.println("};");
|
|
pw.close();
|
|
|
|
System.setProperty("java.security.policy", policyFile.getAbsolutePath());
|
|
System.setSecurityManager(new SecurityManager());
|
|
|
|
String failure = null;
|
|
|
|
for (Method m : ResourceInjectionTest.class.getDeclaredMethods()) {
|
|
if (Modifier.isStatic(m.getModifiers()) &&
|
|
m.getName().startsWith("test") &&
|
|
m.getParameterTypes().length == 0) {
|
|
ExpectException expexc = m.getAnnotation(ExpectException.class);
|
|
mbs = MBeanServerFactory.newMBeanServer();
|
|
try {
|
|
m.invoke(null);
|
|
if (expexc != null) {
|
|
failure =
|
|
m.getName() + " did not got expected exception " +
|
|
expexc.value().getName();
|
|
System.out.println(failure);
|
|
} else
|
|
System.out.println(m.getName() + " OK");
|
|
} catch (InvocationTargetException ite) {
|
|
Throwable t = ite.getCause();
|
|
String prob = null;
|
|
if (expexc != null) {
|
|
if (expexc.value().isInstance(t)) {
|
|
System.out.println(m.getName() + " OK (got expected " +
|
|
expexc.value().getName() + ")");
|
|
} else
|
|
prob = "got wrong exception";
|
|
} else
|
|
prob = "got exception";
|
|
if (prob != null) {
|
|
failure = m.getName() + ": " + prob + " " +
|
|
t.getClass().getName();
|
|
System.out.println(failure);
|
|
t.printStackTrace(System.out);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (failure == null)
|
|
System.out.println("TEST PASSED");
|
|
else
|
|
throw new Exception("TEST FAILED: " + failure);
|
|
}
|
|
|
|
private static interface Send {
|
|
public void send();
|
|
}
|
|
|
|
// Test @Resource in MBean defined by annotations
|
|
|
|
@MBean
|
|
public static class Annotated {
|
|
@Resource
|
|
private volatile MBeanServer mbeanServer;
|
|
@Resource
|
|
private volatile ObjectName myName;
|
|
|
|
@ManagedAttribute
|
|
public ObjectName getMyName() {
|
|
return myName;
|
|
}
|
|
|
|
@ManagedOperation
|
|
public void unregisterSelf()
|
|
throws InstanceNotFoundException, MBeanRegistrationException {
|
|
mbeanServer.unregisterMBean(myName);
|
|
}
|
|
}
|
|
|
|
private static void testAnnotated() throws Exception {
|
|
testMBean(new Annotated());
|
|
}
|
|
|
|
private static void testAnnotatedWrapped() throws Exception {
|
|
testMBean(new StandardMBean(new Annotated(), null));
|
|
}
|
|
|
|
@MBean
|
|
public static class AnnotatedSend extends Annotated implements Send {
|
|
@Resource
|
|
private volatile SendNotification sender;
|
|
|
|
@ManagedOperation
|
|
public void send() {
|
|
sender.sendNotification(new Notification("type", this, 0L));
|
|
}
|
|
}
|
|
|
|
private static void testAnnotatedSend() throws Exception {
|
|
testMBean(new AnnotatedSend());
|
|
}
|
|
|
|
private static void testAnnotatedSendWrapped() throws Exception {
|
|
testMBean(new StandardEmitterMBean(
|
|
new AnnotatedSend(), null, withWrappedVisible, null));
|
|
}
|
|
|
|
// Test @Resource in MXBean defined by annotations
|
|
|
|
@MXBean
|
|
public static class AnnotatedMX {
|
|
@Resource
|
|
private volatile MBeanServer mbeanServer;
|
|
@Resource
|
|
private volatile ObjectName myName;
|
|
|
|
@ManagedAttribute
|
|
public ObjectName getMyName() {
|
|
return myName;
|
|
}
|
|
|
|
@ManagedOperation
|
|
public void unregisterSelf()
|
|
throws InstanceNotFoundException, MBeanRegistrationException {
|
|
mbeanServer.unregisterMBean(myName);
|
|
}
|
|
}
|
|
|
|
private static void testAnnotatedMX() throws Exception {
|
|
testMBean(new AnnotatedMX());
|
|
}
|
|
|
|
private static void testAnnotatedMXWrapped() throws Exception {
|
|
testMBean(new StandardMBean(new AnnotatedMX(), null, true));
|
|
}
|
|
|
|
public static class AnnotatedMXSend extends AnnotatedMX implements Send {
|
|
@Resource
|
|
private volatile SendNotification sender;
|
|
|
|
@ManagedOperation
|
|
public void send() {
|
|
sender.sendNotification(new Notification("type", this, 0L));
|
|
}
|
|
}
|
|
|
|
private static void testAnnotatedMXSend() throws Exception {
|
|
testMBean(new AnnotatedMXSend());
|
|
}
|
|
|
|
private static void testAnnotatedMXSendWrapped() throws Exception {
|
|
testMBean(new StandardEmitterMBean(
|
|
new AnnotatedMXSend(), null, withWrappedVisibleMX, null));
|
|
}
|
|
|
|
// Test @Resource in Standard MBean
|
|
|
|
public static interface SimpleStandardMBean {
|
|
public ObjectName getMyName();
|
|
public void unregisterSelf() throws Exception;
|
|
}
|
|
|
|
public static class SimpleStandard implements SimpleStandardMBean {
|
|
@Resource(type = MBeanServer.class)
|
|
private volatile Object mbeanServer;
|
|
@Resource(type = ObjectName.class)
|
|
private volatile Object myName;
|
|
|
|
public ObjectName getMyName() {
|
|
return (ObjectName) myName;
|
|
}
|
|
|
|
public void unregisterSelf() throws Exception {
|
|
((MBeanServer) mbeanServer).unregisterMBean(getMyName());
|
|
}
|
|
}
|
|
|
|
private static void testStandard() throws Exception {
|
|
testMBean(new SimpleStandard());
|
|
}
|
|
|
|
private static void testStandardWrapped() throws Exception {
|
|
testMBean(new StandardMBean(new SimpleStandard(), SimpleStandardMBean.class));
|
|
}
|
|
|
|
public static interface SimpleStandardSendMBean extends SimpleStandardMBean {
|
|
public void send();
|
|
}
|
|
|
|
public static class SimpleStandardSend
|
|
extends SimpleStandard implements SimpleStandardSendMBean {
|
|
@Resource(type = SendNotification.class)
|
|
private volatile Object sender;
|
|
|
|
public void send() {
|
|
((SendNotification) sender).sendNotification(
|
|
new Notification("type", this, 0L));
|
|
}
|
|
}
|
|
|
|
private static void testStandardSend() throws Exception {
|
|
testMBean(new SimpleStandardSend());
|
|
}
|
|
|
|
private static void testStandardSendWrapped() throws Exception {
|
|
testMBean(new StandardEmitterMBean(
|
|
new SimpleStandardSend(), SimpleStandardSendMBean.class,
|
|
withWrappedVisible, null));
|
|
}
|
|
|
|
// Test @Resource in MXBean
|
|
|
|
public static interface SimpleMXBean {
|
|
public ObjectName getMyName();
|
|
public void unregisterSelf() throws Exception;
|
|
}
|
|
|
|
public static class SimpleMX implements SimpleMXBean {
|
|
@Resource(type = MBeanServer.class)
|
|
private volatile Object mbeanServer;
|
|
@Resource(type = ObjectName.class)
|
|
private volatile Object myName;
|
|
|
|
public ObjectName getMyName() {
|
|
return (ObjectName) myName;
|
|
}
|
|
|
|
public void unregisterSelf() throws Exception {
|
|
((MBeanServer) mbeanServer).unregisterMBean(getMyName());
|
|
}
|
|
}
|
|
|
|
private static void testMX() throws Exception {
|
|
testMBean(new SimpleMX());
|
|
}
|
|
|
|
private static void testMXWrapped() throws Exception {
|
|
testMBean(new StandardMBean(new SimpleMX(), SimpleMXBean.class, true));
|
|
}
|
|
|
|
public static interface SimpleMXBeanSend extends SimpleMXBean {
|
|
public void send();
|
|
}
|
|
|
|
public MBeanServer getMbs() {
|
|
return mbs;
|
|
}
|
|
|
|
public static class SimpleMXSend extends SimpleMX implements SimpleMXBeanSend {
|
|
@Resource(type = SendNotification.class)
|
|
private volatile Object sender;
|
|
|
|
public void send() {
|
|
((SendNotification) sender).sendNotification(
|
|
new Notification("type", this, 0L));
|
|
}
|
|
}
|
|
|
|
private static void testMXSend() throws Exception {
|
|
testMBean(new SimpleMXSend());
|
|
}
|
|
|
|
private static void testMXSendWrapped() throws Exception {
|
|
testMBean(new StandardEmitterMBean(
|
|
new SimpleMXSend(), SimpleMXBeanSend.class,
|
|
withWrappedVisibleMX, null));
|
|
}
|
|
|
|
// Test @Resource in Dynamic MBean
|
|
|
|
private static class SimpleDynamic implements DynamicMBean {
|
|
private MBeanServer mbeanServer;
|
|
private ObjectName myName;
|
|
|
|
@Resource
|
|
private synchronized void setMBeanServer(MBeanServer mbs) {
|
|
mbeanServer = mbs;
|
|
}
|
|
|
|
@Resource(type = ObjectName.class)
|
|
private synchronized void setObjectName(Serializable name) {
|
|
myName = (ObjectName) name;
|
|
}
|
|
|
|
public synchronized Object getAttribute(String attribute)
|
|
throws AttributeNotFoundException {
|
|
if (attribute.equals("MyName"))
|
|
return myName;
|
|
throw new AttributeNotFoundException(attribute);
|
|
}
|
|
|
|
public void setAttribute(Attribute attribute)
|
|
throws AttributeNotFoundException {
|
|
throw new AttributeNotFoundException(attribute.getName());
|
|
}
|
|
|
|
public synchronized AttributeList getAttributes(String[] attributes) {
|
|
AttributeList list = new AttributeList();
|
|
for (String name : attributes) {
|
|
if (name.equals("MyName"))
|
|
list.add(new Attribute("MyName", myName));
|
|
}
|
|
return list;
|
|
}
|
|
|
|
public AttributeList setAttributes(AttributeList attributes) {
|
|
return new AttributeList();
|
|
}
|
|
|
|
public synchronized Object invoke(
|
|
String actionName, Object[] params, String[] signature)
|
|
throws MBeanException, ReflectionException {
|
|
if (actionName.equals("unregisterSelf") &&
|
|
(params == null || params.length == 0) &&
|
|
(signature == null || signature.length == 0)) {
|
|
try {
|
|
mbeanServer.unregisterMBean(myName);
|
|
return null;
|
|
} catch (Exception x) {
|
|
throw new MBeanException(x);
|
|
}
|
|
} else {
|
|
Exception x = new NoSuchMethodException(
|
|
actionName + Arrays.toString(signature));
|
|
throw new MBeanException(x);
|
|
}
|
|
}
|
|
|
|
public MBeanInfo getMBeanInfo() {
|
|
DynamicMBean mbean = new StandardMBean(
|
|
new SimpleStandard(), SimpleStandardMBean.class, false);
|
|
return mbean.getMBeanInfo();
|
|
}
|
|
}
|
|
|
|
private static void testDynamic() throws Exception {
|
|
testMBean(new SimpleDynamic());
|
|
}
|
|
|
|
private static class SimpleDynamicSend extends SimpleDynamic {
|
|
private SendNotification sender;
|
|
|
|
@Resource
|
|
private synchronized void setSender(SendNotification sender) {
|
|
this.sender = sender;
|
|
}
|
|
|
|
@Override
|
|
public synchronized Object invoke(
|
|
String actionName, Object[] params, String[] signature)
|
|
throws MBeanException, ReflectionException {
|
|
if (actionName.equals("send")) {
|
|
sender.sendNotification(new Notification("type", this, 0L));
|
|
return null;
|
|
} else
|
|
return super.invoke(actionName, params, signature);
|
|
}
|
|
}
|
|
|
|
private static void testDynamicSend() throws Exception {
|
|
testMBean(new SimpleDynamicSend());
|
|
}
|
|
|
|
// Test that @Resource classes don't have to be public
|
|
// They can even be defined within methods!
|
|
// But you can't have any @ManagedAttributes or @ManagedOperations
|
|
// in such MBeans so their utility is limited.
|
|
|
|
private static void testNonPublic() throws Exception {
|
|
@MBean
|
|
class NonPublic {
|
|
@Resource
|
|
ObjectName myName;
|
|
}
|
|
assert !Modifier.isPublic(NonPublic.class.getModifiers());
|
|
NonPublic mbean = new NonPublic();
|
|
mbs.registerMBean(mbean, objectName);
|
|
assert objectName.equals(mbean.myName);
|
|
}
|
|
|
|
// Test inheritance and multiple injections of the same value
|
|
|
|
private static class ManyResources extends AnnotatedSend {
|
|
@Resource
|
|
private volatile ObjectName myName; // same name as in parent!
|
|
@Resource(type=ObjectName.class)
|
|
private volatile Object myOtherName;
|
|
private volatile ObjectName myThirdName;
|
|
private volatile ObjectName myFourthName;
|
|
private volatile int methodCalls;
|
|
@Resource
|
|
private volatile SendNotification send1;
|
|
@Resource(type = SendNotification.class)
|
|
private volatile Object send2;
|
|
|
|
@Resource
|
|
void setMyName(ObjectName name) {
|
|
myThirdName = name;
|
|
methodCalls++;
|
|
}
|
|
|
|
@Resource(type=ObjectName.class)
|
|
private void setMyNameAgain(ObjectName name) {
|
|
myFourthName = name;
|
|
methodCalls++;
|
|
}
|
|
|
|
void check() {
|
|
assert objectName.equals(myName) : myName;
|
|
for (ObjectName name : new ObjectName[] {
|
|
(ObjectName)myOtherName, myThirdName, myFourthName
|
|
}) {
|
|
assert myName == name : name;
|
|
}
|
|
assert methodCalls == 2 : methodCalls;
|
|
assert send1 != null && send2 == send1;
|
|
}
|
|
}
|
|
|
|
private static void testManyResources() throws Exception {
|
|
ManyResources mr = new ManyResources();
|
|
testMBean(mr);
|
|
mr.check();
|
|
}
|
|
|
|
// Test that method override doesn't lead to multiple calls of the same method
|
|
|
|
private static class ManyResourcesSub extends ManyResources {
|
|
private boolean called;
|
|
|
|
@Override
|
|
@Resource
|
|
void setMyName(ObjectName name) {
|
|
super.setMyName(name);
|
|
called = true;
|
|
}
|
|
|
|
void check2() {
|
|
assert called;
|
|
}
|
|
}
|
|
|
|
private static void testOverride() throws Exception {
|
|
ManyResourcesSub mrs = new ManyResourcesSub();
|
|
testMBean(mrs);
|
|
mrs.check();
|
|
mrs.check2();
|
|
}
|
|
|
|
// Test that @Resource is illegal on static fields
|
|
|
|
@MBean
|
|
public static class StaticResource {
|
|
@Resource
|
|
private static ObjectName name;
|
|
}
|
|
|
|
@ExpectException(NotCompliantMBeanException.class)
|
|
private static void testStaticResource() throws Exception {
|
|
testMBean(new StaticResource());
|
|
}
|
|
|
|
// Test that @Resource is illegal on static methods
|
|
|
|
@MBean
|
|
public static class StaticResourceMethod {
|
|
@Resource
|
|
private static void setObjectName(ObjectName name) {}
|
|
}
|
|
|
|
@ExpectException(NotCompliantMBeanException.class)
|
|
private static void testStaticResourceMethod() throws Exception {
|
|
testMBean(new StaticResourceMethod());
|
|
}
|
|
|
|
// Test that @Resource is illegal on methods that don't return void
|
|
|
|
@MBean
|
|
public static class NonVoidMethod {
|
|
@Resource
|
|
private String setObjectName(ObjectName name) {
|
|
return "oops";
|
|
}
|
|
}
|
|
|
|
@ExpectException(NotCompliantMBeanException.class)
|
|
private static void testNonVoidMethod() throws Exception {
|
|
testMBean(new NonVoidMethod());
|
|
}
|
|
|
|
// Test that @Resource is illegal on methods with no arguments
|
|
|
|
@MBean
|
|
public static class NoArgMethod {
|
|
@Resource(type=ObjectName.class)
|
|
private void setObjectName() {}
|
|
}
|
|
|
|
@ExpectException(NotCompliantMBeanException.class)
|
|
private static void testNoArgMethod() throws Exception {
|
|
testMBean(new NoArgMethod());
|
|
}
|
|
|
|
// Test that @Resource is illegal on methods with more than one argument
|
|
|
|
@MBean
|
|
public static class MultiArgMethod {
|
|
@Resource
|
|
private void setObjectName(ObjectName name, String what) {}
|
|
}
|
|
|
|
@ExpectException(NotCompliantMBeanException.class)
|
|
private static void testMultiArgMethod() throws Exception {
|
|
testMBean(new MultiArgMethod());
|
|
}
|
|
|
|
private static class CountListener implements NotificationListener {
|
|
volatile int count;
|
|
public void handleNotification(Notification notification, Object handback) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
private static void testMBean(Object mbean) throws Exception {
|
|
mbs.registerMBean(mbean, objectName);
|
|
|
|
final ObjectName name = (ObjectName) mbs.getAttribute(objectName, "MyName");
|
|
assert objectName.equals(name) : name;
|
|
|
|
if (mbean instanceof Send || mbean instanceof NotificationEmitter) {
|
|
assert mbs.isInstanceOf(name, NotificationEmitter.class.getName());
|
|
CountListener countL = new CountListener();
|
|
mbs.addNotificationListener(name, countL, null, null);
|
|
NotificationListener checkSource = new NotificationListener() {
|
|
public void handleNotification(Notification n, Object h) {
|
|
assert n.getSource().equals(name) : n.getSource();
|
|
}
|
|
};
|
|
mbs.addNotificationListener(name, checkSource, null, null);
|
|
mbs.invoke(objectName, "send", null, null);
|
|
assert countL.count == 1;
|
|
mbs.removeNotificationListener(name, checkSource);
|
|
mbs.removeNotificationListener(name, countL, null, null);
|
|
}
|
|
|
|
mbs.invoke(objectName, "unregisterSelf", null, null);
|
|
assert !mbs.isRegistered(objectName);
|
|
}
|
|
}
|