| /* |
| * Copyright 2007 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); |
| } |
| } |