1.1 --- a/.hgtags Tue Aug 19 07:50:03 2008 -0700
1.2 +++ b/.hgtags Thu Aug 21 09:55:18 2008 -0700
1.3 @@ -7,3 +7,4 @@
1.4 b6d6877c1155621a175dccd12dc14c54f938fb8b jdk7-b30
1.5 b7474b739d13bacd9972f88ac91f6350b7b0be12 jdk7-b31
1.6 c51121419e30eac5f0fbbce45ff1711c4ce0de28 jdk7-b32
1.7 +fa4c0a6cdd25d97d4e6f5d7aa180bcbb0e0d56af jdk7-b33
2.1 --- a/make/docs/CORE_PKGS.gmk Tue Aug 19 07:50:03 2008 -0700
2.2 +++ b/make/docs/CORE_PKGS.gmk Thu Aug 21 09:55:18 2008 -0700
2.3 @@ -155,6 +155,7 @@
2.4 javax.lang.model.type \
2.5 javax.lang.model.util \
2.6 javax.management \
2.7 + javax.management.event \
2.8 javax.management.loading \
2.9 javax.management.monitor \
2.10 javax.management.relation \
3.1 --- a/make/netbeans/jconsole/build.properties Tue Aug 19 07:50:03 2008 -0700
3.2 +++ b/make/netbeans/jconsole/build.properties Thu Aug 21 09:55:18 2008 -0700
3.3 @@ -44,3 +44,4 @@
3.4 build.release = ${build.jdk.version}-opensource
3.5 build.number = b00
3.6 jconsole.version = ${build.release}-${user.name}-${build.number}
3.7 +jconsole.args = -debug
4.1 --- a/make/netbeans/jconsole/build.xml Tue Aug 19 07:50:03 2008 -0700
4.2 +++ b/make/netbeans/jconsole/build.xml Thu Aug 21 09:55:18 2008 -0700
4.3 @@ -30,9 +30,9 @@
4.4 -->
4.5
4.6 <project name="jconsole" default="build" basedir=".">
4.7 -
4.8 +
4.9 <import file="../common/shared.xml"/>
4.10 -
4.11 +
4.12 <target name="-pre-compile">
4.13 <copy
4.14 file="${root}/src/share/classes/sun/tools/jconsole/Version-template.java"
4.15 @@ -42,7 +42,7 @@
4.16 token="@@jconsole_version@@"
4.17 value="${jconsole.version}"/>
4.18 </target>
4.19 -
4.20 +
4.21 <target name="-post-compile">
4.22 <mkdir dir="${dist.dir}/lib"/>
4.23 <jar destfile="${dist.dir}/lib/jconsole.jar"
4.24 @@ -55,18 +55,19 @@
4.25 </fileset>
4.26 </jar>
4.27 </target>
4.28 -
4.29 +
4.30 <target name="run" depends="-init,build">
4.31 <property name="jvm.args" value=""/>
4.32 <java classname="sun.tools.jconsole.JConsole"
4.33 fork="true"
4.34 classpath="${classes.dir}:${bootstrap.jdk}/lib/tools.jar">
4.35 <jvmarg line="${jvm.args}"/>
4.36 + <arg line="${jconsole.args}"/>
4.37 </java>
4.38 </target>
4.39 -
4.40 +
4.41 <target name="clean" depends="-init,shared.clean">
4.42 <delete file="${dist.dir}/lib/jconsole.jar"/>
4.43 </target>
4.44 -
4.45 +
4.46 </project>
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/src/share/classes/com/sun/jmx/event/DaemonThreadFactory.java Thu Aug 21 09:55:18 2008 -0700
5.3 @@ -0,0 +1,77 @@
5.4 +/*
5.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5.7 + *
5.8 + * This code is free software; you can redistribute it and/or modify it
5.9 + * under the terms of the GNU General Public License version 2 only, as
5.10 + * published by the Free Software Foundation. Sun designates this
5.11 + * particular file as subject to the "Classpath" exception as provided
5.12 + * by Sun in the LICENSE file that accompanied this code.
5.13 + *
5.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
5.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
5.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
5.17 + * version 2 for more details (a copy is included in the LICENSE file that
5.18 + * accompanied this code).
5.19 + *
5.20 + * You should have received a copy of the GNU General Public License version
5.21 + * 2 along with this work; if not, write to the Free Software Foundation,
5.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
5.23 + *
5.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
5.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
5.26 + * have any questions.
5.27 + */
5.28 +
5.29 +package com.sun.jmx.event;
5.30 +
5.31 +import com.sun.jmx.remote.util.ClassLogger;
5.32 +import java.util.concurrent.ThreadFactory;
5.33 +import java.util.concurrent.atomic.AtomicInteger;
5.34 +
5.35 +public class DaemonThreadFactory implements ThreadFactory {
5.36 + public DaemonThreadFactory(String nameTemplate) {
5.37 + this(nameTemplate, null);
5.38 + }
5.39 +
5.40 + // nameTemplate should be a format with %d in it, which will be replaced
5.41 + // by a sequence number of threads created by this factory.
5.42 + public DaemonThreadFactory(String nameTemplate, ThreadGroup threadGroup) {
5.43 + if (logger.debugOn()) {
5.44 + logger.debug("DaemonThreadFactory",
5.45 + "Construct a new daemon factory: "+nameTemplate);
5.46 + }
5.47 +
5.48 + if (threadGroup == null) {
5.49 + SecurityManager s = System.getSecurityManager();
5.50 + threadGroup = (s != null) ? s.getThreadGroup() :
5.51 + Thread.currentThread().getThreadGroup();
5.52 + }
5.53 +
5.54 + this.nameTemplate = nameTemplate;
5.55 + this.threadGroup = threadGroup;
5.56 + }
5.57 +
5.58 + public Thread newThread(Runnable r) {
5.59 + final String name =
5.60 + String.format(nameTemplate, threadNumber.getAndIncrement());
5.61 + Thread t = new Thread(threadGroup, r, name, 0);
5.62 + t.setDaemon(true);
5.63 + if (t.getPriority() != Thread.NORM_PRIORITY)
5.64 + t.setPriority(Thread.NORM_PRIORITY);
5.65 +
5.66 + if (logger.debugOn()) {
5.67 + logger.debug("newThread",
5.68 + "Create a new daemon thread with the name "+t.getName());
5.69 + }
5.70 +
5.71 + return t;
5.72 + }
5.73 +
5.74 + private final String nameTemplate;
5.75 + private final ThreadGroup threadGroup;
5.76 + private final AtomicInteger threadNumber = new AtomicInteger(1);
5.77 +
5.78 + private static final ClassLogger logger =
5.79 + new ClassLogger("com.sun.jmx.event", "DaemonThreadFactory");
5.80 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/src/share/classes/com/sun/jmx/event/EventBuffer.java Thu Aug 21 09:55:18 2008 -0700
6.3 @@ -0,0 +1,252 @@
6.4 +/*
6.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
6.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6.7 + *
6.8 + * This code is free software; you can redistribute it and/or modify it
6.9 + * under the terms of the GNU General Public License version 2 only, as
6.10 + * published by the Free Software Foundation. Sun designates this
6.11 + * particular file as subject to the "Classpath" exception as provided
6.12 + * by Sun in the LICENSE file that accompanied this code.
6.13 + *
6.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
6.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
6.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
6.17 + * version 2 for more details (a copy is included in the LICENSE file that
6.18 + * accompanied this code).
6.19 + *
6.20 + * You should have received a copy of the GNU General Public License version
6.21 + * 2 along with this work; if not, write to the Free Software Foundation,
6.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
6.23 + *
6.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
6.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
6.26 + * have any questions.
6.27 + */
6.28 +
6.29 +package com.sun.jmx.event;
6.30 +
6.31 +import com.sun.jmx.remote.util.ClassLogger;
6.32 +import java.util.ArrayList;
6.33 +import java.util.Collections;
6.34 +import java.util.List;
6.35 +import javax.management.remote.NotificationResult;
6.36 +import javax.management.remote.TargetedNotification;
6.37 +
6.38 +public class EventBuffer {
6.39 +
6.40 + public EventBuffer() {
6.41 + this(Integer.MAX_VALUE, null);
6.42 + }
6.43 +
6.44 + public EventBuffer(int capacity) {
6.45 + this(capacity, new ArrayList<TargetedNotification>());
6.46 + }
6.47 +
6.48 + public EventBuffer(int capacity, final List<TargetedNotification> list) {
6.49 + if (logger.traceOn()) {
6.50 + logger.trace("EventBuffer", "New buffer with the capacity: "
6.51 + +capacity);
6.52 + }
6.53 + if (capacity < 1) {
6.54 + throw new IllegalArgumentException(
6.55 + "The capacity must be bigger than 0");
6.56 + }
6.57 +
6.58 + if (list == null) {
6.59 + throw new NullPointerException("Null list.");
6.60 + }
6.61 +
6.62 + this.capacity = capacity;
6.63 + this.list = list;
6.64 + }
6.65 +
6.66 + public void add(TargetedNotification tn) {
6.67 + if (logger.traceOn()) {
6.68 + logger.trace("add", "Add one notif.");
6.69 + }
6.70 +
6.71 + synchronized(lock) {
6.72 + if (list.size() == capacity) { // have to throw one
6.73 + passed++;
6.74 + list.remove(0);
6.75 +
6.76 + if (logger.traceOn()) {
6.77 + logger.trace("add", "Over, remove the oldest one.");
6.78 + }
6.79 + }
6.80 +
6.81 + list.add(tn);
6.82 + lock.notify();
6.83 + }
6.84 + }
6.85 +
6.86 + public void add(TargetedNotification[] tns) {
6.87 + if (tns == null || tns.length == 0) {
6.88 + return;
6.89 + }
6.90 +
6.91 + if (logger.traceOn()) {
6.92 + logger.trace("add", "Add notifs: "+tns.length);
6.93 + }
6.94 +
6.95 + synchronized(lock) {
6.96 + final int d = list.size() - capacity + tns.length;
6.97 + if (d > 0) { // have to throw
6.98 + passed += d;
6.99 + if (logger.traceOn()) {
6.100 + logger.trace("add",
6.101 + "Over, remove the oldest: "+d);
6.102 + }
6.103 + if (tns.length <= capacity){
6.104 + list.subList(0, d).clear();
6.105 + } else {
6.106 + list.clear();
6.107 + TargetedNotification[] tmp =
6.108 + new TargetedNotification[capacity];
6.109 + System.arraycopy(tns, tns.length-capacity, tmp, 0, capacity);
6.110 + tns = tmp;
6.111 + }
6.112 + }
6.113 +
6.114 + Collections.addAll(list,tns);
6.115 + lock.notify();
6.116 + }
6.117 + }
6.118 +
6.119 + public NotificationResult fetchNotifications(long startSequenceNumber,
6.120 + long timeout,
6.121 + int maxNotifications) {
6.122 + if (logger.traceOn()) {
6.123 + logger.trace("fetchNotifications",
6.124 + "Being called: "
6.125 + +startSequenceNumber+" "
6.126 + +timeout+" "+maxNotifications);
6.127 + }
6.128 + if (startSequenceNumber < 0 ||
6.129 + timeout < 0 ||
6.130 + maxNotifications < 0) {
6.131 + throw new IllegalArgumentException("Negative value.");
6.132 + }
6.133 +
6.134 + TargetedNotification[] tns = new TargetedNotification[0];
6.135 + long earliest = startSequenceNumber < passed ?
6.136 + passed : startSequenceNumber;
6.137 + long next = earliest;
6.138 +
6.139 + final long startTime = System.currentTimeMillis();
6.140 + long toWait = timeout;
6.141 + synchronized(lock) {
6.142 + int toSkip = (int)(startSequenceNumber - passed);
6.143 +
6.144 + // skip those before startSequenceNumber.
6.145 + while (!closed && toSkip > 0) {
6.146 + toWait = timeout - (System.currentTimeMillis() - startTime);
6.147 + if (list.size() == 0) {
6.148 + if (toWait <= 0) {
6.149 + // the notification of startSequenceNumber
6.150 + // does not arrive yet.
6.151 + return new NotificationResult(startSequenceNumber,
6.152 + startSequenceNumber,
6.153 + new TargetedNotification[0]);
6.154 + }
6.155 +
6.156 + waiting(toWait);
6.157 + continue;
6.158 + }
6.159 +
6.160 + if (toSkip <= list.size()) {
6.161 + list.subList(0, toSkip).clear();
6.162 + passed += toSkip;
6.163 +
6.164 + break;
6.165 + } else {
6.166 + passed += list.size();
6.167 + toSkip -= list.size();
6.168 +
6.169 + list.clear();
6.170 + }
6.171 + }
6.172 +
6.173 + earliest = passed;
6.174 +
6.175 + if (list.size() == 0) {
6.176 + toWait = timeout - (System.currentTimeMillis() - startTime);
6.177 +
6.178 + waiting(toWait);
6.179 + }
6.180 +
6.181 + if (list.size() == 0) {
6.182 + tns = new TargetedNotification[0];
6.183 + } else if (list.size() <= maxNotifications) {
6.184 + tns = list.toArray(new TargetedNotification[0]);
6.185 + } else {
6.186 + tns = new TargetedNotification[maxNotifications];
6.187 + for (int i=0; i<maxNotifications; i++) {
6.188 + tns[i] = list.get(i);
6.189 + }
6.190 + }
6.191 +
6.192 + next = earliest + tns.length;
6.193 + }
6.194 +
6.195 + if (logger.traceOn()) {
6.196 + logger.trace("fetchNotifications",
6.197 + "Return: "+earliest+" "+next+" "+tns.length);
6.198 + }
6.199 +
6.200 + return new NotificationResult(earliest, next, tns);
6.201 + }
6.202 +
6.203 + public int size() {
6.204 + return list.size();
6.205 + }
6.206 +
6.207 + public void addLost(long nb) {
6.208 + synchronized(lock) {
6.209 + passed += nb;
6.210 + }
6.211 + }
6.212 +
6.213 + public void close() {
6.214 + if (logger.traceOn()) {
6.215 + logger.trace("clear", "done");
6.216 + }
6.217 +
6.218 + synchronized(lock) {
6.219 + list.clear();
6.220 + closed = true;
6.221 + lock.notifyAll();
6.222 + }
6.223 + }
6.224 +
6.225 +
6.226 + // -------------------------------------------
6.227 + // private classes
6.228 + // -------------------------------------------
6.229 + private void waiting(long timeout) {
6.230 + final long startTime = System.currentTimeMillis();
6.231 + long toWait = timeout;
6.232 + synchronized(lock) {
6.233 + while (!closed && list.size() == 0 && toWait > 0) {
6.234 + try {
6.235 + lock.wait(toWait);
6.236 +
6.237 + toWait = timeout - (System.currentTimeMillis() - startTime);
6.238 + } catch (InterruptedException ire) {
6.239 + logger.trace("waiting", ire);
6.240 + break;
6.241 + }
6.242 + }
6.243 + }
6.244 + }
6.245 +
6.246 + private final int capacity;
6.247 + private final List<TargetedNotification> list;
6.248 + private boolean closed;
6.249 +
6.250 + private long passed = 0;
6.251 + private final int[] lock = new int[0];
6.252 +
6.253 + private static final ClassLogger logger =
6.254 + new ClassLogger("javax.management.event", "EventBuffer");
6.255 +}
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/src/share/classes/com/sun/jmx/event/EventClientFactory.java Thu Aug 21 09:55:18 2008 -0700
7.3 @@ -0,0 +1,46 @@
7.4 +/*
7.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
7.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
7.7 + *
7.8 + * This code is free software; you can redistribute it and/or modify it
7.9 + * under the terms of the GNU General Public License version 2 only, as
7.10 + * published by the Free Software Foundation. Sun designates this
7.11 + * particular file as subject to the "Classpath" exception as provided
7.12 + * by Sun in the LICENSE file that accompanied this code.
7.13 + *
7.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
7.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
7.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
7.17 + * version 2 for more details (a copy is included in the LICENSE file that
7.18 + * accompanied this code).
7.19 + *
7.20 + * You should have received a copy of the GNU General Public License version
7.21 + * 2 along with this work; if not, write to the Free Software Foundation,
7.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
7.23 + *
7.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
7.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
7.26 + * have any questions.
7.27 + */
7.28 +
7.29 +package com.sun.jmx.event;
7.30 +
7.31 +import javax.management.event.*;
7.32 +
7.33 +/**
7.34 + * Implemented by objects which are using an {@link EventClient} to
7.35 + * subscribe for Notifications.
7.36 + *
7.37 + */
7.38 +public interface EventClientFactory {
7.39 + /**
7.40 + * Returns the {@code EventClient} that the object implementing this
7.41 + * interface uses to subscribe for Notifications. This method returns
7.42 + * {@code null} if no {@code EventClient} can be used - e.g. because
7.43 + * the underlying server does not have any {@link EventDelegate}.
7.44 + *
7.45 + * @return an {@code EventClient} or {@code null}.
7.46 + **/
7.47 + public EventClient getEventClient();
7.48 +
7.49 +}
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/src/share/classes/com/sun/jmx/event/EventConnection.java Thu Aug 21 09:55:18 2008 -0700
8.3 @@ -0,0 +1,81 @@
8.4 +/*
8.5 + * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved.
8.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
8.7 + *
8.8 + * This code is free software; you can redistribute it and/or modify it
8.9 + * under the terms of the GNU General Public License version 2 only, as
8.10 + * published by the Free Software Foundation. Sun designates this
8.11 + * particular file as subject to the "Classpath" exception as provided
8.12 + * by Sun in the LICENSE file that accompanied this code.
8.13 + *
8.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
8.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
8.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
8.17 + * version 2 for more details (a copy is included in the LICENSE file that
8.18 + * accompanied this code).
8.19 + *
8.20 + * You should have received a copy of the GNU General Public License version
8.21 + * 2 along with this work; if not, write to the Free Software Foundation,
8.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
8.23 + *
8.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
8.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
8.26 + * have any questions.
8.27 + */
8.28 +
8.29 +package com.sun.jmx.event;
8.30 +
8.31 +import java.io.IOException;
8.32 +import java.lang.reflect.InvocationHandler;
8.33 +import java.lang.reflect.InvocationTargetException;
8.34 +import java.lang.reflect.Method;
8.35 +import java.lang.reflect.Proxy;
8.36 +import javax.management.MBeanServerConnection;
8.37 +import javax.management.event.EventClient;
8.38 +import javax.management.event.EventClientDelegate;
8.39 +import javax.management.event.EventConsumer;
8.40 +import javax.management.event.NotificationManager;
8.41 +
8.42 +/**
8.43 + * Override the methods related to the notification to use the
8.44 + * Event service.
8.45 + */
8.46 +public interface EventConnection extends MBeanServerConnection, EventConsumer {
8.47 + public EventClient getEventClient();
8.48 +
8.49 + public static class Factory {
8.50 + public static EventConnection make(
8.51 + final MBeanServerConnection mbsc,
8.52 + final EventClient eventClient)
8.53 + throws IOException {
8.54 + if (!mbsc.isRegistered(EventClientDelegate.OBJECT_NAME)) {
8.55 + throw new IOException(
8.56 + "The server does not support the event service.");
8.57 + }
8.58 + InvocationHandler ih = new InvocationHandler() {
8.59 + public Object invoke(Object proxy, Method method, Object[] args)
8.60 + throws Throwable {
8.61 + Class<?> intf = method.getDeclaringClass();
8.62 + try {
8.63 + if (intf.isInstance(eventClient))
8.64 + return method.invoke(eventClient, args);
8.65 + else
8.66 + return method.invoke(mbsc, args);
8.67 + } catch (InvocationTargetException e) {
8.68 + throw e.getCause();
8.69 + }
8.70 + }
8.71 + };
8.72 + // It is important to declare NotificationManager.class first
8.73 + // in the array below, so that the relevant addNL and removeNL
8.74 + // methods will show up with method.getDeclaringClass() as
8.75 + // being from that interface and not MBeanServerConnection.
8.76 + return (EventConnection) Proxy.newProxyInstance(
8.77 + NotificationManager.class.getClassLoader(),
8.78 + new Class<?>[] {
8.79 + NotificationManager.class, EventConnection.class,
8.80 + },
8.81 + ih);
8.82 + }
8.83 + }
8.84 +}
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/src/share/classes/com/sun/jmx/event/EventParams.java Thu Aug 21 09:55:18 2008 -0700
9.3 @@ -0,0 +1,65 @@
9.4 +/*
9.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
9.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
9.7 + *
9.8 + * This code is free software; you can redistribute it and/or modify it
9.9 + * under the terms of the GNU General Public License version 2 only, as
9.10 + * published by the Free Software Foundation. Sun designates this
9.11 + * particular file as subject to the "Classpath" exception as provided
9.12 + * by Sun in the LICENSE file that accompanied this code.
9.13 + *
9.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
9.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
9.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
9.17 + * version 2 for more details (a copy is included in the LICENSE file that
9.18 + * accompanied this code).
9.19 + *
9.20 + * You should have received a copy of the GNU General Public License version
9.21 + * 2 along with this work; if not, write to the Free Software Foundation,
9.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
9.23 + *
9.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
9.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
9.26 + * have any questions.
9.27 + */
9.28 +
9.29 +package com.sun.jmx.event;
9.30 +
9.31 +import com.sun.jmx.mbeanserver.GetPropertyAction;
9.32 +import com.sun.jmx.remote.util.ClassLogger;
9.33 +import java.security.AccessController;
9.34 +import javax.management.event.EventClient;
9.35 +
9.36 +/**
9.37 + *
9.38 + * @author sjiang
9.39 + */
9.40 +public class EventParams {
9.41 + public static final String DEFAULT_LEASE_TIMEOUT =
9.42 + "com.sun.event.lease.time";
9.43 +
9.44 +
9.45 + @SuppressWarnings("cast") // cast for jdk 1.5
9.46 + public static long getLeaseTimeout() {
9.47 + long timeout = EventClient.DEFAULT_LEASE_TIMEOUT;
9.48 + try {
9.49 + final GetPropertyAction act =
9.50 + new GetPropertyAction(DEFAULT_LEASE_TIMEOUT);
9.51 + final String s = (String)AccessController.doPrivileged(act);
9.52 + if (s != null) {
9.53 + timeout = Long.parseLong(s);
9.54 + }
9.55 + } catch (RuntimeException e) {
9.56 + logger.fine("getLeaseTimeout", "exception getting property", e);
9.57 + }
9.58 +
9.59 + return timeout;
9.60 + }
9.61 +
9.62 + /** Creates a new instance of EventParams */
9.63 + private EventParams() {
9.64 + }
9.65 +
9.66 + private static final ClassLogger logger =
9.67 + new ClassLogger("javax.management.event", "EventParams");
9.68 +}
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/src/share/classes/com/sun/jmx/event/LeaseManager.java Thu Aug 21 09:55:18 2008 -0700
10.3 @@ -0,0 +1,146 @@
10.4 +/*
10.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
10.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
10.7 + *
10.8 + * This code is free software; you can redistribute it and/or modify it
10.9 + * under the terms of the GNU General Public License version 2 only, as
10.10 + * published by the Free Software Foundation. Sun designates this
10.11 + * particular file as subject to the "Classpath" exception as provided
10.12 + * by Sun in the LICENSE file that accompanied this code.
10.13 + *
10.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
10.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
10.17 + * version 2 for more details (a copy is included in the LICENSE file that
10.18 + * accompanied this code).
10.19 + *
10.20 + * You should have received a copy of the GNU General Public License version
10.21 + * 2 along with this work; if not, write to the Free Software Foundation,
10.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
10.23 + *
10.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
10.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
10.26 + * have any questions.
10.27 + */
10.28 +
10.29 +package com.sun.jmx.event;
10.30 +
10.31 +import com.sun.jmx.remote.util.ClassLogger;
10.32 +import java.util.concurrent.Executors;
10.33 +import java.util.concurrent.FutureTask;
10.34 +import java.util.concurrent.ScheduledExecutorService;
10.35 +import java.util.concurrent.ScheduledFuture;
10.36 +import java.util.concurrent.TimeUnit;
10.37 +
10.38 +/**
10.39 + * <p>Manage a renewable lease. The lease can be renewed indefinitely
10.40 + * but if the lease runs to its current expiry date without being renewed
10.41 + * then the expiry callback is invoked. If the lease has already expired
10.42 + * when renewal is attempted then the lease method returns zero.</p>
10.43 + * @author sjiang
10.44 + * @author emcmanus
10.45 + */
10.46 +// The synchronization logic of this class is tricky to deal correctly with the
10.47 +// case where the lease expires at the same time as the |lease| or |stop| method
10.48 +// is called. If the lease is active then the field |scheduled| represents
10.49 +// the expiry task; otherwise |scheduled| is null. Renewing or stopping the
10.50 +// lease involves canceling this task and setting |scheduled| either to a new
10.51 +// task (to renew) or to null (to stop).
10.52 +//
10.53 +// Suppose the expiry task runs at the same time as the |lease| method is called.
10.54 +// If the task enters its synchronized block before the method starts, then
10.55 +// it will set |scheduled| to null and the method will return 0. If the method
10.56 +// starts before the task enters its synchronized block, then the method will
10.57 +// cancel the task which will see that when it later enters the block.
10.58 +// Similar reasoning applies to the |stop| method. It is not expected that
10.59 +// different threads will call |lease| or |stop| simultaneously, although the
10.60 +// logic should be correct then too.
10.61 +public class LeaseManager {
10.62 + public LeaseManager(Runnable callback) {
10.63 + this(callback, EventParams.getLeaseTimeout());
10.64 + }
10.65 +
10.66 + public LeaseManager(Runnable callback, long timeout) {
10.67 + if (logger.traceOn()) {
10.68 + logger.trace("LeaseManager", "new manager with lease: "+timeout);
10.69 + }
10.70 + if (callback == null) {
10.71 + throw new NullPointerException("Null callback.");
10.72 + }
10.73 + if (timeout <= 0)
10.74 + throw new IllegalArgumentException("Timeout must be positive: " + timeout);
10.75 +
10.76 + this.callback = callback;
10.77 + schedule(timeout);
10.78 + }
10.79 +
10.80 + /**
10.81 + * <p>Renew the lease for the given time. The new time can be shorter
10.82 + * than the previous one, in which case the lease will expire earlier
10.83 + * than it would have.</p>
10.84 + *
10.85 + * <p>Calling this method after the lease has expired will return zero
10.86 + * immediately and have no other effect.</p>
10.87 + *
10.88 + * @param timeout the new lifetime. If zero, the lease
10.89 + * will expire immediately.
10.90 + */
10.91 + public synchronized long lease(long timeout) {
10.92 + if (logger.traceOn()) {
10.93 + logger.trace("lease", "new lease to: "+timeout);
10.94 + }
10.95 +
10.96 + if (timeout < 0)
10.97 + throw new IllegalArgumentException("Negative lease: " + timeout);
10.98 +
10.99 + if (scheduled == null)
10.100 + return 0L;
10.101 +
10.102 + scheduled.cancel(false);
10.103 +
10.104 + if (logger.traceOn())
10.105 + logger.trace("lease", "start lease: "+timeout);
10.106 + schedule(timeout);
10.107 +
10.108 + return timeout;
10.109 + }
10.110 +
10.111 + private class Expire implements Runnable {
10.112 + ScheduledFuture<?> task;
10.113 +
10.114 + public void run() {
10.115 + synchronized (LeaseManager.this) {
10.116 + if (task.isCancelled())
10.117 + return;
10.118 + scheduled = null;
10.119 + }
10.120 + callback.run();
10.121 + }
10.122 + }
10.123 +
10.124 + private synchronized void schedule(long timeout) {
10.125 + Expire expire = new Expire();
10.126 + scheduled = executor.schedule(expire, timeout, TimeUnit.MILLISECONDS);
10.127 + expire.task = scheduled;
10.128 + }
10.129 +
10.130 + /**
10.131 + * <p>Cancel the lease without calling the expiry callback.</p>
10.132 + */
10.133 + public synchronized void stop() {
10.134 + logger.trace("stop", "canceling lease");
10.135 + scheduled.cancel(false);
10.136 + scheduled = null;
10.137 + }
10.138 +
10.139 + private final Runnable callback;
10.140 + private ScheduledFuture scheduled; // If null, the lease has expired.
10.141 +
10.142 + private final ScheduledExecutorService executor
10.143 + = Executors.newScheduledThreadPool(1,
10.144 + new DaemonThreadFactory("LeaseManager"));
10.145 +
10.146 + private static final ClassLogger logger =
10.147 + new ClassLogger("javax.management.event", "LeaseManager");
10.148 +
10.149 +}
11.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
11.2 +++ b/src/share/classes/com/sun/jmx/event/LeaseRenewer.java Thu Aug 21 09:55:18 2008 -0700
11.3 @@ -0,0 +1,143 @@
11.4 +/*
11.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
11.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
11.7 + *
11.8 + * This code is free software; you can redistribute it and/or modify it
11.9 + * under the terms of the GNU General Public License version 2 only, as
11.10 + * published by the Free Software Foundation. Sun designates this
11.11 + * particular file as subject to the "Classpath" exception as provided
11.12 + * by Sun in the LICENSE file that accompanied this code.
11.13 + *
11.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
11.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11.17 + * version 2 for more details (a copy is included in the LICENSE file that
11.18 + * accompanied this code).
11.19 + *
11.20 + * You should have received a copy of the GNU General Public License version
11.21 + * 2 along with this work; if not, write to the Free Software Foundation,
11.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
11.23 + *
11.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
11.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
11.26 + * have any questions.
11.27 + */
11.28 +
11.29 +package com.sun.jmx.event;
11.30 +
11.31 +import com.sun.jmx.remote.util.ClassLogger;
11.32 +import java.util.concurrent.Callable;
11.33 +import java.util.concurrent.ScheduledExecutorService;
11.34 +import java.util.concurrent.ScheduledFuture;
11.35 +import java.util.concurrent.TimeUnit;
11.36 +
11.37 +/**
11.38 + *
11.39 + * @author sjiang
11.40 + */
11.41 +public class LeaseRenewer {
11.42 + public LeaseRenewer(ScheduledExecutorService scheduler, Callable<Long> doRenew) {
11.43 + if (logger.traceOn()) {
11.44 + logger.trace("LeaseRenewer", "New LeaseRenewer.");
11.45 + }
11.46 +
11.47 + if (doRenew == null) {
11.48 + throw new NullPointerException("Null job to call server.");
11.49 + }
11.50 +
11.51 + this.doRenew = doRenew;
11.52 + nextRenewTime = System.currentTimeMillis();
11.53 +
11.54 + this.scheduler = scheduler;
11.55 + future = this.scheduler.schedule(myRenew, 0, TimeUnit.MILLISECONDS);
11.56 + }
11.57 +
11.58 + public void close() {
11.59 + if (logger.traceOn()) {
11.60 + logger.trace("close", "Close the lease.");
11.61 + }
11.62 +
11.63 + synchronized(lock) {
11.64 + if (closed) {
11.65 + return;
11.66 + } else {
11.67 + closed = true;
11.68 + }
11.69 + }
11.70 +
11.71 + try {
11.72 + future.cancel(false); // not interrupt if running
11.73 + } catch (Exception e) {
11.74 + // OK
11.75 + if (logger.debugOn()) {
11.76 + logger.debug("close", "Failed to cancel the leasing job.", e);
11.77 + }
11.78 + }
11.79 + }
11.80 +
11.81 + public boolean closed() {
11.82 + synchronized(lock) {
11.83 + return closed;
11.84 + }
11.85 + }
11.86 +
11.87 + // ------------------------------
11.88 + // private
11.89 + // ------------------------------
11.90 + private final Runnable myRenew = new Runnable() {
11.91 + public void run() {
11.92 + synchronized(lock) {
11.93 + if (closed()) {
11.94 + return;
11.95 + }
11.96 + }
11.97 +
11.98 + long next = nextRenewTime - System.currentTimeMillis();
11.99 + if (next < MIN_MILLIS) {
11.100 + try {
11.101 + if (logger.traceOn()) {
11.102 + logger.trace("myRenew-run", "");
11.103 + }
11.104 + next = doRenew.call().longValue();
11.105 +
11.106 + } catch (Exception e) {
11.107 + logger.fine("myRenew-run", "Failed to renew lease", e);
11.108 + close();
11.109 + }
11.110 +
11.111 + if (next > 0 && next < Long.MAX_VALUE) {
11.112 + next = next/2;
11.113 + next = (next < MIN_MILLIS) ? MIN_MILLIS : next;
11.114 + } else {
11.115 + close();
11.116 + }
11.117 + }
11.118 +
11.119 + nextRenewTime = System.currentTimeMillis() + next;
11.120 +
11.121 + if (logger.traceOn()) {
11.122 + logger.trace("myRenew-run", "Next leasing: "+next);
11.123 + }
11.124 +
11.125 + synchronized(lock) {
11.126 + if (!closed) {
11.127 + future = scheduler.schedule(this, next, TimeUnit.MILLISECONDS);
11.128 + }
11.129 + }
11.130 + }
11.131 + };
11.132 +
11.133 + private final Callable<Long> doRenew;
11.134 + private ScheduledFuture future;
11.135 + private boolean closed = false;
11.136 + private long nextRenewTime;
11.137 +
11.138 + private final int[] lock = new int[0];
11.139 +
11.140 + private final ScheduledExecutorService scheduler;
11.141 +
11.142 + private static final long MIN_MILLIS = 50;
11.143 +
11.144 + private static final ClassLogger logger =
11.145 + new ClassLogger("javax.management.event", "LeaseRenewer");
11.146 +}
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/src/share/classes/com/sun/jmx/event/ReceiverBuffer.java Thu Aug 21 09:55:18 2008 -0700
12.3 @@ -0,0 +1,97 @@
12.4 +/*
12.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
12.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
12.7 + *
12.8 + * This code is free software; you can redistribute it and/or modify it
12.9 + * under the terms of the GNU General Public License version 2 only, as
12.10 + * published by the Free Software Foundation. Sun designates this
12.11 + * particular file as subject to the "Classpath" exception as provided
12.12 + * by Sun in the LICENSE file that accompanied this code.
12.13 + *
12.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
12.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12.17 + * version 2 for more details (a copy is included in the LICENSE file that
12.18 + * accompanied this code).
12.19 + *
12.20 + * You should have received a copy of the GNU General Public License version
12.21 + * 2 along with this work; if not, write to the Free Software Foundation,
12.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
12.23 + *
12.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
12.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
12.26 + * have any questions.
12.27 + */
12.28 +
12.29 +package com.sun.jmx.event;
12.30 +
12.31 +import com.sun.jmx.remote.util.ClassLogger;
12.32 +import java.util.ArrayList;
12.33 +import java.util.Collections;
12.34 +import java.util.List;
12.35 +import javax.management.remote.NotificationResult;
12.36 +import javax.management.remote.TargetedNotification;
12.37 +
12.38 +
12.39 +public class ReceiverBuffer {
12.40 + public void addNotifs(NotificationResult nr) {
12.41 + if (nr == null) {
12.42 + return;
12.43 + }
12.44 +
12.45 + TargetedNotification[] tns = nr.getTargetedNotifications();
12.46 +
12.47 + if (logger.traceOn()) {
12.48 + logger.trace("addNotifs", "" + tns.length);
12.49 + }
12.50 +
12.51 + long impliedStart = nr.getEarliestSequenceNumber();
12.52 + final long missed = impliedStart - start;
12.53 + start = nr.getNextSequenceNumber();
12.54 +
12.55 + if (missed > 0) {
12.56 + if (logger.traceOn()) {
12.57 + logger.trace("addNotifs",
12.58 + "lost: "+missed);
12.59 + }
12.60 +
12.61 + lost += missed;
12.62 + }
12.63 +
12.64 + Collections.addAll(notifList, nr.getTargetedNotifications());
12.65 + }
12.66 +
12.67 + public TargetedNotification[] removeNotifs() {
12.68 + if (logger.traceOn()) {
12.69 + logger.trace("removeNotifs", String.valueOf(notifList.size()));
12.70 + }
12.71 +
12.72 + if (notifList.size() == 0) {
12.73 + return null;
12.74 + }
12.75 +
12.76 + TargetedNotification[] ret = notifList.toArray(
12.77 + new TargetedNotification[]{});
12.78 + notifList.clear();
12.79 +
12.80 + return ret;
12.81 + }
12.82 +
12.83 + public int size() {
12.84 + return notifList.size();
12.85 + }
12.86 +
12.87 + public int removeLost() {
12.88 + int ret = lost;
12.89 + lost = 0;
12.90 + return ret;
12.91 + }
12.92 +
12.93 + private List<TargetedNotification> notifList
12.94 + = new ArrayList<TargetedNotification>();
12.95 + private long start = 0;
12.96 + private int lost = 0;
12.97 +
12.98 + private static final ClassLogger logger =
12.99 + new ClassLogger("javax.management.event", "ReceiverBuffer");
12.100 +}
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
13.2 +++ b/src/share/classes/com/sun/jmx/event/RepeatedSingletonJob.java Thu Aug 21 09:55:18 2008 -0700
13.3 @@ -0,0 +1,117 @@
13.4 +/*
13.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
13.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
13.7 + *
13.8 + * This code is free software; you can redistribute it and/or modify it
13.9 + * under the terms of the GNU General Public License version 2 only, as
13.10 + * published by the Free Software Foundation. Sun designates this
13.11 + * particular file as subject to the "Classpath" exception as provided
13.12 + * by Sun in the LICENSE file that accompanied this code.
13.13 + *
13.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
13.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13.17 + * version 2 for more details (a copy is included in the LICENSE file that
13.18 + * accompanied this code).
13.19 + *
13.20 + * You should have received a copy of the GNU General Public License version
13.21 + * 2 along with this work; if not, write to the Free Software Foundation,
13.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
13.23 + *
13.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
13.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
13.26 + * have any questions.
13.27 + */
13.28 +
13.29 +package com.sun.jmx.event;
13.30 +
13.31 +import com.sun.jmx.remote.util.ClassLogger;
13.32 +import java.util.concurrent.Executor;
13.33 +import java.util.concurrent.RejectedExecutionException;
13.34 +
13.35 +/**
13.36 + * <p>A task that is repeatedly run by an Executor. The task will be
13.37 + * repeated as long as the {@link #isSuspended()} method returns true. Once
13.38 + * that method returns false, the task is no longer executed until someone
13.39 + * calls {@link #resume()}.</p>
13.40 + * @author sjiang
13.41 + */
13.42 +public abstract class RepeatedSingletonJob implements Runnable {
13.43 + public RepeatedSingletonJob(Executor executor) {
13.44 + if (executor == null) {
13.45 + throw new NullPointerException("Null executor!");
13.46 + }
13.47 +
13.48 + this.executor = executor;
13.49 + }
13.50 +
13.51 + public boolean isWorking() {
13.52 + return working;
13.53 + }
13.54 +
13.55 + public void resume() {
13.56 +
13.57 + synchronized(this) {
13.58 + if (!working) {
13.59 + if (logger.traceOn()) {
13.60 + logger.trace("resume", "");
13.61 + }
13.62 + working = true;
13.63 + execute();
13.64 + }
13.65 + }
13.66 + }
13.67 +
13.68 + public abstract void task();
13.69 + public abstract boolean isSuspended();
13.70 +
13.71 + public void run() {
13.72 + if (logger.traceOn()) {
13.73 + logger.trace("run", "execute the task");
13.74 + }
13.75 + try {
13.76 + task();
13.77 + } catch (Exception e) {
13.78 + // A correct task() implementation should not throw exceptions.
13.79 + // It may cause isSuspended() to start returning true, though.
13.80 + logger.trace("run", "failed to execute the task", e);
13.81 + }
13.82 +
13.83 + synchronized(this) {
13.84 + if (!isSuspended()) {
13.85 + execute();
13.86 + } else {
13.87 + if (logger.traceOn()) {
13.88 + logger.trace("run", "suspend the task");
13.89 + }
13.90 + working = false;
13.91 + }
13.92 + }
13.93 +
13.94 + }
13.95 +
13.96 + private void execute() {
13.97 + try {
13.98 + executor.execute(this);
13.99 + } catch (RejectedExecutionException e) {
13.100 + logger.warning(
13.101 + "setEventReceiver", "Executor threw exception", e);
13.102 + throw new RejectedExecutionException(
13.103 + "Executor.execute threw exception -" +
13.104 + "should not be possible", e);
13.105 + // User-supplied Executor should not be configured in a way that
13.106 + // might cause this exception, for example if it is shared between
13.107 + // several client objects and doesn't have capacity for one job
13.108 + // from each one. CR 6732037 will add text to the spec explaining
13.109 + // the problem. The rethrown exception will propagate either out
13.110 + // of resume() to user code, or out of run() to the Executor
13.111 + // (which will probably ignore it).
13.112 + }
13.113 + }
13.114 +
13.115 + private boolean working = false;
13.116 + private final Executor executor;
13.117 +
13.118 + private static final ClassLogger logger =
13.119 + new ClassLogger("javax.management.event", "RepeatedSingletonJob");
13.120 +}
14.1 --- a/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java Tue Aug 19 07:50:03 2008 -0700
14.2 +++ b/src/share/classes/com/sun/jmx/interceptor/DefaultMBeanServerInterceptor.java Thu Aug 21 09:55:18 2008 -0700
14.3 @@ -453,11 +453,12 @@
14.4 final ResourceContext context =
14.5 unregisterFromRepository(resource, instance, name);
14.6
14.7 -
14.8 - if (instance instanceof MBeanRegistration)
14.9 - postDeregisterInvoke((MBeanRegistration) instance);
14.10 -
14.11 - context.done();
14.12 + try {
14.13 + if (instance instanceof MBeanRegistration)
14.14 + postDeregisterInvoke(name,(MBeanRegistration) instance);
14.15 + } finally {
14.16 + context.done();
14.17 + }
14.18 }
14.19
14.20 public ObjectInstance getObjectInstance(ObjectName name)
14.21 @@ -989,10 +990,12 @@
14.22 registerFailed = false;
14.23 registered = true;
14.24 } finally {
14.25 - postRegister(mbean, registered, registerFailed);
14.26 + try {
14.27 + postRegister(logicalName, mbean, registered, registerFailed);
14.28 + } finally {
14.29 + if (registered) context.done();
14.30 + }
14.31 }
14.32 -
14.33 - context.done();
14.34 return new ObjectInstance(logicalName, classname);
14.35 }
14.36
14.37 @@ -1051,7 +1054,8 @@
14.38 }
14.39
14.40 private static void postRegister(
14.41 - DynamicMBean mbean, boolean registrationDone, boolean registerFailed) {
14.42 + ObjectName logicalName, DynamicMBean mbean,
14.43 + boolean registrationDone, boolean registerFailed) {
14.44
14.45 if (registerFailed && mbean instanceof DynamicMBean2)
14.46 ((DynamicMBean2) mbean).registerFailed();
14.47 @@ -1059,11 +1063,19 @@
14.48 if (mbean instanceof MBeanRegistration)
14.49 ((MBeanRegistration) mbean).postRegister(registrationDone);
14.50 } catch (RuntimeException e) {
14.51 + MBEANSERVER_LOGGER.fine("While registering MBean ["+logicalName+
14.52 + "]: " + "Exception thrown by postRegister: " +
14.53 + "rethrowing <"+e+">, but keeping the MBean registered");
14.54 throw new RuntimeMBeanException(e,
14.55 - "RuntimeException thrown in postRegister method");
14.56 + "RuntimeException thrown in postRegister method: "+
14.57 + "rethrowing <"+e+">, but keeping the MBean registered");
14.58 } catch (Error er) {
14.59 + MBEANSERVER_LOGGER.fine("While registering MBean ["+logicalName+
14.60 + "]: " + "Error thrown by postRegister: " +
14.61 + "rethrowing <"+er+">, but keeping the MBean registered");
14.62 throw new RuntimeErrorException(er,
14.63 - "Error thrown in postRegister method");
14.64 + "Error thrown in postRegister method: "+
14.65 + "rethrowing <"+er+">, but keeping the MBean registered");
14.66 }
14.67 }
14.68
14.69 @@ -1076,15 +1088,28 @@
14.70 }
14.71 }
14.72
14.73 - private static void postDeregisterInvoke(MBeanRegistration moi) {
14.74 + private static void postDeregisterInvoke(ObjectName mbean,
14.75 + MBeanRegistration moi) {
14.76 try {
14.77 moi.postDeregister();
14.78 } catch (RuntimeException e) {
14.79 + MBEANSERVER_LOGGER.fine("While unregistering MBean ["+mbean+
14.80 + "]: " + "Exception thrown by postDeregister: " +
14.81 + "rethrowing <"+e+">, although the MBean is succesfully " +
14.82 + "unregistered");
14.83 throw new RuntimeMBeanException(e,
14.84 - "RuntimeException thrown in postDeregister method");
14.85 + "RuntimeException thrown in postDeregister method: "+
14.86 + "rethrowing <"+e+
14.87 + ">, although the MBean is sucessfully unregistered");
14.88 } catch (Error er) {
14.89 + MBEANSERVER_LOGGER.fine("While unregistering MBean ["+mbean+
14.90 + "]: " + "Error thrown by postDeregister: " +
14.91 + "rethrowing <"+er+">, although the MBean is succesfully " +
14.92 + "unregistered");
14.93 throw new RuntimeErrorException(er,
14.94 - "Error thrown in postDeregister method");
14.95 + "Error thrown in postDeregister method: "+
14.96 + "rethrowing <"+er+
14.97 + ">, although the MBean is sucessfully unregistered");
14.98 }
14.99 }
14.100
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
15.2 +++ b/src/share/classes/com/sun/jmx/interceptor/MBeanServerSupport.java Thu Aug 21 09:55:18 2008 -0700
15.3 @@ -0,0 +1,1341 @@
15.4 +/*
15.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
15.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
15.7 + *
15.8 + * This code is free software; you can redistribute it and/or modify it
15.9 + * under the terms of the GNU General Public License version 2 only, as
15.10 + * published by the Free Software Foundation. Sun designates this
15.11 + * particular file as subject to the "Classpath" exception as provided
15.12 + * by Sun in the LICENSE file that accompanied this code.
15.13 + *
15.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
15.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15.17 + * version 2 for more details (a copy is included in the LICENSE file that
15.18 + * accompanied this code).
15.19 + *
15.20 + * You should have received a copy of the GNU General Public License version
15.21 + * 2 along with this work; if not, write to the Free Software Foundation,
15.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
15.23 + *
15.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
15.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
15.26 + * have any questions.
15.27 + */
15.28 +package com.sun.jmx.interceptor;
15.29 +
15.30 +import com.sun.jmx.mbeanserver.Util;
15.31 +import java.io.ObjectInputStream;
15.32 +import java.util.Collections;
15.33 +import java.util.HashSet;
15.34 +import java.util.Set;
15.35 +import java.util.TreeSet;
15.36 +import java.util.logging.Level;
15.37 +import java.util.logging.Logger;
15.38 +import javax.management.Attribute;
15.39 +import javax.management.AttributeList;
15.40 +import javax.management.AttributeNotFoundException;
15.41 +import javax.management.DynamicMBean;
15.42 +import javax.management.DynamicWrapperMBean;
15.43 +import javax.management.InstanceAlreadyExistsException;
15.44 +import javax.management.InstanceNotFoundException;
15.45 +import javax.management.IntrospectionException;
15.46 +import javax.management.InvalidAttributeValueException;
15.47 +import javax.management.JMRuntimeException;
15.48 +import javax.management.ListenerNotFoundException;
15.49 +import javax.management.MBeanException;
15.50 +import javax.management.MBeanInfo;
15.51 +import javax.management.MBeanRegistrationException;
15.52 +import javax.management.MBeanServer;
15.53 +import javax.management.MalformedObjectNameException;
15.54 +import javax.management.NotCompliantMBeanException;
15.55 +import javax.management.NotificationBroadcaster;
15.56 +import javax.management.NotificationEmitter;
15.57 +import javax.management.NotificationFilter;
15.58 +import javax.management.NotificationListener;
15.59 +import javax.management.ObjectInstance;
15.60 +import javax.management.ObjectName;
15.61 +import javax.management.OperationsException;
15.62 +import javax.management.QueryEval;
15.63 +import javax.management.QueryExp;
15.64 +import javax.management.ReflectionException;
15.65 +import javax.management.RuntimeOperationsException;
15.66 +import javax.management.loading.ClassLoaderRepository;
15.67 +
15.68 +/**
15.69 + * <p>Base class for custom implementations of the {@link MBeanServer}
15.70 + * interface. The commonest use of this class is as the {@linkplain
15.71 + * JMXNamespace#getSourceServer() source server} for a {@link
15.72 + * JMXNamespace}, although this class can be used anywhere an {@code
15.73 + * MBeanServer} instance is required. Note that the usual ways to
15.74 + * obtain an {@code MBeanServer} instance are either to use {@link
15.75 + * java.lang.management.ManagementFactory#getPlatformMBeanServer()
15.76 + * ManagementFactory.getPlatformMBeanServer()} or to use the {@code
15.77 + * newMBeanServer} or {@code createMBeanServer} methods from {@link
15.78 + * javax.management.MBeanServerFactory MBeanServerFactory}. {@code
15.79 + * MBeanServerSupport} is for certain cases where those are not
15.80 + * appropriate.</p>
15.81 + *
15.82 + * <p>There are two main use cases for this class: <a
15.83 + * href="#special-purpose">special-purpose MBeanServer implementations</a>,
15.84 + * and <a href="#virtual">namespaces containing Virtual MBeans</a>. The next
15.85 + * sections explain these use cases.</p>
15.86 + *
15.87 + * <p>In the simplest case, a subclass needs to implement only two methods:</p>
15.88 + *
15.89 + * <ul>
15.90 + * <li>
15.91 + * {@link #getNames getNames} which returns the name of
15.92 + * all MBeans handled by this {@code MBeanServer}.
15.93 + * </li>
15.94 + * <li>
15.95 + * {@link #getDynamicMBeanFor getDynamicMBeanFor} which returns a
15.96 + * {@link DynamicMBean} that can be used to invoke operations and
15.97 + * obtain meta data (MBeanInfo) on a given MBean.
15.98 + * </li>
15.99 + * </ul>
15.100 + *
15.101 + * <p>Subclasses can create such {@link DynamicMBean} MBeans on the fly - for
15.102 + * instance, using the class {@link javax.management.StandardMBean}, just for
15.103 + * the duration of an MBeanServer method call.</p>
15.104 + *
15.105 + * <h4 id="special-purpose">Special-purpose MBeanServer implementations</h4>
15.106 + *
15.107 + * <p>In some cases
15.108 + * the general-purpose {@code MBeanServer} that you get from
15.109 + * {@link javax.management.MBeanServerFactory MBeanServerFactory} is not
15.110 + * appropriate. You might need different security checks, or you might
15.111 + * want a mock {@code MBeanServer} suitable for use in tests, or you might
15.112 + * want a simplified and optimized {@code MBeanServer} for a special purpose.</p>
15.113 + *
15.114 + * <p>As an example of a special-purpose {@code MBeanServer}, the class {@link
15.115 + * javax.management.QueryNotificationFilter QueryNotificationFilter} constructs
15.116 + * an {@code MBeanServer} instance every time it filters a notification,
15.117 + * with just one MBean that represents the notification. Although it could
15.118 + * use {@code MBeanServerFactory.newMBeanServer}, a special-purpose {@code
15.119 + * MBeanServer} will be quicker to create, use less memory, and have simpler
15.120 + * methods that execute faster.</p>
15.121 + *
15.122 + * <p>Here is an example of a special-purpose {@code MBeanServer}
15.123 + * implementation that contains exactly one MBean, which is specified at the
15.124 + * time of creation.</p>
15.125 + *
15.126 + * <pre>
15.127 + * public class SingletonMBeanServer extends MBeanServerSupport {
15.128 + * private final ObjectName objectName;
15.129 + * private final DynamicMBean mbean;
15.130 + *
15.131 + * public SingletonMBeanServer(ObjectName objectName, DynamicMBean mbean) {
15.132 + * this.objectName = objectName;
15.133 + * this.mbean = mbean;
15.134 + * }
15.135 + *
15.136 + * @Override
15.137 + * protected {@code Set<ObjectName>} {@link #getNames getNames}() {
15.138 + * return Collections.singleton(objectName);
15.139 + * }
15.140 + *
15.141 + * @Override
15.142 + * public DynamicMBean {@link #getDynamicMBeanFor
15.143 + * getDynamicMBeanFor}(ObjectName name)
15.144 + * throws InstanceNotFoundException {
15.145 + * if (objectName.equals(name))
15.146 + * return mbean;
15.147 + * else
15.148 + * throw new InstanceNotFoundException(name);
15.149 + * }
15.150 + * }
15.151 + * </pre>
15.152 + *
15.153 + * <p>Using this class, you could make an {@code MBeanServer} that contains
15.154 + * a {@link javax.management.timer.Timer Timer} MBean like this:</p>
15.155 + *
15.156 + * <pre>
15.157 + * Timer timer = new Timer();
15.158 + * DynamicMBean mbean = new {@link javax.management.StandardMBean
15.159 + * StandardMBean}(timer, TimerMBean.class);
15.160 + * ObjectName name = new ObjectName("com.example:type=Timer");
15.161 + * MBeanServer timerMBS = new SingletonMBeanServer(name, mbean);
15.162 + * </pre>
15.163 + *
15.164 + * <p>When {@code getDynamicMBeanFor} always returns the same object for the
15.165 + * same name, as here, notifications work in the expected way: if the object
15.166 + * is a {@link NotificationEmitter} then listeners can be added using
15.167 + * {@link MBeanServer#addNotificationListener(ObjectName, NotificationListener,
15.168 + * NotificationFilter, Object) MBeanServer.addNotificationListener}. If
15.169 + * {@code getDynamicMBeanFor} does not always return the same object for the
15.170 + * same name, more work is needed to make notifications work, as described
15.171 + * <a href="#notifs">below</a>.</p>
15.172 + *
15.173 + * <h4 id="virtual">Namespaces containing Virtual MBeans</h4>
15.174 + *
15.175 + * <p>Virtual MBeans are MBeans that do not exist as Java objects,
15.176 + * except transiently while they are being accessed. This is useful when
15.177 + * there might be very many of them, or when keeping track of their creation
15.178 + * and deletion might be expensive or hard. For example, you might have one
15.179 + * MBean per system process. With an ordinary {@code MBeanServer}, you would
15.180 + * have to list the system processes in order to create an MBean object for
15.181 + * each one, and you would have to track the arrival and departure of system
15.182 + * processes in order to create or delete the corresponding MBeans. With
15.183 + * Virtual MBeans, you only need the MBean for a given process at the exact
15.184 + * point where it is referenced with a call such as
15.185 + * {@link MBeanServer#getAttribute MBeanServer.getAttribute}.</p>
15.186 + *
15.187 + * <p>Here is an example of an {@code MBeanServer} implementation that has
15.188 + * one MBean for every system property. The system property {@code "java.home"}
15.189 + * is represented by the MBean called {@code
15.190 + * com.example:type=Property,name="java.home"}, with an attribute called
15.191 + * {@code Value} that is the value of the property.</p>
15.192 + *
15.193 + * <pre>
15.194 + * public interface PropertyMBean {
15.195 + * public String getValue();
15.196 + * }
15.197 + *
15.198 + * <a name="PropsMBS"></a>public class PropsMBS extends MBeanServerSupport {
15.199 + * private static ObjectName newObjectName(String name) {
15.200 + * try {
15.201 + * return new ObjectName(name);
15.202 + * } catch (MalformedObjectNameException e) {
15.203 + * throw new AssertionError(e);
15.204 + * }
15.205 + * }
15.206 + *
15.207 + * public static class PropertyImpl implements PropertyMBean {
15.208 + * private final String name;
15.209 + *
15.210 + * public PropertyImpl(String name) {
15.211 + * this.name = name;
15.212 + * }
15.213 + *
15.214 + * public String getValue() {
15.215 + * return System.getProperty(name);
15.216 + * }
15.217 + * }
15.218 + *
15.219 + * @Override
15.220 + * public DynamicMBean {@link #getDynamicMBeanFor
15.221 + * getDynamicMBeanFor}(ObjectName name)
15.222 + * throws InstanceNotFoundException {
15.223 + *
15.224 + * // Check that the name is a legal one for a Property MBean
15.225 + * ObjectName namePattern = newObjectName(
15.226 + * "com.example:type=Property,name=\"*\"");
15.227 + * if (!namePattern.apply(name))
15.228 + * throw new InstanceNotFoundException(name);
15.229 + *
15.230 + * // Extract the name of the property that the MBean corresponds to
15.231 + * String propName = ObjectName.unquote(name.getKeyProperty("name"));
15.232 + * if (System.getProperty(propName) == null)
15.233 + * throw new InstanceNotFoundException(name);
15.234 + *
15.235 + * // Construct and return a transient MBean object
15.236 + * PropertyMBean propMBean = new PropertyImpl(propName);
15.237 + * return new StandardMBean(propMBean, PropertyMBean.class, false);
15.238 + * }
15.239 + *
15.240 + * @Override
15.241 + * protected {@code Set<ObjectName>} {@link #getNames getNames}() {
15.242 + * {@code Set<ObjectName> names = new TreeSet<ObjectName>();}
15.243 + * Properties props = System.getProperties();
15.244 + * for (String propName : props.stringPropertyNames()) {
15.245 + * ObjectName objectName = newObjectName(
15.246 + * "com.example:type=Property,name=" +
15.247 + * ObjectName.quote(propName));
15.248 + * names.add(objectName);
15.249 + * }
15.250 + * return names;
15.251 + * }
15.252 + * }
15.253 + * </pre>
15.254 + *
15.255 + * <p id="virtual-notif-example">Because the {@code getDynamicMBeanFor} method
15.256 + * returns a different object every time it is called, the default handling
15.257 + * of notifications will not work, as explained <a href="#notifs">below</a>.
15.258 + * In this case it does not matter, because the object returned by {@code
15.259 + * getDynamicMBeanFor} is not a {@code NotificationEmitter}, so {@link
15.260 + * MBeanServer#addNotificationListener(ObjectName, NotificationListener,
15.261 + * NotificationFilter, Object) MBeanServer.addNotificationListener} will
15.262 + * always fail. But if we wanted to extend {@code PropsMBS} so that the MBean
15.263 + * for property {@code "foo"} emitted a notification every time that property
15.264 + * changed, we would need to do it as shown below. (Because there is no API to
15.265 + * be informed when a property changes, this code assumes that some other code
15.266 + * calls the {@code propertyChanged} method every time a property changes.)</p>
15.267 + *
15.268 + * <pre>
15.269 + * public class PropsMBS {
15.270 + * ...as <a href="#PropsMBS">above</a>...
15.271 + *
15.272 + * private final {@link VirtualEventManager} vem = new VirtualEventManager();
15.273 + *
15.274 + * @Override
15.275 + * public NotificationEmitter {@link #getNotificationEmitterFor
15.276 + * getNotificationEmitterFor}(
15.277 + * ObjectName name) throws InstanceNotFoundException {
15.278 + * getDynamicMBeanFor(name); // check that the name is valid
15.279 + * return vem.{@link VirtualEventManager#getNotificationEmitterFor
15.280 + * getNotificationEmitterFor}(name);
15.281 + * }
15.282 + *
15.283 + * public void propertyChanged(String name, String newValue) {
15.284 + * ObjectName objectName = newObjectName(
15.285 + * "com.example:type=Property,name=" + ObjectName.quote(name));
15.286 + * Notification n = new Notification(
15.287 + * "com.example.property.changed", objectName, 0L,
15.288 + * "Property " + name + " changed");
15.289 + * n.setUserData(newValue);
15.290 + * vem.{@link VirtualEventManager#publish publish}(objectName, n);
15.291 + * }
15.292 + * }
15.293 + * </pre>
15.294 + *
15.295 + * <h4 id="creation">MBean creation and deletion</h4>
15.296 + *
15.297 + * <p>MBean creation through {@code MBeanServer.createMBean} is disabled
15.298 + * by default. Subclasses which need to support MBean creation
15.299 + * through {@code createMBean} need to implement a single method {@link
15.300 + * #createMBean(String, ObjectName, ObjectName, Object[], String[],
15.301 + * boolean)}.</p>
15.302 + *
15.303 + * <p>Similarly MBean registration and unregistration through {@code
15.304 + * registerMBean} and {@code unregisterMBean} are disabled by default.
15.305 + * Subclasses which need to support MBean registration and
15.306 + * unregistration will need to implement {@link #registerMBean registerMBean}
15.307 + * and {@link #unregisterMBean unregisterMBean}.</p>
15.308 + *
15.309 + * <h4 id="notifs">Notifications</h4>
15.310 + *
15.311 + * <p>By default {@link MBeanServer#addNotificationListener(ObjectName,
15.312 + * NotificationListener, NotificationFilter, Object) addNotificationListener}
15.313 + * is accepted for an MBean <em>{@code name}</em> if {@link #getDynamicMBeanFor
15.314 + * getDynamicMBeanFor}<code>(<em>name</em>)</code> returns an object that is a
15.315 + * {@link NotificationEmitter}. That is appropriate if
15.316 + * {@code getDynamicMBeanFor}<code>(<em>name</em>)</code> always returns the
15.317 + * same object for the same <em>{@code name}</em>. But with
15.318 + * Virtual MBeans, every call to {@code getDynamicMBeanFor} returns a new object,
15.319 + * which is discarded as soon as the MBean request has finished.
15.320 + * So a listener added to that object would be immediately forgotten.</p>
15.321 + *
15.322 + * <p>The simplest way for a subclass that defines Virtual MBeans
15.323 + * to support notifications is to create a private {@link VirtualEventManager}
15.324 + * and override the method {@link
15.325 + * #getNotificationEmitterFor getNotificationEmitterFor} as follows:</p>
15.326 + *
15.327 + * <pre>
15.328 + * private final VirtualEventManager vem = new VirtualEventManager();
15.329 + *
15.330 + * @Override
15.331 + * public NotificationEmitter getNotificationEmitterFor(
15.332 + * ObjectName name) throws InstanceNotFoundException {
15.333 + * // Check that the name is a valid Virtual MBean.
15.334 + * // This is the easiest way to do that, but not always the
15.335 + * // most efficient:
15.336 + * getDynamicMBeanFor(name);
15.337 + *
15.338 + * // Return an object that supports add/removeNotificationListener
15.339 + * // through the VirtualEventManager.
15.340 + * return vem.getNotificationEmitterFor(name);
15.341 + * }
15.342 + * </pre>
15.343 + *
15.344 + * <p>A notification <em>{@code n}</em> can then be sent from the Virtual MBean
15.345 + * called <em>{@code name}</em> by calling {@link VirtualEventManager#publish
15.346 + * vem.publish}<code>(<em>name</em>, <em>n</em>)</code>. See the example
15.347 + * <a href="#virtual-notif-example">above</a>.</p>
15.348 + *
15.349 + * @since Java SE 7
15.350 + */
15.351 +public abstract class MBeanServerSupport implements MBeanServer {
15.352 +
15.353 + /**
15.354 + * A logger for this class.
15.355 + */
15.356 + private static final Logger LOG =
15.357 + Logger.getLogger(MBeanServerSupport.class.getName());
15.358 +
15.359 + /**
15.360 + * <p>Make a new {@code MBeanServerSupport} instance.</p>
15.361 + */
15.362 + protected MBeanServerSupport() {
15.363 + }
15.364 +
15.365 + /**
15.366 + * <p>Returns a dynamically created handle that makes it possible to
15.367 + * access the named MBean for the duration of a method call.</p>
15.368 + *
15.369 + * <p>An easy way to create such a {@link DynamicMBean} handle is, for
15.370 + * instance, to create a temporary MXBean instance and to wrap it in
15.371 + * an instance of
15.372 + * {@link javax.management.StandardMBean}.
15.373 + * This handle should remain valid for the duration of the call
15.374 + * but can then be discarded.</p>
15.375 + * @param name the name of the MBean for which a request was received.
15.376 + * @return a {@link DynamicMBean} handle that can be used to invoke
15.377 + * operations on the named MBean.
15.378 + * @throws InstanceNotFoundException if no such MBean is supposed
15.379 + * to exist.
15.380 + */
15.381 + public abstract DynamicMBean getDynamicMBeanFor(ObjectName name)
15.382 + throws InstanceNotFoundException;
15.383 +
15.384 + /**
15.385 + * <p>Subclasses should implement this method to return
15.386 + * the names of all MBeans handled by this object instance.</p>
15.387 + *
15.388 + * <p>The object returned by getNames() should be safely {@linkplain
15.389 + * Set#iterator iterable} even in the presence of other threads that may
15.390 + * cause the set of names to change. Typically this means one of the
15.391 + * following:</p>
15.392 + *
15.393 + * <ul>
15.394 + * <li>the returned set of names is always the same; or
15.395 + * <li>the returned set of names is an object such as a {@link
15.396 + * java.util.concurrent.CopyOnWriteArraySet CopyOnWriteArraySet} that is
15.397 + * safely iterable even if the set is changed by other threads; or
15.398 + * <li>a new Set is constructed every time this method is called.
15.399 + * </ul>
15.400 + *
15.401 + * @return the names of all MBeans handled by this object.
15.402 + */
15.403 + protected abstract Set<ObjectName> getNames();
15.404 +
15.405 + /**
15.406 + * <p>List names matching the given pattern.
15.407 + * The default implementation of this method calls {@link #getNames()}
15.408 + * and returns the subset of those names matching {@code pattern}.</p>
15.409 + *
15.410 + * @param pattern an ObjectName pattern
15.411 + * @return the list of MBean names that match the given pattern.
15.412 + */
15.413 + protected Set<ObjectName> getMatchingNames(ObjectName pattern) {
15.414 + return Util.filterMatchingNames(pattern, getNames());
15.415 + }
15.416 +
15.417 + /**
15.418 + * <p>Returns a {@link NotificationEmitter} which can be used to
15.419 + * subscribe or unsubscribe for notifications with the named
15.420 + * mbean.</p>
15.421 + *
15.422 + * <p>The default implementation of this method calls {@link
15.423 + * #getDynamicMBeanFor getDynamicMBeanFor(name)} and returns that object
15.424 + * if it is a {@code NotificationEmitter}, otherwise null. See <a
15.425 + * href="#notifs">above</a> for further discussion of notification
15.426 + * handling.</p>
15.427 + *
15.428 + * @param name The name of the MBean whose notifications are being
15.429 + * subscribed, or unsuscribed.
15.430 + *
15.431 + * @return A {@link NotificationEmitter} that can be used to subscribe or
15.432 + * unsubscribe for notifications emitted by the named MBean, or {@code
15.433 + * null} if the MBean does not emit notifications and should not be
15.434 + * considered as a {@code NotificationEmitter}.
15.435 + *
15.436 + * @throws InstanceNotFoundException if {@code name} is not the name of
15.437 + * an MBean in this {@code MBeanServer}.
15.438 + */
15.439 + public NotificationEmitter getNotificationEmitterFor(ObjectName name)
15.440 + throws InstanceNotFoundException {
15.441 + DynamicMBean mbean = getDynamicMBeanFor(name);
15.442 + if (mbean instanceof NotificationEmitter)
15.443 + return (NotificationEmitter) mbean;
15.444 + else
15.445 + return null;
15.446 + }
15.447 +
15.448 + private NotificationEmitter getNonNullNotificationEmitterFor(
15.449 + ObjectName name)
15.450 + throws InstanceNotFoundException {
15.451 + NotificationEmitter emitter = getNotificationEmitterFor(name);
15.452 + if (emitter == null) {
15.453 + IllegalArgumentException iae = new IllegalArgumentException(
15.454 + "Not a NotificationEmitter: " + name);
15.455 + throw new RuntimeOperationsException(iae);
15.456 + }
15.457 + return emitter;
15.458 + }
15.459 +
15.460 + /**
15.461 + * <p>Creates a new MBean in the MBean name space.
15.462 + * This operation is not supported in this base class implementation.</p>
15.463 + * The default implementation of this method always throws an {@link
15.464 + * UnsupportedOperationException}
15.465 + * wrapped in a {@link RuntimeOperationsException}.</p>
15.466 + *
15.467 + * <p>Subclasses may redefine this method to provide an implementation.
15.468 + * All the various flavors of {@code MBeanServer.createMBean} methods
15.469 + * will eventually call this method. A subclass that wishes to
15.470 + * support MBean creation through {@code createMBean} thus only
15.471 + * needs to provide an implementation for this one method.
15.472 + *
15.473 + * @param className The class name of the MBean to be instantiated.
15.474 + * @param name The object name of the MBean. May be null.
15.475 + * @param params An array containing the parameters of the
15.476 + * constructor to be invoked.
15.477 + * @param signature An array containing the signature of the
15.478 + * constructor to be invoked.
15.479 + * @param loaderName The object name of the class loader to be used.
15.480 + * @param useCLR This parameter is {@code true} when this method
15.481 + * is called from one of the {@code MBeanServer.createMBean} methods
15.482 + * whose signature does not include the {@code ObjectName} of an
15.483 + * MBean class loader to use for loading the MBean class.
15.484 + *
15.485 + * @return An <CODE>ObjectInstance</CODE>, containing the
15.486 + * <CODE>ObjectName</CODE> and the Java class name of the newly
15.487 + * instantiated MBean. If the contained <code>ObjectName</code>
15.488 + * is <code>n</code>, the contained Java class name is
15.489 + * <code>{@link javax.management.MBeanServer#getMBeanInfo
15.490 + * getMBeanInfo(n)}.getClassName()</code>.
15.491 + *
15.492 + * @exception ReflectionException Wraps a
15.493 + * <CODE>java.lang.ClassNotFoundException</CODE> or a
15.494 + * <CODE>java.lang.Exception</CODE> that occurred when trying to
15.495 + * invoke the MBean's constructor.
15.496 + * @exception InstanceAlreadyExistsException The MBean is already
15.497 + * under the control of the MBean server.
15.498 + * @exception MBeanRegistrationException The
15.499 + * <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
15.500 + * interface) method of the MBean has thrown an exception. The
15.501 + * MBean will not be registered.
15.502 + * @exception MBeanException The constructor of the MBean has
15.503 + * thrown an exception
15.504 + * @exception NotCompliantMBeanException This class is not a JMX
15.505 + * compliant MBean
15.506 + * @exception InstanceNotFoundException The specified class loader
15.507 + * is not registered in the MBean server.
15.508 + * @exception RuntimeOperationsException Wraps either:
15.509 + * <ul>
15.510 + * <li>a <CODE>java.lang.IllegalArgumentException</CODE>: The className
15.511 + * passed in parameter is null, the <CODE>ObjectName</CODE> passed in
15.512 + * parameter contains a pattern or no <CODE>ObjectName</CODE> is specified
15.513 + * for the MBean; or</li>
15.514 + * <li>an {@code UnsupportedOperationException} if creating MBeans is not
15.515 + * supported by this {@code MBeanServer} implementation.
15.516 + * </ul>
15.517 + */
15.518 + public ObjectInstance createMBean(String className,
15.519 + ObjectName name, ObjectName loaderName, Object[] params,
15.520 + String[] signature, boolean useCLR)
15.521 + throws ReflectionException, InstanceAlreadyExistsException,
15.522 + MBeanRegistrationException, MBeanException,
15.523 + NotCompliantMBeanException, InstanceNotFoundException {
15.524 + throw newUnsupportedException("createMBean");
15.525 + }
15.526 +
15.527 +
15.528 + /**
15.529 + * <p>Attempts to determine whether the named MBean should be
15.530 + * considered as an instance of a given class. The default implementation
15.531 + * of this method calls {@link #getDynamicMBeanFor getDynamicMBeanFor(name)}
15.532 + * to get an MBean object. Then its behaviour is the same as the standard
15.533 + * {@link MBeanServer#isInstanceOf MBeanServer.isInstanceOf} method.</p>
15.534 + *
15.535 + * {@inheritDoc}
15.536 + */
15.537 + public boolean isInstanceOf(ObjectName name, String className)
15.538 + throws InstanceNotFoundException {
15.539 +
15.540 + final DynamicMBean instance = nonNullMBeanFor(name);
15.541 +
15.542 + try {
15.543 + final String mbeanClassName = instance.getMBeanInfo().getClassName();
15.544 +
15.545 + if (mbeanClassName.equals(className))
15.546 + return true;
15.547 +
15.548 + final Object resource;
15.549 + final ClassLoader cl;
15.550 + if (instance instanceof DynamicWrapperMBean) {
15.551 + DynamicWrapperMBean d = (DynamicWrapperMBean) instance;
15.552 + resource = d.getWrappedObject();
15.553 + cl = d.getWrappedClassLoader();
15.554 + } else {
15.555 + resource = instance;
15.556 + cl = instance.getClass().getClassLoader();
15.557 + }
15.558 +
15.559 + final Class<?> classNameClass = Class.forName(className, false, cl);
15.560 +
15.561 + if (classNameClass.isInstance(resource))
15.562 + return true;
15.563 +
15.564 + if (classNameClass == NotificationBroadcaster.class ||
15.565 + classNameClass == NotificationEmitter.class) {
15.566 + try {
15.567 + getNotificationEmitterFor(name);
15.568 + return true;
15.569 + } catch (Exception x) {
15.570 + LOG.finest("MBean " + name +
15.571 + " is not a notification emitter. Ignoring: "+x);
15.572 + return false;
15.573 + }
15.574 + }
15.575 +
15.576 + final Class<?> resourceClass = Class.forName(mbeanClassName, false, cl);
15.577 + return classNameClass.isAssignableFrom(resourceClass);
15.578 + } catch (Exception x) {
15.579 + /* Could be SecurityException or ClassNotFoundException */
15.580 + LOG.logp(Level.FINEST,
15.581 + MBeanServerSupport.class.getName(),
15.582 + "isInstanceOf", "Exception calling isInstanceOf", x);
15.583 + return false;
15.584 + }
15.585 + }
15.586 +
15.587 + /**
15.588 + * {@inheritDoc}
15.589 + *
15.590 + * <p>The default implementation of this method returns the string
15.591 + * "DefaultDomain".</p>
15.592 + */
15.593 + public String getDefaultDomain() {
15.594 + return "DefaultDomain";
15.595 + }
15.596 +
15.597 + /**
15.598 + * {@inheritDoc}
15.599 + *
15.600 + * <p>The default implementation of this method returns
15.601 + * {@link #getNames()}.size().</p>
15.602 + */
15.603 + public Integer getMBeanCount() {
15.604 + return getNames().size();
15.605 + }
15.606 +
15.607 + /**
15.608 + * {@inheritDoc}
15.609 + *
15.610 + * <p>The default implementation of this method first calls {@link #getNames
15.611 + * getNames()} to get a list of all MBean names,
15.612 + * and from this set of names, derives the set of domains which contain
15.613 + * MBeans.</p>
15.614 + */
15.615 + public String[] getDomains() {
15.616 + final Set<ObjectName> names = getNames();
15.617 + final Set<String> res = new TreeSet<String>();
15.618 + for (ObjectName n : names) {
15.619 + if (n == null) continue; // not allowed but you never know.
15.620 + res.add(n.getDomain());
15.621 + }
15.622 + return res.toArray(new String[res.size()]);
15.623 + }
15.624 +
15.625 +
15.626 + /**
15.627 + * {@inheritDoc}
15.628 + *
15.629 + * <p>The default implementation of this method will first
15.630 + * call {@link
15.631 + * #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a handle
15.632 + * to the named MBean,
15.633 + * and then call {@link DynamicMBean#getAttribute getAttribute}
15.634 + * on that {@link DynamicMBean} handle.</p>
15.635 + *
15.636 + * @throws RuntimeOperationsException {@inheritDoc}
15.637 + */
15.638 + public Object getAttribute(ObjectName name, String attribute)
15.639 + throws MBeanException, AttributeNotFoundException,
15.640 + InstanceNotFoundException, ReflectionException {
15.641 + final DynamicMBean mbean = nonNullMBeanFor(name);
15.642 + return mbean.getAttribute(attribute);
15.643 + }
15.644 +
15.645 + /**
15.646 + * {@inheritDoc}
15.647 + *
15.648 + * <p>The default implementation of this method will first
15.649 + * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)}
15.650 + * to obtain a handle to the named MBean,
15.651 + * and then call {@link DynamicMBean#setAttribute setAttribute}
15.652 + * on that {@link DynamicMBean} handle.</p>
15.653 + *
15.654 + * @throws RuntimeOperationsException {@inheritDoc}
15.655 + */
15.656 + public void setAttribute(ObjectName name, Attribute attribute)
15.657 + throws InstanceNotFoundException, AttributeNotFoundException,
15.658 + InvalidAttributeValueException, MBeanException,
15.659 + ReflectionException {
15.660 + final DynamicMBean mbean = nonNullMBeanFor(name);
15.661 + mbean.setAttribute(attribute);
15.662 + }
15.663 +
15.664 + /**
15.665 + * {@inheritDoc}
15.666 + *
15.667 + * <p>The default implementation of this method will first
15.668 + * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
15.669 + * handle to the named MBean,
15.670 + * and then call {@link DynamicMBean#getAttributes getAttributes}
15.671 + * on that {@link DynamicMBean} handle.</p>
15.672 + *
15.673 + * @throws RuntimeOperationsException {@inheritDoc}
15.674 + */
15.675 + public AttributeList getAttributes(ObjectName name,
15.676 + String[] attributes) throws InstanceNotFoundException,
15.677 + ReflectionException {
15.678 + final DynamicMBean mbean = nonNullMBeanFor(name);
15.679 + return mbean.getAttributes(attributes);
15.680 + }
15.681 +
15.682 + /**
15.683 + * {@inheritDoc}
15.684 + *
15.685 + * <p>The default implementation of this method will first
15.686 + * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
15.687 + * handle to the named MBean,
15.688 + * and then call {@link DynamicMBean#setAttributes setAttributes}
15.689 + * on that {@link DynamicMBean} handle.</p>
15.690 + *
15.691 + * @throws RuntimeOperationsException {@inheritDoc}
15.692 + */
15.693 + public AttributeList setAttributes(ObjectName name, AttributeList attributes)
15.694 + throws InstanceNotFoundException, ReflectionException {
15.695 + final DynamicMBean mbean = nonNullMBeanFor(name);
15.696 + return mbean.setAttributes(attributes);
15.697 + }
15.698 +
15.699 + /**
15.700 + * {@inheritDoc}
15.701 + *
15.702 + * <p>The default implementation of this method will first
15.703 + * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
15.704 + * handle to the named MBean,
15.705 + * and then call {@link DynamicMBean#invoke invoke}
15.706 + * on that {@link DynamicMBean} handle.</p>
15.707 + */
15.708 + public Object invoke(ObjectName name, String operationName,
15.709 + Object[] params, String[] signature)
15.710 + throws InstanceNotFoundException, MBeanException,
15.711 + ReflectionException {
15.712 + final DynamicMBean mbean = nonNullMBeanFor(name);
15.713 + return mbean.invoke(operationName, params, signature);
15.714 + }
15.715 +
15.716 + /**
15.717 + * {@inheritDoc}
15.718 + *
15.719 + * <p>The default implementation of this method will first
15.720 + * call {@link #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a
15.721 + * handle to the named MBean,
15.722 + * and then call {@link DynamicMBean#getMBeanInfo getMBeanInfo}
15.723 + * on that {@link DynamicMBean} handle.</p>
15.724 + */
15.725 + public MBeanInfo getMBeanInfo(ObjectName name)
15.726 + throws InstanceNotFoundException, IntrospectionException,
15.727 + ReflectionException {
15.728 + final DynamicMBean mbean = nonNullMBeanFor(name);
15.729 + return mbean.getMBeanInfo();
15.730 + }
15.731 +
15.732 + /**
15.733 + * {@inheritDoc}
15.734 + *
15.735 + * <p>The default implementation of this method will call
15.736 + * {@link #getDynamicMBeanFor getDynamicMBeanFor(name)}.<!--
15.737 + * -->{@link DynamicMBean#getMBeanInfo getMBeanInfo()}.<!--
15.738 + * -->{@link MBeanInfo#getClassName getClassName()} to get the
15.739 + * class name to combine with {@code name} to produce a new
15.740 + * {@code ObjectInstance}.</p>
15.741 + */
15.742 + public ObjectInstance getObjectInstance(ObjectName name)
15.743 + throws InstanceNotFoundException {
15.744 + final DynamicMBean mbean = nonNullMBeanFor(name);
15.745 + final String className = mbean.getMBeanInfo().getClassName();
15.746 + return new ObjectInstance(name, className);
15.747 + }
15.748 +
15.749 + /**
15.750 + * {@inheritDoc}
15.751 + *
15.752 + * <p>The default implementation of this method will first call {@link
15.753 + * #getDynamicMBeanFor getDynamicMBeanFor(name)} to obtain a handle to the
15.754 + * named MBean. If {@code getDynamicMBeanFor} returns an object, {@code
15.755 + * isRegistered} will return true. If {@code getDynamicMBeanFor} returns
15.756 + * null or throws {@link InstanceNotFoundException}, {@code isRegistered}
15.757 + * will return false.</p>
15.758 + *
15.759 + * @throws RuntimeOperationsException {@inheritDoc}
15.760 + */
15.761 + public boolean isRegistered(ObjectName name) {
15.762 + try {
15.763 + final DynamicMBean mbean = getDynamicMBeanFor(name);
15.764 + return mbean!=null;
15.765 + } catch (InstanceNotFoundException x) {
15.766 + if (LOG.isLoggable(Level.FINEST))
15.767 + LOG.finest("MBean "+name+" is not registered: "+x);
15.768 + return false;
15.769 + }
15.770 + }
15.771 +
15.772 +
15.773 + /**
15.774 + * {@inheritDoc}
15.775 + *
15.776 + * <p>The default implementation of this method will first
15.777 + * call {@link #queryNames queryNames}
15.778 + * to get a list of all matching MBeans, and then, for each returned name,
15.779 + * call {@link #getObjectInstance getObjectInstance(name)}.</p>
15.780 + */
15.781 + public Set<ObjectInstance> queryMBeans(ObjectName pattern, QueryExp query) {
15.782 + final Set<ObjectName> names = queryNames(pattern, query);
15.783 + if (names.isEmpty()) return Collections.emptySet();
15.784 + final Set<ObjectInstance> mbeans = new HashSet<ObjectInstance>();
15.785 + for (ObjectName name : names) {
15.786 + try {
15.787 + mbeans.add(getObjectInstance(name));
15.788 + } catch (SecurityException x) { // DLS: OK
15.789 + continue;
15.790 + } catch (InstanceNotFoundException x) { // DLS: OK
15.791 + continue;
15.792 + }
15.793 + }
15.794 + return mbeans;
15.795 + }
15.796 +
15.797 + /**
15.798 + * {@inheritDoc}
15.799 + *
15.800 + * <p>The default implementation of this method calls {@link #getMatchingNames
15.801 + * getMatchingNames(pattern)} to obtain a list of MBeans matching
15.802 + * the given name pattern. If the {@code query} parameter is null,
15.803 + * this will be the result. Otherwise, it will evaluate the
15.804 + * {@code query} parameter for each of the returned names, exactly
15.805 + * as an {@code MBeanServer} would. This might result in
15.806 + * {@link #getDynamicMBeanFor getDynamicMBeanFor} being called
15.807 + * several times for each returned name.</p>
15.808 + */
15.809 + public Set<ObjectName> queryNames(ObjectName pattern, QueryExp query) {
15.810 + try {
15.811 + final Set<ObjectName> res = getMatchingNames(pattern);
15.812 + return filterListOfObjectNames(res, query);
15.813 + } catch (Exception x) {
15.814 + LOG.fine("Unexpected exception raised in queryNames: "+x);
15.815 + LOG.log(Level.FINEST, "Unexpected exception raised in queryNames", x);
15.816 + }
15.817 + // We reach here only when an exception was raised.
15.818 + //
15.819 + return Collections.emptySet();
15.820 + }
15.821 +
15.822 + private final static boolean apply(final QueryExp query,
15.823 + final ObjectName on,
15.824 + final MBeanServer srv) {
15.825 + boolean res = false;
15.826 + MBeanServer oldServer = QueryEval.getMBeanServer();
15.827 + query.setMBeanServer(srv);
15.828 + try {
15.829 + res = query.apply(on);
15.830 + } catch (Exception e) {
15.831 + LOG.finest("QueryExp.apply threw exception, returning false." +
15.832 + " Cause: "+e);
15.833 + res = false;
15.834 + } finally {
15.835 + /*
15.836 + * query.setMBeanServer is probably
15.837 + * QueryEval.setMBeanServer so put back the old
15.838 + * value. Since that method uses a ThreadLocal
15.839 + * variable, this code is only needed for the
15.840 + * unusual case where the user creates a custom
15.841 + * QueryExp that calls a nested query on another
15.842 + * MBeanServer.
15.843 + */
15.844 + query.setMBeanServer(oldServer);
15.845 + }
15.846 + return res;
15.847 + }
15.848 +
15.849 + /**
15.850 + * Filters a {@code Set<ObjectName>} according to a pattern and a query.
15.851 + * This might be quite inefficient for virtual name spaces.
15.852 + */
15.853 + Set<ObjectName>
15.854 + filterListOfObjectNames(Set<ObjectName> list,
15.855 + QueryExp query) {
15.856 + if (list.isEmpty() || query == null)
15.857 + return list;
15.858 +
15.859 + // create a new result set
15.860 + final Set<ObjectName> result = new HashSet<ObjectName>();
15.861 +
15.862 + for (ObjectName on : list) {
15.863 + // if on doesn't match query exclude it.
15.864 + if (apply(query, on, this))
15.865 + result.add(on);
15.866 + }
15.867 + return result;
15.868 + }
15.869 +
15.870 +
15.871 + // Don't use {@inheritDoc}, because we don't want to say that the
15.872 + // MBeanServer replaces a reference to the MBean by its ObjectName.
15.873 + /**
15.874 + * <p>Adds a listener to a registered MBean. A notification emitted by
15.875 + * the MBean will be forwarded to the listener.</p>
15.876 + *
15.877 + * <p>This implementation calls
15.878 + * {@link #getNotificationEmitterFor getNotificationEmitterFor}
15.879 + * and invokes {@code addNotificationListener} on the
15.880 + * {@link NotificationEmitter} it returns.
15.881 + *
15.882 + * @see #getDynamicMBeanFor getDynamicMBeanFor
15.883 + * @see #getNotificationEmitterFor getNotificationEmitterFor
15.884 + */
15.885 + public void addNotificationListener(ObjectName name,
15.886 + NotificationListener listener, NotificationFilter filter,
15.887 + Object handback) throws InstanceNotFoundException {
15.888 + final NotificationEmitter emitter =
15.889 + getNonNullNotificationEmitterFor(name);
15.890 + emitter.addNotificationListener(listener, filter, handback);
15.891 + }
15.892 +
15.893 + /**
15.894 + * {@inheritDoc}
15.895 + *
15.896 + * <p>This implementation calls
15.897 + * {@link #getNotificationEmitterFor getNotificationEmitterFor}
15.898 + * and invokes {@code removeNotificationListener} on the
15.899 + * {@link NotificationEmitter} it returns.
15.900 + * @see #getDynamicMBeanFor getDynamicMBeanFor
15.901 + * @see #getNotificationEmitterFor getNotificationEmitterFor
15.902 + */
15.903 + public void removeNotificationListener(ObjectName name,
15.904 + NotificationListener listener)
15.905 + throws InstanceNotFoundException, ListenerNotFoundException {
15.906 + final NotificationEmitter emitter =
15.907 + getNonNullNotificationEmitterFor(name);
15.908 + emitter.removeNotificationListener(listener);
15.909 + }
15.910 +
15.911 + /**
15.912 + * {@inheritDoc}
15.913 + *
15.914 + * <p>This implementation calls
15.915 + * {@link #getNotificationEmitterFor getNotificationEmitterFor}
15.916 + * and invokes {@code removeNotificationListener} on the
15.917 + * {@link NotificationEmitter} it returns.
15.918 + * @see #getDynamicMBeanFor getDynamicMBeanFor
15.919 + * @see #getNotificationEmitterFor getNotificationEmitterFor
15.920 + */
15.921 + public void removeNotificationListener(ObjectName name,
15.922 + NotificationListener listener, NotificationFilter filter,
15.923 + Object handback)
15.924 + throws InstanceNotFoundException, ListenerNotFoundException {
15.925 + NotificationEmitter emitter =
15.926 + getNonNullNotificationEmitterFor(name);
15.927 + emitter.removeNotificationListener(listener);
15.928 + }
15.929 +
15.930 +
15.931 + /**
15.932 + * <p>Adds a listener to a registered MBean.</p>
15.933 + *
15.934 + * <p>The default implementation of this method first calls
15.935 + * {@link #getDynamicMBeanFor getDynamicMBeanFor(listenerName)}.
15.936 + * If that successfully returns an object, call it {@code
15.937 + * mbean}, then (a) if {@code mbean} is an instance of {@link
15.938 + * NotificationListener} then this method calls {@link
15.939 + * #addNotificationListener(ObjectName, NotificationListener,
15.940 + * NotificationFilter, Object) addNotificationListener(name, mbean, filter,
15.941 + * handback)}, otherwise (b) this method throws an exception as specified
15.942 + * for this case.</p>
15.943 + *
15.944 + * <p>This default implementation is not appropriate for Virtual MBeans,
15.945 + * although that only matters if the object returned by {@code
15.946 + * getDynamicMBeanFor} can be an instance of
15.947 + * {@code NotificationListener}.</p>
15.948 + *
15.949 + * @throws RuntimeOperationsException {@inheritDoc}
15.950 + */
15.951 + public void addNotificationListener(ObjectName name, ObjectName listenerName,
15.952 + NotificationFilter filter, Object handback)
15.953 + throws InstanceNotFoundException {
15.954 + NotificationListener listener = getListenerMBean(listenerName);
15.955 + addNotificationListener(name, listener, filter, handback);
15.956 + }
15.957 +
15.958 + /**
15.959 + * {@inheritDoc}
15.960 + *
15.961 + * <p>This operation is not supported in this base class implementation.
15.962 + * The default implementation of this method always throws
15.963 + * {@link RuntimeOperationsException} wrapping
15.964 + * {@link UnsupportedOperationException}.</p>
15.965 + *
15.966 + * @throws javax.management.RuntimeOperationsException wrapping
15.967 + * {@link UnsupportedOperationException}
15.968 + */
15.969 + public void removeNotificationListener(ObjectName name,
15.970 + ObjectName listenerName)
15.971 + throws InstanceNotFoundException, ListenerNotFoundException {
15.972 + NotificationListener listener = getListenerMBean(listenerName);
15.973 + removeNotificationListener(name, listener);
15.974 + }
15.975 +
15.976 + /**
15.977 + * {@inheritDoc}
15.978 + *
15.979 + * <p>This operation is not supported in this base class implementation.
15.980 + * The default implementation of this method always throws
15.981 + * {@link RuntimeOperationsException} wrapping
15.982 + * {@link UnsupportedOperationException}.</p>
15.983 + *
15.984 + * @throws javax.management.RuntimeOperationsException wrapping
15.985 + * {@link UnsupportedOperationException}
15.986 + */
15.987 + public void removeNotificationListener(ObjectName name,
15.988 + ObjectName listenerName, NotificationFilter filter,
15.989 + Object handback)
15.990 + throws InstanceNotFoundException, ListenerNotFoundException {
15.991 + NotificationListener listener = getListenerMBean(listenerName);
15.992 + removeNotificationListener(name, listener, filter, handback);
15.993 + }
15.994 +
15.995 + private NotificationListener getListenerMBean(ObjectName listenerName)
15.996 + throws InstanceNotFoundException {
15.997 + Object mbean = getDynamicMBeanFor(listenerName);
15.998 + if (mbean instanceof NotificationListener)
15.999 + return (NotificationListener) mbean;
15.1000 + else {
15.1001 + throw newIllegalArgumentException(
15.1002 + "MBean is not a NotificationListener: " + listenerName);
15.1003 + }
15.1004 + }
15.1005 +
15.1006 +
15.1007 + /**
15.1008 + * {@inheritDoc}
15.1009 + *
15.1010 + * <p>This operation is not supported in this base class implementation.
15.1011 + * The default implementation of this method always throws
15.1012 + * {@link InstanceNotFoundException} wrapping
15.1013 + * {@link UnsupportedOperationException}.</p>
15.1014 + *
15.1015 + * @return the default implementation of this method never returns.
15.1016 + * @throws javax.management.RuntimeOperationsException wrapping
15.1017 + * {@link UnsupportedOperationException}
15.1018 + */
15.1019 + public ClassLoader getClassLoader(ObjectName loaderName)
15.1020 + throws InstanceNotFoundException {
15.1021 + final UnsupportedOperationException failed =
15.1022 + new UnsupportedOperationException("getClassLoader");
15.1023 + final InstanceNotFoundException x =
15.1024 + new InstanceNotFoundException(String.valueOf(loaderName));
15.1025 + x.initCause(failed);
15.1026 + throw x;
15.1027 + }
15.1028 +
15.1029 + /**
15.1030 + * {@inheritDoc}
15.1031 + *
15.1032 + * <p>The default implementation of this method calls
15.1033 + * {@link #getDynamicMBeanFor getDynamicMBeanFor(mbeanName)} and applies
15.1034 + * the logic just described to the result.</p>
15.1035 + */
15.1036 + public ClassLoader getClassLoaderFor(ObjectName mbeanName)
15.1037 + throws InstanceNotFoundException {
15.1038 + final DynamicMBean mbean = nonNullMBeanFor(mbeanName);
15.1039 + if (mbean instanceof DynamicWrapperMBean)
15.1040 + return ((DynamicWrapperMBean) mbean).getWrappedClassLoader();
15.1041 + else
15.1042 + return mbean.getClass().getClassLoader();
15.1043 + }
15.1044 +
15.1045 + /**
15.1046 + * {@inheritDoc}
15.1047 + *
15.1048 + * <p>The default implementation of this method returns a
15.1049 + * {@link ClassLoaderRepository} containing exactly one loader,
15.1050 + * the {@linkplain Thread#getContextClassLoader() context class loader}
15.1051 + * for the current thread.
15.1052 + * Subclasses can override this method to return a different
15.1053 + * {@code ClassLoaderRepository}.</p>
15.1054 + */
15.1055 + public ClassLoaderRepository getClassLoaderRepository() {
15.1056 + // We return a new ClassLoaderRepository each time this
15.1057 + // method is called. This is by design, because the
15.1058 + // SingletonClassLoaderRepository is a very small object and
15.1059 + // getClassLoaderRepository() will not be called very often
15.1060 + // (the connector server calls it once) - in the context of
15.1061 + // MBeanServerSupport there's a very good chance that this method will
15.1062 + // *never* be called.
15.1063 + ClassLoader ccl = Thread.currentThread().getContextClassLoader();
15.1064 + return Util.getSingleClassLoaderRepository(ccl);
15.1065 + }
15.1066 +
15.1067 +
15.1068 + /**
15.1069 + * {@inheritDoc}
15.1070 + *
15.1071 + * <p>This operation is not supported in this base class implementation.
15.1072 + * The default implementation of this method always throws
15.1073 + * {@link RuntimeOperationsException} wrapping
15.1074 + * {@link UnsupportedOperationException}.</p>
15.1075 + * @throws javax.management.RuntimeOperationsException wrapping
15.1076 + * {@link UnsupportedOperationException}
15.1077 + */
15.1078 + public ObjectInstance registerMBean(Object object, ObjectName name)
15.1079 + throws InstanceAlreadyExistsException, MBeanRegistrationException,
15.1080 + NotCompliantMBeanException {
15.1081 + throw newUnsupportedException("registerMBean");
15.1082 + }
15.1083 +
15.1084 + /**
15.1085 + * {@inheritDoc}
15.1086 + *
15.1087 + * <p>This operation is not supported in this base class implementation.
15.1088 + * The default implementation of this method always throws
15.1089 + * {@link RuntimeOperationsException} wrapping
15.1090 + * {@link UnsupportedOperationException}.
15.1091 + * @throws javax.management.RuntimeOperationsException wrapping
15.1092 + * {@link UnsupportedOperationException}
15.1093 + */
15.1094 + public void unregisterMBean(ObjectName name)
15.1095 + throws InstanceNotFoundException, MBeanRegistrationException {
15.1096 + throw newUnsupportedException("unregisterMBean");
15.1097 + }
15.1098 +
15.1099 + /**
15.1100 + * Calls {@link #createMBean(String, ObjectName,
15.1101 + * ObjectName, Object[], String[], boolean)
15.1102 + * createMBean(className, name, null, params, signature, true)};
15.1103 + */
15.1104 + public final ObjectInstance createMBean(String className, ObjectName name,
15.1105 + Object[] params, String[] signature)
15.1106 + throws ReflectionException, InstanceAlreadyExistsException,
15.1107 + MBeanRegistrationException, MBeanException,
15.1108 + NotCompliantMBeanException {
15.1109 + try {
15.1110 + return safeCreateMBean(className, name, null, params, signature, true);
15.1111 + } catch (InstanceNotFoundException ex) {
15.1112 + // should not happen!
15.1113 + throw new MBeanException(ex, "Unexpected exception: " + ex);
15.1114 + }
15.1115 + }
15.1116 +
15.1117 + /**
15.1118 + * Calls {@link #createMBean(String, ObjectName,
15.1119 + * ObjectName, Object[], String[], boolean)
15.1120 + * createMBean(className,name, loaderName, params, signature, false)};
15.1121 + */
15.1122 + public final ObjectInstance createMBean(String className, ObjectName name,
15.1123 + ObjectName loaderName, Object[] params, String[] signature)
15.1124 + throws ReflectionException, InstanceAlreadyExistsException,
15.1125 + MBeanRegistrationException, MBeanException,
15.1126 + NotCompliantMBeanException, InstanceNotFoundException {
15.1127 + return safeCreateMBean(className, name, loaderName, params, signature, false);
15.1128 + }
15.1129 +
15.1130 + /**
15.1131 + * Calls {@link #createMBean(String, ObjectName,
15.1132 + * ObjectName, Object[], String[], boolean)
15.1133 + * createMBean(className, name, null, null, null, true)};
15.1134 + */
15.1135 + public final ObjectInstance createMBean(String className, ObjectName name)
15.1136 + throws ReflectionException, InstanceAlreadyExistsException,
15.1137 + MBeanRegistrationException, MBeanException,
15.1138 + NotCompliantMBeanException {
15.1139 + try {
15.1140 + return safeCreateMBean(className, name, null, null, null, true);
15.1141 + } catch (InstanceNotFoundException ex) {
15.1142 + // should not happen!
15.1143 + throw new MBeanException(ex, "Unexpected exception: " + ex);
15.1144 + }
15.1145 + }
15.1146 +
15.1147 + /**
15.1148 + * Calls {@link #createMBean(String, ObjectName,
15.1149 + * ObjectName, Object[], String[], boolean)
15.1150 + * createMBean(className, name, loaderName, null, null, false)};
15.1151 + */
15.1152 + public final ObjectInstance createMBean(String className, ObjectName name,
15.1153 + ObjectName loaderName)
15.1154 + throws ReflectionException, InstanceAlreadyExistsException,
15.1155 + MBeanRegistrationException, MBeanException,
15.1156 + NotCompliantMBeanException, InstanceNotFoundException {
15.1157 + return safeCreateMBean(className, name, loaderName, null, null, false);
15.1158 + }
15.1159 +
15.1160 + // make sure all exceptions are correctly wrapped in a JMXException
15.1161 + private ObjectInstance safeCreateMBean(String className,
15.1162 + ObjectName name, ObjectName loaderName, Object[] params,
15.1163 + String[] signature, boolean useRepository)
15.1164 + throws ReflectionException, InstanceAlreadyExistsException,
15.1165 + MBeanRegistrationException, MBeanException,
15.1166 + NotCompliantMBeanException, InstanceNotFoundException {
15.1167 + try {
15.1168 + return createMBean(className, name, loaderName, params,
15.1169 + signature, useRepository);
15.1170 + } catch (ReflectionException x) { throw x;
15.1171 + } catch (InstanceAlreadyExistsException x) { throw x;
15.1172 + } catch (MBeanRegistrationException x) { throw x;
15.1173 + } catch (MBeanException x) { throw x;
15.1174 + } catch (NotCompliantMBeanException x) { throw x;
15.1175 + } catch (InstanceNotFoundException x) { throw x;
15.1176 + } catch (SecurityException x) { throw x;
15.1177 + } catch (JMRuntimeException x) { throw x;
15.1178 + } catch (RuntimeException x) {
15.1179 + throw new RuntimeOperationsException(x, x.toString());
15.1180 + } catch (Exception x) {
15.1181 + throw new MBeanException(x, x.toString());
15.1182 + }
15.1183 + }
15.1184 +
15.1185 +
15.1186 + /**
15.1187 + * {@inheritDoc}
15.1188 + *
15.1189 + * <p>This operation is not supported in this base class implementation.
15.1190 + * The default implementation of this method always throws
15.1191 + * {@link RuntimeOperationsException} wrapping
15.1192 + * {@link UnsupportedOperationException}.</p>
15.1193 + *
15.1194 + * @throws javax.management.RuntimeOperationsException wrapping
15.1195 + * {@link UnsupportedOperationException}
15.1196 + */
15.1197 + public Object instantiate(String className)
15.1198 + throws ReflectionException, MBeanException {
15.1199 + throw new UnsupportedOperationException("Not applicable.");
15.1200 + }
15.1201 +
15.1202 + /**
15.1203 + * {@inheritDoc}
15.1204 + *
15.1205 + * <p>This operation is not supported in this base class implementation.
15.1206 + * The default implementation of this method always throws
15.1207 + * {@link RuntimeOperationsException} wrapping
15.1208 + * {@link UnsupportedOperationException}.</p>
15.1209 + *
15.1210 + * @throws javax.management.RuntimeOperationsException wrapping
15.1211 + * {@link UnsupportedOperationException}
15.1212 + */
15.1213 + public Object instantiate(String className, ObjectName loaderName)
15.1214 + throws ReflectionException, MBeanException,
15.1215 + InstanceNotFoundException {
15.1216 + throw new UnsupportedOperationException("Not applicable.");
15.1217 + }
15.1218 +
15.1219 + /**
15.1220 + * {@inheritDoc}
15.1221 + *
15.1222 + * <p>This operation is not supported in this base class implementation.
15.1223 + * The default implementation of this method always throws
15.1224 + * {@link RuntimeOperationsException} wrapping
15.1225 + * {@link UnsupportedOperationException}.</p>
15.1226 + *
15.1227 + * @throws javax.management.RuntimeOperationsException wrapping
15.1228 + * {@link UnsupportedOperationException}
15.1229 + */
15.1230 + public Object instantiate(String className, Object[] params,
15.1231 + String[] signature) throws ReflectionException, MBeanException {
15.1232 + throw new UnsupportedOperationException("Not applicable.");
15.1233 + }
15.1234 +
15.1235 + /**
15.1236 + * {@inheritDoc}
15.1237 + *
15.1238 + * <p>This operation is not supported in this base class implementation.
15.1239 + * The default implementation of this method always throws
15.1240 + * {@link RuntimeOperationsException} wrapping
15.1241 + * {@link UnsupportedOperationException}.</p>
15.1242 + *
15.1243 + * @throws javax.management.RuntimeOperationsException wrapping
15.1244 + * {@link UnsupportedOperationException}
15.1245 + */
15.1246 + public Object instantiate(String className, ObjectName loaderName,
15.1247 + Object[] params, String[] signature)
15.1248 + throws ReflectionException, MBeanException,
15.1249 + InstanceNotFoundException {
15.1250 + throw new UnsupportedOperationException("Not applicable.");
15.1251 + }
15.1252 +
15.1253 +
15.1254 + /**
15.1255 + * {@inheritDoc}
15.1256 + *
15.1257 + * <p>This operation is not supported in this base class implementation.
15.1258 + * The default implementation of this method always throws
15.1259 + * {@link RuntimeOperationsException} wrapping
15.1260 + * {@link UnsupportedOperationException}.</p>
15.1261 + *
15.1262 + * @throws javax.management.RuntimeOperationsException wrapping
15.1263 + * {@link UnsupportedOperationException}
15.1264 + */
15.1265 + @Deprecated
15.1266 + public ObjectInputStream deserialize(ObjectName name, byte[] data)
15.1267 + throws InstanceNotFoundException, OperationsException {
15.1268 + throw new UnsupportedOperationException("Not applicable.");
15.1269 + }
15.1270 +
15.1271 + /**
15.1272 + * {@inheritDoc}
15.1273 + *
15.1274 + * <p>This operation is not supported in this base class implementation.
15.1275 + * The default implementation of this method always throws
15.1276 + * {@link RuntimeOperationsException} wrapping
15.1277 + * {@link UnsupportedOperationException}.</p>
15.1278 + *
15.1279 + * @throws javax.management.RuntimeOperationsException wrapping
15.1280 + * {@link UnsupportedOperationException}
15.1281 + */
15.1282 + @Deprecated
15.1283 + public ObjectInputStream deserialize(String className, byte[] data)
15.1284 + throws OperationsException, ReflectionException {
15.1285 + throw new UnsupportedOperationException("Not applicable.");
15.1286 + }
15.1287 +
15.1288 + /**
15.1289 + * {@inheritDoc}
15.1290 + *
15.1291 + * <p>This operation is not supported in this base class implementation.
15.1292 + * The default implementation of this method always throws
15.1293 + * {@link RuntimeOperationsException} wrapping
15.1294 + * {@link UnsupportedOperationException}.</p>
15.1295 + *
15.1296 + * @throws javax.management.RuntimeOperationsException wrapping
15.1297 + * {@link UnsupportedOperationException}
15.1298 + */
15.1299 + @Deprecated
15.1300 + public ObjectInputStream deserialize(String className,
15.1301 + ObjectName loaderName, byte[] data)
15.1302 + throws InstanceNotFoundException, OperationsException,
15.1303 + ReflectionException {
15.1304 + throw new UnsupportedOperationException("Not applicable.");
15.1305 + }
15.1306 +
15.1307 +
15.1308 + // Calls getDynamicMBeanFor, and throws an InstanceNotFoundException
15.1309 + // if the returned mbean is null.
15.1310 + // The DynamicMBean returned by this method is thus guaranteed to be
15.1311 + // non null.
15.1312 + //
15.1313 + private DynamicMBean nonNullMBeanFor(ObjectName name)
15.1314 + throws InstanceNotFoundException {
15.1315 + if (name == null)
15.1316 + throw newIllegalArgumentException("Null ObjectName");
15.1317 + if (name.getDomain().equals("")) {
15.1318 + String defaultDomain = getDefaultDomain();
15.1319 + try {
15.1320 + // XXX change to ObjectName.switchDomain
15.1321 + // current code DOES NOT PRESERVE the order of keys
15.1322 + name = new ObjectName(defaultDomain, name.getKeyPropertyList());
15.1323 + } catch (Exception e) {
15.1324 + throw newIllegalArgumentException(
15.1325 + "Illegal default domain: " + defaultDomain);
15.1326 + }
15.1327 + }
15.1328 + final DynamicMBean mbean = getDynamicMBeanFor(name);
15.1329 + if (mbean!=null) return mbean;
15.1330 + throw new InstanceNotFoundException(String.valueOf(name));
15.1331 + }
15.1332 +
15.1333 + static RuntimeException newUnsupportedException(String operation) {
15.1334 + return new RuntimeOperationsException(
15.1335 + new UnsupportedOperationException(
15.1336 + operation+": Not supported in this namespace"));
15.1337 + }
15.1338 +
15.1339 + static RuntimeException newIllegalArgumentException(String msg) {
15.1340 + return new RuntimeOperationsException(
15.1341 + new IllegalArgumentException(msg));
15.1342 + }
15.1343 +
15.1344 +}
16.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
16.2 +++ b/src/share/classes/com/sun/jmx/interceptor/SingleMBeanForwarder.java Thu Aug 21 09:55:18 2008 -0700
16.3 @@ -0,0 +1,414 @@
16.4 +/*
16.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
16.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
16.7 + *
16.8 + * This code is free software; you can redistribute it and/or modify it
16.9 + * under the terms of the GNU General Public License version 2 only, as
16.10 + * published by the Free Software Foundation. Sun designates this
16.11 + * particular file as subject to the "Classpath" exception as provided
16.12 + * by Sun in the LICENSE file that accompanied this code.
16.13 + *
16.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
16.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16.17 + * version 2 for more details (a copy is included in the LICENSE file that
16.18 + * accompanied this code).
16.19 + *
16.20 + * You should have received a copy of the GNU General Public License version
16.21 + * 2 along with this work; if not, write to the Free Software Foundation,
16.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
16.23 + *
16.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
16.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
16.26 + * have any questions.
16.27 + */
16.28 +
16.29 +package com.sun.jmx.interceptor;
16.30 +
16.31 +import com.sun.jmx.mbeanserver.Util;
16.32 +import java.util.Arrays;
16.33 +import java.util.Collections;
16.34 +import java.util.Set;
16.35 +import java.util.TreeSet;
16.36 +import javax.management.Attribute;
16.37 +import javax.management.AttributeList;
16.38 +import javax.management.AttributeNotFoundException;
16.39 +import javax.management.DynamicMBean;
16.40 +import javax.management.InstanceAlreadyExistsException;
16.41 +import javax.management.InstanceNotFoundException;
16.42 +import javax.management.IntrospectionException;
16.43 +import javax.management.InvalidAttributeValueException;
16.44 +import javax.management.ListenerNotFoundException;
16.45 +import javax.management.MBeanException;
16.46 +import javax.management.MBeanInfo;
16.47 +import javax.management.MBeanRegistrationException;
16.48 +import javax.management.MBeanServer;
16.49 +import javax.management.NotCompliantMBeanException;
16.50 +import javax.management.NotificationEmitter;
16.51 +import javax.management.NotificationFilter;
16.52 +import javax.management.NotificationListener;
16.53 +import javax.management.ObjectInstance;
16.54 +import javax.management.ObjectName;
16.55 +import javax.management.QueryExp;
16.56 +import javax.management.ReflectionException;
16.57 +import javax.management.remote.IdentityMBeanServerForwarder;
16.58 +
16.59 +public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
16.60 +
16.61 + private final ObjectName mbeanName;
16.62 + private DynamicMBean mbean;
16.63 +
16.64 + private MBeanServer mbeanMBS = new MBeanServerSupport() {
16.65 +
16.66 + @Override
16.67 + public DynamicMBean getDynamicMBeanFor(ObjectName name)
16.68 + throws InstanceNotFoundException {
16.69 + if (mbeanName.equals(name)) {
16.70 + return mbean;
16.71 + } else {
16.72 + throw new InstanceNotFoundException(name.toString());
16.73 + }
16.74 + }
16.75 +
16.76 + @Override
16.77 + protected Set<ObjectName> getNames() {
16.78 + return Collections.singleton(mbeanName);
16.79 + }
16.80 +
16.81 + @Override
16.82 + public NotificationEmitter getNotificationEmitterFor(
16.83 + ObjectName name) {
16.84 + if (mbean instanceof NotificationEmitter)
16.85 + return (NotificationEmitter) mbean;
16.86 + return null;
16.87 + }
16.88 +
16.89 + };
16.90 +
16.91 + public SingleMBeanForwarder(ObjectName mbeanName, DynamicMBean mbean) {
16.92 + this.mbeanName = mbeanName;
16.93 + setSingleMBean(mbean);
16.94 + }
16.95 +
16.96 + protected void setSingleMBean(DynamicMBean mbean) {
16.97 + this.mbean = mbean;
16.98 + }
16.99 +
16.100 + @Override
16.101 + public void addNotificationListener(ObjectName name, ObjectName listener,
16.102 + NotificationFilter filter,
16.103 + Object handback)
16.104 + throws InstanceNotFoundException {
16.105 + if (mbeanName.equals(name))
16.106 + mbeanMBS.addNotificationListener(name, listener, filter, handback);
16.107 + else
16.108 + super.addNotificationListener(name, listener, filter, handback);
16.109 + }
16.110 +
16.111 + @Override
16.112 + public void addNotificationListener(ObjectName name,
16.113 + NotificationListener listener,
16.114 + NotificationFilter filter,
16.115 + Object handback)
16.116 + throws InstanceNotFoundException {
16.117 + if (mbeanName.equals(name))
16.118 + mbeanMBS.addNotificationListener(name, listener, filter, handback);
16.119 + else
16.120 + super.addNotificationListener(name, listener, filter, handback);
16.121 + }
16.122 +
16.123 + @Override
16.124 + public ObjectInstance createMBean(String className, ObjectName name,
16.125 + ObjectName loaderName, Object[] params,
16.126 + String[] signature)
16.127 + throws ReflectionException,
16.128 + InstanceAlreadyExistsException,
16.129 + MBeanRegistrationException,
16.130 + MBeanException,
16.131 + NotCompliantMBeanException,
16.132 + InstanceNotFoundException {
16.133 + if (mbeanName.equals(name))
16.134 + throw new InstanceAlreadyExistsException(mbeanName.toString());
16.135 + else
16.136 + return super.createMBean(className, name, loaderName, params, signature);
16.137 + }
16.138 +
16.139 + @Override
16.140 + public ObjectInstance createMBean(String className, ObjectName name,
16.141 + Object[] params, String[] signature)
16.142 + throws ReflectionException, InstanceAlreadyExistsException,
16.143 + MBeanRegistrationException, MBeanException,
16.144 + NotCompliantMBeanException {
16.145 + if (mbeanName.equals(name))
16.146 + throw new InstanceAlreadyExistsException(mbeanName.toString());
16.147 + return super.createMBean(className, name, params, signature);
16.148 + }
16.149 +
16.150 + @Override
16.151 + public ObjectInstance createMBean(String className, ObjectName name,
16.152 + ObjectName loaderName)
16.153 + throws ReflectionException,
16.154 + InstanceAlreadyExistsException,
16.155 + MBeanRegistrationException,
16.156 + MBeanException,
16.157 + NotCompliantMBeanException,
16.158 + InstanceNotFoundException {
16.159 + if (mbeanName.equals(name))
16.160 + throw new InstanceAlreadyExistsException(mbeanName.toString());
16.161 + return super.createMBean(className, name, loaderName);
16.162 + }
16.163 +
16.164 + @Override
16.165 + public ObjectInstance createMBean(String className, ObjectName name)
16.166 + throws ReflectionException,
16.167 + InstanceAlreadyExistsException,
16.168 + MBeanRegistrationException,
16.169 + MBeanException,
16.170 + NotCompliantMBeanException {
16.171 + if (mbeanName.equals(name))
16.172 + throw new InstanceAlreadyExistsException(mbeanName.toString());
16.173 + return super.createMBean(className, name);
16.174 + }
16.175 +
16.176 + @Override
16.177 + public Object getAttribute(ObjectName name, String attribute)
16.178 + throws MBeanException,
16.179 + AttributeNotFoundException,
16.180 + InstanceNotFoundException,
16.181 + ReflectionException {
16.182 + if (mbeanName.equals(name))
16.183 + return mbeanMBS.getAttribute(name, attribute);
16.184 + else
16.185 + return super.getAttribute(name, attribute);
16.186 + }
16.187 +
16.188 + @Override
16.189 + public AttributeList getAttributes(ObjectName name, String[] attributes)
16.190 + throws InstanceNotFoundException, ReflectionException {
16.191 + if (mbeanName.equals(name))
16.192 + return mbeanMBS.getAttributes(name, attributes);
16.193 + else
16.194 + return super.getAttributes(name, attributes);
16.195 + }
16.196 +
16.197 + @Override
16.198 + public ClassLoader getClassLoader(ObjectName loaderName)
16.199 + throws InstanceNotFoundException {
16.200 + if (mbeanName.equals(loaderName))
16.201 + return mbeanMBS.getClassLoader(loaderName);
16.202 + else
16.203 + return super.getClassLoader(loaderName);
16.204 + }
16.205 +
16.206 + @Override
16.207 + public ClassLoader getClassLoaderFor(ObjectName name)
16.208 + throws InstanceNotFoundException {
16.209 + if (mbeanName.equals(name))
16.210 + return mbeanMBS.getClassLoaderFor(name);
16.211 + else
16.212 + return super.getClassLoaderFor(name);
16.213 + }
16.214 +
16.215 + @Override
16.216 + public String[] getDomains() {
16.217 + TreeSet<String> domainSet =
16.218 + new TreeSet<String>(Arrays.asList(super.getDomains()));
16.219 + domainSet.add(mbeanName.getDomain());
16.220 + return domainSet.toArray(new String[domainSet.size()]);
16.221 + }
16.222 +
16.223 + @Override
16.224 + public Integer getMBeanCount() {
16.225 + Integer count = super.getMBeanCount();
16.226 + if (!super.isRegistered(mbeanName))
16.227 + count++;
16.228 + return count;
16.229 + }
16.230 +
16.231 + @Override
16.232 + public MBeanInfo getMBeanInfo(ObjectName name)
16.233 + throws InstanceNotFoundException,
16.234 + IntrospectionException,
16.235 + ReflectionException {
16.236 + if (mbeanName.equals(name))
16.237 + return mbeanMBS.getMBeanInfo(name);
16.238 + else
16.239 + return super.getMBeanInfo(name);
16.240 + }
16.241 +
16.242 + @Override
16.243 + public ObjectInstance getObjectInstance(ObjectName name)
16.244 + throws InstanceNotFoundException {
16.245 + if (mbeanName.equals(name))
16.246 + return mbeanMBS.getObjectInstance(name);
16.247 + else
16.248 + return super.getObjectInstance(name);
16.249 + }
16.250 +
16.251 + @Override
16.252 + public Object invoke(ObjectName name, String operationName, Object[] params,
16.253 + String[] signature)
16.254 + throws InstanceNotFoundException,
16.255 + MBeanException,
16.256 + ReflectionException {
16.257 + if (mbeanName.equals(name))
16.258 + return mbeanMBS.invoke(name, operationName, params, signature);
16.259 + else
16.260 + return super.invoke(name, operationName, params, signature);
16.261 + }
16.262 +
16.263 + @Override
16.264 + public boolean isInstanceOf(ObjectName name, String className)
16.265 + throws InstanceNotFoundException {
16.266 + if (mbeanName.equals(name))
16.267 + return mbeanMBS.isInstanceOf(name, className);
16.268 + else
16.269 + return super.isInstanceOf(name, className);
16.270 + }
16.271 +
16.272 + @Override
16.273 + public boolean isRegistered(ObjectName name) {
16.274 + if (mbeanName.equals(name))
16.275 + return true;
16.276 + else
16.277 + return super.isRegistered(name);
16.278 + }
16.279 +
16.280 + /**
16.281 + * This is a ugly hack. Although jmx.context//*:* matches jmx.context//:*
16.282 + * queryNames(jmx.context//*:*,null) must not return jmx.context//:*
16.283 + * @param pattern the pattern to match against. must not be null.
16.284 + * @return true if mbeanName can be included, false if it must not.
16.285 + */
16.286 + private boolean applies(ObjectName pattern) {
16.287 + // we know pattern is not null.
16.288 + if (!pattern.apply(mbeanName))
16.289 + return false;
16.290 +
16.291 +// final String dompat = pattern.getDomain();
16.292 +// if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR))
16.293 +// return true; // We already checked that patterns apply.
16.294 +//
16.295 +// if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) {
16.296 +// // only matches if pattern ends with //
16.297 +// return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR);
16.298 +// }
16.299 +
16.300 + // should not come here, unless mbeanName contains a // in the
16.301 + // middle of its domain, which would be weird.
16.302 + // let query on mbeanMBS proceed and take care of that.
16.303 + //
16.304 + return true;
16.305 + }
16.306 +
16.307 + @Override
16.308 + public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
16.309 + Set<ObjectInstance> names = super.queryMBeans(name, query);
16.310 + if (name == null || applies(name) ) {
16.311 + // Don't assume mbs.queryNames returns a writable set.
16.312 + names = Util.cloneSet(names);
16.313 + names.addAll(mbeanMBS.queryMBeans(name, query));
16.314 + }
16.315 + return names;
16.316 + }
16.317 +
16.318 + @Override
16.319 + public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
16.320 + Set<ObjectName> names = super.queryNames(name, query);
16.321 + if (name == null || applies(name)) {
16.322 + // Don't assume mbs.queryNames returns a writable set.
16.323 + names = Util.cloneSet(names);
16.324 + names.addAll(mbeanMBS.queryNames(name, query));
16.325 + }
16.326 + return names;
16.327 + }
16.328 +
16.329 +
16.330 + @Override
16.331 + public ObjectInstance registerMBean(Object object, ObjectName name)
16.332 + throws InstanceAlreadyExistsException,
16.333 + MBeanRegistrationException,
16.334 + NotCompliantMBeanException {
16.335 + if (mbeanName.equals(name))
16.336 + throw new InstanceAlreadyExistsException(mbeanName.toString());
16.337 + else
16.338 + return super.registerMBean(object, name);
16.339 + }
16.340 +
16.341 + @Override
16.342 + public void removeNotificationListener(ObjectName name,
16.343 + NotificationListener listener,
16.344 + NotificationFilter filter,
16.345 + Object handback)
16.346 + throws InstanceNotFoundException,
16.347 + ListenerNotFoundException {
16.348 + if (mbeanName.equals(name))
16.349 + mbeanMBS.removeNotificationListener(name, listener, filter, handback);
16.350 + else
16.351 + super.removeNotificationListener(name, listener, filter, handback);
16.352 + }
16.353 +
16.354 + @Override
16.355 + public void removeNotificationListener(ObjectName name,
16.356 + NotificationListener listener)
16.357 + throws InstanceNotFoundException, ListenerNotFoundException {
16.358 + if (mbeanName.equals(name))
16.359 + mbeanMBS.removeNotificationListener(name, listener);
16.360 + else
16.361 + super.removeNotificationListener(name, listener);
16.362 + }
16.363 +
16.364 + @Override
16.365 + public void removeNotificationListener(ObjectName name, ObjectName listener,
16.366 + NotificationFilter filter,
16.367 + Object handback)
16.368 + throws InstanceNotFoundException,
16.369 + ListenerNotFoundException {
16.370 + if (mbeanName.equals(name))
16.371 + mbeanMBS.removeNotificationListener(name, listener, filter, handback);
16.372 + else
16.373 + super.removeNotificationListener(name, listener, filter, handback);
16.374 + }
16.375 +
16.376 + @Override
16.377 + public void removeNotificationListener(ObjectName name, ObjectName listener)
16.378 + throws InstanceNotFoundException, ListenerNotFoundException {
16.379 + if (mbeanName.equals(name))
16.380 + mbeanMBS.removeNotificationListener(name, listener);
16.381 + else
16.382 + super.removeNotificationListener(name, listener);
16.383 + }
16.384 +
16.385 + @Override
16.386 + public void setAttribute(ObjectName name, Attribute attribute)
16.387 + throws InstanceNotFoundException,
16.388 + AttributeNotFoundException,
16.389 + InvalidAttributeValueException,
16.390 + MBeanException,
16.391 + ReflectionException {
16.392 + if (mbeanName.equals(name))
16.393 + mbeanMBS.setAttribute(name, attribute);
16.394 + else
16.395 + super.setAttribute(name, attribute);
16.396 + }
16.397 +
16.398 + @Override
16.399 + public AttributeList setAttributes(ObjectName name,
16.400 + AttributeList attributes)
16.401 + throws InstanceNotFoundException, ReflectionException {
16.402 + if (mbeanName.equals(name))
16.403 + return mbeanMBS.setAttributes(name, attributes);
16.404 + else
16.405 + return super.setAttributes(name, attributes);
16.406 + }
16.407 +
16.408 + @Override
16.409 + public void unregisterMBean(ObjectName name)
16.410 + throws InstanceNotFoundException,
16.411 + MBeanRegistrationException {
16.412 + if (mbeanName.equals(name))
16.413 + mbeanMBS.unregisterMBean(name);
16.414 + else
16.415 + super.unregisterMBean(name);
16.416 + }
16.417 +}
17.1 --- a/src/share/classes/com/sun/jmx/interceptor/package.html Tue Aug 19 07:50:03 2008 -0700
17.2 +++ b/src/share/classes/com/sun/jmx/interceptor/package.html Thu Aug 21 09:55:18 2008 -0700
17.3 @@ -29,5 +29,8 @@
17.4 </head>
17.5 <body bgcolor="white">
17.6 Provides specific classes to <B>Sun JMX Reference Implementation</B>.
17.7 + <p><b>
17.8 + This API is a Sun internal API and is subject to changes without notice.
17.9 + </b></p>
17.10 </BODY>
17.11 </HTML>
18.1 --- a/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java Tue Aug 19 07:50:03 2008 -0700
18.2 +++ b/src/share/classes/com/sun/jmx/mbeanserver/DefaultMXBeanMappingFactory.java Thu Aug 21 09:55:18 2008 -0700
18.3 @@ -825,7 +825,7 @@
18.4 final TabularData table = (TabularData) openValue;
18.5 final Collection<CompositeData> rows = cast(table.values());
18.6 final Map<Object, Object> valueMap =
18.7 - sortedMap ? newSortedMap() : newMap();
18.8 + sortedMap ? newSortedMap() : newInsertionOrderMap();
18.9 for (CompositeData row : rows) {
18.10 final Object key =
18.11 keyMapping.fromOpenValue(row.get("key"));
19.1 --- a/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java Tue Aug 19 07:50:03 2008 -0700
19.2 +++ b/src/share/classes/com/sun/jmx/mbeanserver/MBeanInjector.java Thu Aug 21 09:55:18 2008 -0700
19.3 @@ -172,7 +172,7 @@
19.4 * reference.
19.5 *
19.6 * So we accept a Field if it has a @Resource annotation and either
19.7 - * (a) its type is ObjectName or a subclass and its @Resource type is
19.8 + * (a) its type is exactly ObjectName and its @Resource type is
19.9 * compatible with ObjectName (e.g. it is Object); or
19.10 * (b) its type is compatible with ObjectName and its @Resource type
19.11 * is exactly ObjectName. Fields that meet these criteria will not
20.1 --- a/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java Tue Aug 19 07:50:03 2008 -0700
20.2 +++ b/src/share/classes/com/sun/jmx/mbeanserver/MBeanSupport.java Thu Aug 21 09:55:18 2008 -0700
20.3 @@ -25,7 +25,6 @@
20.4
20.5 package com.sun.jmx.mbeanserver;
20.6
20.7 -import static com.sun.jmx.mbeanserver.Util.*;
20.8
20.9 import javax.management.Attribute;
20.10 import javax.management.AttributeList;
21.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
21.2 +++ b/src/share/classes/com/sun/jmx/mbeanserver/PerThreadGroupPool.java Thu Aug 21 09:55:18 2008 -0700
21.3 @@ -0,0 +1,71 @@
21.4 +/*
21.5 + * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
21.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
21.7 + *
21.8 + * This code is free software; you can redistribute it and/or modify it
21.9 + * under the terms of the GNU General Public License version 2 only, as
21.10 + * published by the Free Software Foundation. Sun designates this
21.11 + * particular file as subject to the "Classpath" exception as provided
21.12 + * by Sun in the LICENSE file that accompanied this code.
21.13 + *
21.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
21.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21.17 + * version 2 for more details (a copy is included in the LICENSE file that
21.18 + * accompanied this code).
21.19 + *
21.20 + * You should have received a copy of the GNU General Public License version
21.21 + * 2 along with this work; if not, write to the Free Software Foundation,
21.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21.23 + *
21.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
21.26 + * have any questions.
21.27 + */
21.28 +
21.29 +package com.sun.jmx.mbeanserver;
21.30 +
21.31 +import java.lang.ref.WeakReference;
21.32 +import java.util.concurrent.ThreadPoolExecutor;
21.33 +
21.34 +/**
21.35 + * <p>A factory for ThreadPoolExecutor objects that allows the same object to
21.36 + * be shared by all users of the factory that are in the same ThreadGroup.</p>
21.37 + */
21.38 +// We return a ThreadPoolExecutor rather than the more general ExecutorService
21.39 +// because we need to be able to call allowCoreThreadTimeout so that threads in
21.40 +// the pool will eventually be destroyed when the pool is no longer in use.
21.41 +// Otherwise these threads would keep the ThreadGroup alive forever.
21.42 +public class PerThreadGroupPool<T extends ThreadPoolExecutor> {
21.43 + private final WeakIdentityHashMap<ThreadGroup, WeakReference<T>> map =
21.44 + WeakIdentityHashMap.make();
21.45 +
21.46 + public static interface Create<T extends ThreadPoolExecutor> {
21.47 + public T createThreadPool(ThreadGroup group);
21.48 + }
21.49 +
21.50 + private PerThreadGroupPool() {}
21.51 +
21.52 + public static <T extends ThreadPoolExecutor> PerThreadGroupPool<T> make() {
21.53 + return new PerThreadGroupPool<T>();
21.54 + }
21.55 +
21.56 + public synchronized T getThreadPoolExecutor(Create<T> create) {
21.57 + // Find out if there's already an existing executor for the calling
21.58 + // thread and reuse it. Otherwise, create a new one and store it in
21.59 + // the executors map. If there is a SecurityManager, the group of
21.60 + // System.getSecurityManager() is used, else the group of the calling
21.61 + // thread.
21.62 + SecurityManager s = System.getSecurityManager();
21.63 + ThreadGroup group = (s != null) ? s.getThreadGroup() :
21.64 + Thread.currentThread().getThreadGroup();
21.65 + WeakReference<T> wr = map.get(group);
21.66 + T executor = (wr == null) ? null : wr.get();
21.67 + if (executor == null) {
21.68 + executor = create.createThreadPool(group);
21.69 + executor.allowCoreThreadTimeOut(true);
21.70 + map.put(group, new WeakReference<T>(executor));
21.71 + }
21.72 + return executor;
21.73 + }
21.74 +}
22.1 --- a/src/share/classes/com/sun/jmx/mbeanserver/Util.java Tue Aug 19 07:50:03 2008 -0700
22.2 +++ b/src/share/classes/com/sun/jmx/mbeanserver/Util.java Thu Aug 21 09:55:18 2008 -0700
22.3 @@ -38,10 +38,13 @@
22.4 import java.util.Map;
22.5 import java.util.Set;
22.6 import java.util.SortedMap;
22.7 +import java.util.SortedSet;
22.8 import java.util.TreeMap;
22.9 +import java.util.TreeSet;
22.10 import java.util.WeakHashMap;
22.11 import javax.management.MalformedObjectNameException;
22.12 import javax.management.ObjectName;
22.13 +import javax.management.loading.ClassLoaderRepository;
22.14
22.15 public class Util {
22.16 static <K, V> Map<K, V> newMap() {
22.17 @@ -142,4 +145,97 @@
22.18 return hash;
22.19 }
22.20
22.21 + /**
22.22 + * Filters a set of ObjectName according to a given pattern.
22.23 + *
22.24 + * @param pattern the pattern that the returned names must match.
22.25 + * @param all the set of names to filter.
22.26 + * @return a set of ObjectName from which non matching names
22.27 + * have been removed.
22.28 + */
22.29 + public static Set<ObjectName> filterMatchingNames(ObjectName pattern,
22.30 + Set<ObjectName> all) {
22.31 + // If no pattern, just return all names
22.32 + if (pattern == null
22.33 + || all.isEmpty()
22.34 + || ObjectName.WILDCARD.equals(pattern))
22.35 + return all;
22.36 +
22.37 + // If there's a pattern, do the matching.
22.38 + final Set<ObjectName> res = equivalentEmptySet(all);
22.39 + for (ObjectName n : all) if (pattern.apply(n)) res.add(n);
22.40 + return res;
22.41 + }
22.42 +
22.43 + /**
22.44 + * An abstract ClassLoaderRepository that contains a single class loader.
22.45 + **/
22.46 + private final static class SingleClassLoaderRepository
22.47 + implements ClassLoaderRepository {
22.48 + private final ClassLoader singleLoader;
22.49 +
22.50 + SingleClassLoaderRepository(ClassLoader loader) {
22.51 + this.singleLoader = loader;
22.52 + }
22.53 +
22.54 + ClassLoader getSingleClassLoader() {
22.55 + return singleLoader;
22.56 + }
22.57 +
22.58 + private Class<?> loadClass(String className, ClassLoader loader)
22.59 + throws ClassNotFoundException {
22.60 + return Class.forName(className, false, loader);
22.61 + }
22.62 +
22.63 + public Class<?> loadClass(String className)
22.64 + throws ClassNotFoundException {
22.65 + return loadClass(className, getSingleClassLoader());
22.66 + }
22.67 +
22.68 + public Class<?> loadClassWithout(ClassLoader exclude,
22.69 + String className) throws ClassNotFoundException {
22.70 + final ClassLoader loader = getSingleClassLoader();
22.71 + if (exclude != null && exclude.equals(loader))
22.72 + throw new ClassNotFoundException(className);
22.73 + return loadClass(className, loader);
22.74 + }
22.75 +
22.76 + public Class<?> loadClassBefore(ClassLoader stop, String className)
22.77 + throws ClassNotFoundException {
22.78 + return loadClassWithout(stop, className);
22.79 + }
22.80 + }
22.81 +
22.82 + /**
22.83 + * Returns a ClassLoaderRepository that contains a single class loader.
22.84 + * @param loader the class loader contained in the returned repository.
22.85 + * @return a ClassLoaderRepository that contains the single loader.
22.86 + */
22.87 + public static ClassLoaderRepository getSingleClassLoaderRepository(
22.88 + final ClassLoader loader) {
22.89 + return new SingleClassLoaderRepository(loader);
22.90 + }
22.91 +
22.92 + public static <T> Set<T> cloneSet(Set<T> set) {
22.93 + if (set instanceof SortedSet) {
22.94 + @SuppressWarnings("unchecked")
22.95 + SortedSet<T> sset = (SortedSet<T>) set;
22.96 + set = new TreeSet<T>(sset.comparator());
22.97 + set.addAll(sset);
22.98 + } else
22.99 + set = new HashSet<T>(set);
22.100 + return set;
22.101 + }
22.102 +
22.103 + public static <T> Set<T> equivalentEmptySet(Set<T> set) {
22.104 + if (set instanceof SortedSet) {
22.105 + @SuppressWarnings("unchecked")
22.106 + SortedSet<T> sset = (SortedSet<T>) set;
22.107 + set = new TreeSet<T>(sset.comparator());
22.108 + } else if (set != null) {
22.109 + set = new HashSet<T>(set.size());
22.110 + } else
22.111 + set = new HashSet<T>();
22.112 + return set;
22.113 + }
22.114 }
23.1 --- a/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java Tue Aug 19 07:50:03 2008 -0700
23.2 +++ b/src/share/classes/com/sun/jmx/remote/internal/ClientNotifForwarder.java Thu Aug 21 09:55:18 2008 -0700
23.3 @@ -576,6 +576,7 @@
23.4 int notFoundCount = 0;
23.5
23.6 NotificationResult result = null;
23.7 + long firstEarliest = -1;
23.8
23.9 while (result == null && !shouldStop()) {
23.10 NotificationResult nr;
23.11 @@ -598,6 +599,8 @@
23.12 return null;
23.13
23.14 startSequenceNumber = nr.getNextSequenceNumber();
23.15 + if (firstEarliest < 0)
23.16 + firstEarliest = nr.getEarliestSequenceNumber();
23.17
23.18 try {
23.19 // 1 notif to skip possible missing class
23.20 @@ -628,6 +631,17 @@
23.21 (notFoundCount == 1 ? "" : "s") +
23.22 " because classes were missing locally";
23.23 lostNotifs(msg, notFoundCount);
23.24 + // Even if result.getEarliestSequenceNumber() is now greater than
23.25 + // it was initially, meaning some notifs have been dropped
23.26 + // from the buffer, we don't want the caller to see that
23.27 + // because it is then likely to renotify about the lost notifs.
23.28 + // So we put back the first value of earliestSequenceNumber
23.29 + // that we saw.
23.30 + if (result != null) {
23.31 + result = new NotificationResult(
23.32 + firstEarliest, result.getNextSequenceNumber(),
23.33 + result.getTargetedNotifications());
23.34 + }
23.35 }
23.36
23.37 return result;
24.1 --- a/src/share/classes/com/sun/jmx/remote/internal/ProxyInputStream.java Tue Aug 19 07:50:03 2008 -0700
24.2 +++ b/src/share/classes/com/sun/jmx/remote/internal/ProxyInputStream.java Thu Aug 21 09:55:18 2008 -0700
24.3 @@ -33,10 +33,8 @@
24.4 import org.omg.CORBA.Context;
24.5 import org.omg.CORBA.NO_IMPLEMENT;
24.6 import org.omg.CORBA.ORB;
24.7 -import org.omg.CORBA.Principal;
24.8 import org.omg.CORBA.TypeCode;
24.9 import org.omg.CORBA.portable.BoxedValueHelper;
24.10 -import org.omg.CORBA_2_3.portable.InputStream;
24.11
24.12 @SuppressWarnings("deprecation")
24.13 public class ProxyInputStream extends org.omg.CORBA_2_3.portable.InputStream {
24.14 @@ -160,54 +158,71 @@
24.15 return in.read_any();
24.16 }
24.17
24.18 - public Principal read_Principal() {
24.19 + /**
24.20 + * @deprecated
24.21 + */
24.22 + @Override
24.23 + @Deprecated
24.24 + public org.omg.CORBA.Principal read_Principal() {
24.25 return in.read_Principal();
24.26 }
24.27
24.28 + @Override
24.29 public int read() throws IOException {
24.30 return in.read();
24.31 }
24.32
24.33 + @Override
24.34 public BigDecimal read_fixed() {
24.35 return in.read_fixed();
24.36 }
24.37
24.38 + @Override
24.39 public Context read_Context() {
24.40 return in.read_Context();
24.41 }
24.42
24.43 + @Override
24.44 public org.omg.CORBA.Object read_Object(java.lang.Class clz) {
24.45 return in.read_Object(clz);
24.46 }
24.47
24.48 + @Override
24.49 public ORB orb() {
24.50 return in.orb();
24.51 }
24.52
24.53 + @Override
24.54 public Serializable read_value() {
24.55 return narrow().read_value();
24.56 }
24.57
24.58 + @Override
24.59 public Serializable read_value(Class clz) {
24.60 return narrow().read_value(clz);
24.61 }
24.62
24.63 + @Override
24.64 public Serializable read_value(BoxedValueHelper factory) {
24.65 return narrow().read_value(factory);
24.66 }
24.67
24.68 + @Override
24.69 public Serializable read_value(String rep_id) {
24.70 return narrow().read_value(rep_id);
24.71 }
24.72
24.73 + @Override
24.74 public Serializable read_value(Serializable value) {
24.75 return narrow().read_value(value);
24.76 }
24.77
24.78 + @Override
24.79 public Object read_abstract_interface() {
24.80 return narrow().read_abstract_interface();
24.81 }
24.82
24.83 + @Override
24.84 public Object read_abstract_interface(Class clz) {
24.85 return narrow().read_abstract_interface(clz);
24.86 }
25.1 --- a/src/share/classes/com/sun/jmx/remote/internal/ProxyRef.java Tue Aug 19 07:50:03 2008 -0700
25.2 +++ b/src/share/classes/com/sun/jmx/remote/internal/ProxyRef.java Thu Aug 21 09:55:18 2008 -0700
25.3 @@ -31,8 +31,6 @@
25.4 import java.lang.reflect.Method;
25.5 import java.rmi.Remote;
25.6 import java.rmi.RemoteException;
25.7 -import java.rmi.server.Operation;
25.8 -import java.rmi.server.RemoteCall;
25.9 import java.rmi.server.RemoteObject;
25.10 import java.rmi.server.RemoteRef;
25.11
25.12 @@ -54,7 +52,11 @@
25.13 ref.writeExternal(out);
25.14 }
25.15
25.16 - public void invoke(RemoteCall call) throws Exception {
25.17 + /**
25.18 + * @deprecated
25.19 + */
25.20 + @Deprecated
25.21 + public void invoke(java.rmi.server.RemoteCall call) throws Exception {
25.22 ref.invoke(call);
25.23 }
25.24
25.25 @@ -63,7 +65,11 @@
25.26 return ref.invoke(obj, method, params, opnum);
25.27 }
25.28
25.29 - public void done(RemoteCall call) throws RemoteException {
25.30 + /**
25.31 + * @deprecated
25.32 + */
25.33 + @Deprecated
25.34 + public void done(java.rmi.server.RemoteCall call) throws RemoteException {
25.35 ref.done(call);
25.36 }
25.37
25.38 @@ -71,7 +77,12 @@
25.39 return ref.getRefClass(out);
25.40 }
25.41
25.42 - public RemoteCall newCall(RemoteObject obj, Operation[] op, int opnum,
25.43 + /**
25.44 + * @deprecated
25.45 + */
25.46 + @Deprecated
25.47 + public java.rmi.server.RemoteCall newCall(RemoteObject obj,
25.48 + java.rmi.server.Operation[] op, int opnum,
25.49 long hash) throws RemoteException {
25.50 return ref.newCall(obj, op, opnum, hash);
25.51 }
26.1 --- a/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java Tue Aug 19 07:50:03 2008 -0700
26.2 +++ b/src/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java Thu Aug 21 09:55:18 2008 -0700
26.3 @@ -25,16 +25,16 @@
26.4
26.5 package com.sun.jmx.remote.internal;
26.6
26.7 +import com.sun.jmx.mbeanserver.Util;
26.8 import com.sun.jmx.remote.security.NotificationAccessController;
26.9 import com.sun.jmx.remote.util.ClassLogger;
26.10 import com.sun.jmx.remote.util.EnvHelp;
26.11 import java.io.IOException;
26.12 import java.security.AccessControlContext;
26.13 import java.security.AccessController;
26.14 +import java.security.PrivilegedAction;
26.15 import java.security.PrivilegedActionException;
26.16 import java.security.PrivilegedExceptionAction;
26.17 -import java.util.ArrayList;
26.18 -import java.util.Arrays;
26.19 import java.util.Collections;
26.20 import java.util.HashMap;
26.21 import java.util.HashSet;
26.22 @@ -67,9 +67,9 @@
26.23 connectionTimeout = EnvHelp.getServerConnectionTimeout(env);
26.24 checkNotificationEmission = EnvHelp.computeBooleanFromString(
26.25 env,
26.26 - "jmx.remote.x.check.notification.emission");
26.27 - notificationAccessController = (NotificationAccessController)
26.28 - env.get("com.sun.jmx.remote.notification.access.controller");
26.29 + "jmx.remote.x.check.notification.emission",false);
26.30 + notificationAccessController =
26.31 + EnvHelp.getNotificationAccessController(env);
26.32 }
26.33
26.34 public Integer addNotificationListener(final ObjectName name,
26.35 @@ -88,9 +88,7 @@
26.36 checkMBeanPermission(name, "addNotificationListener");
26.37 if (notificationAccessController != null) {
26.38 notificationAccessController.addNotificationListener(
26.39 - connectionId,
26.40 - name,
26.41 - Subject.getSubject(AccessController.getContext()));
26.42 + connectionId, name, getSubject());
26.43 }
26.44 try {
26.45 boolean instanceOf =
26.46 @@ -160,9 +158,7 @@
26.47 checkMBeanPermission(name, "removeNotificationListener");
26.48 if (notificationAccessController != null) {
26.49 notificationAccessController.removeNotificationListener(
26.50 - connectionId,
26.51 - name,
26.52 - Subject.getSubject(AccessController.getContext()));
26.53 + connectionId, name, getSubject());
26.54 }
26.55
26.56 Exception re = null;
26.57 @@ -312,6 +308,10 @@
26.58 // PRIVATE METHODS
26.59 //----------------
26.60
26.61 + private Subject getSubject() {
26.62 + return Subject.getSubject(AccessController.getContext());
26.63 + }
26.64 +
26.65 private void checkState() throws IOException {
26.66 synchronized(terminationLock) {
26.67 if (terminated) {
26.68 @@ -332,7 +332,13 @@
26.69 */
26.70 private void checkMBeanPermission(final ObjectName name,
26.71 final String actions)
26.72 - throws InstanceNotFoundException, SecurityException {
26.73 + throws InstanceNotFoundException, SecurityException {
26.74 + checkMBeanPermission(mbeanServer, name, actions);
26.75 + }
26.76 +
26.77 + public static void checkMBeanPermission(
26.78 + final MBeanServer mbs, final ObjectName name, final String actions)
26.79 + throws InstanceNotFoundException, SecurityException {
26.80 SecurityManager sm = System.getSecurityManager();
26.81 if (sm != null) {
26.82 AccessControlContext acc = AccessController.getContext();
26.83 @@ -342,7 +348,7 @@
26.84 new PrivilegedExceptionAction<ObjectInstance>() {
26.85 public ObjectInstance run()
26.86 throws InstanceNotFoundException {
26.87 - return mbeanServer.getObjectInstance(name);
26.88 + return mbs.getObjectInstance(name);
26.89 }
26.90 });
26.91 } catch (PrivilegedActionException e) {
26.92 @@ -364,14 +370,12 @@
26.93 TargetedNotification tn) {
26.94 try {
26.95 if (checkNotificationEmission) {
26.96 - checkMBeanPermission(name, "addNotificationListener");
26.97 + checkMBeanPermission(
26.98 + name, "addNotificationListener");
26.99 }
26.100 if (notificationAccessController != null) {
26.101 notificationAccessController.fetchNotification(
26.102 - connectionId,
26.103 - name,
26.104 - tn.getNotification(),
26.105 - Subject.getSubject(AccessController.getContext()));
26.106 + connectionId, name, tn.getNotification(), getSubject());
26.107 }
26.108 return true;
26.109 } catch (SecurityException e) {
27.1 --- a/src/share/classes/com/sun/jmx/remote/security/FileLoginModule.java Tue Aug 19 07:50:03 2008 -0700
27.2 +++ b/src/share/classes/com/sun/jmx/remote/security/FileLoginModule.java Thu Aug 21 09:55:18 2008 -0700
27.3 @@ -25,6 +25,7 @@
27.4
27.5 package com.sun.jmx.remote.security;
27.6
27.7 +import com.sun.jmx.mbeanserver.GetPropertyAction;
27.8 import java.io.BufferedInputStream;
27.9 import java.io.File;
27.10 import java.io.FileInputStream;
27.11 @@ -47,8 +48,6 @@
27.12 import com.sun.jmx.remote.util.EnvHelp;
27.13 import sun.management.jmxremote.ConnectorBootstrap;
27.14
27.15 -import sun.security.action.GetPropertyAction;
27.16 -
27.17 /**
27.18 * This {@link LoginModule} performs file-based authentication.
27.19 *
27.20 @@ -479,7 +478,7 @@
27.21 if (userSuppliedPasswordFile || hasJavaHomePermission) {
27.22 throw e;
27.23 } else {
27.24 - FilePermission fp =
27.25 + final FilePermission fp =
27.26 new FilePermission(passwordFileDisplayName, "read");
27.27 AccessControlException ace = new AccessControlException(
27.28 "access denied " + fp.toString());
27.29 @@ -488,10 +487,13 @@
27.30 }
27.31 }
27.32 try {
27.33 - BufferedInputStream bis = new BufferedInputStream(fis);
27.34 - userCredentials = new Properties();
27.35 - userCredentials.load(bis);
27.36 - bis.close();
27.37 + final BufferedInputStream bis = new BufferedInputStream(fis);
27.38 + try {
27.39 + userCredentials = new Properties();
27.40 + userCredentials.load(bis);
27.41 + } finally {
27.42 + bis.close();
27.43 + }
27.44 } finally {
27.45 fis.close();
27.46 }
28.1 --- a/src/share/classes/com/sun/jmx/remote/util/EnvHelp.java Tue Aug 19 07:50:03 2008 -0700
28.2 +++ b/src/share/classes/com/sun/jmx/remote/util/EnvHelp.java Thu Aug 21 09:55:18 2008 -0700
28.3 @@ -40,9 +40,6 @@
28.4 import java.util.TreeSet;
28.5
28.6 import java.security.AccessController;
28.7 -import java.security.PrivilegedAction;
28.8 -import java.security.PrivilegedActionException;
28.9 -import java.security.PrivilegedExceptionAction;
28.10
28.11 import javax.management.ObjectName;
28.12 import javax.management.MBeanServer;
28.13 @@ -50,6 +47,9 @@
28.14 import javax.management.remote.JMXConnectorFactory;
28.15 import javax.management.remote.JMXConnectorServerFactory;
28.16 import com.sun.jmx.mbeanserver.GetPropertyAction;
28.17 +import com.sun.jmx.remote.security.NotificationAccessController;
28.18 +import javax.management.remote.JMXConnector;
28.19 +import javax.management.remote.JMXConnectorServer;
28.20
28.21 public class EnvHelp {
28.22
28.23 @@ -346,7 +346,24 @@
28.24 */
28.25 public static long getFetchTimeout(Map env) {
28.26 return getIntegerAttribute(env, FETCH_TIMEOUT, 60000L, 0,
28.27 - Long.MAX_VALUE);
28.28 + Long.MAX_VALUE);
28.29 + }
28.30 +
28.31 + /**
28.32 + * <p>Name of the attribute that specifies an object that will check
28.33 + * accesses to add/removeNotificationListener and also attempts to
28.34 + * receive notifications. The value associated with this attribute
28.35 + * should be a <code>NotificationAccessController</code> object.
28.36 + * The default value is null.</p>
28.37 + * This field is not public because of its com.sun dependency.
28.38 + */
28.39 + public static final String NOTIF_ACCESS_CONTROLLER =
28.40 + "com.sun.jmx.remote.notification.access.controller";
28.41 +
28.42 + public static NotificationAccessController getNotificationAccessController(
28.43 + Map env) {
28.44 + return (env == null) ? null :
28.45 + (NotificationAccessController) env.get(NOTIF_ACCESS_CONTROLLER);
28.46 }
28.47
28.48 /**
28.49 @@ -470,24 +487,24 @@
28.50 }
28.51
28.52 /**
28.53 - The value of this attribute, if present, is a string specifying
28.54 - what other attributes should not appear in
28.55 - JMXConnectorServer.getAttributes(). It is a space-separated
28.56 - list of attribute patterns, where each pattern is either an
28.57 - attribute name, or an attribute prefix followed by a "*"
28.58 - character. The "*" has no special significance anywhere except
28.59 - at the end of a pattern. By default, this list is added to the
28.60 - list defined by {@link #DEFAULT_HIDDEN_ATTRIBUTES} (which
28.61 - uses the same format). If the value of this attribute begins
28.62 - with an "=", then the remainder of the string defines the
28.63 - complete list of attribute patterns.
28.64 + * The value of this attribute, if present, is a string specifying
28.65 + * what other attributes should not appear in
28.66 + * JMXConnectorServer.getAttributes(). It is a space-separated
28.67 + * list of attribute patterns, where each pattern is either an
28.68 + * attribute name, or an attribute prefix followed by a "*"
28.69 + * character. The "*" has no special significance anywhere except
28.70 + * at the end of a pattern. By default, this list is added to the
28.71 + * list defined by {@link #DEFAULT_HIDDEN_ATTRIBUTES} (which
28.72 + * uses the same format). If the value of this attribute begins
28.73 + * with an "=", then the remainder of the string defines the
28.74 + * complete list of attribute patterns.
28.75 */
28.76 public static final String HIDDEN_ATTRIBUTES =
28.77 "jmx.remote.x.hidden.attributes";
28.78
28.79 /**
28.80 - Default list of attributes not to show.
28.81 - @see #HIDDEN_ATTRIBUTES
28.82 + * Default list of attributes not to show.
28.83 + * @see #HIDDEN_ATTRIBUTES
28.84 */
28.85 /* This list is copied directly from the spec, plus
28.86 java.naming.security.*. Most of the attributes here would have
28.87 @@ -651,6 +668,8 @@
28.88 * @param env the environment map.
28.89 * @param prop the name of the property in the environment map whose
28.90 * returned string value must be converted into a boolean value.
28.91 + * @param systemProperty if true, consult a system property of the
28.92 + * same name if there is no entry in the environment map.
28.93 *
28.94 * @return
28.95 * <ul>
28.96 @@ -671,16 +690,73 @@
28.97 * @throws ClassCastException if {@code env.get(prop)} cannot be cast
28.98 * to {@code String}.
28.99 */
28.100 - public static boolean computeBooleanFromString(Map env, String prop)
28.101 - throws IllegalArgumentException, ClassCastException {
28.102 + public static boolean computeBooleanFromString(
28.103 + Map env, String prop, boolean systemProperty) {
28.104 +
28.105 + if (env == null)
28.106 + throw new IllegalArgumentException("env map cannot be null");
28.107 +
28.108 + // returns a default value of 'false' if no property is found...
28.109 + return computeBooleanFromString(env,prop,systemProperty,false);
28.110 + }
28.111 +
28.112 + /**
28.113 + * Computes a boolean value from a string value retrieved from a
28.114 + * property in the given map.
28.115 + *
28.116 + * @param env the environment map.
28.117 + * @param prop the name of the property in the environment map whose
28.118 + * returned string value must be converted into a boolean value.
28.119 + * @param systemProperty if true, consult a system property of the
28.120 + * same name if there is no entry in the environment map.
28.121 + * @param defaultValue a default value to return in case no property
28.122 + * was defined.
28.123 + *
28.124 + * @return
28.125 + * <ul>
28.126 + * <li>{@code defaultValue} if {@code env.get(prop)} is {@code null}
28.127 + * and {@code systemProperty} is {@code false}</li>
28.128 + * <li>{@code defaultValue} if {@code env.get(prop)} is {@code null}
28.129 + * and {@code systemProperty} is {@code true} and
28.130 + * {@code System.getProperty(prop)} is {@code null}</li>
28.131 + * <li>{@code false} if {@code env.get(prop)} is {@code null}
28.132 + * and {@code systemProperty} is {@code true} and
28.133 + * {@code System.getProperty(prop).equalsIgnoreCase("false")}
28.134 + * is {@code true}</li>
28.135 + * <li>{@code true} if {@code env.get(prop)} is {@code null}
28.136 + * and {@code systemProperty} is {@code true} and
28.137 + * {@code System.getProperty(prop).equalsIgnoreCase("true")}
28.138 + * is {@code true}</li>
28.139 + * <li>{@code false} if
28.140 + * {@code ((String)env.get(prop)).equalsIgnoreCase("false")}
28.141 + * is {@code true}</li>
28.142 + * <li>{@code true} if
28.143 + * {@code ((String)env.get(prop)).equalsIgnoreCase("true")}
28.144 + * is {@code true}</li>
28.145 + * </ul>
28.146 + *
28.147 + * @throws IllegalArgumentException if {@code env} is {@code null} or
28.148 + * {@code env.get(prop)} is not {@code null} and
28.149 + * {@code ((String)env.get(prop)).equalsIgnoreCase("false")} and
28.150 + * {@code ((String)env.get(prop)).equalsIgnoreCase("true")} are
28.151 + * {@code false}.
28.152 + * @throws ClassCastException if {@code env.get(prop)} cannot be cast
28.153 + * to {@code String}.
28.154 + */
28.155 + public static boolean computeBooleanFromString(
28.156 + Map env, String prop, boolean systemProperty, boolean defaultValue) {
28.157
28.158 if (env == null)
28.159 throw new IllegalArgumentException("env map cannot be null");
28.160
28.161 String stringBoolean = (String) env.get(prop);
28.162 + if (stringBoolean == null && systemProperty) {
28.163 + stringBoolean =
28.164 + AccessController.doPrivileged(new GetPropertyAction(prop));
28.165 + }
28.166
28.167 if (stringBoolean == null)
28.168 - return false;
28.169 + return defaultValue;
28.170 else if (stringBoolean.equalsIgnoreCase("true"))
28.171 return true;
28.172 else if (stringBoolean.equalsIgnoreCase("false"))
28.173 @@ -703,6 +779,65 @@
28.174 return new Hashtable<K, V>(m);
28.175 }
28.176
28.177 + /**
28.178 + * Returns true if the parameter JMXConnector.USE_EVENT_SERVICE is set to a
28.179 + * String equals "true" by ignoring case in the map or in the System.
28.180 + */
28.181 + public static boolean eventServiceEnabled(Map env) {
28.182 + return computeBooleanFromString(env, JMXConnector.USE_EVENT_SERVICE, true);
28.183 + }
28.184 +
28.185 + /**
28.186 + * Returns true if the parameter JMXConnectorServer.DELEGATE_TO_EVENT_SERVICE
28.187 + * is set to a String equals "true" (ignores case).
28.188 + * If the property DELEGATE_TO_EVENT_SERVICE is not set, returns
28.189 + * a default value of "true".
28.190 + */
28.191 + public static boolean delegateToEventService(Map env) {
28.192 + return computeBooleanFromString(env,
28.193 + JMXConnectorServer.DELEGATE_TO_EVENT_SERVICE, true, true);
28.194 + }
28.195 +
28.196 +// /**
28.197 +// * <p>Name of the attribute that specifies an EventRelay object to use.
28.198 +// */
28.199 +// public static final String EVENT_RELAY =
28.200 +// "jmx.remote.x.event.relay";
28.201 +//
28.202 +//
28.203 +// /**
28.204 +// * Returns an EventRelay object. The default one is FetchingEventRelay.
28.205 +// * If {@code EVENT_RELAY} is specified in {@code env} as a key,
28.206 +// * its value will be returned as an EventRelay object, if the value is
28.207 +// * not of type {@code EventRelay}, the default {@code FetchingEventRelay}
28.208 +// * will be returned.
28.209 +// * If {@code EVENT_RELAY} is not specified but {@code ENABLE_EVENT_RELAY}
28.210 +// * is specified as a key and its value is <code true>, the default {@code FetchingEventRelay}
28.211 +// * will be returned.
28.212 +// */
28.213 +// public static EventRelay getEventRelay(Map env) {
28.214 +// Map info = env == null ?
28.215 +// Collections.EMPTY_MAP : env;
28.216 +//
28.217 +// Object o = env.get(EVENT_RELAY);
28.218 +// if (o instanceof EventRelay) {
28.219 +// return (EventRelay)o;
28.220 +// } else if (o != null) {
28.221 +// logger.warning("getEventRelay",
28.222 +// "The user specified object is not an EventRelay object, " +
28.223 +// "using the default class FetchingEventRelay.");
28.224 +//
28.225 +// return new FetchingEventRelay();
28.226 +// }
28.227 +//
28.228 +// if (enableEventRelay(env)) {
28.229 +// return new FetchingEventRelay();
28.230 +// }
28.231 +//
28.232 +// return null;
28.233 +// }
28.234 +
28.235 +
28.236 private static final class SinkOutputStream extends OutputStream {
28.237 public void write(byte[] b, int off, int len) {}
28.238 public void write(int b) {}
29.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
29.2 +++ b/src/share/classes/com/sun/jmx/remote/util/EventClientConnection.java Thu Aug 21 09:55:18 2008 -0700
29.3 @@ -0,0 +1,471 @@
29.4 +/*
29.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
29.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
29.7 + *
29.8 + * This code is free software; you can redistribute it and/or modify it
29.9 + * under the terms of the GNU General Public License version 2 only, as
29.10 + * published by the Free Software Foundation. Sun designates this
29.11 + * particular file as subject to the "Classpath" exception as provided
29.12 + * by Sun in the LICENSE file that accompanied this code.
29.13 + *
29.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
29.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
29.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
29.17 + * version 2 for more details (a copy is included in the LICENSE file that
29.18 + * accompanied this code).
29.19 + *
29.20 + * You should have received a copy of the GNU General Public License version
29.21 + * 2 along with this work; if not, write to the Free Software Foundation,
29.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
29.23 + *
29.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
29.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
29.26 + * have any questions.
29.27 + */
29.28 +
29.29 +package com.sun.jmx.remote.util;
29.30 +
29.31 +import com.sun.jmx.event.EventClientFactory;
29.32 +
29.33 +import java.lang.reflect.InvocationHandler;
29.34 +import java.lang.reflect.InvocationTargetException;
29.35 +import java.lang.reflect.Method;
29.36 +import java.lang.reflect.Proxy;
29.37 +import java.util.Arrays;
29.38 +import java.util.concurrent.Callable;
29.39 +import java.util.concurrent.TimeUnit;
29.40 +import java.util.concurrent.locks.Lock;
29.41 +import java.util.concurrent.locks.ReentrantLock;
29.42 +import java.util.logging.Level;
29.43 +import java.util.logging.Logger;
29.44 +
29.45 +import javax.management.MBeanServerConnection;
29.46 +import javax.management.NotificationFilter;
29.47 +import javax.management.NotificationListener;
29.48 +import javax.management.ObjectName;
29.49 +import javax.management.event.EventClient;
29.50 +import javax.management.event.EventClientDelegate;
29.51 +
29.52 +/**
29.53 + * Class EventClientConnection - a {@link Proxy} that wraps an
29.54 + * {@link MBeanServerConnection} and an {@link EventClient}.
29.55 + * All methods are routed to the underlying {@code MBeanServerConnection},
29.56 + * except add/remove notification listeners which are routed to the
29.57 + * {@code EventClient}.
29.58 + * The caller only sees an {@code MBeanServerConnection} which uses an
29.59 + * {@code EventClient} behind the scenes.
29.60 + *
29.61 + * @author Sun Microsystems, Inc.
29.62 + */
29.63 +public class EventClientConnection implements InvocationHandler,
29.64 + EventClientFactory {
29.65 +
29.66 + /**
29.67 + * A logger for this class.
29.68 + **/
29.69 + private static final Logger LOG =
29.70 + Logger.getLogger(EventClientConnection.class.getName());
29.71 +
29.72 + private static final String NAMESPACE_SEPARATOR = "//";
29.73 + private static final int NAMESPACE_SEPARATOR_LENGTH =
29.74 + NAMESPACE_SEPARATOR.length();
29.75 +
29.76 + /**
29.77 + * Creates a new {@code EventClientConnection}.
29.78 + * @param connection The underlying MBeanServerConnection.
29.79 + */
29.80 + public EventClientConnection(MBeanServerConnection connection) {
29.81 + this(connection,null);
29.82 + }
29.83 +
29.84 + /**
29.85 + * Creates a new {@code EventClientConnection}.
29.86 + * @param connection The underlying MBeanServerConnection.
29.87 + * @param eventClientFactory a factory object that will be invoked
29.88 + * to create an {@link EventClient} when needed.
29.89 + * The {@code EventClient} is created lazily, when it is needed
29.90 + * for the first time. If null, a default factory will be used
29.91 + * (see {@link #createEventClient}).
29.92 + */
29.93 + public EventClientConnection(MBeanServerConnection connection,
29.94 + Callable<EventClient> eventClientFactory) {
29.95 +
29.96 + if (connection == null) {
29.97 + throw new IllegalArgumentException("Null connection");
29.98 + }
29.99 + this.connection = connection;
29.100 + if (eventClientFactory == null) {
29.101 + eventClientFactory = new Callable<EventClient>() {
29.102 + public final EventClient call() throws Exception {
29.103 + return createEventClient(EventClientConnection.this.connection);
29.104 + }
29.105 + };
29.106 + }
29.107 + this.eventClientFactory = eventClientFactory;
29.108 + this.lock = new ReentrantLock();
29.109 + }
29.110 +
29.111 + /**
29.112 + * <p>The MBean server connection through which the methods of
29.113 + * a proxy using this handler are forwarded.</p>
29.114 + *
29.115 + * @return the MBean server connection.
29.116 + *
29.117 + * @since 1.6
29.118 + */
29.119 + public MBeanServerConnection getMBeanServerConnection() {
29.120 + return connection;
29.121 + }
29.122 +
29.123 +
29.124 +
29.125 +
29.126 + /**
29.127 + * Creates a new EventClientConnection proxy instance.
29.128 + *
29.129 + * @param <T> The underlying {@code MBeanServerConnection} - which should
29.130 + * not be using the Event Service itself.
29.131 + * @param interfaceClass {@code MBeanServerConnection.class}, or a subclass.
29.132 + * @param eventClientFactory a factory used to create the EventClient.
29.133 + * If null, a default factory is used (see {@link
29.134 + * #createEventClient}).
29.135 + * @return the new proxy instance, which will route add/remove notification
29.136 + * listener calls through an {@code EventClient}.
29.137 + *
29.138 + */
29.139 + private static <T extends MBeanServerConnection> T
29.140 + newProxyInstance(T connection,
29.141 + Class<T> interfaceClass, Callable<EventClient> eventClientFactory) {
29.142 + final InvocationHandler handler =
29.143 + new EventClientConnection(connection,eventClientFactory);
29.144 + final Class[] interfaces =
29.145 + new Class[] {interfaceClass, EventClientFactory.class};
29.146 +
29.147 + Object proxy =
29.148 + Proxy.newProxyInstance(interfaceClass.getClassLoader(),
29.149 + interfaces,
29.150 + handler);
29.151 + return interfaceClass.cast(proxy);
29.152 + }
29.153 +
29.154 +
29.155 + public Object invoke(Object proxy, Method method, Object[] args)
29.156 + throws Throwable {
29.157 + final String methodName = method.getName();
29.158 +
29.159 + // add/remove notification listener are routed to the EventClient
29.160 + if (methodName.equals("addNotificationListener")
29.161 + || methodName.equals("removeNotificationListener")) {
29.162 + final Class[] sig = method.getParameterTypes();
29.163 + if (sig.length>1 &&
29.164 + NotificationListener.class.isAssignableFrom(sig[1])) {
29.165 + return invokeBroadcasterMethod(proxy,method,args);
29.166 + }
29.167 + }
29.168 +
29.169 + // subscribe/unsubscribe are also routed to the EventClient.
29.170 + final Class clazz = method.getDeclaringClass();
29.171 + if (clazz.equals(EventClientFactory.class)) {
29.172 + return invokeEventClientSubscriberMethod(proxy,method,args);
29.173 + }
29.174 +
29.175 + // local or not: equals, toString, hashCode
29.176 + if (shouldDoLocally(proxy, method))
29.177 + return doLocally(proxy, method, args);
29.178 +
29.179 + return call(connection,method,args);
29.180 + }
29.181 +
29.182 + // The purpose of this method is to unwrap InvocationTargetException,
29.183 + // in order to avoid throwing UndeclaredThrowableException for
29.184 + // declared exceptions.
29.185 + //
29.186 + // When calling method.invoke(), any exception thrown by the invoked
29.187 + // method will be wrapped in InvocationTargetException. If we don't
29.188 + // unwrap this exception, the proxy will always throw
29.189 + // UndeclaredThrowableException, even for runtime exceptions.
29.190 + //
29.191 + private Object call(final Object obj, final Method m,
29.192 + final Object[] args)
29.193 + throws Throwable {
29.194 + try {
29.195 + return m.invoke(obj,args);
29.196 + } catch (InvocationTargetException x) {
29.197 + final Throwable xx = x.getTargetException();
29.198 + if (xx == null) throw x;
29.199 + else throw xx;
29.200 + }
29.201 + }
29.202 +
29.203 + /**
29.204 + * Route add/remove notification listener to the event client.
29.205 + **/
29.206 + private Object invokeBroadcasterMethod(Object proxy, Method method,
29.207 + Object[] args) throws Exception {
29.208 + final String methodName = method.getName();
29.209 + final int nargs = (args == null) ? 0 : args.length;
29.210 +
29.211 + if (nargs < 1) {
29.212 + final String msg =
29.213 + "Bad arg count: " + nargs;
29.214 + throw new IllegalArgumentException(msg);
29.215 + }
29.216 +
29.217 + final ObjectName mbean = (ObjectName) args[0];
29.218 + final EventClient client = getEventClient();
29.219 +
29.220 + // Fails if client is null AND the MBean we try to listen to is
29.221 + // in a subnamespace. We fail here because we know this will not
29.222 + // work.
29.223 + //
29.224 + // Note that if the wrapped MBeanServerConnection points to a an
29.225 + // earlier agent (JDK 1.6 or earlier), then the EventClient will
29.226 + // be null (we can't use the event service with earlier JDKs).
29.227 + //
29.228 + // In principle a null client indicates that the remote VM is of
29.229 + // an earlier version, in which case it shouldn't contain any namespace.
29.230 + //
29.231 + // So having a null client AND an MBean contained in a namespace is
29.232 + // clearly an error case.
29.233 + //
29.234 + if (client == null) {
29.235 + final String domain = mbean.getDomain();
29.236 + final int index = domain.indexOf(NAMESPACE_SEPARATOR);
29.237 + if (index > -1 && index <
29.238 + (domain.length()-NAMESPACE_SEPARATOR_LENGTH)) {
29.239 + throw new UnsupportedOperationException(method.getName()+
29.240 + " on namespace "+domain.substring(0,index+
29.241 + NAMESPACE_SEPARATOR_LENGTH));
29.242 + }
29.243 + }
29.244 +
29.245 + if (methodName.equals("addNotificationListener")) {
29.246 + /* The various throws of IllegalArgumentException here
29.247 + should not happen, since we know what the methods in
29.248 + NotificationBroadcaster and NotificationEmitter
29.249 + are. */
29.250 + if (nargs != 4) {
29.251 + final String msg =
29.252 + "Bad arg count to addNotificationListener: " + nargs;
29.253 + throw new IllegalArgumentException(msg);
29.254 + }
29.255 + /* Other inconsistencies will produce ClassCastException
29.256 + below. */
29.257 +
29.258 + final NotificationListener listener = (NotificationListener) args[1];
29.259 + final NotificationFilter filter = (NotificationFilter) args[2];
29.260 + final Object handback = args[3];
29.261 +
29.262 + if (client != null) {
29.263 + // general case
29.264 + client.addNotificationListener(mbean,listener,filter,handback);
29.265 + } else {
29.266 + // deprecated case. Only works for mbean in local namespace.
29.267 + connection.addNotificationListener(mbean,listener,filter,
29.268 + handback);
29.269 + }
29.270 + return null;
29.271 +
29.272 + } else if (methodName.equals("removeNotificationListener")) {
29.273 +
29.274 + /* NullPointerException if method with no args, but that
29.275 + shouldn't happen because removeNL does have args. */
29.276 + NotificationListener listener = (NotificationListener) args[1];
29.277 +
29.278 + switch (nargs) {
29.279 + case 2:
29.280 + if (client != null) {
29.281 + // general case
29.282 + client.removeNotificationListener(mbean,listener);
29.283 + } else {
29.284 + // deprecated case. Only works for mbean in local namespace.
29.285 + connection.removeNotificationListener(mbean, listener);
29.286 + }
29.287 + return null;
29.288 +
29.289 + case 4:
29.290 + NotificationFilter filter = (NotificationFilter) args[2];
29.291 + Object handback = args[3];
29.292 + if (client != null) {
29.293 + client.removeNotificationListener(mbean,
29.294 + listener,
29.295 + filter,
29.296 + handback);
29.297 + } else {
29.298 + connection.removeNotificationListener(mbean,
29.299 + listener,
29.300 + filter,
29.301 + handback);
29.302 + }
29.303 + return null;
29.304 +
29.305 + default:
29.306 + final String msg =
29.307 + "Bad arg count to removeNotificationListener: " + nargs;
29.308 + throw new IllegalArgumentException(msg);
29.309 + }
29.310 +
29.311 + } else {
29.312 + throw new IllegalArgumentException("Bad method name: " +
29.313 + methodName);
29.314 + }
29.315 + }
29.316 +
29.317 + private boolean shouldDoLocally(Object proxy, Method method) {
29.318 + final String methodName = method.getName();
29.319 + if ((methodName.equals("hashCode") || methodName.equals("toString"))
29.320 + && method.getParameterTypes().length == 0
29.321 + && isLocal(proxy, method))
29.322 + return true;
29.323 + if (methodName.equals("equals")
29.324 + && Arrays.equals(method.getParameterTypes(),
29.325 + new Class[] {Object.class})
29.326 + && isLocal(proxy, method))
29.327 + return true;
29.328 + return false;
29.329 + }
29.330 +
29.331 + private Object doLocally(Object proxy, Method method, Object[] args) {
29.332 + final String methodName = method.getName();
29.333 +
29.334 + if (methodName.equals("equals")) {
29.335 +
29.336 + if (this == args[0]) {
29.337 + return true;
29.338 + }
29.339 +
29.340 + if (!(args[0] instanceof Proxy)) {
29.341 + return false;
29.342 + }
29.343 +
29.344 + final InvocationHandler ihandler =
29.345 + Proxy.getInvocationHandler(args[0]);
29.346 +
29.347 + if (ihandler == null ||
29.348 + !(ihandler instanceof EventClientConnection)) {
29.349 + return false;
29.350 + }
29.351 +
29.352 + final EventClientConnection handler =
29.353 + (EventClientConnection)ihandler;
29.354 +
29.355 + return connection.equals(handler.connection) &&
29.356 + proxy.getClass().equals(args[0].getClass());
29.357 + } else if (methodName.equals("hashCode")) {
29.358 + return connection.hashCode();
29.359 + }
29.360 +
29.361 + throw new RuntimeException("Unexpected method name: " + methodName);
29.362 + }
29.363 +
29.364 + private static boolean isLocal(Object proxy, Method method) {
29.365 + final Class<?>[] interfaces = proxy.getClass().getInterfaces();
29.366 + if(interfaces == null) {
29.367 + return true;
29.368 + }
29.369 +
29.370 + final String methodName = method.getName();
29.371 + final Class<?>[] params = method.getParameterTypes();
29.372 + for (Class<?> intf : interfaces) {
29.373 + try {
29.374 + intf.getMethod(methodName, params);
29.375 + return false; // found method in one of our interfaces
29.376 + } catch (NoSuchMethodException nsme) {
29.377 + // OK.
29.378 + }
29.379 + }
29.380 +
29.381 + return true; // did not find in any interface
29.382 + }
29.383 +
29.384 + /**
29.385 + * Return the EventClient used by this object. Can be null if the
29.386 + * remote VM is of an earlier JDK version which doesn't have the
29.387 + * event service.<br>
29.388 + * This method will invoke the event client factory the first time
29.389 + * it is called.
29.390 + **/
29.391 + public final EventClient getEventClient() {
29.392 + if (initialized) return client;
29.393 + try {
29.394 + if (!lock.tryLock(TRYLOCK_TIMEOUT,TimeUnit.SECONDS))
29.395 + throw new IllegalStateException("can't acquire lock");
29.396 + try {
29.397 + client = eventClientFactory.call();
29.398 + initialized = true;
29.399 + } finally {
29.400 + lock.unlock();
29.401 + }
29.402 + } catch (RuntimeException x) {
29.403 + throw x;
29.404 + } catch (Exception x) {
29.405 + throw new IllegalStateException("Can't create EventClient: "+x,x);
29.406 + }
29.407 + return client;
29.408 + }
29.409 +
29.410 + /**
29.411 + * Returns an event client for the wrapped {@code MBeanServerConnection}.
29.412 + * This is the method invoked by the default event client factory.
29.413 + * @param connection the wrapped {@code MBeanServerConnection}.
29.414 + **/
29.415 + protected EventClient createEventClient(MBeanServerConnection connection)
29.416 + throws Exception {
29.417 + final ObjectName name =
29.418 + EventClientDelegate.OBJECT_NAME;
29.419 + if (connection.isRegistered(name)) {
29.420 + return new EventClient(connection);
29.421 + }
29.422 + return null;
29.423 + }
29.424 +
29.425 + /**
29.426 + * Creates a new {@link MBeanServerConnection} that goes through an
29.427 + * {@link EventClient} to receive/subscribe to notifications.
29.428 + * @param connection the underlying {@link MBeanServerConnection}.
29.429 + * The given <code>connection</code> shouldn't be already
29.430 + * using an {@code EventClient}.
29.431 + * @param eventClientFactory a factory object that will be invoked
29.432 + * to create an {@link EventClient} when needed.
29.433 + * The {@code EventClient} is created lazily, when it is needed
29.434 + * for the first time. If null, a default factory will be used
29.435 + * (see {@link #createEventClient}).
29.436 + * @return the
29.437 + **/
29.438 + public static MBeanServerConnection getEventConnectionFor(
29.439 + MBeanServerConnection connection,
29.440 + Callable<EventClient> eventClientFactory) {
29.441 + // if c already uses an EventClient no need to create a new one.
29.442 + //
29.443 + if (connection instanceof EventClientFactory
29.444 + && eventClientFactory != null)
29.445 + throw new IllegalArgumentException("connection already uses EventClient");
29.446 +
29.447 + if (connection instanceof EventClientFactory)
29.448 + return connection;
29.449 +
29.450 + // create a new proxy using an event client.
29.451 + //
29.452 + if (LOG.isLoggable(Level.FINE))
29.453 + LOG.fine("Creating EventClient for: "+connection);
29.454 + return newProxyInstance(connection,
29.455 + MBeanServerConnection.class,
29.456 + eventClientFactory);
29.457 + }
29.458 +
29.459 + private Object invokeEventClientSubscriberMethod(Object proxy,
29.460 + Method method, Object[] args) throws Throwable {
29.461 + return call(this,method,args);
29.462 + }
29.463 +
29.464 + // Maximum lock timeout in seconds. Obviously arbitrary.
29.465 + //
29.466 + private final static short TRYLOCK_TIMEOUT = 3;
29.467 +
29.468 + private final MBeanServerConnection connection;
29.469 + private final Callable<EventClient> eventClientFactory;
29.470 + private final Lock lock;
29.471 + private volatile EventClient client = null;
29.472 + private volatile boolean initialized = false;
29.473 +
29.474 +}
30.1 --- a/src/share/classes/com/sun/jmx/snmp/tasks/ThreadService.java Tue Aug 19 07:50:03 2008 -0700
30.2 +++ b/src/share/classes/com/sun/jmx/snmp/tasks/ThreadService.java Thu Aug 21 09:55:18 2008 -0700
30.3 @@ -45,15 +45,9 @@
30.4 minThreads = threadNumber;
30.5 threadList = new ExecutorThread[threadNumber];
30.6
30.7 -// for (int i=0; i<threadNumber; i++) {
30.8 -// threadList[i] = new ExecutorThread();
30.9 -// threadList[i].start();
30.10 -// }
30.11 -
30.12 priority = Thread.currentThread().getPriority();
30.13 cloader = Thread.currentThread().getContextClassLoader();
30.14
30.15 -//System.out.println("---jsl: ThreadService: running threads = "+threadNumber);
30.16 }
30.17
30.18 // public methods
30.19 @@ -89,7 +83,6 @@
30.20
30.21 synchronized(jobList) {
30.22 jobList.add(jobList.size(), task);
30.23 -//System.out.println("jsl-ThreadService: added job "+addedJobs++);
30.24
30.25 jobList.notify();
30.26 }
30.27 @@ -196,8 +189,6 @@
30.28 try {
30.29 idle--;
30.30 job.run();
30.31 -//System.out.println("jsl-ThreadService: done job "+doneJobs++);
30.32 -
30.33 } catch (Exception e) {
30.34 // TODO
30.35 e.printStackTrace();
30.36 @@ -228,7 +219,6 @@
30.37 ExecutorThread et = new ExecutorThread();
30.38 et.start();
30.39 threadList[currThreds++] = et;
30.40 -//System.out.println("jsl-ThreadService: create new thread: "+currThreds);
30.41 }
30.42 }
30.43 }
31.1 --- a/src/share/classes/java/util/Timer.java Tue Aug 19 07:50:03 2008 -0700
31.2 +++ b/src/share/classes/java/util/Timer.java Thu Aug 21 09:55:18 2008 -0700
31.3 @@ -25,6 +25,7 @@
31.4
31.5 package java.util;
31.6 import java.util.Date;
31.7 +import java.util.concurrent.atomic.AtomicInteger;
31.8
31.9 /**
31.10 * A facility for threads to schedule tasks for future execution in a
31.11 @@ -92,12 +93,12 @@
31.12 * and the timer thread consumes, executing timer tasks as appropriate,
31.13 * and removing them from the queue when they're obsolete.
31.14 */
31.15 - private TaskQueue queue = new TaskQueue();
31.16 + private final TaskQueue queue = new TaskQueue();
31.17
31.18 /**
31.19 * The timer thread.
31.20 */
31.21 - private TimerThread thread = new TimerThread(queue);
31.22 + private final TimerThread thread = new TimerThread(queue);
31.23
31.24 /**
31.25 * This object causes the timer's task execution thread to exit
31.26 @@ -106,7 +107,7 @@
31.27 * Timer as such a finalizer would be susceptible to a subclass's
31.28 * finalizer forgetting to call it.
31.29 */
31.30 - private Object threadReaper = new Object() {
31.31 + private final Object threadReaper = new Object() {
31.32 protected void finalize() throws Throwable {
31.33 synchronized(queue) {
31.34 thread.newTasksMayBeScheduled = false;
31.35 @@ -116,12 +117,11 @@
31.36 };
31.37
31.38 /**
31.39 - * This ID is used to generate thread names. (It could be replaced
31.40 - * by an AtomicInteger as soon as they become available.)
31.41 + * This ID is used to generate thread names.
31.42 */
31.43 - private static int nextSerialNumber = 0;
31.44 - private static synchronized int serialNumber() {
31.45 - return nextSerialNumber++;
31.46 + private final static AtomicInteger nextSerialNumber = new AtomicInteger(0);
31.47 + private static int serialNumber() {
31.48 + return nextSerialNumber.getAndIncrement();
31.49 }
31.50
31.51 /**
31.52 @@ -387,6 +387,11 @@
31.53 if (time < 0)
31.54 throw new IllegalArgumentException("Illegal execution time.");
31.55
31.56 + // Constrain value of period sufficiently to prevent numeric
31.57 + // overflow while still being effectively infinitely large.
31.58 + if (Math.abs(period) > (Long.MAX_VALUE >> 1))
31.59 + period >>= 1;
31.60 +
31.61 synchronized(queue) {
31.62 if (!thread.newTasksMayBeScheduled)
31.63 throw new IllegalStateException("Timer already cancelled.");
32.1 --- a/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java Tue Aug 19 07:50:03 2008 -0700
32.2 +++ b/src/share/classes/java/util/concurrent/ScheduledThreadPoolExecutor.java Thu Aug 21 09:55:18 2008 -0700
32.3 @@ -223,8 +223,7 @@
32.4 }
32.5
32.6 public long getDelay(TimeUnit unit) {
32.7 - long d = unit.convert(time - now(), TimeUnit.NANOSECONDS);
32.8 - return d;
32.9 + return unit.convert(time - now(), TimeUnit.NANOSECONDS);
32.10 }
32.11
32.12 public int compareTo(Delayed other) {
32.13 @@ -264,7 +263,7 @@
32.14 if (p > 0)
32.15 time += p;
32.16 else
32.17 - time = now() - p;
32.18 + time = triggerTime(-p);
32.19 }
32.20
32.21 public boolean cancel(boolean mayInterruptIfRunning) {
32.22 @@ -473,6 +472,38 @@
32.23 }
32.24
32.25 /**
32.26 + * Returns the trigger time of a delayed action.
32.27 + */
32.28 + private long triggerTime(long delay, TimeUnit unit) {
32.29 + return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));
32.30 + }
32.31 +
32.32 + /**
32.33 + * Returns the trigger time of a delayed action.
32.34 + */
32.35 + long triggerTime(long delay) {
32.36 + return now() +
32.37 + ((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
32.38 + }
32.39 +
32.40 + /**
32.41 + * Constrains the values of all delays in the queue to be within
32.42 + * Long.MAX_VALUE of each other, to avoid overflow in compareTo.
32.43 + * This may occur if a task is eligible to be dequeued, but has
32.44 + * not yet been, while some other task is added with a delay of
32.45 + * Long.MAX_VALUE.
32.46 + */
32.47 + private long overflowFree(long delay) {
32.48 + Delayed head = (Delayed) super.getQueue().peek();
32.49 + if (head != null) {
32.50 + long headDelay = head.getDelay(TimeUnit.NANOSECONDS);
32.51 + if (headDelay < 0 && (delay - headDelay < 0))
32.52 + delay = Long.MAX_VALUE + headDelay;
32.53 + }
32.54 + return delay;
32.55 + }
32.56 +
32.57 + /**
32.58 * @throws RejectedExecutionException {@inheritDoc}
32.59 * @throws NullPointerException {@inheritDoc}
32.60 */
32.61 @@ -481,10 +512,9 @@
32.62 TimeUnit unit) {
32.63 if (command == null || unit == null)
32.64 throw new NullPointerException();
32.65 - if (delay < 0) delay = 0;
32.66 - long triggerTime = now() + unit.toNanos(delay);
32.67 RunnableScheduledFuture<?> t = decorateTask(command,
32.68 - new ScheduledFutureTask<Void>(command, null, triggerTime));
32.69 + new ScheduledFutureTask<Void>(command, null,
32.70 + triggerTime(delay, unit)));
32.71 delayedExecute(t);
32.72 return t;
32.73 }
32.74 @@ -498,10 +528,9 @@
32.75 TimeUnit unit) {
32.76 if (callable == null || unit == null)
32.77 throw new NullPointerException();
32.78 - if (delay < 0) delay = 0;
32.79 - long triggerTime = now() + unit.toNanos(delay);
32.80 RunnableScheduledFuture<V> t = decorateTask(callable,
32.81 - new ScheduledFutureTask<V>(callable, triggerTime));
32.82 + new ScheduledFutureTask<V>(callable,
32.83 + triggerTime(delay, unit)));
32.84 delayedExecute(t);
32.85 return t;
32.86 }
32.87 @@ -519,12 +548,10 @@
32.88 throw new NullPointerException();
32.89 if (period <= 0)
32.90 throw new IllegalArgumentException();
32.91 - if (initialDelay < 0) initialDelay = 0;
32.92 - long triggerTime = now() + unit.toNanos(initialDelay);
32.93 ScheduledFutureTask<Void> sft =
32.94 new ScheduledFutureTask<Void>(command,
32.95 null,
32.96 - triggerTime,
32.97 + triggerTime(initialDelay, unit),
32.98 unit.toNanos(period));
32.99 RunnableScheduledFuture<Void> t = decorateTask(command, sft);
32.100 sft.outerTask = t;
32.101 @@ -545,12 +572,10 @@
32.102 throw new NullPointerException();
32.103 if (delay <= 0)
32.104 throw new IllegalArgumentException();
32.105 - if (initialDelay < 0) initialDelay = 0;
32.106 - long triggerTime = now() + unit.toNanos(initialDelay);
32.107 ScheduledFutureTask<Void> sft =
32.108 new ScheduledFutureTask<Void>(command,
32.109 null,
32.110 - triggerTime,
32.111 + triggerTime(initialDelay, unit),
32.112 unit.toNanos(-delay));
32.113 RunnableScheduledFuture<Void> t = decorateTask(command, sft);
32.114 sft.outerTask = t;
33.1 --- a/src/share/classes/javax/management/ImmutableDescriptor.java Tue Aug 19 07:50:03 2008 -0700
33.2 +++ b/src/share/classes/javax/management/ImmutableDescriptor.java Thu Aug 21 09:55:18 2008 -0700
33.3 @@ -128,13 +128,13 @@
33.4 * @throws InvalidObjectException if the read object has invalid fields.
33.5 */
33.6 private Object readResolve() throws InvalidObjectException {
33.7 - if (names.length == 0 && getClass() == ImmutableDescriptor.class)
33.8 - return EMPTY_DESCRIPTOR;
33.9
33.10 boolean bad = false;
33.11 if (names == null || values == null || names.length != values.length)
33.12 bad = true;
33.13 if (!bad) {
33.14 + if (names.length == 0 && getClass() == ImmutableDescriptor.class)
33.15 + return EMPTY_DESCRIPTOR;
33.16 final Comparator<String> compare = String.CASE_INSENSITIVE_ORDER;
33.17 String lastName = ""; // also catches illegal null name
33.18 for (int i = 0; i < names.length; i++) {
34.1 --- a/src/share/classes/javax/management/MBeanRegistration.java Tue Aug 19 07:50:03 2008 -0700
34.2 +++ b/src/share/classes/javax/management/MBeanRegistration.java Thu Aug 21 09:55:18 2008 -0700
34.3 @@ -158,7 +158,19 @@
34.4 /**
34.5 * Allows the MBean to perform any operations needed after having been
34.6 * registered in the MBean server or after the registration has failed.
34.7 - *
34.8 + * <p>If the implementation of this method throws a {@link RuntimeException}
34.9 + * or an {@link Error}, the MBean Server will rethrow those inside
34.10 + * a {@link RuntimeMBeanException} or {@link RuntimeErrorException},
34.11 + * respectively. However, throwing an exception in {@code postRegister}
34.12 + * will not change the state of the MBean:
34.13 + * if the MBean was already registered ({@code registrationDone} is
34.14 + * {@code true}), the MBean will remain registered. </p>
34.15 + * <p>This might be confusing for the code calling {@code createMBean()}
34.16 + * or {@code registerMBean()}, as such code might assume that MBean
34.17 + * registration has failed when such an exception is raised.
34.18 + * Therefore it is recommended that implementations of
34.19 + * {@code postRegister} do not throw Runtime Exceptions or Errors if it
34.20 + * can be avoided.</p>
34.21 * @param registrationDone Indicates whether or not the MBean has
34.22 * been successfully registered in the MBean server. The value
34.23 * false means that the registration phase has failed.
34.24 @@ -178,6 +190,17 @@
34.25 /**
34.26 * Allows the MBean to perform any operations needed after having been
34.27 * unregistered in the MBean server.
34.28 + * <p>If the implementation of this method throws a {@link RuntimeException}
34.29 + * or an {@link Error}, the MBean Server will rethrow those inside
34.30 + * a {@link RuntimeMBeanException} or {@link RuntimeErrorException},
34.31 + * respectively. However, throwing an excepption in {@code postDeregister}
34.32 + * will not change the state of the MBean:
34.33 + * the MBean was already successfully deregistered and will remain so. </p>
34.34 + * <p>This might be confusing for the code calling
34.35 + * {@code unregisterMBean()}, as it might assume that MBean deregistration
34.36 + * has failed. Therefore it is recommended that implementations of
34.37 + * {@code postDeregister} do not throw Runtime Exceptions or Errors if it
34.38 + * can be avoided.</p>
34.39 */
34.40 public void postDeregister();
34.41
35.1 --- a/src/share/classes/javax/management/MBeanServer.java Tue Aug 19 07:50:03 2008 -0700
35.2 +++ b/src/share/classes/javax/management/MBeanServer.java Thu Aug 21 09:55:18 2008 -0700
35.3 @@ -50,8 +50,8 @@
35.4 * server. A Java object cannot be registered in the MBean server
35.5 * unless it is a JMX compliant MBean.</p>
35.6 *
35.7 - * <p id="notif">When an MBean is registered or unregistered in the MBean server
35.8 - * a {@link javax.management.MBeanServerNotification
35.9 + * <p id="notif">When an MBean is registered or unregistered in the
35.10 + * MBean server a {@link javax.management.MBeanServerNotification
35.11 * MBeanServerNotification} Notification is emitted. To register an
35.12 * object as listener to MBeanServerNotifications you should call the
35.13 * MBean server method {@link #addNotificationListener
35.14 @@ -262,6 +262,8 @@
35.15 * {@inheritDoc}
35.16 * <p>If this method successfully creates an MBean, a notification
35.17 * is sent as described <a href="#notif">above</a>.</p>
35.18 + *
35.19 + * @throws RuntimeOperationsException {@inheritDoc}
35.20 */
35.21 public ObjectInstance createMBean(String className, ObjectName name)
35.22 throws ReflectionException, InstanceAlreadyExistsException,
35.23 @@ -272,6 +274,8 @@
35.24 * {@inheritDoc}
35.25 * <p>If this method successfully creates an MBean, a notification
35.26 * is sent as described <a href="#notif">above</a>.</p>
35.27 + *
35.28 + * @throws RuntimeOperationsException {@inheritDoc}
35.29 */
35.30 public ObjectInstance createMBean(String className, ObjectName name,
35.31 ObjectName loaderName)
35.32 @@ -283,6 +287,8 @@
35.33 * {@inheritDoc}
35.34 * <p>If this method successfully creates an MBean, a notification
35.35 * is sent as described <a href="#notif">above</a>.</p>
35.36 + *
35.37 + * @throws RuntimeOperationsException {@inheritDoc}
35.38 */
35.39 public ObjectInstance createMBean(String className, ObjectName name,
35.40 Object params[], String signature[])
35.41 @@ -294,6 +300,8 @@
35.42 * {@inheritDoc}
35.43 * <p>If this method successfully creates an MBean, a notification
35.44 * is sent as described <a href="#notif">above</a>.</p>
35.45 + *
35.46 + * @throws RuntimeOperationsException {@inheritDoc}
35.47 */
35.48 public ObjectInstance createMBean(String className, ObjectName name,
35.49 ObjectName loaderName, Object params[],
35.50 @@ -328,11 +336,30 @@
35.51 * <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
35.52 * interface) method of the MBean has thrown an exception. The
35.53 * MBean will not be registered.
35.54 + * @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
35.55 + * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
35.56 + * <CODE>RuntimeException</CODE>, the <CODE>registerMBean<CODE> method will
35.57 + * throw a <CODE>RuntimeMBeanException</CODE>, although the MBean
35.58 + * registration succeeded. In such a case, the MBean will be actually
35.59 + * registered even though the <CODE>registerMBean<CODE> method
35.60 + * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
35.61 + * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
35.62 + * will not be registered.
35.63 + * @exception RuntimeErrorException If the <CODE>postRegister</CODE>
35.64 + * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
35.65 + * <CODE>Error</CODE>, the <CODE>registerMBean<CODE> method will
35.66 + * throw a <CODE>RuntimeErrorException</CODE>, although the MBean
35.67 + * registration succeeded. In such a case, the MBean will be actually
35.68 + * registered even though the <CODE>registerMBean<CODE> method
35.69 + * threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
35.70 + * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
35.71 + * will not be registered.
35.72 * @exception NotCompliantMBeanException This object is not a JMX
35.73 * compliant MBean
35.74 * @exception RuntimeOperationsException Wraps a
35.75 * <CODE>java.lang.IllegalArgumentException</CODE>: The object
35.76 * passed in parameter is null or no object name is specified.
35.77 + * @see javax.management.MBeanRegistration
35.78 */
35.79 public ObjectInstance registerMBean(Object object, ObjectName name)
35.80 throws InstanceAlreadyExistsException, MBeanRegistrationException,
35.81 @@ -343,6 +370,8 @@
35.82 *
35.83 * <p>If this method successfully unregisters an MBean, a notification
35.84 * is sent as described <a href="#notif">above</a>.</p>
35.85 + *
35.86 + * @throws RuntimeOperationsException {@inheritDoc}
35.87 */
35.88 public void unregisterMBean(ObjectName name)
35.89 throws InstanceNotFoundException, MBeanRegistrationException;
35.90 @@ -358,6 +387,9 @@
35.91 public Set<ObjectName> queryNames(ObjectName name, QueryExp query);
35.92
35.93 // doc comment inherited from MBeanServerConnection
35.94 + /**
35.95 + * @throws RuntimeOperationsException {@inheritDoc}
35.96 + */
35.97 public boolean isRegistered(ObjectName name);
35.98
35.99 /**
35.100 @@ -370,21 +402,33 @@
35.101 public Integer getMBeanCount();
35.102
35.103 // doc comment inherited from MBeanServerConnection
35.104 + /**
35.105 + * @throws RuntimeOperationsException {@inheritDoc}
35.106 + */
35.107 public Object getAttribute(ObjectName name, String attribute)
35.108 throws MBeanException, AttributeNotFoundException,
35.109 InstanceNotFoundException, ReflectionException;
35.110
35.111 // doc comment inherited from MBeanServerConnection
35.112 + /**
35.113 + * @throws RuntimeOperationsException {@inheritDoc}
35.114 + */
35.115 public AttributeList getAttributes(ObjectName name, String[] attributes)
35.116 throws InstanceNotFoundException, ReflectionException;
35.117
35.118 // doc comment inherited from MBeanServerConnection
35.119 + /**
35.120 + * @throws RuntimeOperationsException {@inheritDoc}
35.121 + */
35.122 public void setAttribute(ObjectName name, Attribute attribute)
35.123 throws InstanceNotFoundException, AttributeNotFoundException,
35.124 InvalidAttributeValueException, MBeanException,
35.125 ReflectionException;
35.126
35.127 // doc comment inherited from MBeanServerConnection
35.128 + /**
35.129 + * @throws RuntimeOperationsException {@inheritDoc}
35.130 + */
35.131 public AttributeList setAttributes(ObjectName name,
35.132 AttributeList attributes)
35.133 throws InstanceNotFoundException, ReflectionException;
35.134 @@ -401,14 +445,23 @@
35.135 // doc comment inherited from MBeanServerConnection
35.136 public String[] getDomains();
35.137
35.138 - // doc comment inherited from MBeanServerConnection
35.139 + // doc comment inherited from MBeanServerConnection, plus:
35.140 + /**
35.141 + * {@inheritDoc}
35.142 + * If the source of the notification
35.143 + * is a reference to an MBean object, the MBean server will replace it
35.144 + * by that MBean's ObjectName. Otherwise the source is unchanged.
35.145 + */
35.146 public void addNotificationListener(ObjectName name,
35.147 NotificationListener listener,
35.148 NotificationFilter filter,
35.149 Object handback)
35.150 throws InstanceNotFoundException;
35.151
35.152 - // doc comment inherited from MBeanServerConnection
35.153 + /**
35.154 + * {@inheritDoc}
35.155 + * @throws RuntimeOperationsException {@inheritDoc}
35.156 + */
35.157 public void addNotificationListener(ObjectName name,
35.158 ObjectName listener,
35.159 NotificationFilter filter,
36.1 --- a/src/share/classes/javax/management/MBeanServerConnection.java Tue Aug 19 07:50:03 2008 -0700
36.2 +++ b/src/share/classes/javax/management/MBeanServerConnection.java Thu Aug 21 09:55:18 2008 -0700
36.3 @@ -29,6 +29,7 @@
36.4 // java import
36.5 import java.io.IOException;
36.6 import java.util.Set;
36.7 +import javax.management.event.NotificationManager;
36.8
36.9
36.10 /**
36.11 @@ -39,7 +40,7 @@
36.12 *
36.13 * @since 1.5
36.14 */
36.15 -public interface MBeanServerConnection {
36.16 +public interface MBeanServerConnection extends NotificationManager {
36.17 /**
36.18 * <p>Instantiates and registers an MBean in the MBean server. The
36.19 * MBean server will use its {@link
36.20 @@ -75,6 +76,24 @@
36.21 * <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
36.22 * interface) method of the MBean has thrown an exception. The
36.23 * MBean will not be registered.
36.24 + * @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
36.25 + * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
36.26 + * <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
36.27 + * throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
36.28 + * and registration succeeded. In such a case, the MBean will be actually
36.29 + * registered even though the <CODE>createMBean<CODE> method
36.30 + * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
36.31 + * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
36.32 + * will not be registered.
36.33 + * @exception RuntimeErrorException If the <CODE>postRegister</CODE>
36.34 + * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
36.35 + * <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
36.36 + * throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
36.37 + * and registration succeeded. In such a case, the MBean will be actually
36.38 + * registered even though the <CODE>createMBean<CODE> method
36.39 + * threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
36.40 + * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
36.41 + * will not be registered.
36.42 * @exception MBeanException The constructor of the MBean has
36.43 * thrown an exception
36.44 * @exception NotCompliantMBeanException This class is not a JMX
36.45 @@ -86,7 +105,7 @@
36.46 * is specified for the MBean.
36.47 * @exception IOException A communication problem occurred when
36.48 * talking to the MBean server.
36.49 - *
36.50 + * @see javax.management.MBeanRegistration
36.51 */
36.52 public ObjectInstance createMBean(String className, ObjectName name)
36.53 throws ReflectionException, InstanceAlreadyExistsException,
36.54 @@ -129,6 +148,24 @@
36.55 * <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
36.56 * interface) method of the MBean has thrown an exception. The
36.57 * MBean will not be registered.
36.58 + * @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
36.59 + * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
36.60 + * <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
36.61 + * throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
36.62 + * and registration succeeded. In such a case, the MBean will be actually
36.63 + * registered even though the <CODE>createMBean<CODE> method
36.64 + * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
36.65 + * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
36.66 + * will not be registered.
36.67 + * @exception RuntimeErrorException If the <CODE>postRegister</CODE>
36.68 + * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
36.69 + * <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
36.70 + * throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
36.71 + * and registration succeeded. In such a case, the MBean will be actually
36.72 + * registered even though the <CODE>createMBean<CODE> method
36.73 + * threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
36.74 + * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
36.75 + * will not be registered.
36.76 * @exception MBeanException The constructor of the MBean has
36.77 * thrown an exception
36.78 * @exception NotCompliantMBeanException This class is not a JMX
36.79 @@ -142,6 +179,7 @@
36.80 * is specified for the MBean.
36.81 * @exception IOException A communication problem occurred when
36.82 * talking to the MBean server.
36.83 + * @see javax.management.MBeanRegistration
36.84 */
36.85 public ObjectInstance createMBean(String className, ObjectName name,
36.86 ObjectName loaderName)
36.87 @@ -185,6 +223,24 @@
36.88 * <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
36.89 * interface) method of the MBean has thrown an exception. The
36.90 * MBean will not be registered.
36.91 + * @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
36.92 + * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
36.93 + * <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
36.94 + * throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
36.95 + * and registration succeeded. In such a case, the MBean will be actually
36.96 + * registered even though the <CODE>createMBean<CODE> method
36.97 + * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
36.98 + * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
36.99 + * will not be registered.
36.100 + * @exception RuntimeErrorException If the <CODE>postRegister</CODE>
36.101 + * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
36.102 + * <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
36.103 + * throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
36.104 + * and registration succeeded. In such a case, the MBean will be actually
36.105 + * registered even though the <CODE>createMBean<CODE> method
36.106 + * threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
36.107 + * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
36.108 + * will not be registered.
36.109 * @exception MBeanException The constructor of the MBean has
36.110 * thrown an exception
36.111 * @exception NotCompliantMBeanException This class is not a JMX
36.112 @@ -196,7 +252,7 @@
36.113 * is specified for the MBean.
36.114 * @exception IOException A communication problem occurred when
36.115 * talking to the MBean server.
36.116 - *
36.117 + * @see javax.management.MBeanRegistration
36.118 */
36.119 public ObjectInstance createMBean(String className, ObjectName name,
36.120 Object params[], String signature[])
36.121 @@ -239,6 +295,24 @@
36.122 * <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
36.123 * interface) method of the MBean has thrown an exception. The
36.124 * MBean will not be registered.
36.125 + * @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
36.126 + * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
36.127 + * <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
36.128 + * throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
36.129 + * and registration succeeded. In such a case, the MBean will be actually
36.130 + * registered even though the <CODE>createMBean<CODE> method
36.131 + * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
36.132 + * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
36.133 + * will not be registered.
36.134 + * @exception RuntimeErrorException If the <CODE>postRegister</CODE> method
36.135 + * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
36.136 + * <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
36.137 + * throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
36.138 + * and registration succeeded. In such a case, the MBean will be actually
36.139 + * registered even though the <CODE>createMBean<CODE> method
36.140 + * threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
36.141 + * also be thrown by <CODE>preRegister</CODE>, in which case the MBean
36.142 + * will not be registered.
36.143 * @exception MBeanException The constructor of the MBean has
36.144 * thrown an exception
36.145 * @exception NotCompliantMBeanException This class is not a JMX
36.146 @@ -252,7 +326,7 @@
36.147 * is specified for the MBean.
36.148 * @exception IOException A communication problem occurred when
36.149 * talking to the MBean server.
36.150 - *
36.151 + * @see javax.management.MBeanRegistration
36.152 */
36.153 public ObjectInstance createMBean(String className, ObjectName name,
36.154 ObjectName loaderName, Object params[],
36.155 @@ -275,6 +349,24 @@
36.156 * @exception MBeanRegistrationException The preDeregister
36.157 * ((<CODE>MBeanRegistration</CODE> interface) method of the MBean
36.158 * has thrown an exception.
36.159 + * @exception RuntimeMBeanException If the <CODE>postDeregister</CODE>
36.160 + * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
36.161 + * <CODE>RuntimeException</CODE>, the <CODE>unregisterMBean<CODE> method
36.162 + * will throw a <CODE>RuntimeMBeanException</CODE>, although the MBean
36.163 + * unregistration succeeded. In such a case, the MBean will be actually
36.164 + * unregistered even though the <CODE>unregisterMBean<CODE> method
36.165 + * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
36.166 + * also be thrown by <CODE>preDeregister</CODE>, in which case the MBean
36.167 + * will remain registered.
36.168 + * @exception RuntimeErrorException If the <CODE>postDeregister</CODE>
36.169 + * (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
36.170 + * <CODE>Error</CODE>, the <CODE>unregisterMBean<CODE> method will
36.171 + * throw a <CODE>RuntimeErrorException</CODE>, although the MBean
36.172 + * unregistration succeeded. In such a case, the MBean will be actually
36.173 + * unregistered even though the <CODE>unregisterMBean<CODE> method
36.174 + * threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
36.175 + * also be thrown by <CODE>preDeregister</CODE>, in which case the MBean
36.176 + * will remain registered.
36.177 * @exception RuntimeOperationsException Wraps a
36.178 * <CODE>java.lang.IllegalArgumentException</CODE>: The object
36.179 * name in parameter is null or the MBean you are when trying to
36.180 @@ -282,7 +374,7 @@
36.181 * MBeanServerDelegate} MBean.
36.182 * @exception IOException A communication problem occurred when
36.183 * talking to the MBean server.
36.184 - *
36.185 + * @see javax.management.MBeanRegistration
36.186 */
36.187 public void unregisterMBean(ObjectName name)
36.188 throws InstanceNotFoundException, MBeanRegistrationException,
36.189 @@ -585,32 +677,7 @@
36.190 public String[] getDomains()
36.191 throws IOException;
36.192
36.193 - /**
36.194 - * <p>Adds a listener to a registered MBean.</p>
36.195 - *
36.196 - * <P> A notification emitted by an MBean will be forwarded by the
36.197 - * MBeanServer to the listener. If the source of the notification
36.198 - * is a reference to an MBean object, the MBean server will replace it
36.199 - * by that MBean's ObjectName. Otherwise the source is unchanged.
36.200 - *
36.201 - * @param name The name of the MBean on which the listener should
36.202 - * be added.
36.203 - * @param listener The listener object which will handle the
36.204 - * notifications emitted by the registered MBean.
36.205 - * @param filter The filter object. If filter is null, no
36.206 - * filtering will be performed before handling notifications.
36.207 - * @param handback The context to be sent to the listener when a
36.208 - * notification is emitted.
36.209 - *
36.210 - * @exception InstanceNotFoundException The MBean name provided
36.211 - * does not match any of the registered MBeans.
36.212 - * @exception IOException A communication problem occurred when
36.213 - * talking to the MBean server.
36.214 - *
36.215 - * @see #removeNotificationListener(ObjectName, NotificationListener)
36.216 - * @see #removeNotificationListener(ObjectName, NotificationListener,
36.217 - * NotificationFilter, Object)
36.218 - */
36.219 + // doc inherited from NotificationManager
36.220 public void addNotificationListener(ObjectName name,
36.221 NotificationListener listener,
36.222 NotificationFilter filter,
36.223 @@ -727,65 +794,13 @@
36.224 throws InstanceNotFoundException, ListenerNotFoundException,
36.225 IOException;
36.226
36.227 -
36.228 - /**
36.229 - * <p>Removes a listener from a registered MBean.</p>
36.230 - *
36.231 - * <P> If the listener is registered more than once, perhaps with
36.232 - * different filters or callbacks, this method will remove all
36.233 - * those registrations.
36.234 - *
36.235 - * @param name The name of the MBean on which the listener should
36.236 - * be removed.
36.237 - * @param listener The listener to be removed.
36.238 - *
36.239 - * @exception InstanceNotFoundException The MBean name provided
36.240 - * does not match any of the registered MBeans.
36.241 - * @exception ListenerNotFoundException The listener is not
36.242 - * registered in the MBean.
36.243 - * @exception IOException A communication problem occurred when
36.244 - * talking to the MBean server.
36.245 - *
36.246 - * @see #addNotificationListener(ObjectName, NotificationListener,
36.247 - * NotificationFilter, Object)
36.248 - */
36.249 + // doc inherited from NotificationManager
36.250 public void removeNotificationListener(ObjectName name,
36.251 NotificationListener listener)
36.252 throws InstanceNotFoundException, ListenerNotFoundException,
36.253 IOException;
36.254
36.255 - /**
36.256 - * <p>Removes a listener from a registered MBean.</p>
36.257 - *
36.258 - * <p>The MBean must have a listener that exactly matches the
36.259 - * given <code>listener</code>, <code>filter</code>, and
36.260 - * <code>handback</code> parameters. If there is more than one
36.261 - * such listener, only one is removed.</p>
36.262 - *
36.263 - * <p>The <code>filter</code> and <code>handback</code> parameters
36.264 - * may be null if and only if they are null in a listener to be
36.265 - * removed.</p>
36.266 - *
36.267 - * @param name The name of the MBean on which the listener should
36.268 - * be removed.
36.269 - * @param listener The listener to be removed.
36.270 - * @param filter The filter that was specified when the listener
36.271 - * was added.
36.272 - * @param handback The handback that was specified when the
36.273 - * listener was added.
36.274 - *
36.275 - * @exception InstanceNotFoundException The MBean name provided
36.276 - * does not match any of the registered MBeans.
36.277 - * @exception ListenerNotFoundException The listener is not
36.278 - * registered in the MBean, or it is not registered with the given
36.279 - * filter and handback.
36.280 - * @exception IOException A communication problem occurred when
36.281 - * talking to the MBean server.
36.282 - *
36.283 - * @see #addNotificationListener(ObjectName, NotificationListener,
36.284 - * NotificationFilter, Object)
36.285 - *
36.286 - */
36.287 + // doc inherited from NotificationManager
36.288 public void removeNotificationListener(ObjectName name,
36.289 NotificationListener listener,
36.290 NotificationFilter filter,
37.1 --- a/src/share/classes/javax/management/MBeanServerNotification.java Tue Aug 19 07:50:03 2008 -0700
37.2 +++ b/src/share/classes/javax/management/MBeanServerNotification.java Thu Aug 21 09:55:18 2008 -0700
37.3 @@ -38,56 +38,64 @@
37.4 *
37.5 * @since 1.5
37.6 */
37.7 - public class MBeanServerNotification extends Notification {
37.8 +public class MBeanServerNotification extends Notification {
37.9
37.10
37.11 - /* Serial version */
37.12 - private static final long serialVersionUID = 2876477500475969677L;
37.13 + /* Serial version */
37.14 + private static final long serialVersionUID = 2876477500475969677L;
37.15 + /**
37.16 + * Notification type denoting that an MBean has been registered.
37.17 + * Value is "JMX.mbean.registered".
37.18 + */
37.19 + public static final String REGISTRATION_NOTIFICATION =
37.20 + "JMX.mbean.registered";
37.21 + /**
37.22 + * Notification type denoting that an MBean has been unregistered.
37.23 + * Value is "JMX.mbean.unregistered".
37.24 + */
37.25 + public static final String UNREGISTRATION_NOTIFICATION =
37.26 + "JMX.mbean.unregistered";
37.27 + /**
37.28 + * @serial The object names of the MBeans concerned by this notification
37.29 + */
37.30 + private final ObjectName objectName;
37.31
37.32 - /**
37.33 - * Notification type denoting that an MBean has been registered. Value is "JMX.mbean.registered".
37.34 - */
37.35 - public static final String REGISTRATION_NOTIFICATION = "JMX.mbean.registered" ;
37.36 + /**
37.37 + * Creates an MBeanServerNotification object specifying object names of
37.38 + * the MBeans that caused the notification and the specified notification
37.39 + * type.
37.40 + *
37.41 + * @param type A string denoting the type of the
37.42 + * notification. Set it to one these values: {@link
37.43 + * #REGISTRATION_NOTIFICATION}, {@link
37.44 + * #UNREGISTRATION_NOTIFICATION}.
37.45 + * @param source The MBeanServerNotification object responsible
37.46 + * for forwarding MBean server notification.
37.47 + * @param sequenceNumber A sequence number that can be used to order
37.48 + * received notifications.
37.49 + * @param objectName The object name of the MBean that caused the
37.50 + * notification.
37.51 + *
37.52 + */
37.53 + public MBeanServerNotification(String type, Object source,
37.54 + long sequenceNumber, ObjectName objectName) {
37.55 + super(type, source, sequenceNumber);
37.56 + this.objectName = objectName;
37.57 + }
37.58
37.59 - /**
37.60 - * Notification type denoting that an MBean has been unregistered. Value is "JMX.mbean.unregistered".
37.61 - */
37.62 - public static final String UNREGISTRATION_NOTIFICATION = "JMX.mbean.unregistered" ;
37.63 + /**
37.64 + * Returns the object name of the MBean that caused the notification.
37.65 + *
37.66 + * @return the object name of the MBean that caused the notification.
37.67 + */
37.68 + public ObjectName getMBeanName() {
37.69 + return objectName;
37.70 + }
37.71
37.72 + @Override
37.73 + public String toString() {
37.74 + return super.toString() + "[mbeanName=" + objectName + "]";
37.75
37.76 - /**
37.77 - * @serial The object names of the MBeans concerned by this notification
37.78 - */
37.79 - private final ObjectName objectName;
37.80 -
37.81 -
37.82 - /**
37.83 - * Creates an MBeanServerNotification object specifying object names of
37.84 - * the MBeans that caused the notification and the specified notification type.
37.85 - *
37.86 - * @param type A string denoting the type of the
37.87 - * notification. Set it to one these values: {@link
37.88 - * #REGISTRATION_NOTIFICATION}, {@link
37.89 - * #UNREGISTRATION_NOTIFICATION}.
37.90 - * @param source The MBeanServerNotification object responsible
37.91 - * for forwarding MBean server notification.
37.92 - * @param sequenceNumber A sequence number that can be used to order
37.93 - * received notifications.
37.94 - * @param objectName The object name of the MBean that caused the notification.
37.95 - *
37.96 - */
37.97 - public MBeanServerNotification(String type, Object source, long sequenceNumber, ObjectName objectName ) {
37.98 - super (type,source,sequenceNumber) ;
37.99 - this.objectName = objectName ;
37.100 - }
37.101 -
37.102 - /**
37.103 - * Returns the object name of the MBean that caused the notification.
37.104 - *
37.105 - * @return the object name of the MBean that caused the notification.
37.106 - */
37.107 - public ObjectName getMBeanName() {
37.108 - return objectName ;
37.109 - }
37.110 + }
37.111
37.112 }
38.1 --- a/src/share/classes/javax/management/MXBean.java Tue Aug 19 07:50:03 2008 -0700
38.2 +++ b/src/share/classes/javax/management/MXBean.java Thu Aug 21 09:55:18 2008 -0700
38.3 @@ -33,7 +33,6 @@
38.4 import java.lang.annotation.Target;
38.5
38.6 // remaining imports are for Javadoc
38.7 -import java.beans.ConstructorProperties;
38.8 import java.io.InvalidObjectException;
38.9 import java.lang.management.MemoryUsage;
38.10 import java.lang.reflect.UndeclaredThrowableException;
38.11 @@ -865,7 +864,8 @@
38.12 <em>J</em>.</p></li>
38.13
38.14 <li><p>Otherwise, if <em>J</em> has at least one public
38.15 - constructor with a {@link ConstructorProperties} annotation, then one
38.16 + constructor with a {@link java.beans.ConstructorProperties
38.17 + ConstructorProperties} annotation, then one
38.18 of those constructors (not necessarily always the same one)
38.19 will be called to reconstruct an instance of <em>J</em>.
38.20 Every such annotation must list as many strings as the
38.21 @@ -1081,9 +1081,10 @@
38.22 MXBean is determined as follows.</p>
38.23
38.24 <ul>
38.25 - <li><p>If an {@link JMX.MBeanOptions} argument is supplied to
38.26 + <li><p>If a {@link JMX.MBeanOptions} argument is supplied to
38.27 the {@link StandardMBean} constructor that makes an MXBean,
38.28 - or to the {@link JMX#newMXBeanProxy JMX.newMXBeanProxy}
38.29 + or to the {@link JMX#newMBeanProxy(MBeanServerConnection,
38.30 + ObjectName, Class, JMX.MBeanOptions) JMX.newMBeanProxy}
38.31 method, and the {@code MBeanOptions} object defines a non-null
38.32 {@code MXBeanMappingFactory}, then that is the value of
38.33 <code><em>f</em></code>.</p></li>
39.1 --- a/src/share/classes/javax/management/ObjectName.java Tue Aug 19 07:50:03 2008 -0700
39.2 +++ b/src/share/classes/javax/management/ObjectName.java Thu Aug 21 09:55:18 2008 -0700
39.3 @@ -38,9 +38,6 @@
39.4 import java.util.HashMap;
39.5 import java.util.Hashtable;
39.6 import java.util.Map;
39.7 -import javax.management.MBeanServer;
39.8 -import javax.management.MalformedObjectNameException;
39.9 -import javax.management.QueryExp;
39.10
39.11 /**
39.12 * <p>Represents the object name of an MBean, or a pattern that can
39.13 @@ -1160,9 +1157,19 @@
39.14 //
39.15 //in.defaultReadObject();
39.16 final ObjectInputStream.GetField fields = in.readFields();
39.17 + String propListString =
39.18 + (String)fields.get("propertyListString", "");
39.19 +
39.20 + // 6616825: take care of property patterns
39.21 + final boolean propPattern =
39.22 + fields.get("propertyPattern" , false);
39.23 + if (propPattern) {
39.24 + propListString =
39.25 + (propListString.length()==0?"*":(propListString+",*"));
39.26 + }
39.27 +
39.28 cn = (String)fields.get("domain", "default")+
39.29 - ":"+
39.30 - (String)fields.get("propertyListString", "");
39.31 + ":"+ propListString;
39.32 } else {
39.33 // Read an object serialized in the new serial form
39.34 //
39.35 @@ -1796,6 +1803,7 @@
39.36 * @return True if <code>object</code> is an ObjectName whose
39.37 * canonical form is equal to that of this ObjectName.
39.38 */
39.39 + @Override
39.40 public boolean equals(Object object) {
39.41
39.42 // same object case
39.43 @@ -1819,6 +1827,7 @@
39.44 * Returns a hash code for this object name.
39.45 *
39.46 */
39.47 + @Override
39.48 public int hashCode() {
39.49 return _canonicalName.hashCode();
39.50 }
40.1 --- a/src/share/classes/javax/management/QueryParser.java Tue Aug 19 07:50:03 2008 -0700
40.2 +++ b/src/share/classes/javax/management/QueryParser.java Thu Aug 21 09:55:18 2008 -0700
40.3 @@ -312,7 +312,7 @@
40.4 if (e > 0)
40.5 ss = s.substring(0, e);
40.6 ss = ss.replace("0", "").replace(".", "");
40.7 - if (!ss.isEmpty())
40.8 + if (!ss.equals(""))
40.9 throw new NumberFormatException("Underflow: " + s);
40.10 }
40.11 return d;
41.1 --- a/src/share/classes/javax/management/StringValueExp.java Tue Aug 19 07:50:03 2008 -0700
41.2 +++ b/src/share/classes/javax/management/StringValueExp.java Thu Aug 21 09:55:18 2008 -0700
41.3 @@ -85,6 +85,7 @@
41.4 /* There is no need for this method, because if a query is being
41.5 evaluated a StringValueExp can only appear inside a QueryExp,
41.6 and that QueryExp will itself have done setMBeanServer. */
41.7 + @Deprecated
41.8 public void setMBeanServer(MBeanServer s) { }
41.9
41.10 /**
42.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
42.2 +++ b/src/share/classes/javax/management/event/EventClient.java Thu Aug 21 09:55:18 2008 -0700
42.3 @@ -0,0 +1,1068 @@
42.4 +/*
42.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
42.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
42.7 + *
42.8 + * This code is free software; you can redistribute it and/or modify it
42.9 + * under the terms of the GNU General Public License version 2 only, as
42.10 + * published by the Free Software Foundation. Sun designates this
42.11 + * particular file as subject to the "Classpath" exception as provided
42.12 + * by Sun in the LICENSE file that accompanied this code.
42.13 + *
42.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
42.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
42.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
42.17 + * version 2 for more details (a copy is included in the LICENSE file that
42.18 + * accompanied this code).
42.19 + *
42.20 + * You should have received a copy of the GNU General Public License version
42.21 + * 2 along with this work; if not, write to the Free Software Foundation,
42.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
42.23 + *
42.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
42.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
42.26 + * have any questions.
42.27 + */
42.28 +
42.29 +package javax.management.event;
42.30 +
42.31 +import com.sun.jmx.event.DaemonThreadFactory;
42.32 +import com.sun.jmx.event.LeaseRenewer;
42.33 +import com.sun.jmx.event.ReceiverBuffer;
42.34 +import com.sun.jmx.event.RepeatedSingletonJob;
42.35 +import com.sun.jmx.mbeanserver.PerThreadGroupPool;
42.36 +import com.sun.jmx.remote.util.ClassLogger;
42.37 +
42.38 +import java.io.IOException;
42.39 +import java.lang.reflect.Method;
42.40 +import java.util.ArrayList;
42.41 +import java.util.Collection;
42.42 +import java.util.Collections;
42.43 +import java.util.HashMap;
42.44 +import java.util.List;
42.45 +import java.util.Map;
42.46 +import java.util.concurrent.Callable;
42.47 +import java.util.concurrent.Executor;
42.48 +
42.49 +import java.util.concurrent.ScheduledExecutorService;
42.50 +import java.util.concurrent.ScheduledThreadPoolExecutor;
42.51 +import java.util.concurrent.ThreadFactory;
42.52 +import java.util.concurrent.TimeUnit;
42.53 +import java.util.concurrent.atomic.AtomicLong;
42.54 +import javax.management.InstanceNotFoundException;
42.55 +import javax.management.ListenerNotFoundException;
42.56 +import javax.management.MBeanNotificationInfo;
42.57 +import javax.management.MBeanServerConnection;
42.58 +import javax.management.Notification;
42.59 +import javax.management.NotificationBroadcasterSupport;
42.60 +import javax.management.NotificationFilter;
42.61 +import javax.management.NotificationListener;
42.62 +import javax.management.ObjectName;
42.63 +import javax.management.remote.JMXConnector;
42.64 +import javax.management.remote.NotificationResult;
42.65 +import javax.management.remote.TargetedNotification;
42.66 +
42.67 +/**
42.68 + * <p>This class is used to manage its notification listeners on the client
42.69 + * side in the same way as on the MBean server side. This class needs to work
42.70 + * with an {@link EventClientDelegateMBean} on the server side.</p>
42.71 + *
42.72 + * <P>A user can specify an {@link EventRelay} object to specify how to receive
42.73 + * notifications forwarded by the {@link EventClientDelegateMBean}. By default,
42.74 + * the class {@link FetchingEventRelay} is used.</p>
42.75 + *
42.76 + * <p>A user can specify an {@link java.util.concurrent.Executor Executor}
42.77 + * to distribute notifications to local listeners. If no executor is
42.78 + * specified, the thread in the {@link EventRelay} which calls {@link
42.79 + * EventReceiver#receive EventReceiver.receive} will be reused to distribute
42.80 + * the notifications (in other words, to call the {@link
42.81 + * NotificationListener#handleNotification handleNotification} method of the
42.82 + * appropriate listeners). It is useful to make a separate thread do this
42.83 + * distribution in some cases. For example, if network communication is slow,
42.84 + * the forwarding thread can concentrate on communication while, locally,
42.85 + * the distributing thread distributes the received notifications. Another
42.86 + * usage is to share a thread pool between many clients, for scalability.
42.87 + * Note, though, that if the {@code Executor} can create more than one thread
42.88 + * then it is possible that listeners will see notifications in a different
42.89 + * order from the order in which they were sent.</p>
42.90 + *
42.91 + * <p>An object of this class sends notifications to listeners added with
42.92 + * {@link #addEventClientListener}. The {@linkplain Notification#getType()
42.93 + * type} of each such notification is one of {@link #FAILED}, {@link #NONFATAL},
42.94 + * or {@link #NOTIFS_LOST}.</p>
42.95 + *
42.96 + * @since JMX 2.0
42.97 + */
42.98 +public class EventClient implements EventConsumer, NotificationManager {
42.99 +
42.100 + /**
42.101 + * <p>A notification string type used by an {@code EventClient} object
42.102 + * to inform a listener added by {@link #addEventClientListener} that
42.103 + * it failed to get notifications from a remote server, and that it is
42.104 + * possible that no more notifications will be delivered.</p>
42.105 + *
42.106 + * @see #addEventClientListener
42.107 + * @see EventReceiver#failed
42.108 + */
42.109 + public static final String FAILED = "jmx.event.service.failed";
42.110 +
42.111 + /**
42.112 + * <p>Reports that an unexpected exception has been received by the {@link
42.113 + * EventRelay} object but that it is non-fatal. For example, a notification
42.114 + * received is not serializable or its class is not found.</p>
42.115 + *
42.116 + * @see #addEventClientListener
42.117 + * @see EventReceiver#nonFatal
42.118 + */
42.119 + public static final String NONFATAL = "jmx.event.service.nonfatal";
42.120 +
42.121 + /**
42.122 + * <p>A notification string type used by an {@code EventClient} object to
42.123 + * inform a listener added by {@code #addEventClientListener} that it
42.124 + * has detected that notifications have been lost. The {@link
42.125 + * Notification#getUserData() userData} of the notification is a Long which
42.126 + * is an upper bound on the number of lost notifications that have just
42.127 + * been detected.</p>
42.128 + *
42.129 + * @see #addEventClientListener
42.130 + */
42.131 + public static final String NOTIFS_LOST = "jmx.event.service.notifs.lost";
42.132 +
42.133 + /**
42.134 + * The default lease time, {@value}, in milliseconds.
42.135 + *
42.136 + * @see EventClientDelegateMBean#lease
42.137 + */
42.138 + public static final long DEFAULT_LEASE_TIMEOUT = 300000;
42.139 +
42.140 + /**
42.141 + * <p>Constructs a default {@code EventClient} object.</p>
42.142 + *
42.143 + * <p>This object creates a {@link FetchingEventRelay} object to
42.144 + * receive notifications forwarded by the {@link EventClientDelegateMBean}.
42.145 + * The {@link EventClientDelegateMBean} that it works with is the
42.146 + * one registered with the {@linkplain EventClientDelegate#OBJECT_NAME
42.147 + * default ObjectName}. The thread from the {@link FetchingEventRelay}
42.148 + * object that fetches the notifications is also used to distribute them.
42.149 + *
42.150 + * @param conn An {@link MBeanServerConnection} object used to communicate
42.151 + * with an {@link EventClientDelegateMBean} MBean.
42.152 + *
42.153 + * @throws IllegalArgumentException If {@code conn} is null.
42.154 + * @throws IOException If an I/O error occurs when communicating with the
42.155 + * {@code EventClientDelegateMBean}.
42.156 + */
42.157 + public EventClient(MBeanServerConnection conn) throws IOException {
42.158 + this(EventClientDelegate.getProxy(conn));
42.159 + }
42.160 +
42.161 + /**
42.162 + * Constructs an {@code EventClient} object with a specified
42.163 + * {@link EventClientDelegateMBean}.
42.164 + *
42.165 + * <p>This object creates a {@link FetchingEventRelay} object to receive
42.166 + * notifications forwarded by the {@link EventClientDelegateMBean}. The
42.167 + * thread from the {@link FetchingEventRelay} object that fetches the
42.168 + * notifications is also used to distribute them.
42.169 + *
42.170 + * @param delegate An {@link EventClientDelegateMBean} object to work with.
42.171 + *
42.172 + * @throws IllegalArgumentException If {@code delegate} is null.
42.173 + * @throws IOException If an I/O error occurs when communicating with the
42.174 + * the {@link EventClientDelegateMBean}.
42.175 + */
42.176 + public EventClient(EventClientDelegateMBean delegate)
42.177 + throws IOException {
42.178 + this(delegate, null, null, null, DEFAULT_LEASE_TIMEOUT);
42.179 + }
42.180 +
42.181 + /**
42.182 + * Constructs an {@code EventClient} object with the specified
42.183 + * {@link EventClientDelegateMBean}, {@link EventRelay}
42.184 + * object, and distributing thread.
42.185 + *
42.186 + * @param delegate An {@link EventClientDelegateMBean} object to work with.
42.187 + * Usually, this will be a proxy constructed using
42.188 + * {@link EventClientDelegate#getProxy}.
42.189 + * @param eventRelay An object used to receive notifications
42.190 + * forwarded by the {@link EventClientDelegateMBean}. If {@code null}, a
42.191 + * {@link FetchingEventRelay} object will be used.
42.192 + * @param distributingExecutor Used to distribute notifications to local
42.193 + * listeners. If {@code null}, the thread that calls {@link
42.194 + * EventReceiver#receive EventReceiver.receive} from the {@link EventRelay}
42.195 + * object is used.
42.196 + * @param leaseScheduler An object that will be used to schedule the
42.197 + * periodic {@linkplain EventClientDelegateMBean#lease lease updates}.
42.198 + * If {@code null}, a default scheduler will be used.
42.199 + * @param requestedLeaseTime The lease time used to keep this client alive
42.200 + * in the {@link EventClientDelegateMBean}. A value of zero is equivalent
42.201 + * to the {@linkplain #DEFAULT_LEASE_TIMEOUT default value}.
42.202 + *
42.203 + * @throws IllegalArgumentException If {@code delegate} is null.
42.204 + * @throws IOException If an I/O error occurs when communicating with the
42.205 + * {@link EventClientDelegateMBean}.
42.206 + */
42.207 + public EventClient(EventClientDelegateMBean delegate,
42.208 + EventRelay eventRelay,
42.209 + Executor distributingExecutor,
42.210 + ScheduledExecutorService leaseScheduler,
42.211 + long requestedLeaseTime)
42.212 + throws IOException {
42.213 + if (delegate == null) {
42.214 + throw new IllegalArgumentException("Null EventClientDelegateMBean");
42.215 + }
42.216 +
42.217 + if (requestedLeaseTime == 0)
42.218 + requestedLeaseTime = DEFAULT_LEASE_TIMEOUT;
42.219 + else if (requestedLeaseTime < 0) {
42.220 + throw new IllegalArgumentException(
42.221 + "Negative lease time: " + requestedLeaseTime);
42.222 + }
42.223 +
42.224 + eventClientDelegate = delegate;
42.225 +
42.226 + if (eventRelay != null) {
42.227 + this.eventRelay = eventRelay;
42.228 + } else {
42.229 + try {
42.230 + this.eventRelay = new FetchingEventRelay(delegate);
42.231 + } catch (IOException ioe) {
42.232 + throw ioe;
42.233 + } catch (Exception e) {
42.234 + // impossible?
42.235 + final IOException ioee = new IOException(e.toString());
42.236 + ioee.initCause(e);
42.237 + throw ioee;
42.238 + }
42.239 + }
42.240 +
42.241 + if (distributingExecutor == null)
42.242 + distributingExecutor = callerExecutor;
42.243 + this.distributingExecutor = distributingExecutor;
42.244 + this.dispatchingJob = new DispatchingJob();
42.245 +
42.246 + clientId = this.eventRelay.getClientId();
42.247 +
42.248 + this.requestedLeaseTime = requestedLeaseTime;
42.249 + if (leaseScheduler == null)
42.250 + leaseScheduler = defaultLeaseScheduler();
42.251 + leaseRenewer = new LeaseRenewer(leaseScheduler, renewLease);
42.252 +
42.253 + if (logger.traceOn()) {
42.254 + logger.trace("init", "New EventClient: "+clientId);
42.255 + }
42.256 + }
42.257 +
42.258 + private static ScheduledExecutorService defaultLeaseScheduler() {
42.259 + // The default lease scheduler uses a ScheduledThreadPoolExecutor
42.260 + // with a maximum of 20 threads. This means that if you have many
42.261 + // EventClient instances and some of them get blocked (because of an
42.262 + // unresponsive network, for example), then even the instances that
42.263 + // are connected to responsive servers may have their leases expire.
42.264 + // XXX check if the above is true and possibly fix.
42.265 + PerThreadGroupPool.Create<ScheduledThreadPoolExecutor> create =
42.266 + new PerThreadGroupPool.Create<ScheduledThreadPoolExecutor>() {
42.267 + public ScheduledThreadPoolExecutor createThreadPool(ThreadGroup group) {
42.268 + ThreadFactory daemonThreadFactory = new DaemonThreadFactory(
42.269 + "EventClient lease renewer %d");
42.270 + ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(
42.271 + 20, daemonThreadFactory);
42.272 + exec.setKeepAliveTime(3, TimeUnit.SECONDS);
42.273 + exec.allowCoreThreadTimeOut(true);
42.274 + return exec;
42.275 + }
42.276 + };
42.277 + return leaseRenewerThreadPool.getThreadPoolExecutor(create);
42.278 +
42.279 + }
42.280 +
42.281 + /**
42.282 + * <p>Closes this EventClient, removes all listeners and stops receiving
42.283 + * notifications.</p>
42.284 + *
42.285 + * <p>This method calls {@link
42.286 + * EventClientDelegateMBean#removeClient(String)} and {@link
42.287 + * EventRelay#stop}. Both operations occur even if one of them
42.288 + * throws an {@code IOException}.
42.289 + *
42.290 + * @throws IOException if an I/O error occurs when communicating with
42.291 + * {@link EventClientDelegateMBean}, or if {@link EventRelay#stop}
42.292 + * throws an {@code IOException}.
42.293 + */
42.294 + public void close() throws IOException {
42.295 + if (logger.traceOn()) {
42.296 + logger.trace("close", clientId);
42.297 + }
42.298 +
42.299 + synchronized(listenerInfoMap) {
42.300 + if (closed) {
42.301 + return;
42.302 + }
42.303 +
42.304 + closed = true;
42.305 + listenerInfoMap.clear();
42.306 + }
42.307 +
42.308 + if (leaseRenewer != null)
42.309 + leaseRenewer.close();
42.310 +
42.311 + IOException ioe = null;
42.312 + try {
42.313 + eventRelay.stop();
42.314 + } catch (IOException e) {
42.315 + ioe = e;
42.316 + logger.debug("close", "EventRelay.stop", e);
42.317 + }
42.318 +
42.319 + try {
42.320 + eventClientDelegate.removeClient(clientId);
42.321 + } catch (Exception e) {
42.322 + if (e instanceof IOException)
42.323 + ioe = (IOException) e;
42.324 + else
42.325 + ioe = new IOException(e);
42.326 + logger.debug("close",
42.327 + "Got exception when removing "+clientId, e);
42.328 + }
42.329 +
42.330 + if (ioe != null)
42.331 + throw ioe;
42.332 + }
42.333 +
42.334 + /**
42.335 + * <p>Determine if this {@code EventClient} is closed.</p>
42.336 + *
42.337 + * @return True if the {@code EventClient} is closed.
42.338 + */
42.339 + public boolean closed() {
42.340 + return closed;
42.341 + }
42.342 +
42.343 + /**
42.344 + * <p>Return the {@link EventRelay} associated with this
42.345 + * {@code EventClient}.</p>
42.346 + *
42.347 + * @return The {@link EventRelay} object used.
42.348 + */
42.349 + public EventRelay getEventRelay() {
42.350 + return eventRelay;
42.351 + }
42.352 +
42.353 + /**
42.354 + * <p>Return the lease time that this {@code EventClient} requests
42.355 + * on every lease renewal.</p>
42.356 + *
42.357 + * @return The requested lease time.
42.358 + *
42.359 + * @see EventClientDelegateMBean#lease
42.360 + */
42.361 + public long getRequestedLeaseTime() {
42.362 + return requestedLeaseTime;
42.363 + }
42.364 +
42.365 + /**
42.366 + * @see javax.management.MBeanServerConnection#addNotificationListener(
42.367 + * ObjectName, NotificationListener, NotificationFilter, Object).
42.368 + */
42.369 + public void addNotificationListener(ObjectName name,
42.370 + NotificationListener listener,
42.371 + NotificationFilter filter,
42.372 + Object handback)
42.373 + throws InstanceNotFoundException, IOException {
42.374 + if (logger.traceOn()) {
42.375 + logger.trace("addNotificationListener", "");
42.376 + }
42.377 +
42.378 + checkState();
42.379 +
42.380 + Integer listenerId;
42.381 + try {
42.382 + listenerId =
42.383 + eventClientDelegate.addListener(clientId, name, filter);
42.384 + } catch (EventClientNotFoundException ecnfe) {
42.385 + final IOException ioe = new IOException();
42.386 + ioe.initCause(ecnfe);
42.387 + throw ioe;
42.388 + }
42.389 +
42.390 + synchronized(listenerInfoMap) {
42.391 + listenerInfoMap.put(listenerId, new ListenerInfo(
42.392 + name,
42.393 + listener,
42.394 + filter,
42.395 + handback,
42.396 + false));
42.397 + }
42.398 +
42.399 + startListening();
42.400 + }
42.401 +
42.402 + /**
42.403 + * @see javax.management.MBeanServerConnection#removeNotificationListener(
42.404 + * ObjectName, NotificationListener).
42.405 + */
42.406 + public void removeNotificationListener(ObjectName name,
42.407 + NotificationListener listener)
42.408 + throws InstanceNotFoundException,
42.409 + ListenerNotFoundException,
42.410 + IOException {
42.411 + if (logger.traceOn()) {
42.412 + logger.trace("removeNotificationListener", "");
42.413 + }
42.414 + checkState();
42.415 +
42.416 + for (Integer id : getListenerInfo(name, listener, false)) {
42.417 + removeListener(id);
42.418 + }
42.419 + }
42.420 +
42.421 + /**
42.422 + * @see javax.management.MBeanServerConnection#removeNotificationListener(
42.423 + * ObjectName, NotificationListener, NotificationFilter, Object).
42.424 + */
42.425 + public void removeNotificationListener(ObjectName name,
42.426 + NotificationListener listener,
42.427 + NotificationFilter filter,
42.428 + Object handback)
42.429 + throws InstanceNotFoundException,
42.430 + ListenerNotFoundException,
42.431 + IOException {
42.432 + if (logger.traceOn()) {
42.433 + logger.trace("removeNotificationListener", "with all arguments.");
42.434 + }
42.435 + checkState();
42.436 + final Integer listenerId =
42.437 + getListenerInfo(name, listener, filter, handback, false);
42.438 +
42.439 + removeListener(listenerId);
42.440 + }
42.441 +
42.442 + /**
42.443 + * @see javax.management.event.EventConsumer#unsubscribe(
42.444 + * ObjectName, NotificationListener).
42.445 + */
42.446 + public void unsubscribe(ObjectName name,
42.447 + NotificationListener listener)
42.448 + throws ListenerNotFoundException, IOException {
42.449 + if (logger.traceOn()) {
42.450 + logger.trace("unsubscribe", "");
42.451 + }
42.452 + checkState();
42.453 + final Integer listenerId =
42.454 + getMatchedListenerInfo(name, listener, true);
42.455 +
42.456 + synchronized(listenerInfoMap) {
42.457 + if (listenerInfoMap.remove(listenerId) == null) {
42.458 + throw new ListenerNotFoundException();
42.459 + }
42.460 + }
42.461 +
42.462 + stopListening();
42.463 +
42.464 + try {
42.465 + eventClientDelegate.removeListenerOrSubscriber(clientId, listenerId);
42.466 + } catch (InstanceNotFoundException e) {
42.467 + logger.trace("unsubscribe", "removeSubscriber", e);
42.468 + } catch (EventClientNotFoundException cnfe) {
42.469 + logger.trace("unsubscribe", "removeSubscriber", cnfe);
42.470 + }
42.471 + }
42.472 +
42.473 + /**
42.474 + * @see javax.management.event.EventConsumer#subscribe(
42.475 + * ObjectName, NotificationListener, NotificationFilter, Object).
42.476 + */
42.477 + public void subscribe(ObjectName name,
42.478 + NotificationListener listener,
42.479 + NotificationFilter filter,
42.480 + Object handback) throws IOException {
42.481 + if (logger.traceOn()) {
42.482 + logger.trace("subscribe", "");
42.483 + }
42.484 +
42.485 + checkState();
42.486 +
42.487 + Integer listenerId;
42.488 + try {
42.489 + listenerId =
42.490 + eventClientDelegate.addSubscriber(clientId, name, filter);
42.491 + } catch (EventClientNotFoundException ecnfe) {
42.492 + final IOException ioe = new IOException();
42.493 + ioe.initCause(ecnfe);
42.494 + throw ioe;
42.495 + }
42.496 +
42.497 + synchronized(listenerInfoMap) {
42.498 + listenerInfoMap.put(listenerId, new ListenerInfo(
42.499 + name,
42.500 + listener,
42.501 + filter,
42.502 + handback,
42.503 + true));
42.504 + }
42.505 +
42.506 + startListening();
42.507 + }
42.508 +
42.509 + /**
42.510 + * <p>Adds a set of listeners to the remote MBeanServer. This method can
42.511 + * be used to copy the listeners from one {@code EventClient} to another.</p>
42.512 + *
42.513 + * <p>A listener is represented by a {@link ListenerInfo} object. The listener
42.514 + * is added by calling {@link #subscribe(ObjectName,
42.515 + * NotificationListener, NotificationFilter, Object)} if the method
42.516 + * {@link ListenerInfo#isSubscription() isSubscription}
42.517 + * returns {@code true}; otherwise it is added by calling
42.518 + * {@link #addNotificationListener(ObjectName, NotificationListener,
42.519 + * NotificationFilter, Object)}.</p>
42.520 + *
42.521 + * <P>The method returns the listeners which were added successfully. The
42.522 + * elements in the returned collection are a subset of the elements in
42.523 + * {@code infoList}. If all listeners were added successfully, the two
42.524 + * collections are the same. If no listener was added successfully, the
42.525 + * returned collection is empty.</p>
42.526 + *
42.527 + * @param listeners the listeners to add.
42.528 + *
42.529 + * @return The listeners that were added successfully.
42.530 + *
42.531 + * @throws IOException If an I/O error occurs.
42.532 + *
42.533 + * @see #getListeners()
42.534 + */
42.535 + public Collection<ListenerInfo> addListeners(Collection<ListenerInfo> listeners)
42.536 + throws IOException {
42.537 + if (logger.traceOn()) {
42.538 + logger.trace("addListeners", "");
42.539 + }
42.540 +
42.541 + checkState();
42.542 +
42.543 + if (listeners == null || listeners.isEmpty())
42.544 + return Collections.emptySet();
42.545 +
42.546 + final List<ListenerInfo> list = new ArrayList<ListenerInfo>();
42.547 + for (ListenerInfo l : listeners) {
42.548 + try {
42.549 + if (l.isSubscription()) {
42.550 + subscribe(l.getObjectName(),
42.551 + l.getListener(),
42.552 + l.getFilter(),
42.553 + l.getHandback());
42.554 + } else {
42.555 + addNotificationListener(l.getObjectName(),
42.556 + l.getListener(),
42.557 + l.getFilter(),
42.558 + l.getHandback());
42.559 + }
42.560 +
42.561 + list.add(l);
42.562 + } catch (Exception e) {
42.563 + if (logger.traceOn()) {
42.564 + logger.trace("addListeners", "failed to add: "+l, e);
42.565 + }
42.566 + }
42.567 + }
42.568 +
42.569 + return list;
42.570 + }
42.571 +
42.572 + /**
42.573 + * Returns the set of listeners that have been added through
42.574 + * this {@code EventClient} and not subsequently removed.
42.575 + *
42.576 + * @return A collection of listener information. Empty if there are no
42.577 + * current listeners or if this {@code EventClient} has been {@linkplain
42.578 + * #close closed}.
42.579 + *
42.580 + * @see #addListeners
42.581 + */
42.582 + public Collection<ListenerInfo> getListeners() {
42.583 + if (logger.traceOn()) {
42.584 + logger.trace("getListeners", "");
42.585 + }
42.586 +
42.587 + synchronized(listenerInfoMap) {
42.588 + return Collections.unmodifiableCollection(listenerInfoMap.values());
42.589 + }
42.590 + }
42.591 +
42.592 + /**
42.593 + * Adds a listener to receive the {@code EventClient} notifications specified in
42.594 + * {@link #getEventClientNotificationInfo}.
42.595 + *
42.596 + * @param listener A listener to receive {@code EventClient} notifications.
42.597 + * @param filter A filter to select which notifications are to be delivered
42.598 + * to the listener, or {@code null} if all notifications are to be delivered.
42.599 + * @param handback An object to be given to the listener along with each
42.600 + * notification. Can be null.
42.601 + * @throws NullPointerException If listener is null.
42.602 + * @see #removeEventClientListener
42.603 + */
42.604 + public void addEventClientListener(NotificationListener listener,
42.605 + NotificationFilter filter,
42.606 + Object handback) {
42.607 + if (logger.traceOn()) {
42.608 + logger.trace("addEventClientListener", "");
42.609 + }
42.610 + broadcaster.addNotificationListener(listener, filter, handback);
42.611 + }
42.612 +
42.613 + /**
42.614 + * Removes a listener added to receive {@code EventClient} notifications specified in
42.615 + * {@link #getEventClientNotificationInfo}.
42.616 + *
42.617 + * @param listener A listener to receive {@code EventClient} notifications.
42.618 + * @throws NullPointerException If listener is null.
42.619 + * @throws ListenerNotFoundException If the listener is not added to
42.620 + * this {@code EventClient}.
42.621 + */
42.622 + public void removeEventClientListener(NotificationListener listener)
42.623 + throws ListenerNotFoundException {
42.624 + if (logger.traceOn()) {
42.625 + logger.trace("removeEventClientListener", "");
42.626 + }
42.627 + broadcaster.removeNotificationListener(listener);
42.628 + }
42.629 +
42.630 + /**
42.631 + * <p>Get the types of notification that an {@code EventClient} can send
42.632 + * to listeners added with {@link #addEventClientListener
42.633 + * addEventClientListener}.</p>
42.634 + *
42.635 + * @return Types of notification emitted by this {@code EventClient}.
42.636 + *
42.637 + * @see #FAILED
42.638 + * @see #NONFATAL
42.639 + * @see #NOTIFS_LOST
42.640 + */
42.641 + public MBeanNotificationInfo[] getEventClientNotificationInfo() {
42.642 + return myInfo.clone();
42.643 + }
42.644 +
42.645 + private static boolean match(ListenerInfo li,
42.646 + ObjectName name,
42.647 + NotificationListener listener,
42.648 + boolean subscribed) {
42.649 + return li.getObjectName().equals(name) &&
42.650 + li.getListener() == listener &&
42.651 + li.isSubscription() == subscribed;
42.652 + }
42.653 +
42.654 + private static boolean match(ListenerInfo li,
42.655 + ObjectName name,
42.656 + NotificationListener listener,
42.657 + NotificationFilter filter,
42.658 + Object handback,
42.659 + boolean subscribed) {
42.660 + return li.getObjectName().equals(name) &&
42.661 + li.getFilter() == filter &&
42.662 + li.getListener() == listener &&
42.663 + li.getHandback() == handback &&
42.664 + li.isSubscription() == subscribed;
42.665 + }
42.666 +
42.667 +// ---------------------------------------------------
42.668 +// private classes
42.669 +// ---------------------------------------------------
42.670 + private class DispatchingJob extends RepeatedSingletonJob {
42.671 + public DispatchingJob() {
42.672 + super(distributingExecutor);
42.673 + }
42.674 +
42.675 + public boolean isSuspended() {
42.676 + return closed || buffer.size() == 0;
42.677 + }
42.678 +
42.679 + public void task() {
42.680 + TargetedNotification[] tns ;
42.681 + int lost = 0;
42.682 +
42.683 + synchronized(buffer) {
42.684 + tns = buffer.removeNotifs();
42.685 + lost = buffer.removeLost();
42.686 + }
42.687 +
42.688 + if ((tns == null || tns.length == 0)
42.689 + && lost == 0) {
42.690 + return;
42.691 + }
42.692 +
42.693 + // forwarding
42.694 + if (tns != null && tns.length > 0) {
42.695 + if (logger.traceOn()) {
42.696 + logger.trace("DispatchingJob-task",
42.697 + "Forwarding: "+tns.length);
42.698 + }
42.699 + for (TargetedNotification tn : tns) {
42.700 + final ListenerInfo li = listenerInfoMap.get(tn.getListenerID());
42.701 + try {
42.702 + li.getListener().handleNotification(tn.getNotification(),
42.703 + li.getHandback());
42.704 + } catch (Exception e) {
42.705 + logger.fine(
42.706 + "DispatchingJob.task", "listener got exception", e);
42.707 + }
42.708 + }
42.709 + }
42.710 +
42.711 + if (lost > 0) {
42.712 + if (logger.traceOn()) {
42.713 + logger.trace("DispatchingJob-task",
42.714 + "lost: "+lost);
42.715 + }
42.716 + final Notification n = new Notification(NOTIFS_LOST,
42.717 + EventClient.this,
42.718 + myNotifCounter.getAndIncrement(),
42.719 + System.currentTimeMillis(),
42.720 + "Lost notifications.");
42.721 + n.setUserData(new Long(lost));
42.722 + broadcaster.sendNotification(n);
42.723 + }
42.724 + }
42.725 + }
42.726 +
42.727 +
42.728 + private class EventReceiverImpl implements EventReceiver {
42.729 + public void receive(NotificationResult nr) {
42.730 + if (logger.traceOn()) {
42.731 + logger.trace("MyEventReceiver-receive", "");
42.732 + }
42.733 +
42.734 + synchronized(buffer) {
42.735 + buffer.addNotifs(nr);
42.736 +
42.737 + dispatchingJob.resume();
42.738 + }
42.739 + }
42.740 +
42.741 + public void failed(Throwable t) {
42.742 + if (logger.traceOn()) {
42.743 + logger.trace("MyEventReceiver-failed", "", t);
42.744 + }
42.745 + final Notification n = new Notification(FAILED,
42.746 + this,
42.747 + myNotifCounter.getAndIncrement(),
42.748 + System.currentTimeMillis());
42.749 + n.setSource(t);
42.750 + broadcaster.sendNotification(n);
42.751 + }
42.752 +
42.753 + public void nonFatal(Exception e) {
42.754 + if (logger.traceOn()) {
42.755 + logger.trace("MyEventReceiver-nonFatal", "", e);
42.756 + }
42.757 +
42.758 + final Notification n = new Notification(NONFATAL,
42.759 + this,
42.760 + myNotifCounter.getAndIncrement(),
42.761 + System.currentTimeMillis());
42.762 + n.setSource(e);
42.763 + broadcaster.sendNotification(n);
42.764 + }
42.765 + }
42.766 +
42.767 +// ----------------------------------------------------
42.768 +// private class
42.769 +// ----------------------------------------------------
42.770 +
42.771 +
42.772 +// ----------------------------------------------------
42.773 +// private methods
42.774 +// ----------------------------------------------------
42.775 + private Integer getListenerInfo(ObjectName name,
42.776 + NotificationListener listener,
42.777 + NotificationFilter filter,
42.778 + Object handback,
42.779 + boolean subscribed) throws ListenerNotFoundException {
42.780 +
42.781 + synchronized(listenerInfoMap) {
42.782 + for (Map.Entry<Integer, ListenerInfo> entry :
42.783 + listenerInfoMap.entrySet()) {
42.784 + ListenerInfo li = entry.getValue();
42.785 + if (match(li, name, listener, filter, handback, subscribed)) {
42.786 + return entry.getKey();
42.787 + }
42.788 + }
42.789 + }
42.790 +
42.791 + throw new ListenerNotFoundException();
42.792 + }
42.793 +
42.794 + private Integer getMatchedListenerInfo(ObjectName name,
42.795 + NotificationListener listener,
42.796 + boolean subscribed) throws ListenerNotFoundException {
42.797 +
42.798 + synchronized(listenerInfoMap) {
42.799 + for (Map.Entry<Integer, ListenerInfo> entry :
42.800 + listenerInfoMap.entrySet()) {
42.801 + ListenerInfo li = entry.getValue();
42.802 + if (li.getObjectName().equals(name) &&
42.803 + li.getListener() == listener &&
42.804 + li.isSubscription() == subscribed) {
42.805 + return entry.getKey();
42.806 + }
42.807 + }
42.808 + }
42.809 +
42.810 + throw new ListenerNotFoundException();
42.811 + }
42.812 +
42.813 + private Collection<Integer> getListenerInfo(ObjectName name,
42.814 + NotificationListener listener,
42.815 + boolean subscribed) throws ListenerNotFoundException {
42.816 +
42.817 + final ArrayList<Integer> ids = new ArrayList<Integer>();
42.818 + synchronized(listenerInfoMap) {
42.819 + for (Map.Entry<Integer, ListenerInfo> entry :
42.820 + listenerInfoMap.entrySet()) {
42.821 + ListenerInfo li = entry.getValue();
42.822 + if (match(li, name, listener, subscribed)) {
42.823 + ids.add(entry.getKey());
42.824 + }
42.825 + }
42.826 + }
42.827 +
42.828 + if (ids.isEmpty()) {
42.829 + throw new ListenerNotFoundException();
42.830 + }
42.831 +
42.832 + return ids;
42.833 + }
42.834 +
42.835 + private void checkState() throws IOException {
42.836 + synchronized(listenerInfoMap) {
42.837 + if (closed) {
42.838 + throw new IOException("Ended!");
42.839 + }
42.840 + }
42.841 + }
42.842 +
42.843 + private void startListening() throws IOException {
42.844 + synchronized(listenerInfoMap) {
42.845 + if (!startedListening && listenerInfoMap.size() > 0) {
42.846 + eventRelay.setEventReceiver(myReceiver);
42.847 + }
42.848 +
42.849 + startedListening = true;
42.850 +
42.851 + if (logger.traceOn()) {
42.852 + logger.trace("startListening", "listening");
42.853 + }
42.854 + }
42.855 + }
42.856 +
42.857 + private void stopListening() throws IOException {
42.858 + synchronized(listenerInfoMap) {
42.859 + if (listenerInfoMap.size() == 0 && startedListening) {
42.860 + eventRelay.setEventReceiver(null);
42.861 +
42.862 + startedListening = false;
42.863 +
42.864 + if (logger.traceOn()) {
42.865 + logger.trace("stopListening", "non listening");
42.866 + }
42.867 + }
42.868 + }
42.869 + }
42.870 +
42.871 + private void removeListener(Integer id)
42.872 + throws InstanceNotFoundException,
42.873 + ListenerNotFoundException,
42.874 + IOException {
42.875 + synchronized(listenerInfoMap) {
42.876 + if (listenerInfoMap.remove(id) == null) {
42.877 + throw new ListenerNotFoundException();
42.878 + }
42.879 +
42.880 + stopListening();
42.881 + }
42.882 +
42.883 + try {
42.884 + eventClientDelegate.removeListenerOrSubscriber(clientId, id);
42.885 + } catch (EventClientNotFoundException cnfe) {
42.886 + logger.trace("removeListener", "ecd.removeListener", cnfe);
42.887 + }
42.888 + }
42.889 +
42.890 +
42.891 +// ----------------------------------------------------
42.892 +// private variables
42.893 +// ----------------------------------------------------
42.894 + private static final ClassLogger logger =
42.895 + new ClassLogger("javax.management.event", "EventClient");
42.896 +
42.897 + private final Executor distributingExecutor;
42.898 + private final EventClientDelegateMBean eventClientDelegate;
42.899 + private final EventRelay eventRelay;
42.900 + private volatile String clientId = null;
42.901 + private final long requestedLeaseTime;
42.902 +
42.903 + private final ReceiverBuffer buffer = new ReceiverBuffer();
42.904 +
42.905 + private final EventReceiverImpl myReceiver =
42.906 + new EventReceiverImpl();
42.907 + private final DispatchingJob dispatchingJob;
42.908 +
42.909 + private final HashMap<Integer, ListenerInfo> listenerInfoMap =
42.910 + new HashMap<Integer, ListenerInfo>();
42.911 +
42.912 + private volatile boolean closed = false;
42.913 +
42.914 + private volatile boolean startedListening = false;
42.915 +
42.916 + // Could change synchronization here. But at worst a race will mean
42.917 + // sequence numbers are not contiguous, which may not matter much.
42.918 + private final AtomicLong myNotifCounter = new AtomicLong();
42.919 +
42.920 + private final static MBeanNotificationInfo[] myInfo =
42.921 + new MBeanNotificationInfo[] {
42.922 + new MBeanNotificationInfo(
42.923 + new String[] {FAILED, NOTIFS_LOST},
42.924 + Notification.class.getName(), "")};
42.925 +
42.926 + private final NotificationBroadcasterSupport broadcaster =
42.927 + new NotificationBroadcasterSupport();
42.928 +
42.929 + private final static Executor callerExecutor = new Executor() {
42.930 + // DirectExecutor using caller thread
42.931 + public void execute(Runnable r) {
42.932 + r.run();
42.933 + }
42.934 + };
42.935 +
42.936 + private static void checkInit(final MBeanServerConnection conn,
42.937 + final ObjectName delegateName)
42.938 + throws IOException {
42.939 + if (conn == null) {
42.940 + throw new IllegalArgumentException("No connection specified");
42.941 + }
42.942 + if (delegateName != null &&
42.943 + (!conn.isRegistered(delegateName))) {
42.944 + throw new IllegalArgumentException(
42.945 + delegateName +
42.946 + ": not found");
42.947 + }
42.948 + if (delegateName == null &&
42.949 + (!conn.isRegistered(
42.950 + EventClientDelegate.OBJECT_NAME))) {
42.951 + throw new IllegalArgumentException(
42.952 + EventClientDelegate.OBJECT_NAME +
42.953 + ": not found");
42.954 + }
42.955 + }
42.956 +
42.957 +// ----------------------------------------------------
42.958 +// private event lease issues
42.959 +// ----------------------------------------------------
42.960 + private Callable<Long> renewLease = new Callable<Long>() {
42.961 + public Long call() throws IOException, EventClientNotFoundException {
42.962 + return eventClientDelegate.lease(clientId, requestedLeaseTime);
42.963 + }
42.964 + };
42.965 +
42.966 + private final LeaseRenewer leaseRenewer;
42.967 +
42.968 +// ------------------------------------------------------------------------
42.969 + /**
42.970 + * Constructs an {@code MBeanServerConnection} that uses an {@code EventClient} object,
42.971 + * if the underlying connection has an {@link EventClientDelegateMBean}.
42.972 + * <P> The {@code EventClient} object creates a default
42.973 + * {@link FetchingEventRelay} object to
42.974 + * receive notifications forwarded by the {@link EventClientDelegateMBean}.
42.975 + * The {@link EventClientDelegateMBean} it works with is the
42.976 + * default one registered with the ObjectName
42.977 + * {@link EventClientDelegate#OBJECT_NAME
42.978 + * OBJECT_NAME}.
42.979 + * The thread from the {@link FetchingEventRelay} object that fetches the
42.980 + * notifications is also used to distribute them.
42.981 + *
42.982 + * @param conn An {@link MBeanServerConnection} object used to communicate
42.983 + * with an {@link EventClientDelegateMBean}.
42.984 + * @throws IllegalArgumentException If the value of {@code conn} is null,
42.985 + * or the default {@link EventClientDelegateMBean} is not registered.
42.986 + * @throws IOException If an I/O error occurs.
42.987 + */
42.988 + public static MBeanServerConnection getEventClientConnection(
42.989 + final MBeanServerConnection conn)
42.990 + throws IOException {
42.991 + return getEventClientConnection(conn, null);
42.992 + }
42.993 +
42.994 + /**
42.995 + * Constructs an MBeanServerConnection that uses an {@code EventClient}
42.996 + * object with a user-specific {@link EventRelay}
42.997 + * object.
42.998 + * <P>
42.999 + * The {@link EventClientDelegateMBean} which it works with is the
42.1000 + * default one registered with the ObjectName
42.1001 + * {@link EventClientDelegate#OBJECT_NAME
42.1002 + * OBJECT_NAME}
42.1003 + * The thread that calls {@link EventReceiver#receive
42.1004 + * EventReceiver.receive} from the {@link EventRelay} object is used
42.1005 + * to distribute notifications to their listeners.
42.1006 + *
42.1007 + * @param conn An {@link MBeanServerConnection} object used to communicate
42.1008 + * with an {@link EventClientDelegateMBean}.
42.1009 + * @param eventRelay A user-specific object used to receive notifications
42.1010 + * forwarded by the {@link EventClientDelegateMBean}. If null, the default
42.1011 + * {@link FetchingEventRelay} object is used.
42.1012 + * @throws IllegalArgumentException If the value of {@code conn} is null,
42.1013 + * or the default {@link EventClientDelegateMBean} is not registered.
42.1014 + * @throws IOException If an I/O error occurs.
42.1015 + */
42.1016 + public static MBeanServerConnection getEventClientConnection(
42.1017 + final MBeanServerConnection conn,
42.1018 + final EventRelay eventRelay)
42.1019 + throws IOException {
42.1020 +
42.1021 + if (newEventConn == null) {
42.1022 + throw new IllegalArgumentException(
42.1023 + "Class not found: EventClientConnection");
42.1024 + }
42.1025 +
42.1026 + checkInit(conn,null);
42.1027 + final Callable<EventClient> factory = new Callable<EventClient>() {
42.1028 + final public EventClient call() throws Exception {
42.1029 + EventClientDelegateMBean ecd = EventClientDelegate.getProxy(conn);
42.1030 + return new EventClient(ecd, eventRelay, null, null,
42.1031 + DEFAULT_LEASE_TIMEOUT);
42.1032 + }
42.1033 + };
42.1034 +
42.1035 + try {
42.1036 + return (MBeanServerConnection)newEventConn.invoke(null,
42.1037 + conn, factory);
42.1038 + } catch (Exception e) {
42.1039 + throw new IllegalArgumentException(e);
42.1040 + }
42.1041 + }
42.1042 +
42.1043 + private static Method newEventConn = null;
42.1044 + static {
42.1045 + try {
42.1046 + Class<?> c = Class.forName(
42.1047 + "com.sun.jmx.remote.util.EventClientConnection",
42.1048 + false, Thread.currentThread().getContextClassLoader());
42.1049 + newEventConn = c.getMethod("getEventConnectionFor",
42.1050 + MBeanServerConnection.class, Callable.class);
42.1051 + } catch (Exception e) {
42.1052 + // OK: we're running in a subset of our classes
42.1053 + }
42.1054 + }
42.1055 +
42.1056 + /**
42.1057 + * <p>Get the client id of this {@code EventClient} in the
42.1058 + * {@link EventClientDelegateMBean}.
42.1059 + *
42.1060 + * @return the client id.
42.1061 + *
42.1062 + * @see EventClientDelegateMBean#addClient(String, Object[], String[])
42.1063 + * EventClientDelegateMBean.addClient
42.1064 + */
42.1065 + public String getClientId() {
42.1066 + return clientId;
42.1067 + }
42.1068 +
42.1069 + private static final PerThreadGroupPool<ScheduledThreadPoolExecutor>
42.1070 + leaseRenewerThreadPool = PerThreadGroupPool.make();
42.1071 +}
43.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
43.2 +++ b/src/share/classes/javax/management/event/EventClientDelegate.java Thu Aug 21 09:55:18 2008 -0700
43.3 @@ -0,0 +1,766 @@
43.4 +/*
43.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
43.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
43.7 + *
43.8 + * This code is free software; you can redistribute it and/or modify it
43.9 + * under the terms of the GNU General Public License version 2 only, as
43.10 + * published by the Free Software Foundation. Sun designates this
43.11 + * particular file as subject to the "Classpath" exception as provided
43.12 + * by Sun in the LICENSE file that accompanied this code.
43.13 + *
43.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
43.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
43.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
43.17 + * version 2 for more details (a copy is included in the LICENSE file that
43.18 + * accompanied this code).
43.19 + *
43.20 + * You should have received a copy of the GNU General Public License version
43.21 + * 2 along with this work; if not, write to the Free Software Foundation,
43.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
43.23 + *
43.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
43.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
43.26 + * have any questions.
43.27 + *
43.28 + * @since JMX 2.0
43.29 + */
43.30 +
43.31 +package javax.management.event;
43.32 +
43.33 +import java.io.IOException;
43.34 +import java.lang.reflect.Method;
43.35 +import java.util.HashMap;
43.36 +import java.util.Collection;
43.37 +import java.util.Collections;
43.38 +import java.util.UUID;
43.39 +
43.40 +import javax.management.InstanceNotFoundException;
43.41 +import javax.management.ListenerNotFoundException;
43.42 +import javax.management.Notification;
43.43 +import javax.management.NotificationFilter;
43.44 +import javax.management.NotificationListener;
43.45 +import javax.management.ObjectName;
43.46 +import javax.management.remote.NotificationResult;
43.47 +import com.sun.jmx.event.EventBuffer;
43.48 +import com.sun.jmx.event.LeaseManager;
43.49 +import com.sun.jmx.interceptor.SingleMBeanForwarder;
43.50 +import com.sun.jmx.mbeanserver.Util;
43.51 +import com.sun.jmx.remote.util.ClassLogger;
43.52 +import java.lang.ref.WeakReference;
43.53 +import java.lang.reflect.Constructor;
43.54 +import java.lang.reflect.InvocationHandler;
43.55 +import java.lang.reflect.Proxy;
43.56 +import java.security.AccessControlContext;
43.57 +import java.security.AccessController;
43.58 +import java.security.PrivilegedAction;
43.59 +import java.security.PrivilegedExceptionAction;
43.60 +import java.util.Arrays;
43.61 +import java.util.Map;
43.62 +import java.util.Set;
43.63 +import java.util.WeakHashMap;
43.64 +import java.util.concurrent.ConcurrentHashMap;
43.65 +import java.util.concurrent.atomic.AtomicInteger;
43.66 +import javax.management.DynamicMBean;
43.67 +import javax.management.MBeanException;
43.68 +import javax.management.MBeanPermission;
43.69 +import javax.management.MBeanServer;
43.70 +import javax.management.MBeanServerConnection;
43.71 +import javax.management.MBeanServerDelegate;
43.72 +import javax.management.MBeanServerInvocationHandler;
43.73 +import javax.management.MBeanServerNotification;
43.74 +import javax.management.ObjectInstance;
43.75 +import javax.management.StandardMBean;
43.76 +import javax.management.remote.MBeanServerForwarder;
43.77 +
43.78 +/**
43.79 + * This is the default implementation of the MBean
43.80 + * {@link EventClientDelegateMBean}.
43.81 + */
43.82 +public class EventClientDelegate implements EventClientDelegateMBean {
43.83 +
43.84 + private EventClientDelegate(MBeanServer server) {
43.85 + if (server == null) {
43.86 + throw new NullPointerException("Null MBeanServer.");
43.87 + }
43.88 +
43.89 + if (logger.traceOn()) {
43.90 + logger.trace("EventClientDelegate", "new one");
43.91 + }
43.92 + mbeanServer = server;
43.93 + eventSubscriber = EventSubscriber.getEventSubscriber(mbeanServer);
43.94 + }
43.95 +
43.96 + /**
43.97 + * Returns an {@code EventClientDelegate} instance for the given
43.98 + * {@code MBeanServer}. Calling this method more than once with the same
43.99 + * {@code server} argument may return the same object or a different object
43.100 + * each time. See {@link EventClientDelegateMBean} for an example use of
43.101 + * this method.
43.102 + *
43.103 + * @param server An MBean server instance to work with.
43.104 + * @return An {@code EventClientDelegate} instance.
43.105 + * @throws NullPointerException If {@code server} is null.
43.106 + */
43.107 + public static EventClientDelegate getEventClientDelegate(MBeanServer server) {
43.108 + EventClientDelegate delegate = null;
43.109 + synchronized(delegateMap) {
43.110 + final WeakReference wrf = delegateMap.get(server);
43.111 + delegate = (wrf == null) ? null : (EventClientDelegate)wrf.get();
43.112 +
43.113 + if (delegate == null) {
43.114 + delegate = new EventClientDelegate(server);
43.115 + try {
43.116 + // TODO: this may not work with federated MBean, because
43.117 + // the delegate will *not* emit notifications for those MBeans.
43.118 + delegate.mbeanServer.addNotificationListener(
43.119 + MBeanServerDelegate.DELEGATE_NAME,
43.120 + delegate.cleanListener, null, null);
43.121 + } catch (InstanceNotFoundException e) {
43.122 + logger.fine(
43.123 + "getEventClientDelegate",
43.124 + "Could not add MBeanServerDelegate listener", e);
43.125 + }
43.126 + delegateMap.put(server,
43.127 + new WeakReference<EventClientDelegate>(delegate));
43.128 + }
43.129 + }
43.130 +
43.131 + return delegate;
43.132 + }
43.133 +
43.134 + // Logic for the MBeanServerForwarder that simulates the existence of the
43.135 + // EventClientDelegate MBean. Things are complicated by the fact that
43.136 + // there may not be anything in the chain after this forwarder when it is
43.137 + // created - the connection to a real MBeanServer might only come later.
43.138 + // Recall that there are two ways of creating a JMXConnectorServer -
43.139 + // either you specify its MBeanServer when you create it, or you specify
43.140 + // no MBeanServer and register it in an MBeanServer later. In the latter
43.141 + // case, the forwarder chain points nowhere until this registration
43.142 + // happens. Since EventClientDelegate wants to add a listener to the
43.143 + // MBeanServerDelegate, we can't create an EventClientDelegate until
43.144 + // there is an MBeanServer. So the forwarder initially has
43.145 + // a dummy ECD where every method throws an exception, and
43.146 + // the real ECD is created as soon as doing so does not produce an
43.147 + // exception.
43.148 + // TODO: rewrite so that the switch from the dummy to the real ECD happens
43.149 + // just before we would otherwise have thrown UnsupportedOperationException.
43.150 + // This is more correct, because it's not guaranteed that we will see the
43.151 + // moment where the real MBeanServer is attached, if it happens by virtue
43.152 + // of a setMBeanServer on some other forwarder later in the chain.
43.153 +
43.154 + private static class Forwarder extends SingleMBeanForwarder {
43.155 +
43.156 + private static class UnsupportedInvocationHandler
43.157 + implements InvocationHandler {
43.158 + public Object invoke(Object proxy, Method method, Object[] args)
43.159 + throws Throwable {
43.160 + throw new UnsupportedOperationException(
43.161 + "EventClientDelegate unavailable: no MBeanServer, or " +
43.162 + "MBeanServer inaccessible");
43.163 + }
43.164 + }
43.165 +
43.166 + private static DynamicMBean makeUnsupportedECD() {
43.167 + EventClientDelegateMBean unsupported = (EventClientDelegateMBean)
43.168 + Proxy.newProxyInstance(
43.169 + EventClientDelegateMBean.class.getClassLoader(),
43.170 + new Class<?>[] {EventClientDelegateMBean.class},
43.171 + new UnsupportedInvocationHandler());
43.172 + return new StandardMBean(
43.173 + unsupported, EventClientDelegateMBean.class, false);
43.174 + }
43.175 +
43.176 + private volatile boolean madeECD;
43.177 +
43.178 + Forwarder() {
43.179 + super(OBJECT_NAME, makeUnsupportedECD());
43.180 + }
43.181 +
43.182 + @Override
43.183 + public synchronized void setMBeanServer(final MBeanServer mbs) {
43.184 + super.setMBeanServer(mbs);
43.185 +
43.186 + if (!madeECD) {
43.187 + try {
43.188 + EventClientDelegate ecd =
43.189 + AccessController.doPrivileged(
43.190 + new PrivilegedAction<EventClientDelegate>() {
43.191 + public EventClientDelegate run() {
43.192 + return getEventClientDelegate(Forwarder.this);
43.193 + }
43.194 + });
43.195 + DynamicMBean mbean = new StandardMBean(
43.196 + ecd, EventClientDelegateMBean.class, false);
43.197 + setSingleMBean(mbean);
43.198 + madeECD = true;
43.199 + } catch (Exception e) {
43.200 + // OK: assume no MBeanServer
43.201 + logger.fine("setMBeanServer", "isRegistered", e);
43.202 + }
43.203 + }
43.204 + }
43.205 + }
43.206 +
43.207 + /**
43.208 + * <p>Create a new {@link MBeanServerForwarder} that simulates the existence
43.209 + * of an {@code EventClientDelegateMBean} with the {@linkplain
43.210 + * #OBJECT_NAME default name}. This forwarder intercepts MBean requests
43.211 + * that are targeted for that MBean and handles them itself. All other
43.212 + * requests are forwarded to the next element in the forwarder chain.</p>
43.213 + *
43.214 + * @return a new {@code MBeanServerForwarder} that simulates the existence
43.215 + * of an {@code EventClientDelegateMBean}.
43.216 + */
43.217 + public static MBeanServerForwarder newForwarder() {
43.218 + return new Forwarder();
43.219 + }
43.220 +
43.221 + /**
43.222 + * Returns a proxy of the default {@code EventClientDelegateMBean}.
43.223 + *
43.224 + * @param conn An {@link MBeanServerConnection} to work with.
43.225 + */
43.226 + @SuppressWarnings("cast") // cast for jdk 1.5
43.227 + public static EventClientDelegateMBean getProxy(MBeanServerConnection conn) {
43.228 + return (EventClientDelegateMBean)MBeanServerInvocationHandler.
43.229 + newProxyInstance(conn,
43.230 + OBJECT_NAME,
43.231 + EventClientDelegateMBean.class,
43.232 + false);
43.233 + }
43.234 +
43.235 + public String addClient(String className, Object[] params, String[] sig)
43.236 + throws MBeanException {
43.237 + return addClient(className, null, params, sig, true);
43.238 + }
43.239 +
43.240 + public String addClient(String className,
43.241 + ObjectName classLoader,
43.242 + Object[] params,
43.243 + String[] sig) throws MBeanException {
43.244 + return addClient(className, classLoader, params, sig, false);
43.245 + }
43.246 +
43.247 + private String addClient(String className,
43.248 + ObjectName classLoader,
43.249 + Object[] params,
43.250 + String[] sig,
43.251 + boolean classLoaderRepository) throws MBeanException {
43.252 + try {
43.253 + return addClientX(
43.254 + className, classLoader, params, sig, classLoaderRepository);
43.255 + } catch (RuntimeException e) {
43.256 + throw e;
43.257 + } catch (Exception e) {
43.258 + throw new MBeanException(e);
43.259 + }
43.260 + }
43.261 +
43.262 + private String addClientX(String className,
43.263 + ObjectName classLoader,
43.264 + Object[] params,
43.265 + String[] sig,
43.266 + boolean classLoaderRepository) throws Exception {
43.267 + if (className == null) {
43.268 + throw new IllegalArgumentException("Null class name.");
43.269 + }
43.270 +
43.271 + final Object o;
43.272 +
43.273 + // The special treatment of standard EventForwarders is so that no
43.274 + // special permissions are necessary to use them. Otherwise you
43.275 + // couldn't use EventClient if you didn't have permission to call
43.276 + // MBeanServer.instantiate. We do require that permission for
43.277 + // non-standard forwarders, because otherwise you could instantiate
43.278 + // any class with possibly adverse consequences. We also avoid using
43.279 + // MBeanInstantiator because it looks up constructors by loading each
43.280 + // class in the sig array, which means a remote user could cause any
43.281 + // class to be loaded. That's probably not hugely risky but still.
43.282 + if (className.startsWith("javax.management.event.")) {
43.283 + Class<?> c = Class.forName(
43.284 + className, false, this.getClass().getClassLoader());
43.285 + Constructor<?> foundCons = null;
43.286 + if (sig == null)
43.287 + sig = new String[0];
43.288 + for (Constructor cons : c.getConstructors()) {
43.289 + Class<?>[] types = cons.getParameterTypes();
43.290 + String[] consSig = new String[types.length];
43.291 + for (int i = 0; i < types.length; i++)
43.292 + consSig[i] = types[i].getName();
43.293 + if (Arrays.equals(sig, consSig)) {
43.294 + foundCons = cons;
43.295 + break;
43.296 + }
43.297 + }
43.298 + if (foundCons == null) {
43.299 + throw new NoSuchMethodException(
43.300 + "Constructor for " + className + " with argument types " +
43.301 + Arrays.toString(sig));
43.302 + }
43.303 + o = foundCons.newInstance(params);
43.304 + } else if (classLoaderRepository) {
43.305 + o = mbeanServer.instantiate(className, params, sig);
43.306 + } else {
43.307 + o = mbeanServer.instantiate(className, classLoader, params, sig);
43.308 + }
43.309 +
43.310 + if (!(o instanceof EventForwarder)) {
43.311 + throw new IllegalArgumentException(
43.312 + className+" is not an EventForwarder class.");
43.313 + }
43.314 +
43.315 + final EventForwarder forwarder = (EventForwarder)o;
43.316 + final String clientId = UUID.randomUUID().toString();
43.317 + ClientInfo clientInfo = new ClientInfo(clientId, forwarder);
43.318 +
43.319 + clientInfoMap.put(clientId, clientInfo);
43.320 +
43.321 + forwarder.setClientId(clientId);
43.322 +
43.323 + if (logger.traceOn()) {
43.324 + logger.trace("addClient", clientId);
43.325 + }
43.326 +
43.327 + return clientId;
43.328 + }
43.329 +
43.330 + public Integer[] getListenerIds(String clientId)
43.331 + throws IOException, EventClientNotFoundException {
43.332 + ClientInfo clientInfo = getClientInfo(clientId);
43.333 +
43.334 + if (clientInfo == null) {
43.335 + throw new EventClientNotFoundException("The client is not found.");
43.336 + }
43.337 +
43.338 + Map<Integer, AddedListener> listenerInfoMap = clientInfo.listenerInfoMap;
43.339 + synchronized (listenerInfoMap) {
43.340 + Set<Integer> ids = listenerInfoMap.keySet();
43.341 + return ids.toArray(new Integer[ids.size()]);
43.342 + }
43.343 + }
43.344 +
43.345 + /**
43.346 + * {@inheritDoc}
43.347 + *
43.348 + * <p>The execution of this method includes a call to
43.349 + * {@link MBeanServer#addNotificationListener(ObjectName,
43.350 + * NotificationListener, NotificationFilter, Object)}.</p>
43.351 + */
43.352 + public Integer addListener(String clientId,
43.353 + final ObjectName name,
43.354 + NotificationFilter filter)
43.355 + throws EventClientNotFoundException, InstanceNotFoundException {
43.356 +
43.357 + if (logger.traceOn()) {
43.358 + logger.trace("addListener", "");
43.359 + }
43.360 +
43.361 + return getClientInfo(clientId).addListenerInfo(name, filter);
43.362 + }
43.363 +
43.364 + /**
43.365 + * {@inheritDoc}
43.366 + *
43.367 + * <p>The execution of this method can include call to
43.368 + * {@link MBeanServer#removeNotificationListener(ObjectName,
43.369 + * NotificationListener, NotificationFilter, Object)}.</p>
43.370 + */
43.371 + public void removeListenerOrSubscriber(String clientId, Integer listenerId)
43.372 + throws InstanceNotFoundException,
43.373 + ListenerNotFoundException,
43.374 + EventClientNotFoundException,
43.375 + IOException {
43.376 + if (logger.traceOn()) {
43.377 + logger.trace("removeListener", ""+listenerId);
43.378 + }
43.379 + getClientInfo(clientId).removeListenerInfo(listenerId);
43.380 + }
43.381 +
43.382 + /**
43.383 + * {@inheritDoc}
43.384 + *
43.385 + * <p>The execution of this method includes a call to
43.386 + * {@link MBeanServer#addNotificationListener(ObjectName,
43.387 + * NotificationListener, NotificationFilter, Object)} for
43.388 + * every MBean matching {@code name}. If {@code name} is
43.389 + * an {@code ObjectName} pattern, then the execution of this
43.390 + * method will include a call to {@link MBeanServer#queryNames}.</p>
43.391 + */
43.392 + public Integer addSubscriber(String clientId, ObjectName name,
43.393 + NotificationFilter filter)
43.394 + throws EventClientNotFoundException, IOException {
43.395 + if (logger.traceOn()) {
43.396 + logger.trace("addSubscriber", "");
43.397 + }
43.398 + return getClientInfo(clientId).subscribeListenerInfo(name, filter);
43.399 + }
43.400 +
43.401 + public NotificationResult fetchNotifications(String clientId,
43.402 + long startSequenceNumber,
43.403 + int maxNotifs,
43.404 + long timeout)
43.405 + throws EventClientNotFoundException {
43.406 + if (logger.traceOn()) {
43.407 + logger.trace("fetchNotifications", "for "+clientId);
43.408 + }
43.409 + return getClientInfo(clientId).fetchNotifications(startSequenceNumber,
43.410 + maxNotifs,
43.411 + timeout);
43.412 + }
43.413 +
43.414 + public void removeClient(String clientId)
43.415 + throws EventClientNotFoundException {
43.416 + if (clientId == null)
43.417 + throw new EventClientNotFoundException("Null clientId");
43.418 + if (logger.traceOn()) {
43.419 + logger.trace("removeClient", clientId);
43.420 + }
43.421 + ClientInfo ci = null;
43.422 + ci = clientInfoMap.remove(clientId);
43.423 +
43.424 + if (ci == null) {
43.425 + throw new EventClientNotFoundException("clientId is "+clientId);
43.426 + } else {
43.427 + ci.clean();
43.428 + }
43.429 + }
43.430 +
43.431 + public long lease(String clientId, long timeout)
43.432 + throws IOException, EventClientNotFoundException {
43.433 + if (logger.traceOn()) {
43.434 + logger.trace("lease", "for "+clientId);
43.435 + }
43.436 + return getClientInfo(clientId).lease(timeout);
43.437 + }
43.438 +
43.439 + // ------------------------------------
43.440 + // private classes
43.441 + // ------------------------------------
43.442 + private class ClientInfo {
43.443 + String clientId;
43.444 + EventBuffer buffer;
43.445 + NotificationListener clientListener;
43.446 + Map<Integer, AddedListener> listenerInfoMap =
43.447 + new HashMap<Integer, AddedListener>();
43.448 +
43.449 + ClientInfo(String clientId, EventForwarder forwarder) {
43.450 + this.clientId = clientId;
43.451 + this.forwarder = forwarder;
43.452 + clientListener =
43.453 + new ForwardingClientListener(listenerInfoMap, forwarder);
43.454 + }
43.455 +
43.456 + Integer addOrSubscribeListenerInfo(
43.457 + ObjectName name, NotificationFilter filter, boolean subscribe)
43.458 + throws InstanceNotFoundException, IOException {
43.459 +
43.460 + final Integer listenerId = nextListenerId();
43.461 + AddedListener listenerInfo = new AddedListener(
43.462 + listenerId, filter, name, subscribe);
43.463 + if (subscribe) {
43.464 + eventSubscriber.subscribe(name,
43.465 + clientListener,
43.466 + filter,
43.467 + listenerInfo);
43.468 + } else {
43.469 + mbeanServer.addNotificationListener(name,
43.470 + clientListener,
43.471 + filter,
43.472 + listenerInfo);
43.473 + }
43.474 +
43.475 + synchronized(listenerInfoMap) {
43.476 + listenerInfoMap.put(listenerId, listenerInfo);
43.477 + }
43.478 +
43.479 + return listenerId;
43.480 + }
43.481 +
43.482 + Integer addListenerInfo(ObjectName name,
43.483 + NotificationFilter filter) throws InstanceNotFoundException {
43.484 + try {
43.485 + return addOrSubscribeListenerInfo(name, filter, false);
43.486 + } catch (IOException e) { // can't happen
43.487 + logger.warning(
43.488 + "EventClientDelegate.addListenerInfo",
43.489 + "unexpected exception", e);
43.490 + throw new RuntimeException(e);
43.491 + }
43.492 + }
43.493 +
43.494 + Integer subscribeListenerInfo(ObjectName name,
43.495 + NotificationFilter filter) throws IOException {
43.496 + try {
43.497 + return addOrSubscribeListenerInfo(name, filter, true);
43.498 + } catch (InstanceNotFoundException e) { // can't happen
43.499 + logger.warning(
43.500 + "EventClientDelegate.subscribeListenerInfo",
43.501 + "unexpected exception", e);
43.502 + throw new RuntimeException(e);
43.503 + }
43.504 + }
43.505 +
43.506 + private final AtomicInteger nextListenerId = new AtomicInteger();
43.507 +
43.508 + private Integer nextListenerId() {
43.509 + return nextListenerId.getAndIncrement();
43.510 + }
43.511 +
43.512 + NotificationResult fetchNotifications(long startSequenceNumber,
43.513 + int maxNotifs,
43.514 + long timeout) {
43.515 +
43.516 + if (!(forwarder instanceof FetchingEventForwarder)) {
43.517 + throw new IllegalArgumentException(
43.518 + "This client is using Event Postal Service!");
43.519 + }
43.520 +
43.521 + return ((FetchingEventForwarder)forwarder).
43.522 + fetchNotifications(startSequenceNumber,
43.523 + maxNotifs, timeout);
43.524 + }
43.525 +
43.526 + void removeListenerInfo(Integer listenerId)
43.527 + throws InstanceNotFoundException, ListenerNotFoundException, IOException {
43.528 + AddedListener listenerInfo;
43.529 + synchronized(listenerInfoMap) {
43.530 + listenerInfo = listenerInfoMap.remove(listenerId);
43.531 + }
43.532 +
43.533 + if (listenerInfo == null) {
43.534 + throw new ListenerNotFoundException("The listener is not found.");
43.535 + }
43.536 +
43.537 + if (listenerInfo.subscription) {
43.538 + eventSubscriber.unsubscribe(listenerInfo.name,
43.539 + clientListener);
43.540 + } else {
43.541 + mbeanServer.removeNotificationListener(listenerInfo.name,
43.542 + clientListener,
43.543 + listenerInfo.filter,
43.544 + listenerInfo);
43.545 + }
43.546 + }
43.547 +
43.548 + void clean(ObjectName name) {
43.549 + synchronized(listenerInfoMap) {
43.550 + for (Map.Entry<Integer, AddedListener> entry :
43.551 + listenerInfoMap.entrySet()) {
43.552 + AddedListener li = entry.getValue();
43.553 + if (name.equals(li.name)) {
43.554 + listenerInfoMap.remove(entry.getKey());
43.555 + }
43.556 + }
43.557 + }
43.558 + }
43.559 +
43.560 + void clean() {
43.561 + synchronized(listenerInfoMap) {
43.562 + for (AddedListener li : listenerInfoMap.values()) {
43.563 + try {
43.564 + mbeanServer.removeNotificationListener(li.name,
43.565 + clientListener);
43.566 + } catch (Exception e) {
43.567 + logger.trace("ClientInfo.clean", "removeNL", e);
43.568 + }
43.569 + }
43.570 + listenerInfoMap.clear();
43.571 + }
43.572 +
43.573 + try {
43.574 + forwarder.close();
43.575 + } catch (Exception e) {
43.576 + logger.trace(
43.577 + "ClientInfo.clean", "forwarder.close", e);
43.578 + }
43.579 +
43.580 + if (leaseManager != null) {
43.581 + leaseManager.stop();
43.582 + }
43.583 + }
43.584 +
43.585 + long lease(long timeout) {
43.586 + return leaseManager.lease(timeout);
43.587 + }
43.588 +
43.589 + @Override
43.590 + public boolean equals(Object o) {
43.591 + if (this == o) {
43.592 + return true;
43.593 + }
43.594 +
43.595 + if (o instanceof ClientInfo &&
43.596 + clientId.equals(((ClientInfo)o).clientId)) {
43.597 + return true;
43.598 + }
43.599 +
43.600 + return false;
43.601 + }
43.602 +
43.603 + @Override
43.604 + public int hashCode() {
43.605 + return clientId.hashCode();
43.606 + }
43.607 +
43.608 + private EventForwarder forwarder = null;
43.609 +
43.610 + private final Runnable leaseExpiryCallback = new Runnable() {
43.611 + public void run() {
43.612 + try {
43.613 + removeClient(clientId);
43.614 + } catch (Exception e) {
43.615 + logger.trace(
43.616 + "ClientInfo.leaseExpiryCallback", "removeClient", e);
43.617 + }
43.618 + }
43.619 + };
43.620 +
43.621 + private LeaseManager leaseManager = new LeaseManager(leaseExpiryCallback);
43.622 + }
43.623 +
43.624 + private class ForwardingClientListener implements NotificationListener {
43.625 + public ForwardingClientListener(Map<Integer, AddedListener> listenerInfoMap,
43.626 + EventForwarder forwarder) {
43.627 + this.listenerInfoMap = listenerInfoMap;
43.628 + this.forwarder = forwarder;
43.629 + }
43.630 +
43.631 + public void handleNotification(Notification n, Object o) {
43.632 + if (n == null || (!(o instanceof AddedListener))) {
43.633 + if (logger.traceOn()) {
43.634 + logger.trace("ForwardingClientListener-handleNotification",
43.635 + "received a unknown notif");
43.636 + }
43.637 + return;
43.638 + }
43.639 +
43.640 + AddedListener li = (AddedListener) o;
43.641 +
43.642 + if (checkListenerPermission(li.name,li.acc)) {
43.643 + try {
43.644 + forwarder.forward(n, li.listenerId);
43.645 + } catch (Exception e) {
43.646 + if (logger.traceOn()) {
43.647 + logger.trace(
43.648 + "ForwardingClientListener-handleNotification",
43.649 + "forwarding failed.", e);
43.650 + }
43.651 + }
43.652 + }
43.653 + }
43.654 +
43.655 + private final Map<Integer, AddedListener> listenerInfoMap;
43.656 + private final EventForwarder forwarder;
43.657 + }
43.658 +
43.659 + private class AddedListener {
43.660 + final int listenerId;
43.661 + final NotificationFilter filter;
43.662 + final ObjectName name;
43.663 + final boolean subscription;
43.664 + final AccessControlContext acc;
43.665 +
43.666 + public AddedListener(
43.667 + int listenerId,
43.668 + NotificationFilter filter,
43.669 + ObjectName name,
43.670 + boolean subscription) {
43.671 + this.listenerId = listenerId;
43.672 + this.filter = filter;
43.673 + this.name = name;
43.674 + this.subscription = subscription;
43.675 + acc = AccessController.getContext();
43.676 + }
43.677 + }
43.678 +
43.679 + private class CleanListener implements NotificationListener {
43.680 + public void handleNotification(Notification notification,
43.681 + Object handback) {
43.682 + if (notification instanceof MBeanServerNotification) {
43.683 + if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(
43.684 + notification.getType())) {
43.685 + final ObjectName name =
43.686 + ((MBeanServerNotification)notification).getMBeanName();
43.687 +
43.688 + final Collection <ClientInfo> list =
43.689 + Collections.unmodifiableCollection(clientInfoMap.values());
43.690 +
43.691 + for (ClientInfo ci : list) {
43.692 + ci.clean(name);
43.693 + }
43.694 + }
43.695 +
43.696 + }
43.697 + }
43.698 + }
43.699 +
43.700 + // -------------------------------------------------
43.701 + // private method
43.702 + // -------------------------------------------------
43.703 + private ClientInfo getClientInfo(String clientId)
43.704 + throws EventClientNotFoundException {
43.705 + ClientInfo clientInfo = null;
43.706 + clientInfo = clientInfoMap.get(clientId);
43.707 +
43.708 + if (clientInfo == null) {
43.709 + throw new EventClientNotFoundException("The client is not found.");
43.710 + }
43.711 +
43.712 + return clientInfo;
43.713 + }
43.714 +
43.715 + /**
43.716 + * Explicitly check the MBeanPermission for
43.717 + * the current access control context.
43.718 + */
43.719 + private boolean checkListenerPermission(final ObjectName name,
43.720 + final AccessControlContext acc) {
43.721 + if (logger.traceOn()) {
43.722 + logger.trace("checkListenerPermission", "");
43.723 + }
43.724 + SecurityManager sm = System.getSecurityManager();
43.725 + if (sm != null) {
43.726 + try {
43.727 + ObjectInstance oi = (ObjectInstance) AccessController.doPrivileged(
43.728 + new PrivilegedExceptionAction<Object>() {
43.729 + public Object run()
43.730 + throws InstanceNotFoundException {
43.731 + return mbeanServer.getObjectInstance(name);
43.732 + }
43.733 + });
43.734 +
43.735 + String classname = oi.getClassName();
43.736 + MBeanPermission perm = new MBeanPermission(
43.737 + classname,
43.738 + null,
43.739 + name,
43.740 + "addNotificationListener");
43.741 + sm.checkPermission(perm, acc);
43.742 + } catch (Exception e) {
43.743 + if (logger.debugOn()) {
43.744 + logger.debug("checkListenerPermission", "refused.", e);
43.745 + }
43.746 + return false;
43.747 + }
43.748 + }
43.749 + return true;
43.750 + }
43.751 +
43.752 + // ------------------------------------
43.753 + // private variables
43.754 + // ------------------------------------
43.755 + private final MBeanServer mbeanServer;
43.756 + private volatile String mbeanServerName = null;
43.757 + private Map<String, ClientInfo> clientInfoMap =
43.758 + new ConcurrentHashMap<String, ClientInfo>();
43.759 +
43.760 + private final CleanListener cleanListener = new CleanListener();
43.761 + private final EventSubscriber eventSubscriber;
43.762 +
43.763 + private static final ClassLogger logger =
43.764 + new ClassLogger("javax.management.event", "EventClientDelegate");
43.765 +
43.766 + private static final
43.767 + Map<MBeanServer, WeakReference<EventClientDelegate>> delegateMap =
43.768 + new WeakHashMap<MBeanServer, WeakReference<EventClientDelegate>>();
43.769 +}
44.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
44.2 +++ b/src/share/classes/javax/management/event/EventClientDelegateMBean.java Thu Aug 21 09:55:18 2008 -0700
44.3 @@ -0,0 +1,318 @@
44.4 +/*
44.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
44.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44.7 + *
44.8 + * This code is free software; you can redistribute it and/or modify it
44.9 + * under the terms of the GNU General Public License version 2 only, as
44.10 + * published by the Free Software Foundation. Sun designates this
44.11 + * particular file as subject to the "Classpath" exception as provided
44.12 + * by Sun in the LICENSE file that accompanied this code.
44.13 + *
44.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
44.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
44.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
44.17 + * version 2 for more details (a copy is included in the LICENSE file that
44.18 + * accompanied this code).
44.19 + *
44.20 + * You should have received a copy of the GNU General Public License version
44.21 + * 2 along with this work; if not, write to the Free Software Foundation,
44.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
44.23 + *
44.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
44.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
44.26 + * have any questions.
44.27 + */
44.28 +
44.29 +package javax.management.event;
44.30 +
44.31 +import com.sun.jmx.mbeanserver.Util;
44.32 +import java.io.IOException;
44.33 +import javax.management.InstanceNotFoundException;
44.34 +import javax.management.ListenerNotFoundException;
44.35 +import javax.management.MBeanException;
44.36 +import javax.management.NotificationFilter;
44.37 +import javax.management.ObjectName;
44.38 +import javax.management.remote.NotificationResult;
44.39 +
44.40 +/**
44.41 + * <p>This interface specifies necessary methods on the MBean server
44.42 + * side for a JMX remote client to manage its notification listeners as
44.43 + * if they are local.
44.44 + * Users do not usually work directly with this MBean; instead, the {@link
44.45 + * EventClient} class is designed to be used directly by the user.</p>
44.46 + *
44.47 + * <p>A default implementation of this interface can be added to an MBean
44.48 + * Server in one of several ways.</p>
44.49 + *
44.50 + * <ul>
44.51 + * <li><p>The most usual is to insert an {@link
44.52 + * javax.management.remote.MBeanServerForwarder MBeanServerForwarder} between
44.53 + * the {@linkplain javax.management.remote.JMXConnectorServer Connector Server}
44.54 + * and the MBean Server, that will intercept accesses to the Event Client
44.55 + * Delegate MBean and treat them as the real MBean would. This forwarder is
44.56 + * inserted by default with the standard RMI Connector Server, and can also
44.57 + * be created explicitly using {@link EventClientDelegate#newForwarder()}.
44.58 + *
44.59 + * <li><p>A variant on the above is to replace the MBean Server that is
44.60 + * used locally with a forwarder as described above. Since
44.61 + * {@code MBeanServerForwarder} extends {@code MBeanServer}, you can use
44.62 + * a forwarder anywhere you would have used the original MBean Server. The
44.63 + * code to do this replacement typically looks something like this:</p>
44.64 + *
44.65 + * <pre>
44.66 + * MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); // or whatever
44.67 + * MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
44.68 + * mbsf.setMBeanServer(mbs);
44.69 + * mbs = mbsf;
44.70 + * // now use mbs just as you did before, but it will have an EventClientDelegate
44.71 + * </pre>
44.72 + *
44.73 + * <li><p>The final way is to create an instance of {@link EventClientDelegate}
44.74 + * and register it in the MBean Server under the standard {@linkplain
44.75 + * #OBJECT_NAME name}:</p>
44.76 + *
44.77 + * <pre>
44.78 + * MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); // or whatever
44.79 + * EventClientDelegate ecd = EventClientDelegate.getEventClientDelegate(mbs);
44.80 + * mbs.registerMBean(ecd, EventClientDelegateMBean.OBJECT_NAME);
44.81 + * <pre>
44.82 + * </ul>
44.83 + *
44.84 + * @since JMX 2.0
44.85 + */
44.86 +public interface EventClientDelegateMBean {
44.87 + /**
44.88 + * The string representation of {@link #OBJECT_NAME}.
44.89 + */
44.90 + // This shouldn't really be necessary but an apparent javadoc bug
44.91 + // meant that the {@value} tags didn't work if this was a
44.92 + // field in EventClientDelegate, even a public field.
44.93 + public static final String OBJECT_NAME_STRING =
44.94 + "javax.management.event:type=EventClientDelegate";
44.95 +
44.96 + /**
44.97 + * The standard <code>ObjectName</code> used to register the default
44.98 + * <code>EventClientDelegateMBean</code>. The name is
44.99 + * <code>{@value #OBJECT_NAME_STRING}</code>.
44.100 + */
44.101 + public final static ObjectName OBJECT_NAME =
44.102 + Util.newObjectName(OBJECT_NAME_STRING);
44.103 +
44.104 + /**
44.105 + * A unique listener identifier specified for an EventClient.
44.106 + * Any notification associated with this id is intended for
44.107 + * the EventClient which receives the notification, rather than
44.108 + * a listener added using that EventClient.
44.109 + */
44.110 + public static final int EVENT_CLIENT_LISTENER_ID = -100;
44.111 +
44.112 + /**
44.113 + * Adds a new client to the <code>EventClientDelegateMBean</code> with
44.114 + * a user-specified
44.115 + * {@link EventForwarder} to forward notifications to the client. The
44.116 + * <code>EventForwarder</code> is created by calling
44.117 + * {@link javax.management.MBeanServer#instantiate(String, Object[],
44.118 + * String[])}.
44.119 + *
44.120 + * @param className The class name used to create an
44.121 + * {@code EventForwarder}.
44.122 + * @param params An array containing the parameters of the constructor to
44.123 + * be invoked.
44.124 + * @param sig An array containing the signature of the constructor to be
44.125 + * invoked
44.126 + * @return A client identifier.
44.127 + * @exception IOException Reserved for a remote call to throw on the client
44.128 + * side.
44.129 + * @exception MBeanException An exception thrown when creating the user
44.130 + * specified <code>EventForwarder</code>.
44.131 + */
44.132 + public String addClient(String className, Object[] params, String[] sig)
44.133 + throws IOException, MBeanException;
44.134 +
44.135 + /**
44.136 + * Adds a new client to the <code>EventClientDelegateMBean</code> with
44.137 + * a user-specified
44.138 + * {@link EventForwarder} to forward notifications to the client. The
44.139 + * <code>EventForwarder</code> is created by calling
44.140 + * {@link javax.management.MBeanServer#instantiate(String, ObjectName,
44.141 + * Object[], String[])}. A user-specified class loader is used to create
44.142 + * this <code>EventForwarder</code>.
44.143 + *
44.144 + * @param className The class name used to create an
44.145 + * {@code EventForwarder}.
44.146 + * @param classLoader An ObjectName registered as a
44.147 + * <code>ClassLoader</code> MBean.
44.148 + * @param params An array containing the parameters of the constructor to
44.149 + * be invoked.
44.150 + * @param sig An array containing the signature of the constructor to be
44.151 + * invoked
44.152 + * @return A client identifier.
44.153 + * @exception IOException Reserved for a remote call to throw on the client
44.154 + * side.
44.155 + * @exception MBeanException An exception thrown when creating the user
44.156 + * specified <code>EventForwarder</code>.
44.157 + */
44.158 + public String addClient(String className,
44.159 + ObjectName classLoader,
44.160 + Object[] params,
44.161 + String[] sig) throws IOException, MBeanException;
44.162 +
44.163 + /**
44.164 + * Removes an added client. Calling this method will remove all listeners
44.165 + * added with the client.
44.166 + *
44.167 + * @exception EventClientNotFoundException If the {@code clientId} is
44.168 + * not found.
44.169 + * @exception IOException Reserved for a remote call to throw on the client
44.170 + * side.
44.171 + */
44.172 + public void removeClient(String clientID)
44.173 + throws EventClientNotFoundException, IOException;
44.174 +
44.175 + /**
44.176 + * Returns the identifiers of listeners added or subscribed to with the
44.177 + * specified client identifier.
44.178 + * <P> If no listener is currently registered with the client, an empty
44.179 + * array is returned.
44.180 + * @param clientID The client identifier with which the listeners are
44.181 + * added or subscribed to.
44.182 + * @return An array of listener identifiers.
44.183 + * @exception EventClientNotFoundException If the {@code clientId} is
44.184 + * not found.
44.185 + * @exception IOException Reserved for a remote call to throw on the client
44.186 + * side.
44.187 + */
44.188 + public Integer[] getListenerIds(String clientID)
44.189 + throws EventClientNotFoundException, IOException;
44.190 +
44.191 + /**
44.192 + * Adds a listener to receive notifications from an MBean and returns
44.193 + * a non-negative integer as the identifier of the listener.
44.194 + * <P>This method is called by an {@link EventClient} to implement the
44.195 + * method {@link EventClient#addNotificationListener(ObjectName,
44.196 + * NotificationListener, NotificationFilter, Object)}.
44.197 + *
44.198 + * @param name The name of the MBean onto which the listener should be added.
44.199 + * @param filter The filter object. If {@code filter} is null,
44.200 + * no filtering will be performed before handling notifications.
44.201 + * @param clientId The client identifier with which the listener is added.
44.202 + * @return A listener identifier.
44.203 + * @throws EventClientNotFoundException Thrown if the {@code clientId} is
44.204 + * not found.
44.205 + * @throws InstanceNotFoundException Thrown if the MBean is not found.
44.206 + * @throws IOException Reserved for a remote call to throw on the client
44.207 + * side.
44.208 + */
44.209 + public Integer addListener(String clientId,
44.210 + ObjectName name,
44.211 + NotificationFilter filter)
44.212 + throws InstanceNotFoundException, EventClientNotFoundException,
44.213 + IOException;
44.214 +
44.215 +
44.216 + /**
44.217 + * <p>Subscribes a listener to receive notifications from an MBean or a
44.218 + * set of MBeans represented by an {@code ObjectName} pattern. (It is
44.219 + * not an error if no MBeans match the pattern at the time this method is
44.220 + * called.)</p>
44.221 + *
44.222 + * <p>Returns a non-negative integer as the identifier of the listener.</p>
44.223 + *
44.224 + * <p>This method is called by an {@link EventClient} to execute its
44.225 + * method {@link EventClient#subscribe(ObjectName, NotificationListener,
44.226 + * NotificationFilter, Object)}.</p>
44.227 + *
44.228 + * @param clientId The remote client's identifier.
44.229 + * @param name The name of an MBean or an {@code ObjectName} pattern
44.230 + * representing a set of MBeans to which the listener should listen.
44.231 + * @param filter The filter object. If {@code filter} is null, no
44.232 + * filtering will be performed before notifications are handled.
44.233 + *
44.234 + * @return A listener identifier.
44.235 + *
44.236 + * @throws IllegalArgumentException If the {@code name} or
44.237 + * {@code listener} is null.
44.238 + * @throws EventClientNotFoundException If the client ID is not found.
44.239 + * @throws IOException Reserved for a remote client to throw if
44.240 + * an I/O error occurs.
44.241 + *
44.242 + * @see EventConsumer#subscribe(ObjectName, NotificationListener,
44.243 + * NotificationFilter,Object)
44.244 + * @see #removeListenerOrSubscriber(String, Integer)
44.245 + */
44.246 + public Integer addSubscriber(String clientId, ObjectName name,
44.247 + NotificationFilter filter)
44.248 + throws EventClientNotFoundException, IOException;
44.249 +
44.250 + /**
44.251 + * Removes a listener, to stop receiving notifications.
44.252 + * <P> This method is called by an {@link EventClient} to execute its
44.253 + * methods {@link EventClient#removeNotificationListener(ObjectName,
44.254 + * NotificationListener, NotificationFilter, Object)},
44.255 + * {@link EventClient#removeNotificationListener(ObjectName,
44.256 + * NotificationListener)}, and {@link EventClient#unsubscribe}.
44.257 + *
44.258 + * @param clientId The client identifier with which the listener was added.
44.259 + * @param listenerId The listener identifier to be removed. This must be
44.260 + * an identifier returned by a previous {@link #addListener addListener}
44.261 + * or {@link #addSubscriber addSubscriber} call.
44.262 + *
44.263 + * @throws InstanceNotFoundException if the MBean on which the listener
44.264 + * was added no longer exists.
44.265 + * @throws ListenerNotFoundException if there is no listener with the
44.266 + * given {@code listenerId}.
44.267 + * @throws EventClientNotFoundException if the {@code clientId} is
44.268 + * not found.
44.269 + * @throws IOException Reserved for a remote call to throw on the client
44.270 + * side.
44.271 + */
44.272 + public void removeListenerOrSubscriber(String clientId, Integer listenerId)
44.273 + throws InstanceNotFoundException, ListenerNotFoundException,
44.274 + EventClientNotFoundException, IOException;
44.275 +
44.276 + /**
44.277 + * Called by a client to fetch notifications that are to be sent to its
44.278 + * listeners.
44.279 + *
44.280 + * @param clientId The client's identifier.
44.281 + * @param startSequenceNumber The first sequence number to
44.282 + * consider.
44.283 + * @param timeout The maximum waiting time.
44.284 + * @param maxNotifs The maximum number of notifications to return.
44.285 + *
44.286 + * @throws EventClientNotFoundException Thrown if the {@code clientId} is
44.287 + * not found.
44.288 + * @throws IllegalArgumentException if the client was {@linkplain
44.289 + * #addClient(String, Object[], String[]) added} with an {@link
44.290 + * EventForwarder} that is not a {@link FetchingEventForwarder}.
44.291 + * @throws IOException Reserved for a remote call to throw on the client
44.292 + * side.
44.293 + */
44.294 + public NotificationResult fetchNotifications(String clientId,
44.295 + long startSequenceNumber,
44.296 + int maxNotifs,
44.297 + long timeout)
44.298 + throws EventClientNotFoundException, IOException;
44.299 +
44.300 + /**
44.301 + * An {@code EventClient} calls this method to keep its {@code clientId}
44.302 + * alive in this MBean. The client will be removed if the lease times out.
44.303 + *
44.304 + * @param clientId The client's identifier.
44.305 + * @param timeout The time in milliseconds by which the lease is to be
44.306 + * extended. The value zero has no special meaning, so it will cause the
44.307 + * lease to time out immediately.
44.308 + *
44.309 + * @return The new lifetime of the lease in milliseconds. This may be
44.310 + * different from the requested time.
44.311 + *
44.312 + * @throws EventClientNotFoundException if the {@code clientId} is
44.313 + * not found.
44.314 + * @throws IOException reserved for a remote call to throw on the client
44.315 + * side.
44.316 + * @throws IllegalArgumentException if {@code clientId} is null or
44.317 + * {@code timeout} is negative.
44.318 + */
44.319 + public long lease(String clientId, long timeout)
44.320 + throws IOException, EventClientNotFoundException;
44.321 +}
45.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
45.2 +++ b/src/share/classes/javax/management/event/EventClientNotFoundException.java Thu Aug 21 09:55:18 2008 -0700
45.3 @@ -0,0 +1,79 @@
45.4 +/*
45.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
45.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
45.7 + *
45.8 + * This code is free software; you can redistribute it and/or modify it
45.9 + * under the terms of the GNU General Public License version 2 only, as
45.10 + * published by the Free Software Foundation. Sun designates this
45.11 + * particular file as subject to the "Classpath" exception as provided
45.12 + * by Sun in the LICENSE file that accompanied this code.
45.13 + *
45.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
45.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
45.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
45.17 + * version 2 for more details (a copy is included in the LICENSE file that
45.18 + * accompanied this code).
45.19 + *
45.20 + * You should have received a copy of the GNU General Public License version
45.21 + * 2 along with this work; if not, write to the Free Software Foundation,
45.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
45.23 + *
45.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
45.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
45.26 + * have any questions.
45.27 + */
45.28 +
45.29 +package javax.management.event;
45.30 +
45.31 +import javax.management.JMException;
45.32 +
45.33 +/**
45.34 + * Thrown if an event client identifier is unknown.
45.35 + */
45.36 +public class EventClientNotFoundException extends JMException {
45.37 +
45.38 + /* Serial version */
45.39 + private static final long serialVersionUID = -3910667345840643089L;
45.40 +
45.41 + /**
45.42 + *Constructs a {@code ClientNotFoundException} without a detail message.
45.43 + */
45.44 + public EventClientNotFoundException() {
45.45 + super();
45.46 + }
45.47 +
45.48 + /**
45.49 + * Constructs a {@code ClientNotFoundException} with the specified detail message.
45.50 + * @param message The message.
45.51 + */
45.52 + public EventClientNotFoundException(String message) {
45.53 + super(message);
45.54 + }
45.55 +
45.56 + /**
45.57 + * Constructs a {@code ClientNotFoundException} with the specified detail message
45.58 + * and cause.
45.59 + *
45.60 + * @param message The message.
45.61 + * @param cause The cause (which is saved for later retrieval by the
45.62 + * {@code Throwable.getCause()} method). A null value is permitted, and indicates
45.63 + * that the cause is non-existent or unknown.
45.64 + */
45.65 + public EventClientNotFoundException(String message, Throwable cause) {
45.66 + super(message);
45.67 +
45.68 + initCause(cause);
45.69 + }
45.70 +
45.71 + /**
45.72 + * Constructs a new exception with the specified cause.
45.73 + * @param cause The cause (which is saved for later retrieval by the
45.74 + * {@code Throwable.getCause()} method). A null value is permitted, and indicates
45.75 + * that the cause is non-existent or unknown.
45.76 + */
45.77 + public EventClientNotFoundException(Throwable cause) {
45.78 + super();
45.79 +
45.80 + initCause(cause);
45.81 + }
45.82 +}
46.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
46.2 +++ b/src/share/classes/javax/management/event/EventConsumer.java Thu Aug 21 09:55:18 2008 -0700
46.3 @@ -0,0 +1,98 @@
46.4 +/*
46.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
46.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
46.7 + *
46.8 + * This code is free software; you can redistribute it and/or modify it
46.9 + * under the terms of the GNU General Public License version 2 only, as
46.10 + * published by the Free Software Foundation. Sun designates this
46.11 + * particular file as subject to the "Classpath" exception as provided
46.12 + * by Sun in the LICENSE file that accompanied this code.
46.13 + *
46.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
46.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
46.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
46.17 + * version 2 for more details (a copy is included in the LICENSE file that
46.18 + * accompanied this code).
46.19 + *
46.20 + * You should have received a copy of the GNU General Public License version
46.21 + * 2 along with this work; if not, write to the Free Software Foundation,
46.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
46.23 + *
46.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
46.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
46.26 + * have any questions.
46.27 + */
46.28 +
46.29 +package javax.management.event;
46.30 +
46.31 +import java.io.IOException;
46.32 +import javax.management.ListenerNotFoundException;
46.33 +import javax.management.NotificationFilter;
46.34 +import javax.management.NotificationListener;
46.35 +import javax.management.ObjectName;
46.36 +
46.37 +/**
46.38 + * This interface specifies methods to subscribe a listener to receive events
46.39 + * from an MBean or a set of MBeans. The MBeans can already be registered in
46.40 + * an MBean server, or they can be pending registration, or they can be MBeans
46.41 + * that will never be registered, or they can be MBeans that will be registered
46.42 + * then unregistered.
46.43 + * @since JMX 2.0
46.44 + */
46.45 +public interface EventConsumer {
46.46 + /**
46.47 + * <p>Subscribes a listener to receive events from an MBean or a set
46.48 + * of MBeans represented by an {@code ObjectName} pattern.</p>
46.49 + *
46.50 + * <P> An event emitted by an MBean is forwarded to every listener that was
46.51 + * subscribed with the name of that MBean, or with a pattern that matches
46.52 + * that name.</p>
46.53 + *
46.54 + * @param name The name of an MBean or an {@code ObjectName} pattern
46.55 + * representing a set of MBeans to which the listener should listen.
46.56 + * @param listener The listener object that will handle the
46.57 + * notifications emitted by the MBeans.
46.58 + * @param filter The filter object. If {@code filter} is null, no
46.59 + * filtering will be performed before notification handling.
46.60 + * @param handback The context to be sent to the listener when a
46.61 + * notification is emitted.
46.62 + *
46.63 + * @throws IllegalArgumentException If the {@code name} or
46.64 + * {@code listener} is null.
46.65 + * @throws IOException for a remote client, thrown if
46.66 + * an I/O error occurs.
46.67 + * @see #unsubscribe(ObjectName, NotificationListener)
46.68 + */
46.69 + public void subscribe(ObjectName name,
46.70 + NotificationListener listener,
46.71 + NotificationFilter filter,
46.72 + Object handback)
46.73 + throws IOException;
46.74 +
46.75 + /**
46.76 + * <p>Unsubscribes a listener which is listening to an MBean or a set of
46.77 + * MBeans represented by an {@code ObjectName} pattern.</p>
46.78 + *
46.79 + * <p>The listener to be removed must have been added by the {@link
46.80 + * #subscribe subscribe} method with the given {@code name}. If the {@code
46.81 + * name} is a pattern, then the {@code subscribe} must have used the same
46.82 + * pattern. If the same listener has been subscribed more than once to the
46.83 + * {@code name}, perhaps with different filters or handbacks, then all such
46.84 + * listeners are removed.</p>
46.85 + *
46.86 + * @param name The name of the MBean or an {@code ObjectName} pattern
46.87 + * representing a set of MBeans to which the listener was subscribed.
46.88 + * @param listener A listener that was previously subscribed to the
46.89 + * MBean(s).
46.90 + *
46.91 + * @throws ListenerNotFoundException The given {@code listener} was not
46.92 + * subscribed to the given {@code name}.
46.93 + * @throws IOException for a remote client, thrown if
46.94 + * an I/O error occurs.
46.95 + *
46.96 + * @see #subscribe
46.97 + */
46.98 + public void unsubscribe(ObjectName name,
46.99 + NotificationListener listener)
46.100 + throws ListenerNotFoundException, IOException;
46.101 +}
47.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
47.2 +++ b/src/share/classes/javax/management/event/EventForwarder.java Thu Aug 21 09:55:18 2008 -0700
47.3 @@ -0,0 +1,63 @@
47.4 +/*
47.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
47.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
47.7 + *
47.8 + * This code is free software; you can redistribute it and/or modify it
47.9 + * under the terms of the GNU General Public License version 2 only, as
47.10 + * published by the Free Software Foundation. Sun designates this
47.11 + * particular file as subject to the "Classpath" exception as provided
47.12 + * by Sun in the LICENSE file that accompanied this code.
47.13 + *
47.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
47.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
47.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
47.17 + * version 2 for more details (a copy is included in the LICENSE file that
47.18 + * accompanied this code).
47.19 + *
47.20 + * You should have received a copy of the GNU General Public License version
47.21 + * 2 along with this work; if not, write to the Free Software Foundation,
47.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
47.23 + *
47.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
47.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
47.26 + * have any questions.
47.27 + */
47.28 +
47.29 +package javax.management.event;
47.30 +
47.31 +import java.io.IOException;
47.32 +import javax.management.Notification;
47.33 +
47.34 +/**
47.35 + * This interface can be used to specify a custom forwarding mechanism for
47.36 + * {@code EventClientDelegateMBean} to forward events to the client.
47.37 + *
47.38 + * @see <a href="package-summary.html#transports">Custom notification
47.39 + * transports</a>
47.40 + */
47.41 +public interface EventForwarder {
47.42 + /**
47.43 + * Forwards a notification.
47.44 + * @param n The notification to be forwarded to a remote listener.
47.45 + * @param listenerId The identifier of the listener to receive the notification.
47.46 + * @throws IOException If it is closed or an I/O error occurs.
47.47 + */
47.48 + public void forward(Notification n, Integer listenerId)
47.49 + throws IOException;
47.50 +
47.51 + /**
47.52 + * Informs the {@code EventForwarder} to shut down.
47.53 + * <p> After this method is called, any call to the method
47.54 + * {@link #forward forward(Notification, Integer)} may get an {@code IOException}.
47.55 + * @throws IOException If an I/O error occurs.
47.56 + */
47.57 + public void close() throws IOException;
47.58 +
47.59 + /**
47.60 + * Sets an event client identifier created by {@link EventClientDelegateMBean}.
47.61 + * <P> This method will be called just after this {@code EventForwarder}
47.62 + * is constructed and before calling the {@code forward} method to forward any
47.63 + * notifications.
47.64 + */
47.65 + public void setClientId(String clientId) throws IOException;
47.66 +}
48.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
48.2 +++ b/src/share/classes/javax/management/event/EventReceiver.java Thu Aug 21 09:55:18 2008 -0700
48.3 @@ -0,0 +1,77 @@
48.4 +/*
48.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
48.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
48.7 + *
48.8 + * This code is free software; you can redistribute it and/or modify it
48.9 + * under the terms of the GNU General Public License version 2 only, as
48.10 + * published by the Free Software Foundation. Sun designates this
48.11 + * particular file as subject to the "Classpath" exception as provided
48.12 + * by Sun in the LICENSE file that accompanied this code.
48.13 + *
48.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
48.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
48.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
48.17 + * version 2 for more details (a copy is included in the LICENSE file that
48.18 + * accompanied this code).
48.19 + *
48.20 + * You should have received a copy of the GNU General Public License version
48.21 + * 2 along with this work; if not, write to the Free Software Foundation,
48.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
48.23 + *
48.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
48.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
48.26 + * have any questions.
48.27 + */
48.28 +
48.29 +package javax.management.event;
48.30 +
48.31 +import javax.management.remote.NotificationResult;
48.32 +
48.33 +/**
48.34 + * An object implementing this interface is passed by an {@link EventClient}
48.35 + * to its {@link EventRelay}, to allow the {@code EventRelay} to communicate
48.36 + * received notifications to the {@code EventClient}.
48.37 + *
48.38 + * @see <a href="package-summary.html#transports">Custom notification
48.39 + * transports</a>
48.40 + */
48.41 +public interface EventReceiver {
48.42 +
48.43 + /**
48.44 + * This method is implemented by {@code EventClient} as a callback to
48.45 + * receive notifications from {@code EventRelay}.
48.46 + * <P>The notifications are included in an object specified by the class
48.47 + * {@link NotificationResult}. In
48.48 + * addition to a set of notifications, the class object also contains two values:
48.49 + * {@code earliestSequenceNumber} and {@code nextSequenceNumber}.
48.50 + * These two values determine whether any notifications have been lost.
48.51 + * The {@code nextSequenceNumber} value of the last time is compared
48.52 + * to the received value {@code earliestSequenceNumber}. If the
48.53 + * received {@code earliesSequenceNumber} is greater, than the difference
48.54 + * signifies the number of lost notifications. A sender should
48.55 + * ensure the sequence of notifications sent, meaning that the value
48.56 + * {@code earliestSequenceNumber} of the next return should be always equal to
48.57 + * or greater than the value {@code nextSequenceNumber} of the last return.
48.58 + *
48.59 + * @param nr the received notifications and sequence numbers.
48.60 + */
48.61 + public void receive(NotificationResult nr);
48.62 +
48.63 + /**
48.64 + * Allows the {@link EventRelay} to report when it receives an unexpected
48.65 + * exception, which may be fatal and which may make it stop receiving
48.66 + * notifications.
48.67 + *
48.68 + * @param t The unexpected exception received while {@link EventRelay} was running.
48.69 + */
48.70 + public void failed(Throwable t);
48.71 +
48.72 + /**
48.73 + * Allows the {@link EventRelay} to report when it receives an unexpected
48.74 + * exception that is not fatal. For example, a notification received is not
48.75 + * serializable or its class is not found.
48.76 + *
48.77 + * @param e The unexpected exception received while notifications are being received.
48.78 + */
48.79 + public void nonFatal(Exception e);
48.80 +}
49.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
49.2 +++ b/src/share/classes/javax/management/event/EventRelay.java Thu Aug 21 09:55:18 2008 -0700
49.3 @@ -0,0 +1,80 @@
49.4 +/*
49.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
49.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
49.7 + *
49.8 + * This code is free software; you can redistribute it and/or modify it
49.9 + * under the terms of the GNU General Public License version 2 only, as
49.10 + * published by the Free Software Foundation. Sun designates this
49.11 + * particular file as subject to the "Classpath" exception as provided
49.12 + * by Sun in the LICENSE file that accompanied this code.
49.13 + *
49.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
49.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
49.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
49.17 + * version 2 for more details (a copy is included in the LICENSE file that
49.18 + * accompanied this code).
49.19 + *
49.20 + * You should have received a copy of the GNU General Public License version
49.21 + * 2 along with this work; if not, write to the Free Software Foundation,
49.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
49.23 + *
49.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
49.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
49.26 + * have any questions.
49.27 + */
49.28 +
49.29 +package javax.management.event;
49.30 +
49.31 +import java.io.IOException;
49.32 +import java.util.concurrent.Executors; // for javadoc
49.33 +import java.util.concurrent.ScheduledFuture;
49.34 +
49.35 +/**
49.36 + * This interface is used to specify a way to receive
49.37 + * notifications from a remote MBean server and then to forward the notifications
49.38 + * to an {@link EventClient}.
49.39 + *
49.40 + * @see <a href="package-summary.html#transports">Custom notification
49.41 + * transports</a>
49.42 + */
49.43 +public interface EventRelay {
49.44 + /**
49.45 + * Returns an identifier that is used by this {@code EventRelay} to identify
49.46 + * the client when communicating with the {@link EventClientDelegateMBean}.
49.47 + * <P> This identifier is obtained by calling
49.48 + * {@link EventClientDelegateMBean#addClient(String, Object[], String[])
49.49 + * EventClientDelegateMBean.addClient}.
49.50 + * <P> It is the {@code EventRelay} that calls {@code EventClientDelegateMBean} to obtain
49.51 + * the client identifier because it is the {@code EventRelay} that decides
49.52 + * how to get notifications from the {@code EventClientDelegateMBean},
49.53 + * by creating the appropriate {@link EventForwarder}.
49.54 + *
49.55 + * @return A client identifier.
49.56 + * @throws IOException If an I/O error occurs when communicating with
49.57 + * the {@code EventClientDelegateMBean}.
49.58 + */
49.59 + public String getClientId() throws IOException;
49.60 +
49.61 + /**
49.62 + * This method is called by {@link EventClient} to register a callback
49.63 + * to receive notifications from an {@link EventClientDelegateMBean} object.
49.64 + * A {@code null} value is allowed, which means that the {@code EventClient} suspends
49.65 + * reception of notifications, so that the {@code EventRelay} can decide to stop receiving
49.66 + * notifications from its {@code EventForwarder}.
49.67 + *
49.68 + * @param eventReceiver An {@link EventClient} callback to receive
49.69 + * events.
49.70 + */
49.71 + public void setEventReceiver(EventReceiver eventReceiver);
49.72 +
49.73 + /**
49.74 + * Stops receiving and forwarding notifications and performs any necessary
49.75 + * cleanup. After calling this method, the {@link EventClient} will never
49.76 + * call any other methods of this object.
49.77 + *
49.78 + * @throws IOException If an I/O exception appears.
49.79 + *
49.80 + * @see EventClient#close
49.81 + */
49.82 + public void stop() throws IOException;
49.83 +}
50.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
50.2 +++ b/src/share/classes/javax/management/event/EventSubscriber.java Thu Aug 21 09:55:18 2008 -0700
50.3 @@ -0,0 +1,381 @@
50.4 +/*
50.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
50.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
50.7 + *
50.8 + * This code is free software; you can redistribute it and/or modify it
50.9 + * under the terms of the GNU General Public License version 2 only, as
50.10 + * published by the Free Software Foundation. Sun designates this
50.11 + * particular file as subject to the "Classpath" exception as provided
50.12 + * by Sun in the LICENSE file that accompanied this code.
50.13 + *
50.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
50.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
50.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
50.17 + * version 2 for more details (a copy is included in the LICENSE file that
50.18 + * accompanied this code).
50.19 + *
50.20 + * You should have received a copy of the GNU General Public License version
50.21 + * 2 along with this work; if not, write to the Free Software Foundation,
50.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
50.23 + *
50.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
50.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
50.26 + * have any questions.
50.27 + */
50.28 +
50.29 +package javax.management.event;
50.30 +
50.31 +import com.sun.jmx.remote.util.ClassLogger;
50.32 +import java.io.IOException;
50.33 +import java.lang.ref.WeakReference;
50.34 +import java.lang.reflect.Method;
50.35 +import java.security.AccessController;
50.36 +import java.security.PrivilegedActionException;
50.37 +import java.security.PrivilegedExceptionAction;
50.38 +import java.util.ArrayList;
50.39 +import java.util.Collections;
50.40 +import java.util.HashMap;
50.41 +import java.util.List;
50.42 +import java.util.Map;
50.43 +import java.util.Set;
50.44 +import java.util.WeakHashMap;
50.45 +import javax.management.InstanceNotFoundException;
50.46 +import javax.management.ListenerNotFoundException;
50.47 +import javax.management.MBeanServer;
50.48 +import javax.management.MBeanServerConnection;
50.49 +import javax.management.MBeanServerDelegate;
50.50 +import javax.management.MBeanServerNotification;
50.51 +import javax.management.Notification;
50.52 +import javax.management.NotificationBroadcaster;
50.53 +import javax.management.NotificationFilter;
50.54 +import javax.management.NotificationListener;
50.55 +import javax.management.ObjectName;
50.56 +import javax.management.Query;
50.57 +import javax.management.QueryEval;
50.58 +import javax.management.QueryExp;
50.59 +
50.60 +/**
50.61 + * <p>An object that can be used to subscribe for notifications from all MBeans
50.62 + * in an MBeanServer that match a pattern. For example, to listen for
50.63 + * notifications from all MBeans in the MBeanServer {@code mbs} that match
50.64 + * {@code com.example:type=Controller,name=*} you could write:</p>
50.65 + *
50.66 + * <pre>
50.67 + * EventSubscriber subscriber = EventSubscriber.getEventSubscriber(mbs);
50.68 + * ObjectName pattern = new ObjectName("com.example:type=Controller,name=*");
50.69 + * NotificationListener myListener = ...;
50.70 + * NotificationFilter myFilter = null; // or whatever
50.71 + * Object handback = null; // or whatever
50.72 + * subscriber.subscribe(pattern, myListener, myFilter, myHandback);
50.73 + * </pre>
50.74 + */
50.75 +public class EventSubscriber implements EventConsumer {
50.76 + /**
50.77 + * Returns an {@code EventSubscriber} object to subscribe for notifications
50.78 + * from the given {@code MBeanServer}. Calling this method more
50.79 + * than once with the same parameter may or may not return the same object.
50.80 + *
50.81 + * @param mbs the {@code MBeanServer} containing MBeans to be subscribed to.
50.82 + * @return An {@code EventSubscriber} object.
50.83 + *
50.84 + * @throws NullPointerException if mbs is null.
50.85 + */
50.86 + public static EventSubscriber getEventSubscriber(MBeanServer mbs) {
50.87 + if (mbs == null)
50.88 + throw new NullPointerException("Null MBeanServer");
50.89 +
50.90 + EventSubscriber eventSubscriber = null;
50.91 + synchronized (subscriberMap) {
50.92 + final WeakReference<EventSubscriber> wrf = subscriberMap.get(mbs);
50.93 + eventSubscriber = (wrf == null) ? null : wrf.get();
50.94 +
50.95 + if (eventSubscriber == null) {
50.96 + eventSubscriber = new EventSubscriber(mbs);
50.97 +
50.98 + subscriberMap.put(mbs,
50.99 + new WeakReference<EventSubscriber>(eventSubscriber));
50.100 + }
50.101 + }
50.102 +
50.103 + return eventSubscriber;
50.104 + }
50.105 +
50.106 + private EventSubscriber(final MBeanServer mbs) {
50.107 + logger.trace("EventSubscriber", "create a new one");
50.108 + this.mbeanServer = mbs;
50.109 +
50.110 + Exception x = null;
50.111 + try {
50.112 + AccessController.doPrivileged(
50.113 + new PrivilegedExceptionAction<Void>() {
50.114 + public Void run() throws Exception {
50.115 + mbs.addNotificationListener(
50.116 + MBeanServerDelegate.DELEGATE_NAME,
50.117 + myMBeanServerListener, null, null);
50.118 + return null;
50.119 + }
50.120 + });
50.121 + } catch (PrivilegedActionException ex) {
50.122 + x = ex.getException();
50.123 + }
50.124 +
50.125 + // handle possible exceptions.
50.126 + //
50.127 + // Fail unless x is null or x is instance of InstanceNotFoundException
50.128 + // The logic here is that if the MBeanServerDelegate is not present,
50.129 + // we will assume that the connection will not emit any
50.130 + // MBeanServerNotifications.
50.131 + //
50.132 + if (x != null && !(x instanceof InstanceNotFoundException)) {
50.133 + if (x instanceof RuntimeException)
50.134 + throw (RuntimeException) x;
50.135 + throw new RuntimeException(
50.136 + "Can't add listener to MBean server delegate: " + x, x);
50.137 + }
50.138 + }
50.139 +
50.140 + public void subscribe(ObjectName name,
50.141 + NotificationListener listener,
50.142 + NotificationFilter filter,
50.143 + Object handback)
50.144 + throws IOException {
50.145 +
50.146 + if (logger.traceOn())
50.147 + logger.trace("subscribe", "" + name);
50.148 +
50.149 + if (name == null)
50.150 + throw new IllegalArgumentException("Null MBean name");
50.151 +
50.152 + if (listener == null)
50.153 + throw new IllegalArgumentException("Null listener");
50.154 +
50.155 + final ListenerInfo li = new ListenerInfo(listener, filter, handback);
50.156 + List<ListenerInfo> list;
50.157 +
50.158 + Map<ObjectName, List<ListenerInfo>> map;
50.159 + Set<ObjectName> names;
50.160 + if (name.isPattern()) {
50.161 + map = patternSubscriptionMap;
50.162 + names = mbeanServer.queryNames(name, notificationBroadcasterExp);
50.163 + } else {
50.164 + map = exactSubscriptionMap;
50.165 + names = Collections.singleton(name);
50.166 + }
50.167 +
50.168 + synchronized (map) {
50.169 + list = map.get(name);
50.170 + if (list == null) {
50.171 + list = new ArrayList<ListenerInfo>();
50.172 + map.put(name, list);
50.173 + }
50.174 + list.add(li);
50.175 + }
50.176 +
50.177 + for (ObjectName mbeanName : names) {
50.178 + try {
50.179 + mbeanServer.addNotificationListener(mbeanName,
50.180 + listener,
50.181 + filter,
50.182 + handback);
50.183 + } catch (Exception e) {
50.184 + logger.fine("subscribe", "addNotificationListener", e);
50.185 + }
50.186 + }
50.187 + }
50.188 +
50.189 + public void unsubscribe(ObjectName name,
50.190 + NotificationListener listener)
50.191 + throws ListenerNotFoundException, IOException {
50.192 +
50.193 + if (logger.traceOn())
50.194 + logger.trace("unsubscribe", "" + name);
50.195 +
50.196 + if (name == null)
50.197 + throw new IllegalArgumentException("Null MBean name");
50.198 +
50.199 + if (listener == null)
50.200 + throw new ListenerNotFoundException();
50.201 +
50.202 + Map<ObjectName, List<ListenerInfo>> map;
50.203 + Set<ObjectName> names;
50.204 +
50.205 + if (name.isPattern()) {
50.206 + map = patternSubscriptionMap;
50.207 + names = mbeanServer.queryNames(name, notificationBroadcasterExp);
50.208 + } else {
50.209 + map = exactSubscriptionMap;
50.210 + names = Collections.singleton(name);
50.211 + }
50.212 +
50.213 + final ListenerInfo li = new ListenerInfo(listener, null, null);
50.214 + List<ListenerInfo> list;
50.215 + synchronized (map) {
50.216 + list = map.get(name);
50.217 + if (list == null || !list.remove(li))
50.218 + throw new ListenerNotFoundException();
50.219 +
50.220 + if (list.isEmpty())
50.221 + map.remove(name);
50.222 + }
50.223 +
50.224 + for (ObjectName mbeanName : names) {
50.225 + try {
50.226 + mbeanServer.removeNotificationListener(mbeanName, li.listener);
50.227 + } catch (Exception e) {
50.228 + logger.fine("unsubscribe", "removeNotificationListener", e);
50.229 + }
50.230 + }
50.231 + }
50.232 +
50.233 + // ---------------------------------
50.234 + // private stuff
50.235 + // ---------------------------------
50.236 + // used to receive MBeanServerNotification
50.237 + private NotificationListener myMBeanServerListener =
50.238 + new NotificationListener() {
50.239 + public void handleNotification(Notification n, Object hb) {
50.240 + if (!(n instanceof MBeanServerNotification) ||
50.241 + !MBeanServerNotification.
50.242 + REGISTRATION_NOTIFICATION.equals(n.getType())) {
50.243 + return;
50.244 + }
50.245 +
50.246 + final ObjectName name =
50.247 + ((MBeanServerNotification)n).getMBeanName();
50.248 + try {
50.249 + if (!mbeanServer.isInstanceOf(name,
50.250 + NotificationBroadcaster.class.getName())) {
50.251 + return;
50.252 + }
50.253 + } catch (Exception e) {
50.254 + // The only documented exception is InstanceNotFoundException,
50.255 + // which could conceivably happen if the MBean is unregistered
50.256 + // immediately after being registered.
50.257 + logger.fine("myMBeanServerListener.handleNotification",
50.258 + "isInstanceOf", e);
50.259 + return;
50.260 + }
50.261 +
50.262 + final List<ListenerInfo> listeners = new ArrayList<ListenerInfo>();
50.263 +
50.264 + // If there are subscribers for the exact name that has just arrived
50.265 + // then add their listeners to the list.
50.266 + synchronized (exactSubscriptionMap) {
50.267 + List<ListenerInfo> exactListeners = exactSubscriptionMap.get(name);
50.268 + if (exactListeners != null)
50.269 + listeners.addAll(exactListeners);
50.270 + }
50.271 +
50.272 + // For every subscription pattern that matches the new name,
50.273 + // add all the listeners for that pattern to "listeners".
50.274 + synchronized (patternSubscriptionMap) {
50.275 + for (ObjectName on : patternSubscriptionMap.keySet()) {
50.276 + if (on.apply(name)) {
50.277 + listeners.addAll(patternSubscriptionMap.get(on));
50.278 + }
50.279 + }
50.280 + }
50.281 +
50.282 + // Add all the listeners just found to the new MBean.
50.283 + for (ListenerInfo li : listeners) {
50.284 + try {
50.285 + mbeanServer.addNotificationListener(
50.286 + name,
50.287 + li.listener,
50.288 + li.filter,
50.289 + li.handback);
50.290 + } catch (Exception e) {
50.291 + logger.fine("myMBeanServerListener.handleNotification",
50.292 + "addNotificationListener", e);
50.293 + }
50.294 + }
50.295 + }
50.296 + };
50.297 +
50.298 + private static class ListenerInfo {
50.299 + public final NotificationListener listener;
50.300 + public final NotificationFilter filter;
50.301 + public final Object handback;
50.302 +
50.303 + public ListenerInfo(NotificationListener listener,
50.304 + NotificationFilter filter,
50.305 + Object handback) {
50.306 +
50.307 + if (listener == null)
50.308 + throw new IllegalArgumentException("Null listener");
50.309 +
50.310 + this.listener = listener;
50.311 + this.filter = filter;
50.312 + this.handback = handback;
50.313 + }
50.314 +
50.315 + /* Two ListenerInfo instances are equal if they have the same
50.316 + * NotificationListener. This means that we can use List.remove
50.317 + * to implement the two-argument removeNotificationListener.
50.318 + */
50.319 + @Override
50.320 + public boolean equals(Object o) {
50.321 + if (o == this)
50.322 + return true;
50.323 +
50.324 + if (!(o instanceof ListenerInfo))
50.325 + return false;
50.326 +
50.327 + return listener.equals(((ListenerInfo)o).listener);
50.328 + }
50.329 +
50.330 + @Override
50.331 + public int hashCode() {
50.332 + return listener.hashCode();
50.333 + }
50.334 + }
50.335 +
50.336 + // ---------------------------------
50.337 + // private methods
50.338 + // ---------------------------------
50.339 + // ---------------------------------
50.340 + // private variables
50.341 + // ---------------------------------
50.342 + private final MBeanServer mbeanServer;
50.343 +
50.344 + private final Map<ObjectName, List<ListenerInfo>> exactSubscriptionMap =
50.345 + new HashMap<ObjectName, List<ListenerInfo>>();
50.346 + private final Map<ObjectName, List<ListenerInfo>> patternSubscriptionMap =
50.347 + new HashMap<ObjectName, List<ListenerInfo>>();
50.348 +
50.349 +
50.350 +
50.351 + // trace issues
50.352 + private static final ClassLogger logger =
50.353 + new ClassLogger("javax.management.event", "EventSubscriber");
50.354 +
50.355 + // Compatibility code, so we can run on Tiger:
50.356 + private static final QueryExp notificationBroadcasterExp;
50.357 + static {
50.358 + QueryExp broadcasterExp;
50.359 + try {
50.360 + final Method m = Query.class.getMethod("isInstanceOf",
50.361 + new Class[] {String.class});
50.362 + broadcasterExp = (QueryExp)m.invoke(Query.class,
50.363 + new Object[] {NotificationBroadcaster.class.getName()});
50.364 + } catch (Exception e) {
50.365 + broadcasterExp = new BroadcasterQueryExp();
50.366 + }
50.367 + notificationBroadcasterExp = broadcasterExp;
50.368 + }
50.369 + private static class BroadcasterQueryExp extends QueryEval implements QueryExp {
50.370 + private static final long serialVersionUID = 1234L;
50.371 + public boolean apply(ObjectName name) {
50.372 + try {
50.373 + return getMBeanServer().isInstanceOf(
50.374 + name, NotificationBroadcaster.class.getName());
50.375 + } catch (Exception e) {
50.376 + return false;
50.377 + }
50.378 + }
50.379 + }
50.380 +
50.381 + private static final
50.382 + Map<MBeanServerConnection, WeakReference<EventSubscriber>> subscriberMap =
50.383 + new WeakHashMap<MBeanServerConnection, WeakReference<EventSubscriber>>();
50.384 +}
51.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
51.2 +++ b/src/share/classes/javax/management/event/FetchingEventForwarder.java Thu Aug 21 09:55:18 2008 -0700
51.3 @@ -0,0 +1,151 @@
51.4 +/*
51.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
51.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
51.7 + *
51.8 + * This code is free software; you can redistribute it and/or modify it
51.9 + * under the terms of the GNU General Public License version 2 only, as
51.10 + * published by the Free Software Foundation. Sun designates this
51.11 + * particular file as subject to the "Classpath" exception as provided
51.12 + * by Sun in the LICENSE file that accompanied this code.
51.13 + *
51.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
51.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
51.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
51.17 + * version 2 for more details (a copy is included in the LICENSE file that
51.18 + * accompanied this code).
51.19 + *
51.20 + * You should have received a copy of the GNU General Public License version
51.21 + * 2 along with this work; if not, write to the Free Software Foundation,
51.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
51.23 + *
51.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
51.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
51.26 + * have any questions.
51.27 + */
51.28 +
51.29 +package javax.management.event;
51.30 +
51.31 +import com.sun.jmx.event.EventBuffer;
51.32 +import com.sun.jmx.remote.util.ClassLogger;
51.33 +import java.io.IOException;
51.34 +import java.util.List;
51.35 +import javax.management.Notification;
51.36 +import javax.management.remote.NotificationResult;
51.37 +import javax.management.remote.TargetedNotification;
51.38 +
51.39 +/**
51.40 + * This class is used by {@link FetchingEventRelay}. When
51.41 + * {@link FetchingEventRelay} calls {@link
51.42 + * EventClientDelegateMBean#addClient(String, Object[], String[])} to get a new
51.43 + * client identifier, it uses
51.44 + * this class name as the first argument to ask {@code EventClientDelegateMBean}
51.45 + * to create an object of this class.
51.46 + * Then {@code EventClientDelegateMBean} forwards client notifications
51.47 + * to this object.
51.48 + * When {@link FetchingEventRelay} calls
51.49 + * {@link EventClientDelegateMBean#fetchNotifications(String, long, int, long)}
51.50 + * to fetch notifications, the {@code EventClientDelegateMBean} will forward
51.51 + * the call to this object.
51.52 + */
51.53 +public class FetchingEventForwarder implements EventForwarder {
51.54 +
51.55 + /**
51.56 + * Construct a new {@code FetchingEventForwarder} with the given
51.57 + * buffer size.
51.58 + * @param bufferSize the size of the buffer that will store notifications
51.59 + * until they have been fetched and acknowledged by the client.
51.60 + */
51.61 + public FetchingEventForwarder(int bufferSize) {
51.62 + if (logger.traceOn()) {
51.63 + logger.trace("Constructor", "buffer size is "+bufferSize);
51.64 + }
51.65 +
51.66 + buffer = new EventBuffer(bufferSize);
51.67 + this.bufferSize = bufferSize;
51.68 + }
51.69 +
51.70 + /**
51.71 + * Called by an {@link EventClientDelegateMBean} to forward a user call
51.72 + * {@link EventClientDelegateMBean#fetchNotifications(String, long, int, long)}.
51.73 + * A call of this method is considered to acknowledge reception of all
51.74 + * notifications whose sequence numbers are less the
51.75 + * {@code startSequenceNumber}, so all these notifications can be deleted
51.76 + * from this object.
51.77 + *
51.78 + * @param startSequenceNumber The first sequence number to
51.79 + * consider.
51.80 + * @param timeout The maximum waiting time in milliseconds.
51.81 + * If no notifications have arrived after this period of time, the call
51.82 + * will return with an empty list of notifications.
51.83 + * @param maxNotifs The maximum number of notifications to return.
51.84 + */
51.85 + public NotificationResult fetchNotifications(long startSequenceNumber,
51.86 + int maxNotifs, long timeout) {
51.87 + if (logger.traceOn()) {
51.88 + logger.trace("fetchNotifications",
51.89 + startSequenceNumber+" "+
51.90 + maxNotifs+" "+
51.91 + timeout);
51.92 + }
51.93 +
51.94 + return buffer.fetchNotifications(startSequenceNumber,
51.95 + timeout,
51.96 + maxNotifs);
51.97 + }
51.98 +
51.99 + /**
51.100 + * {@inheritDoc}
51.101 + * In this implementation, the notification is stored in the local buffer
51.102 + * waiting for {@link #fetchNotifications fetchNotifications} to pick
51.103 + * it up.
51.104 + */
51.105 + public void forward(Notification n, Integer listenerId) throws IOException {
51.106 + if (logger.traceOn()) {
51.107 + logger.trace("forward", n+" "+listenerId);
51.108 + }
51.109 +
51.110 + buffer.add(new TargetedNotification(n, listenerId));
51.111 + }
51.112 +
51.113 + public void close() throws IOException {
51.114 + if (logger.traceOn()) {
51.115 + logger.trace("close", "");
51.116 + }
51.117 +
51.118 + buffer.close();
51.119 + }
51.120 +
51.121 + public void setClientId(String clientId) throws IOException {
51.122 + if (logger.traceOn()) {
51.123 + logger.trace("setClientId", clientId);
51.124 + }
51.125 + this.clientId = clientId;
51.126 + }
51.127 +
51.128 + /**
51.129 + * Sets a user specific list to save notifications in server side
51.130 + * before forwarding to an FetchingEventRelay in client side.
51.131 + * <P> This method should be called before any notification is
51.132 + * forwarded to this forwader.
51.133 + *
51.134 + * @param list a user specific list to save notifications
51.135 + */
51.136 + protected void setList(List<TargetedNotification> list) {
51.137 + if (logger.traceOn()) {
51.138 + logger.trace("setList", "");
51.139 + }
51.140 +
51.141 + if (clientId == null) {
51.142 + buffer = new EventBuffer(bufferSize, list);
51.143 + } else {
51.144 + throw new IllegalStateException();
51.145 + }
51.146 + }
51.147 +
51.148 + private EventBuffer buffer;
51.149 + private int bufferSize;
51.150 + private String clientId;
51.151 +
51.152 + private static final ClassLogger logger =
51.153 + new ClassLogger("javax.management.event", "FetchingEventForwarder");
51.154 +}
52.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
52.2 +++ b/src/share/classes/javax/management/event/FetchingEventRelay.java Thu Aug 21 09:55:18 2008 -0700
52.3 @@ -0,0 +1,389 @@
52.4 +/*
52.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
52.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
52.7 + *
52.8 + * This code is free software; you can redistribute it and/or modify it
52.9 + * under the terms of the GNU General Public License version 2 only, as
52.10 + * published by the Free Software Foundation. Sun designates this
52.11 + * particular file as subject to the "Classpath" exception as provided
52.12 + * by Sun in the LICENSE file that accompanied this code.
52.13 + *
52.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
52.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
52.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
52.17 + * version 2 for more details (a copy is included in the LICENSE file that
52.18 + * accompanied this code).
52.19 + *
52.20 + * You should have received a copy of the GNU General Public License version
52.21 + * 2 along with this work; if not, write to the Free Software Foundation,
52.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
52.23 + *
52.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
52.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
52.26 + * have any questions.
52.27 + */
52.28 +
52.29 +package javax.management.event;
52.30 +
52.31 +import com.sun.jmx.event.DaemonThreadFactory;
52.32 +import com.sun.jmx.event.RepeatedSingletonJob;
52.33 +import com.sun.jmx.remote.util.ClassLogger;
52.34 +import java.io.IOException;
52.35 +import java.io.NotSerializableException;
52.36 +import java.util.concurrent.Executor;
52.37 +import java.util.concurrent.Executors;
52.38 +import java.util.concurrent.RejectedExecutionException;
52.39 +import java.util.concurrent.ScheduledExecutorService;
52.40 +import java.util.concurrent.ScheduledFuture;
52.41 +import java.util.concurrent.ThreadFactory;
52.42 +import java.util.concurrent.TimeUnit;
52.43 +import javax.management.MBeanException;
52.44 +import javax.management.remote.NotificationResult;
52.45 +
52.46 +/**
52.47 + * This class is an implementation of the {@link EventRelay} interface. It calls
52.48 + * {@link EventClientDelegateMBean#fetchNotifications
52.49 + * fetchNotifications(String, long, int, long)} to get
52.50 + * notifications and then forwards them to an {@link EventReceiver} object.
52.51 + *
52.52 + * @since JMX 2.0
52.53 + */
52.54 +public class FetchingEventRelay implements EventRelay {
52.55 + /**
52.56 + * The default buffer size: {@value #DEFAULT_BUFFER_SIZE}.
52.57 + */
52.58 + public final static int DEFAULT_BUFFER_SIZE = 1000;
52.59 +
52.60 + /**
52.61 + * The default waiting timeout: {@value #DEFAULT_WAITING_TIMEOUT}
52.62 + * in millseconds when fetching notifications from
52.63 + * an {@code EventClientDelegateMBean}.
52.64 + */
52.65 + public final static long DEFAULT_WAITING_TIMEOUT = 60000;
52.66 +
52.67 + /**
52.68 + * The default maximum notifications to fetch every time:
52.69 + * {@value #DEFAULT_MAX_NOTIFICATIONS}.
52.70 + */
52.71 + public final static int DEFAULT_MAX_NOTIFICATIONS = DEFAULT_BUFFER_SIZE;
52.72 +
52.73 + /**
52.74 + * Constructs a default {@code FetchingEventRelay} object by using the default
52.75 + * configuration: {@code DEFAULT_BUFFER_SIZE}, {@code DEFAULT_WAITING_TIMEOUT}
52.76 + * {@code DEFAULT_MAX_NOTIFICATIONS}. A single thread is created
52.77 + * to do fetching.
52.78 + *
52.79 + * @param delegate The {@code EventClientDelegateMBean} to work with.
52.80 + * @throws IOException If failed to work with the {@code delegate}.
52.81 + * @throws MBeanException if unable to add a client to the remote
52.82 + * {@code EventClientDelegateMBean} (see {@link
52.83 + * EventClientDelegateMBean#addClient(String, Object[], String[])
52.84 + * EventClientDelegateMBean.addClient}).
52.85 + * @throws IllegalArgumentException If {@code delegate} is {@code null}.
52.86 + */
52.87 + public FetchingEventRelay(EventClientDelegateMBean delegate)
52.88 + throws IOException, MBeanException {
52.89 + this(delegate, null);
52.90 + }
52.91 +
52.92 + /**
52.93 + * Constructs a {@code FetchingEventRelay} object by using the default
52.94 + * configuration: {@code DEFAULT_BUFFER_SIZE}, {@code DEFAULT_WAITING_TIMEOUT}
52.95 + * {@code DEFAULT_MAX_NOTIFICATIONS}, with a user-specific executor to do
52.96 + * the fetching.
52.97 + *
52.98 + * @param delegate The {@code EventClientDelegateMBean} to work with.
52.99 + * @param executor Used to do the fetching. A new thread is created if
52.100 + * {@code null}.
52.101 + * @throws IOException If failed to work with the {@code delegate}.
52.102 + * @throws MBeanException if unable to add a client to the remote
52.103 + * {@code EventClientDelegateMBean} (see {@link
52.104 + * EventClientDelegateMBean#addClient(String, Object[], String[])
52.105 + * EventClientDelegateMBean.addClient}).
52.106 + * @throws IllegalArgumentException If {@code delegate} is {@code null}.
52.107 + */
52.108 + public FetchingEventRelay(EventClientDelegateMBean delegate,
52.109 + Executor executor) throws IOException, MBeanException {
52.110 + this(delegate,
52.111 + DEFAULT_BUFFER_SIZE,
52.112 + DEFAULT_WAITING_TIMEOUT,
52.113 + DEFAULT_MAX_NOTIFICATIONS,
52.114 + executor);
52.115 + }
52.116 +
52.117 + /**
52.118 + * Constructs a {@code FetchingEventRelay} object with user-specific
52.119 + * configuration and executor to fetch notifications via the
52.120 + * {@link EventClientDelegateMBean}.
52.121 + *
52.122 + * @param delegate The {@code EventClientDelegateMBean} to work with.
52.123 + * @param bufferSize The buffer size for saving notifications in
52.124 + * {@link EventClientDelegateMBean} before they are fetched.
52.125 + * @param timeout The waiting time in millseconds when fetching
52.126 + * notifications from an {@code EventClientDelegateMBean}.
52.127 + * @param maxNotifs The maximum notifications to fetch every time.
52.128 + * @param executor Used to do the fetching. A new thread is created if
52.129 + * {@code null}.
52.130 + * @throws IOException if failed to communicate with the {@code delegate}.
52.131 + * @throws MBeanException if unable to add a client to the remote
52.132 + * {@code EventClientDelegateMBean} (see {@link
52.133 + * EventClientDelegateMBean#addClient(String, Object[], String[])
52.134 + * EventClientDelegateMBean.addClient}).
52.135 + * @throws IllegalArgumentException If {@code delegate} is {@code null}.
52.136 + */
52.137 + public FetchingEventRelay(EventClientDelegateMBean delegate,
52.138 + int bufferSize,
52.139 + long timeout,
52.140 + int maxNotifs,
52.141 + Executor executor) throws IOException, MBeanException {
52.142 + this(delegate,
52.143 + bufferSize,
52.144 + timeout,
52.145 + maxNotifs,
52.146 + executor,
52.147 + FetchingEventForwarder.class.getName(),
52.148 + new Object[] {bufferSize},
52.149 + new String[] {int.class.getName()});
52.150 + }
52.151 +
52.152 + /**
52.153 + * Constructs a {@code FetchingEventRelay} object with user-specific
52.154 + * configuration and executor to fetch notifications via the
52.155 + * {@link EventClientDelegateMBean}.
52.156 + *
52.157 + * @param delegate The {@code EventClientDelegateMBean} to work with.
52.158 + * @param bufferSize The buffer size for saving notifications in
52.159 + * {@link EventClientDelegateMBean} before they are fetched.
52.160 + * @param timeout The waiting time in millseconds when fetching
52.161 + * notifications from an {@code EventClientDelegateMBean}.
52.162 + * @param maxNotifs The maximum notifications to fetch every time.
52.163 + * @param executor Used to do the fetching.
52.164 + * @param forwarderName the class name of a user specific EventForwarder
52.165 + * to create in server to forward notifications to this object. The class
52.166 + * should be a subclass of the class {@link FetchingEventForwarder}.
52.167 + * @param params the parameters passed to create {@code forwarderName}
52.168 + * @param sig the signature of the {@code params}
52.169 + * @throws IOException if failed to communicate with the {@code delegate}.
52.170 + * @throws MBeanException if unable to add a client to the remote
52.171 + * {@code EventClientDelegateMBean} (see {@link
52.172 + * EventClientDelegateMBean#addClient(String, Object[], String[])
52.173 + * EventClientDelegateMBean.addClient}).
52.174 + * @throws IllegalArgumentException if {@code bufferSize} or
52.175 + * {@code maxNotifs} is less than {@code 1}
52.176 + * @throws NullPointerException if {@code delegate} is {@code null}.
52.177 + */
52.178 + public FetchingEventRelay(EventClientDelegateMBean delegate,
52.179 + int bufferSize,
52.180 + long timeout,
52.181 + int maxNotifs,
52.182 + Executor executor,
52.183 + String forwarderName,
52.184 + Object[] params,
52.185 + String[] sig) throws IOException, MBeanException {
52.186 +
52.187 + if (logger.traceOn()) {
52.188 + logger.trace("FetchingEventRelay", "delegateMBean "+
52.189 + bufferSize+" "+
52.190 + timeout+" "+
52.191 + maxNotifs+" "+
52.192 + executor+" "+
52.193 + forwarderName+" ");
52.194 + }
52.195 +
52.196 + if(delegate == null) {
52.197 + throw new NullPointerException("Null EventClientDelegateMBean!");
52.198 + }
52.199 +
52.200 +
52.201 + if (bufferSize<=1) {
52.202 + throw new IllegalArgumentException(
52.203 + "The bufferSize cannot be less than 1, no meaning.");
52.204 + }
52.205 +
52.206 + if (maxNotifs<=1) {
52.207 + throw new IllegalArgumentException(
52.208 + "The maxNotifs cannot be less than 1, no meaning.");
52.209 + }
52.210 +
52.211 + clientId = delegate.addClient(
52.212 + forwarderName,
52.213 + params,
52.214 + sig);
52.215 +
52.216 + this.delegate = delegate;
52.217 + this.timeout = timeout;
52.218 + this.maxNotifs = maxNotifs;
52.219 +
52.220 + if (executor == null) {
52.221 + executor = Executors.newSingleThreadScheduledExecutor(
52.222 + daemonThreadFactory);
52.223 + }
52.224 + this.executor = executor;
52.225 + if (executor instanceof ScheduledExecutorService)
52.226 + leaseScheduler = (ScheduledExecutorService) executor;
52.227 + else {
52.228 + leaseScheduler = Executors.newSingleThreadScheduledExecutor(
52.229 + daemonThreadFactory);
52.230 + }
52.231 +
52.232 + startSequenceNumber = 0;
52.233 + fetchingJob = new MyJob();
52.234 + }
52.235 +
52.236 + public void setEventReceiver(EventReceiver eventReceiver) {
52.237 + if (logger.traceOn()) {
52.238 + logger.trace("setEventReceiver", ""+eventReceiver);
52.239 + }
52.240 +
52.241 + EventReceiver old = this.eventReceiver;
52.242 + synchronized(fetchingJob) {
52.243 + this.eventReceiver = eventReceiver;
52.244 + if (old == null && eventReceiver != null)
52.245 + fetchingJob.resume();
52.246 + }
52.247 + }
52.248 +
52.249 + public String getClientId() {
52.250 + return clientId;
52.251 + }
52.252 +
52.253 + public void stop() {
52.254 + if (logger.traceOn()) {
52.255 + logger.trace("stop", "");
52.256 + }
52.257 + synchronized(fetchingJob) {
52.258 + if (stopped) {
52.259 + return;
52.260 + }
52.261 +
52.262 + stopped = true;
52.263 + clientId = null;
52.264 + }
52.265 + }
52.266 +
52.267 + private class MyJob extends RepeatedSingletonJob {
52.268 + public MyJob() {
52.269 + super(executor);
52.270 + }
52.271 +
52.272 + public boolean isSuspended() {
52.273 + boolean b;
52.274 + synchronized(FetchingEventRelay.this) {
52.275 + b = stopped ||
52.276 + (eventReceiver == null) ||
52.277 + (clientId == null);
52.278 + }
52.279 +
52.280 + if (logger.traceOn()) {
52.281 + logger.trace("-MyJob-isSuspended", ""+b);
52.282 + }
52.283 + return b;
52.284 + }
52.285 +
52.286 + public void task() {
52.287 + logger.trace("MyJob-task", "");
52.288 + long fetchTimeout = timeout;
52.289 + NotificationResult nr = null;
52.290 + Throwable failedExcep = null;
52.291 + try {
52.292 + nr = delegate.fetchNotifications(
52.293 + clientId,
52.294 + startSequenceNumber,
52.295 + maxNotifs,
52.296 + fetchTimeout);
52.297 + } catch (Exception e) {
52.298 + if (isSerialOrClassNotFound(e)) {
52.299 + try {
52.300 + nr = fetchOne();
52.301 + } catch (Exception ee) {
52.302 + failedExcep = e;
52.303 + }
52.304 + } else {
52.305 + failedExcep = e;
52.306 + }
52.307 + }
52.308 +
52.309 + if (failedExcep != null &&
52.310 + !isSuspended()) {
52.311 + logger.fine("MyJob-task",
52.312 + "Failed to fetch notification, stopping...", failedExcep);
52.313 + try {
52.314 + eventReceiver.failed(failedExcep);
52.315 + } catch (Exception e) {
52.316 + logger.trace(
52.317 + "MyJob-task", "exception from eventReceiver.failed", e);
52.318 + }
52.319 +
52.320 + stop();
52.321 + } else if (nr != null) {
52.322 + try {
52.323 + eventReceiver.receive(nr);
52.324 + } catch (RuntimeException e) {
52.325 + logger.trace(
52.326 + "MyJob-task",
52.327 + "exception delivering notifs to EventClient", e);
52.328 + } finally {
52.329 + startSequenceNumber = nr.getNextSequenceNumber();
52.330 + }
52.331 + }
52.332 + }
52.333 + }
52.334 +
52.335 + private NotificationResult fetchOne() throws Exception {
52.336 + logger.trace("fetchOne", "");
52.337 +
52.338 + while (true) {
52.339 + try {
52.340 + // 1 notif to skip possible missing class
52.341 + return delegate.fetchNotifications(
52.342 + clientId,
52.343 + startSequenceNumber,
52.344 + 1,
52.345 + timeout);
52.346 + } catch (Exception e) {
52.347 + if (isSerialOrClassNotFound(e)) { // skip and continue
52.348 + if (logger.traceOn()) {
52.349 + logger.trace("fetchOne", "Ignore", e);
52.350 + }
52.351 + eventReceiver.nonFatal(e);
52.352 + startSequenceNumber++;
52.353 + } else {
52.354 + throw e;
52.355 + }
52.356 + }
52.357 + }
52.358 + }
52.359 +
52.360 + static boolean isSerialOrClassNotFound(Exception e) {
52.361 + Throwable cause = e.getCause();
52.362 +
52.363 + while (cause != null &&
52.364 + !(cause instanceof ClassNotFoundException) &&
52.365 + !(cause instanceof NotSerializableException)) {
52.366 + cause = cause.getCause();
52.367 + }
52.368 +
52.369 + return (cause instanceof ClassNotFoundException ||
52.370 + cause instanceof NotSerializableException);
52.371 + }
52.372 +
52.373 + private long startSequenceNumber = 0;
52.374 + private EventReceiver eventReceiver = null;
52.375 + private final EventClientDelegateMBean delegate;
52.376 + private String clientId;
52.377 + private boolean stopped = false;
52.378 + private volatile ScheduledFuture<?> leaseRenewalFuture;
52.379 +
52.380 + private final Executor executor;
52.381 + private final ScheduledExecutorService leaseScheduler;
52.382 + private final MyJob fetchingJob;
52.383 +
52.384 + private final long timeout;
52.385 + private final int maxNotifs;
52.386 +
52.387 + private static final ClassLogger logger =
52.388 + new ClassLogger("javax.management.event",
52.389 + "FetchingEventRelay");
52.390 + private static final ThreadFactory daemonThreadFactory =
52.391 + new DaemonThreadFactory("FetchingEventRelay-executor");
52.392 +}
53.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
53.2 +++ b/src/share/classes/javax/management/event/ListenerInfo.java Thu Aug 21 09:55:18 2008 -0700
53.3 @@ -0,0 +1,169 @@
53.4 +/*
53.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
53.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
53.7 + *
53.8 + * This code is free software; you can redistribute it and/or modify it
53.9 + * under the terms of the GNU General Public License version 2 only, as
53.10 + * published by the Free Software Foundation. Sun designates this
53.11 + * particular file as subject to the "Classpath" exception as provided
53.12 + * by Sun in the LICENSE file that accompanied this code.
53.13 + *
53.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
53.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
53.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
53.17 + * version 2 for more details (a copy is included in the LICENSE file that
53.18 + * accompanied this code).
53.19 + *
53.20 + * You should have received a copy of the GNU General Public License version
53.21 + * 2 along with this work; if not, write to the Free Software Foundation,
53.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
53.23 + *
53.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
53.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
53.26 + * have any questions.
53.27 + */
53.28 +
53.29 +package javax.management.event;
53.30 +
53.31 +import javax.management.NotificationFilter;
53.32 +import javax.management.NotificationListener;
53.33 +import javax.management.ObjectName;
53.34 +
53.35 +/**
53.36 + * This class specifies all the information required to register a user listener into
53.37 + * a remote MBean server. This class is not serializable because a user listener
53.38 + * is not serialized in order to be sent to the remote server.
53.39 + *
53.40 + * @since JMX 2.0
53.41 + */
53.42 +public class ListenerInfo {
53.43 +
53.44 + /**
53.45 + * Constructs a {@code ListenerInfo} object.
53.46 + *
53.47 + * @param name The name of the MBean to which the listener should
53.48 + * be added.
53.49 + * @param listener The listener object which will handle the
53.50 + * notifications emitted by the MBean.
53.51 + * @param filter The filter object. If the filter is null, no
53.52 + * filtering will be performed before notifications are handled.
53.53 + * @param handback The context to be sent to the listener when a
53.54 + * notification is emitted.
53.55 + * @param isSubscription If true, the listener is subscribed via
53.56 + * an {@code EventManager}. Otherwise it is added to a registered MBean.
53.57 + */
53.58 + public ListenerInfo(ObjectName name,
53.59 + NotificationListener listener,
53.60 + NotificationFilter filter,
53.61 + Object handback,
53.62 + boolean isSubscription) {
53.63 + this.name = name;
53.64 + this.listener = listener;
53.65 + this.filter = filter;
53.66 + this.handback = handback;
53.67 + this.isSubscription = isSubscription;
53.68 + }
53.69 +
53.70 + /**
53.71 + * Returns an MBean or an MBean pattern that the listener listens to.
53.72 + *
53.73 + * @return An MBean or an MBean pattern.
53.74 + */
53.75 + public ObjectName getObjectName() {
53.76 + return name;
53.77 + }
53.78 +
53.79 + /**
53.80 + * Returns the listener.
53.81 + *
53.82 + * @return The listener.
53.83 + */
53.84 + public NotificationListener getListener() {
53.85 + return listener;
53.86 + }
53.87 +
53.88 + /**
53.89 + * Returns the listener filter.
53.90 + *
53.91 + * @return The filter.
53.92 + */
53.93 + public NotificationFilter getFilter() {
53.94 + return filter;
53.95 + }
53.96 +
53.97 + /**
53.98 + * Returns the listener handback.
53.99 + *
53.100 + * @return The handback.
53.101 + */
53.102 + public Object getHandback() {
53.103 + return handback;
53.104 + }
53.105 +
53.106 + /**
53.107 + * Returns true if this is a subscription listener.
53.108 + *
53.109 + * @return True if this is a subscription listener.
53.110 + *
53.111 + * @see EventClient#addListeners
53.112 + */
53.113 + public boolean isSubscription() {
53.114 + return isSubscription;
53.115 + }
53.116 +
53.117 + /**
53.118 + * <p>Indicates whether some other object is "equal to" this one.
53.119 + * The return value is true if and only if {@code o} is an instance of
53.120 + * {@code ListenerInfo} and has equal values for all of its properties.</p>
53.121 + */
53.122 + @Override
53.123 + public boolean equals(Object o) {
53.124 + if (this == o) {
53.125 + return true;
53.126 + }
53.127 +
53.128 + if (!(o instanceof ListenerInfo)) {
53.129 + return false;
53.130 + }
53.131 +
53.132 + ListenerInfo li = (ListenerInfo)o;
53.133 +
53.134 + boolean ret = name.equals(li.name) &&
53.135 + (listener == li.listener) &&
53.136 + (isSubscription == li.isSubscription);
53.137 +
53.138 + if (filter != null) {
53.139 + ret &= filter.equals(li.filter);
53.140 + } else {
53.141 + ret &= (li.filter == null);
53.142 + }
53.143 +
53.144 + if (handback != null) {
53.145 + ret &= handback.equals(li.handback);
53.146 + } else {
53.147 + ret &= (li.handback == null);
53.148 + }
53.149 +
53.150 + return ret;
53.151 + }
53.152 +
53.153 + @Override
53.154 + public int hashCode() {
53.155 + return name.hashCode() + listener.hashCode();
53.156 + }
53.157 +
53.158 + @Override
53.159 + public String toString() {
53.160 + return name.toString() + "_" +
53.161 + listener + "_" +
53.162 + filter + "_" +
53.163 + handback + "_" +
53.164 + isSubscription;
53.165 + }
53.166 +
53.167 + private final ObjectName name;
53.168 + private final NotificationListener listener;
53.169 + private final NotificationFilter filter;
53.170 + private final Object handback;
53.171 + private final boolean isSubscription;
53.172 +}
54.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
54.2 +++ b/src/share/classes/javax/management/event/NotificationManager.java Thu Aug 21 09:55:18 2008 -0700
54.3 @@ -0,0 +1,136 @@
54.4 +/*
54.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
54.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
54.7 + *
54.8 + * This code is free software; you can redistribute it and/or modify it
54.9 + * under the terms of the GNU General Public License version 2 only, as
54.10 + * published by the Free Software Foundation. Sun designates this
54.11 + * particular file as subject to the "Classpath" exception as provided
54.12 + * by Sun in the LICENSE file that accompanied this code.
54.13 + *
54.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
54.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
54.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
54.17 + * version 2 for more details (a copy is included in the LICENSE file that
54.18 + * accompanied this code).
54.19 + *
54.20 + * You should have received a copy of the GNU General Public License version
54.21 + * 2 along with this work; if not, write to the Free Software Foundation,
54.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
54.23 + *
54.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
54.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
54.26 + * have any questions.
54.27 + */
54.28 +
54.29 +package javax.management.event;
54.30 +
54.31 +import java.io.IOException;
54.32 +import javax.management.InstanceNotFoundException;
54.33 +import javax.management.ListenerNotFoundException;
54.34 +import javax.management.NotificationFilter;
54.35 +import javax.management.NotificationListener;
54.36 +import javax.management.ObjectName;
54.37 +
54.38 +/**
54.39 + * This interface specifies methods to add and remove notification listeners
54.40 + * on named MBeans.
54.41 + */
54.42 +public interface NotificationManager {
54.43 + /**
54.44 + * <p>Adds a listener to a registered MBean.
54.45 + * Notifications emitted by the MBean will be forwarded
54.46 + * to the listener.
54.47 + *
54.48 + * @param name The name of the MBean on which the listener should
54.49 + * be added.
54.50 + * @param listener The listener object which will handle the
54.51 + * notifications emitted by the registered MBean.
54.52 + * @param filter The filter object. If filter is null, no
54.53 + * filtering will be performed before handling notifications.
54.54 + * @param handback The context to be sent to the listener when a
54.55 + * notification is emitted.
54.56 + *
54.57 + * @exception InstanceNotFoundException The MBean name provided
54.58 + * does not match any of the registered MBeans.
54.59 + * @exception IOException A communication problem occurred when
54.60 + * talking to the MBean server.
54.61 + *
54.62 + * @see #removeNotificationListener(ObjectName, NotificationListener)
54.63 + * @see #removeNotificationListener(ObjectName, NotificationListener,
54.64 + * NotificationFilter, Object)
54.65 + */
54.66 + public void addNotificationListener(ObjectName name,
54.67 + NotificationListener listener,
54.68 + NotificationFilter filter,
54.69 + Object handback)
54.70 + throws InstanceNotFoundException,
54.71 + IOException;
54.72 +
54.73 + /**
54.74 + * <p>Removes a listener from a registered MBean.</p>
54.75 + *
54.76 + * <P> If the listener is registered more than once, perhaps with
54.77 + * different filters or callbacks, this method will remove all
54.78 + * those registrations.
54.79 + *
54.80 + * @param name The name of the MBean on which the listener should
54.81 + * be removed.
54.82 + * @param listener The listener to be removed.
54.83 + *
54.84 + * @exception InstanceNotFoundException The MBean name provided
54.85 + * does not match any of the registered MBeans.
54.86 + * @exception ListenerNotFoundException The listener is not
54.87 + * registered in the MBean.
54.88 + * @exception IOException A communication problem occurred when
54.89 + * talking to the MBean server.
54.90 + *
54.91 + * @see #addNotificationListener(ObjectName, NotificationListener,
54.92 + * NotificationFilter, Object)
54.93 + */
54.94 + public void removeNotificationListener(ObjectName name,
54.95 + NotificationListener listener)
54.96 + throws InstanceNotFoundException,
54.97 + ListenerNotFoundException,
54.98 + IOException;
54.99 +
54.100 + /**
54.101 + * <p>Removes a listener from a registered MBean.</p>
54.102 + *
54.103 + * <p>The MBean must have a listener that exactly matches the
54.104 + * given <code>listener</code>, <code>filter</code>, and
54.105 + * <code>handback</code> parameters. If there is more than one
54.106 + * such listener, only one is removed.</p>
54.107 + *
54.108 + * <p>The <code>filter</code> and <code>handback</code> parameters
54.109 + * may be null if and only if they are null in a listener to be
54.110 + * removed.</p>
54.111 + *
54.112 + * @param name The name of the MBean on which the listener should
54.113 + * be removed.
54.114 + * @param listener The listener to be removed.
54.115 + * @param filter The filter that was specified when the listener
54.116 + * was added.
54.117 + * @param handback The handback that was specified when the
54.118 + * listener was added.
54.119 + *
54.120 + * @exception InstanceNotFoundException The MBean name provided
54.121 + * does not match any of the registered MBeans.
54.122 + * @exception ListenerNotFoundException The listener is not
54.123 + * registered in the MBean, or it is not registered with the given
54.124 + * filter and handback.
54.125 + * @exception IOException A communication problem occurred when
54.126 + * talking to the MBean server.
54.127 + *
54.128 + * @see #addNotificationListener(ObjectName, NotificationListener,
54.129 + * NotificationFilter, Object)
54.130 + *
54.131 + */
54.132 + public void removeNotificationListener(ObjectName name,
54.133 + NotificationListener listener,
54.134 + NotificationFilter filter,
54.135 + Object handback)
54.136 + throws InstanceNotFoundException,
54.137 + ListenerNotFoundException,
54.138 + IOException;
54.139 +}
55.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
55.2 +++ b/src/share/classes/javax/management/event/RMIPushEventForwarder.java Thu Aug 21 09:55:18 2008 -0700
55.3 @@ -0,0 +1,198 @@
55.4 +/*
55.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
55.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
55.7 + *
55.8 + * This code is free software; you can redistribute it and/or modify it
55.9 + * under the terms of the GNU General Public License version 2 only, as
55.10 + * published by the Free Software Foundation. Sun designates this
55.11 + * particular file as subject to the "Classpath" exception as provided
55.12 + * by Sun in the LICENSE file that accompanied this code.
55.13 + *
55.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
55.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
55.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
55.17 + * version 2 for more details (a copy is included in the LICENSE file that
55.18 + * accompanied this code).
55.19 + *
55.20 + * You should have received a copy of the GNU General Public License version
55.21 + * 2 along with this work; if not, write to the Free Software Foundation,
55.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
55.23 + *
55.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
55.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
55.26 + * have any questions.
55.27 + */
55.28 +
55.29 +package javax.management.event;
55.30 +
55.31 +import com.sun.jmx.event.DaemonThreadFactory;
55.32 +import com.sun.jmx.event.RepeatedSingletonJob;
55.33 +import com.sun.jmx.remote.util.ClassLogger;
55.34 +import java.rmi.RemoteException;
55.35 +import java.util.ArrayList;
55.36 +import java.util.List;
55.37 +import java.util.concurrent.ArrayBlockingQueue;
55.38 +import java.util.concurrent.BlockingQueue;
55.39 +import java.util.concurrent.ExecutorService;
55.40 +import java.util.concurrent.Executors;
55.41 +import javax.management.Notification;
55.42 +import javax.management.remote.NotificationResult;
55.43 +import javax.management.remote.TargetedNotification;
55.44 +
55.45 +
55.46 +/**
55.47 + * This class is used by {@link RMIPushEventRelay}. When
55.48 + * {@link RMIPushEventRelay} calls {@link
55.49 + * EventClientDelegateMBean#addClient(String, Object[], String[])} to get a new
55.50 + * client identifier, it uses this class name as the
55.51 + * first argument to ask {@code EventClientDelegateMBean} to create an object of
55.52 + * this class.
55.53 + * Then {@code EventClientDelegateMBean} forwards client notifications
55.54 + * to this object. This object then continues forwarding the notifications
55.55 + * to the {@code RMIPushEventRelay}.
55.56 + */
55.57 +public class RMIPushEventForwarder implements EventForwarder {
55.58 + private static final int DEFAULT_BUFFER_SIZE = 6000;
55.59 +
55.60 + /**
55.61 + * Creates a new instance of {@code RMIPushEventForwarder}.
55.62 + *
55.63 + * @param receiver An RMI stub exported to receive notifications
55.64 + * from this object for its {@link RMIPushEventRelay}.
55.65 + *
55.66 + * @param bufferSize The maximum number of notifications to store
55.67 + * while waiting for the last remote send to complete.
55.68 + */
55.69 + public RMIPushEventForwarder(RMIPushServer receiver, int bufferSize) {
55.70 + if (logger.traceOn()) {
55.71 + logger.trace("RMIEventForwarder", "new one");
55.72 + }
55.73 +
55.74 + if (bufferSize < 0) {
55.75 + throw new IllegalArgumentException(
55.76 + "Negative buffer size: " + bufferSize);
55.77 + } else if (bufferSize == 0)
55.78 + bufferSize = DEFAULT_BUFFER_SIZE;
55.79 +
55.80 + if (receiver == null) {
55.81 + throw new NullPointerException();
55.82 + }
55.83 +
55.84 + this.receiver = receiver;
55.85 + this.buffer = new ArrayBlockingQueue<TargetedNotification>(bufferSize);
55.86 + }
55.87 +
55.88 + public void forward(Notification n, Integer listenerId) {
55.89 + if (logger.traceOn()) {
55.90 + logger.trace("forward", "to the listener: "+listenerId);
55.91 + }
55.92 + synchronized(sendingJob) {
55.93 + TargetedNotification tn = new TargetedNotification(n, listenerId);
55.94 + while (!buffer.offer(tn)) {
55.95 + buffer.remove();
55.96 + passed++;
55.97 + }
55.98 + sendingJob.resume();
55.99 + }
55.100 + }
55.101 +
55.102 + public void close() {
55.103 + if (logger.traceOn()) {
55.104 + logger.trace("close", "called");
55.105 + }
55.106 +
55.107 + synchronized(sendingJob) {
55.108 + ended = true;
55.109 + buffer.clear();
55.110 + }
55.111 + }
55.112 +
55.113 + public void setClientId(String clientId) {
55.114 + if (logger.traceOn()) {
55.115 + logger.trace("setClientId", clientId);
55.116 + }
55.117 + }
55.118 +
55.119 + private class SendingJob extends RepeatedSingletonJob {
55.120 + public SendingJob() {
55.121 + super(executor);
55.122 + }
55.123 +
55.124 + public boolean isSuspended() {
55.125 + return ended || buffer.isEmpty();
55.126 + }
55.127 +
55.128 + public void task() {
55.129 + final long earliest = passed;
55.130 +
55.131 + List<TargetedNotification> tns =
55.132 + new ArrayList<TargetedNotification>(buffer.size());
55.133 + synchronized(sendingJob) {
55.134 + buffer.drainTo(tns);
55.135 + passed += tns.size();
55.136 + }
55.137 +
55.138 + if (logger.traceOn()) {
55.139 + logger.trace("SendingJob-task", "sending: "+tns.size());
55.140 + }
55.141 +
55.142 + if (!tns.isEmpty()) {
55.143 + try {
55.144 + TargetedNotification[] tnArray =
55.145 + new TargetedNotification[tns.size()];
55.146 + tns.toArray(tnArray);
55.147 + receiver.receive(new NotificationResult(earliest, passed, tnArray));
55.148 + } catch (RemoteException e) {
55.149 + if (logger.debugOn()) {
55.150 + logger.debug("SendingJob-task",
55.151 + "Got exception to forward notifs.", e);
55.152 + }
55.153 +
55.154 + long currentLost = passed - earliest;
55.155 + if (FetchingEventRelay.isSerialOrClassNotFound(e)) {
55.156 + // send one by one
55.157 + long tmpPassed = earliest;
55.158 + for (TargetedNotification tn : tns) {
55.159 + try {
55.160 + receiver.receive(new NotificationResult(earliest,
55.161 + ++tmpPassed, new TargetedNotification[]{tn}));
55.162 + } catch (RemoteException ioee) {
55.163 + logger.trace(
55.164 + "SendingJob-task", "send to remote", ioee);
55.165 + // sends nonFatal notifs?
55.166 + }
55.167 + }
55.168 +
55.169 + currentLost = passed - tmpPassed;
55.170 + }
55.171 +
55.172 + if (currentLost > 0) { // inform of the lost.
55.173 + try {
55.174 + receiver.receive(new NotificationResult(
55.175 + passed, passed,
55.176 + new TargetedNotification[]{}));
55.177 + } catch (RemoteException ee) {
55.178 + logger.trace(
55.179 + "SendingJob-task", "receiver.receive", ee);
55.180 + }
55.181 + }
55.182 + }
55.183 + }
55.184 + }
55.185 + }
55.186 +
55.187 + private long passed = 0;
55.188 +
55.189 + private static final ExecutorService executor =
55.190 + Executors.newCachedThreadPool(
55.191 + new DaemonThreadFactory("RMIEventForwarder Executor"));
55.192 + private final SendingJob sendingJob = new SendingJob();
55.193 +
55.194 + private final BlockingQueue<TargetedNotification> buffer;
55.195 +
55.196 + private final RMIPushServer receiver;
55.197 + private boolean ended = false;
55.198 +
55.199 + private static final ClassLogger logger =
55.200 + new ClassLogger("javax.management.event", "RMIEventForwarder");
55.201 +}
56.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
56.2 +++ b/src/share/classes/javax/management/event/RMIPushEventRelay.java Thu Aug 21 09:55:18 2008 -0700
56.3 @@ -0,0 +1,161 @@
56.4 +/*
56.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
56.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
56.7 + *
56.8 + * This code is free software; you can redistribute it and/or modify it
56.9 + * under the terms of the GNU General Public License version 2 only, as
56.10 + * published by the Free Software Foundation. Sun designates this
56.11 + * particular file as subject to the "Classpath" exception as provided
56.12 + * by Sun in the LICENSE file that accompanied this code.
56.13 + *
56.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
56.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
56.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
56.17 + * version 2 for more details (a copy is included in the LICENSE file that
56.18 + * accompanied this code).
56.19 + *
56.20 + * You should have received a copy of the GNU General Public License version
56.21 + * 2 along with this work; if not, write to the Free Software Foundation,
56.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
56.23 + *
56.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
56.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
56.26 + * have any questions.
56.27 + */
56.28 +
56.29 +package javax.management.event;
56.30 +
56.31 +import com.sun.jmx.remote.util.ClassLogger;
56.32 +import java.io.IOException;
56.33 +import java.rmi.NoSuchObjectException;
56.34 +import java.rmi.RemoteException;
56.35 +import java.rmi.server.UnicastRemoteObject;
56.36 +import java.rmi.server.RMIClientSocketFactory;
56.37 +import java.rmi.server.RMIServerSocketFactory;
56.38 +import javax.management.MBeanException;
56.39 +import javax.management.remote.NotificationResult;
56.40 +
56.41 +/**
56.42 + * This class is an implementation of the {@link EventRelay} interface, using
56.43 + * push mode. It exports an RMI object that {@link RMIPushEventForwarder} uses
56.44 + * to forward notifications.
56.45 + *
56.46 + * @since JMX 2.0
56.47 + */
56.48 +public class RMIPushEventRelay implements EventRelay {
56.49 + /**
56.50 + * Constructs a default {@code RMIPushEventRelay} object
56.51 + * and exports its {@linkplain RMIPushServer notification
56.52 + * receiver} on any free port. This constructor is equivalent
56.53 + * to {@link #RMIPushEventRelay(EventClientDelegateMBean,
56.54 + * int, RMIClientSocketFactory, RMIServerSocketFactory, int)
56.55 + * RMIPushEventRelay(delegate, 0, null, null, <em><default buffer
56.56 + * size></em>)}.
56.57 + *
56.58 + * @param delegate The {@link EventClientDelegateMBean} proxy to work with.
56.59 + * @throws IOException if failed to communicate with
56.60 + * {@link EventClientDelegateMBean}.
56.61 + * @throws MBeanException if the {@link EventClientDelegateMBean} failed
56.62 + * to create an {@code EventForwarder} for this object.
56.63 + */
56.64 + public RMIPushEventRelay(EventClientDelegateMBean delegate)
56.65 + throws IOException, MBeanException {
56.66 + this(delegate, 0, null, null, 0);
56.67 + }
56.68 +
56.69 + /**
56.70 + * Constructs a {@code RMIPushEventRelay} object and exports its
56.71 + * {@linkplain RMIPushServer notification receiver} on a specified port.
56.72 + *
56.73 + * @param delegate The {@link EventClientDelegateMBean} proxy to work with.
56.74 + * @param port The port used to export an RMI object to receive notifications
56.75 + * from a server. If the port is zero, an anonymous port is used.
56.76 + * @param csf The client socket factory used to export the RMI object.
56.77 + * Can be null.
56.78 + * @param ssf The server socket factory used to export the RMI object.
56.79 + * Can be null.
56.80 + * @param bufferSize The number of notifications held on the server
56.81 + * while waiting for the previous transmission to complete. A value of
56.82 + * zero means the default buffer size.
56.83 + *
56.84 + * @throws IOException if failed to communicate with
56.85 + * {@link EventClientDelegateMBean}.
56.86 + * @throws MBeanException if the {@link EventClientDelegateMBean} failed
56.87 + * to create an {@code EventForwarder} for this object.
56.88 + *
56.89 + * @see RMIPushEventForwarder#RMIPushEventForwarder(RMIPushServer, int)
56.90 + */
56.91 + public RMIPushEventRelay(EventClientDelegateMBean delegate,
56.92 + int port,
56.93 + RMIClientSocketFactory csf,
56.94 + RMIServerSocketFactory ssf,
56.95 + int bufferSize)
56.96 + throws IOException, MBeanException {
56.97 +
56.98 + UnicastRemoteObject.exportObject(exportedReceiver, port, csf, ssf);
56.99 +
56.100 + clientId = delegate.addClient(
56.101 + RMIPushEventForwarder.class.getName(),
56.102 + new Object[] {exportedReceiver, bufferSize},
56.103 + new String[] {RMIPushServer.class.getName(),
56.104 + int.class.getName()});
56.105 + }
56.106 +
56.107 + public String getClientId() {
56.108 + return clientId;
56.109 + }
56.110 +
56.111 + public void setEventReceiver(EventReceiver receiver) {
56.112 + if (logger.traceOn()) {
56.113 + logger.trace("setEventReceiver", ""+receiver);
56.114 + }
56.115 + synchronized(lock) {
56.116 + this.receiver = receiver;
56.117 + }
56.118 + }
56.119 +
56.120 + public void stop() {
56.121 + if (logger.traceOn()) {
56.122 + logger.trace("stop", "");
56.123 + }
56.124 + synchronized(lock) {
56.125 + if (stopped) {
56.126 + return;
56.127 + } else {
56.128 + stopped = true;
56.129 + }
56.130 +
56.131 + if (clientId == null) {
56.132 + return;
56.133 + }
56.134 +
56.135 + try {
56.136 + UnicastRemoteObject.unexportObject(exportedReceiver, true);
56.137 + } catch (NoSuchObjectException nsoe) {
56.138 + logger.fine("RMIPushEventRelay.stop", "unexport", nsoe);
56.139 + // OK: we wanted it unexported, and apparently it already is
56.140 + }
56.141 + }
56.142 + }
56.143 +
56.144 + private volatile String clientId;
56.145 + private volatile EventReceiver receiver;
56.146 +
56.147 + private RMIPushServer exportedReceiver = new RMIPushServer() {
56.148 + public void receive(NotificationResult nr) throws RemoteException {
56.149 + if (logger.traceOn()) {
56.150 + logger.trace("EventPusherImpl-receive","");
56.151 + }
56.152 + receiver.receive(nr);
56.153 + // Any exception will be sent back to the client.
56.154 + }
56.155 + };
56.156 +
56.157 + private boolean stopped = false;
56.158 +
56.159 + private final int[] lock = new int[0];
56.160 +
56.161 + private static final ClassLogger logger =
56.162 + new ClassLogger("javax.management.event",
56.163 + "PushEventRelay");
56.164 +}
57.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
57.2 +++ b/src/share/classes/javax/management/event/RMIPushServer.java Thu Aug 21 09:55:18 2008 -0700
57.3 @@ -0,0 +1,48 @@
57.4 +/*
57.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
57.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
57.7 + *
57.8 + * This code is free software; you can redistribute it and/or modify it
57.9 + * under the terms of the GNU General Public License version 2 only, as
57.10 + * published by the Free Software Foundation. Sun designates this
57.11 + * particular file as subject to the "Classpath" exception as provided
57.12 + * by Sun in the LICENSE file that accompanied this code.
57.13 + *
57.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
57.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
57.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
57.17 + * version 2 for more details (a copy is included in the LICENSE file that
57.18 + * accompanied this code).
57.19 + *
57.20 + * You should have received a copy of the GNU General Public License version
57.21 + * 2 along with this work; if not, write to the Free Software Foundation,
57.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
57.23 + *
57.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
57.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
57.26 + * have any questions.
57.27 + */
57.28 +
57.29 +package javax.management.event;
57.30 +
57.31 +import java.rmi.Remote;
57.32 +import java.rmi.RemoteException;
57.33 +import javax.management.remote.NotificationResult;
57.34 +
57.35 +/**
57.36 + * The {@link RMIPushEventRelay} exports an RMI object of this class and
57.37 + * sends a client stub for that object to the associated
57.38 + * {@link RMIPushEventForwarder} in a remote MBean server. The
57.39 + * {@code RMIPushEventForwarder} then sends notifications to the
57.40 + * RMI object.
57.41 + */
57.42 +public interface RMIPushServer extends Remote {
57.43 + /**
57.44 + * <p>Dispatch the notifications in {@code nr} to the {@link RMIPushEventRelay}
57.45 + * associated with this object.</p>
57.46 + * @param nr the notification result to dispatch.
57.47 + * @throws java.rmi.RemoteException if the remote invocation of this method
57.48 + * failed.
57.49 + */
57.50 + public void receive(NotificationResult nr) throws RemoteException;
57.51 +}
58.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
58.2 +++ b/src/share/classes/javax/management/event/package-info.java Thu Aug 21 09:55:18 2008 -0700
58.3 @@ -0,0 +1,312 @@
58.4 +/**
58.5 + * <p>Defines the <em>Event Service</em>, which provides extended support
58.6 + * for JMX notifications.</p>
58.7 + *
58.8 + * <p>The Event Service provides greater control over
58.9 + * notification handling than the default technique using {@link
58.10 + * javax.management.MBeanServer#addNotificationListener(ObjectName,
58.11 + * NotificationListener, NotificationFilter, Object)
58.12 + * MBeanServer.addNotificationListener} or {@link
58.13 + * javax.management.MBeanServerConnection#addNotificationListener(ObjectName,
58.14 + * NotificationListener, NotificationFilter, Object)
58.15 + * MBeanServerConnection.addNotificationListener}.</p>
58.16 + *
58.17 + * <p>Here are some reasons you may want to use the Event Service:</p>
58.18 + *
58.19 + * <ul>
58.20 + * <li>To receive notifications from a set of MBeans defined by an
58.21 + * ObjectName pattern, such as {@code com.example.config:type=Cache,*}.
58.22 + *
58.23 + * <li>When the notification-handling behavior of the connector you are
58.24 + * using does not match your requirements. For example, with the standard
58.25 + * RMI connector you can lose notifications if there are very many of them
58.26 + * in the MBean Server you are connected to, even if you are only listening
58.27 + * for a small proportion of them.
58.28 + *
58.29 + * <li>To change the threading behavior of notification dispatch.
58.30 + *
58.31 + * <li>To define a different transport for notifications, for example to
58.32 + * arrange for them to be delivered through the Java Message Service (<a
58.33 + * href="http://java.sun.com/jms">JMS</a>). The Event Service comes with
58.34 + * one alternative transport as standard, a "push-mode" RMI transport.
58.35 + *
58.36 + * <li>To handle notifications on behalf of MBeans (often virtual) in a
58.37 + * namespace.
58.38 + * </ul>
58.39 + *
58.40 + * <p>The Event Service is new in version 2.0 of the JMX API, which is the
58.41 + * version introduced in version 7 of the Java SE platform. It is not usually
58.42 + * possible to use the Event Service when connecting remotely to an
58.43 + * MBean Server that is running an earlier version.</p>
58.44 + *
58.45 + *
58.46 + * <h3 id="handlingremote">Handling remote notifications with the Event
58.47 + * Service</h3>
58.48 + *
58.49 + * <p>Prior to version 2.0 of the JMX API, every connector
58.50 + * had to include logic to handle notifications. The standard {@linkplain
58.51 + * javax.management.remote.rmi RMI} and JMXMP connectors defined by <a
58.52 + * href="http://jcp.org/en/jsr/detail?id=160">JSR 160</a> handle notifications
58.53 + * in a way that is not always appropriate for applications. Specifically,
58.54 + * the connector server adds one listener to every MBean that might emit
58.55 + * notifications, and adds all received notifications to a fixed-size
58.56 + * buffer. This means that if there are very many notifications, a
58.57 + * remote client may miss some, even if it is only registered for a
58.58 + * very small subset of notifications. Furthermore, since every {@link
58.59 + * javax.management.NotificationBroadcaster NotificationBroadcaster} MBean
58.60 + * gets a listener from the connector server, MBeans cannot usefully optimize
58.61 + * by only sending notifications when there is a listener. Finally, since
58.62 + * the connector server uses just one listener per MBean, MBeans cannot
58.63 + * impose custom behavior per listener, such as security checks or localized
58.64 + * notifications.</p>
58.65 + *
58.66 + * <p>The Event Service does not have these restrictions. The RMI connector
58.67 + * that is included in this version of the JMX API uses the Event Service by
58.68 + * default, although it can be configured to have the previous behavior if
58.69 + * required.</p>
58.70 + *
58.71 + * <p>The Event Service can be used with <em>any</em> connector via the
58.72 + * method {@link javax.management.event.EventClient#getEventClientConnection
58.73 + * EventClient.getEventClientConnection}, like this:</p>
58.74 + *
58.75 + * <pre>
58.76 + * JMXConnector conn = ...;
58.77 + * MBeanServerConnection mbsc = conn.getMBeanServerConnection();
58.78 + * MBeanServerConnection eventMbsc = EventClient.getEventClientConnection(mbsc);
58.79 + * </pre>
58.80 + *
58.81 + * <p>If you add listeners using {@code eventMbsc.addNotificationListener}
58.82 + * instead of {@code mbsc.addNotificationListener}, then they will be handled
58.83 + * by the Event Service rather than by the connector's notification system.</p>
58.84 + *
58.85 + * <p>For the Event Service to work, either the {@link
58.86 + * javax.management.event.EventClientDelegateMBean EventClientDelegateMBean}
58.87 + * must be registered in the MBean Server, or the connector server must
58.88 + * be configured to simulate the existence of this MBean, for example
58.89 + * using {@link javax.management.event.EventClientDelegate#newForwarder()
58.90 + * EventClientDelegate.newForwarder()}. The standard RMI connector is so
58.91 + * configured by default. The {@code EventClientDelegateMBean} documentation
58.92 + * has further details.</p>
58.93 + *
58.94 + *
58.95 + * <h3 id="subscribepattern">Receiving notifications from a set of MBeans</h3>
58.96 + *
58.97 + * <p>The Event Server allows you to receive notifications from every MBean
58.98 + * that matches an {@link javax.management.ObjectName ObjectName} pattern.
58.99 + * For local clients (in the same JVM as the MBean Server), the {@link
58.100 + * javax.management.event.EventSubscriber EventSubscriber} class can be used for
58.101 + * this. For remote clients, or if the same code is to be used locally and
58.102 + * remotely, use an
58.103 + * {@link javax.management.event.EventClient EventClient}.</p>
58.104 + *
58.105 + * <p>EventSubscriber and EventClient correctly handle the case where a new
58.106 + * MBean is registered under a name that matches the pattern. Notifications
58.107 + * from the new MBean will also be received.</p>
58.108 + *
58.109 + * <p>Here is how to receive notifications from all MBeans in a local
58.110 + * {@code MBeanServer} that match {@code com.example.config:type=Cache,*}:</p>
58.111 + *
58.112 + * <pre>
58.113 + * MBeanServer mbs = ...;
58.114 + * NotificationListener listener = ...;
58.115 + * ObjectName pattern = new ObjectName("com.example.config:type=Cache,*");
58.116 + * EventSubscriber esub = EventSubscriber.getEventSubscriber(mbs);
58.117 + * esub.{@link javax.management.event.EventSubscriber#subscribe
58.118 + * subscribe}(pattern, listener, null, null);
58.119 + * </pre>
58.120 + *
58.121 + * <p>Here is how to do the same thing remotely:</p>
58.122 + *
58.123 + * <pre>
58.124 + * MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection();
58.125 + * EventClient events = new EventClient(mbsc);
58.126 + * NotificationListener listener = ...;
58.127 + * ObjectName pattern = new ObjectName("com.example.config:type=Cache,*");
58.128 + * events.{@link javax.management.event.EventClient#subscribe
58.129 + * subscribe}(pattern, listener, null, null);
58.130 + * </pre>
58.131 + *
58.132 + *
58.133 + * <h3 id="threading">Controlling threading behavior for notification
58.134 + * dispatch</h3>
58.135 + *
58.136 + * <p>The EventClient class can be used to control threading of listener
58.137 + * dispatch. For example, to arrange for all listeners to be invoked
58.138 + * in the same thread, you can create an {@code EventClient} like this:</p>
58.139 + *
58.140 + * <pre>
58.141 + * MBeanServerConnection mbsc = ...;
58.142 + * Executor singleThreadExecutor = {@link
58.143 + * java.util.concurrent.Executors#newSingleThreadExecutor()
58.144 + * Executors.newSingleThreadExecutor}();
58.145 + * EventClient events = new EventClient(
58.146 + * mbsc, null, singleThreadExecutor, EventClient.DEFAULT_LEASE_TIMEOUT);
58.147 + * events.addNotificationListener(...);
58.148 + * events.subscribe(...);
58.149 + * </pre>
58.150 + *
58.151 + *
58.152 + * <h3 id="leasing">Leasing</h3>
58.153 + *
58.154 + * <p>The {@code EventClient} uses a <em>lease</em> mechanism to ensure
58.155 + * that resources are eventually released on the server even if the client
58.156 + * does not explicitly clean up. (This can happen through network
58.157 + * partitioning, for example.)</p>
58.158 + *
58.159 + * <p>When an {@code EventClient} registers with the {@code
58.160 + * EventClientDelegateMBean} using one of the {@code addClient} methods,
58.161 + * an initial lease is created with a default expiry time. The {@code
58.162 + * EventClient} requests an explicit lease shortly after that, with a
58.163 + * configurable expiry time. Then the {@code EventClient} periodically
58.164 + * <em>renews</em> the lease before it expires, typically about half way
58.165 + * through the lifetime of the lease. If at any point the lease reaches
58.166 + * the expiry time of the last renewal then it expires, and {@code
58.167 + * EventClient} is unregistered as if it had called the {@link
58.168 + * javax.management.event.EventClientDelegateMBean#removeClient removeClient}
58.169 + * method.</p>
58.170 + *
58.171 + *
58.172 + * <h3 id="transports">Custom notification transports</h3>
58.173 + *
58.174 + * <p>When you create an {@code EventClient}, you can define the transport
58.175 + * that it uses to deliver notifications. The transport might use the
58.176 + * Java Message Service (<a href="http://java.sun.com/jms">JMS</a>) or
58.177 + * any other communication system. Specifying a transport is useful for
58.178 + * example when you want different network behavior from the default, or
58.179 + * different reliability guarantees. The default transport calls {@link
58.180 + * javax.management.event.EventClientDelegateMBean#fetchNotifications
58.181 + * EventClientDelegateMBean.fetchNotifications} repeatedly, which usually means
58.182 + * that there must be a network connection permanently open between the client
58.183 + * and the server. If the same client is connected to many servers this can
58.184 + * cause scalability problems. If notifications are relatively rare, then
58.185 + * JMS or the {@linkplain javax.management.event.RMIPushEventRelay push-mode
58.186 + * RMI transport} may be more suitable.</p>
58.187 + *
58.188 + * <p>A transport is implemented by an {@link javax.management.event.EventRelay
58.189 + * EventRelay} on the client side and a corresponding {@link
58.190 + * javax.management.event.EventForwarder EventForwarder}
58.191 + * on the server side. An example is the {@link
58.192 + * javax.management.event.RMIPushEventRelay RMIPushEventRelay} and its
58.193 + * {@link javax.management.event.RMIPushEventForwarder RMIPushEventForwarder}.</p>
58.194 + *
58.195 + * <p>To use a given transport with an {@code EventClient}, you first create
58.196 + * an instance of its {@code EventRelay}. Typically the {@code EventRelay}'s
58.197 + * constructor will have a parameter of type {@code MBeanServerConnection}
58.198 + * or {@code EventClientDelegateMBean}, so that it can communicate with the
58.199 + * {@code EventClientDelegateMBean} in the server. For example, the {@link
58.200 + * javax.management.event.RMIPushEventForwarder RMIPushEventForwarder}'s constructors
58.201 + * all take an {@code EventClientDelegateMBean} parameter, which is expected to
58.202 + * be a {@linkplain javax.management.JMX#newMBeanProxy(MBeanServerConnection,
58.203 + * ObjectName, Class) proxy} for the {@code EventClientDelegateMBean} in the
58.204 + * server.</p>
58.205 + *
58.206 + * <p>When it is created, the {@code EventRelay} will call
58.207 + * {@link javax.management.event.EventClientDelegateMBean#addClient(String,
58.208 + * Object[], String[]) EventClientDelegateMBean.addClient}. It passes the
58.209 + * name of the {@code EventForwarder} class and its constructor parameters.
58.210 + * The {@code EventClientDelegateMBean} will instantiate this class using
58.211 + * {@link javax.management.MBeanServer#instantiate(String, Object[], String[])
58.212 + * MBeanServer.instantiate}, and it will return a unique <em>client id</em>.</p>
58.213 + *
58.214 + * <p>Then you pass the newly-created {@code EventRelay} to one of the {@code
58.215 + * EventClient} constructors, and you have an {@code EventClient} that uses the
58.216 + * chosen transport.</p>
58.217 + *
58.218 + * <p>For example, when you create an {@code RMIPushEventRelay}, it
58.219 + * uses {@code MBeanServerDelegateMBean.addClient} to create an {@code
58.220 + * RMIEventForwarder} in the server. Notifications will then be delivered
58.221 + * through an RMI communication from the {@code RMIEventForwarder} to the
58.222 + * {@code RMIPushEventRelay}.</p>
58.223 + *
58.224 + *
58.225 + * <h4 id="writingcustomtransport">Writing a custom transport</h4>
58.226 + *
58.227 + * <p>To write a custom transport, you need to understand the sequence
58.228 + * of events when an {@code EventRelay} and its corresponding {@code
58.229 + * EventForwarder} are created, and when a notification is sent from the {@code
58.230 + * EventForwarder} to the {@code EventRelay}.</p>
58.231 + *
58.232 + * <p>When an {@code EventRelay} is created:</p>
58.233 + *
58.234 + * <ul>
58.235 + * <li><p>The {@code EventRelay} must call {@code
58.236 + * EventClientDelegateMBean.addClient} with the name of the {@code
58.237 + * EventForwarder} and the constructor parameters.</p>
58.238 + *
58.239 + * <li><p>{@code EventClientDelegateMBean.addClient} will do the following
58.240 + * steps:</p>
58.241 + *
58.242 + * <ul>
58.243 + * <li>create the {@code EventForwarder} using {@code MBeanServer.instantiate};
58.244 + * <li>allocate a unique client id;
58.245 + * <li>call the new {@code EventForwarder}'s {@link
58.246 + * javax.management.event.EventForwarder#setClientId setClientId} method with
58.247 + * the new client id;
58.248 + * <li>return the client id to the caller.
58.249 + * </ul>
58.250 + *
58.251 + * </ul>
58.252 + *
58.253 + * <p>When an {@code EventClient} is created with an {@code EventRelay}
58.254 + * parameter, it calls {@link javax.management.event.EventRelay#setEventReceiver
58.255 + * EventRelay.setEventReceiver} with an {@code EventReceiver} that the
58.256 + * {@code EventRelay} will use to deliver notifications.</p>
58.257 + *
58.258 + * <p>When a listener is added using the {@code EventClient}, the
58.259 + * {@code EventRelay} and {@code EventForwarder} are not involved.</p>
58.260 + *
58.261 + * <p>When an MBean emits a notification and a listener has been added
58.262 + * to that MBean using the {@code EventClient}:</p>
58.263 + *
58.264 + * <ul>
58.265 + * <li><p>The {@code EventForwarder}'s
58.266 + * {@link javax.management.event.EventForwarder#forward forward} method
58.267 + * is called with the notification and a <em>listener id</em>.</p>
58.268 + *
58.269 + * <li><p>The {@code EventForwarder} sends the notification and listener id
58.270 + * to the {@code EventRelay} using the custom transport.</p>
58.271 + *
58.272 + * <li><p>The {@code EventRelay} delivers the notification by calling
58.273 + * {@link javax.management.event.EventReceiver#receive EventReceiver.receive}.</p>
58.274 + * </ul>
58.275 + *
58.276 + * <p>When the {@code EventClient} is closed ({@link
58.277 + * javax.management.event.EventClient#close EventClient.close}):</p>
58.278 + *
58.279 + * <ul>
58.280 + * <li><p>The {@code EventClient} calls {@link
58.281 + * javax.management.event.EventRelay#stop EventRelay.stop}.</p>
58.282 + *
58.283 + * <li><p>The {@code EventClient} calls {@link
58.284 + * javax.management.event.EventClientDelegateMBean#removeClient
58.285 + * EventClientDelegateMBean.removeClient}.</p>
58.286 + *
58.287 + * <li><p>The {@code EventClientDelegateMBean} removes any listeners it
58.288 + * had added on behalf of this {@code EventClient}.</p>
58.289 + *
58.290 + * <li><p>The {@code EventClientDelegateMBean} calls
58.291 + * {@link javax.management.event.EventForwarder#close EventForwarder.close}.</p>
58.292 + * </ul>
58.293 + *
58.294 + *
58.295 + * <h4 id="threading">Threading and buffering</h3>
58.296 + *
58.297 + * <p>The {@link javax.management.event.EventForwarder#forward
58.298 + * EventForwarder.forward} method may be called in the thread that the
58.299 + * source MBean is using to send its notification. MBeans can expect
58.300 + * that notification sending does not block. Therefore a {@code forward}
58.301 + * method will typically add the notification to a queue, with a separate
58.302 + * thread that takes notifications off the queue and sends them.</p>
58.303 + *
58.304 + * <p>An {@code EventRelay} does not usually need to buffer notifications
58.305 + * before giving them to
58.306 + * {@link javax.management.event.EventReceiver#receive EventReceiver.receive}.
58.307 + * Although by default each such notification will be sent to potentially-slow
58.308 + * listeners, if this is a problem then an {@code Executor} can be given to
58.309 + * the {@code EventClient} constructor to cause the listeners to be called
58.310 + * in a different thread.</p>
58.311 + *
58.312 + * @since 1.7
58.313 + */
58.314 +
58.315 +package javax.management.event;
59.1 --- a/src/share/classes/javax/management/loading/MLet.java Tue Aug 19 07:50:03 2008 -0700
59.2 +++ b/src/share/classes/javax/management/loading/MLet.java Thu Aug 21 09:55:18 2008 -0700
59.3 @@ -1154,21 +1154,29 @@
59.4 */
59.5 private synchronized String loadLibraryAsResource(String libname) {
59.6 try {
59.7 - InputStream is = getResourceAsStream(libname.replace(File.separatorChar,'/'));
59.8 + InputStream is = getResourceAsStream(
59.9 + libname.replace(File.separatorChar,'/'));
59.10 if (is != null) {
59.11 - File directory = new File(libraryDirectory);
59.12 - directory.mkdirs();
59.13 - File file = File.createTempFile(libname + ".", null, directory);
59.14 - file.deleteOnExit();
59.15 - FileOutputStream fileOutput = new FileOutputStream(file);
59.16 - int c;
59.17 - while ((c = is.read()) != -1) {
59.18 - fileOutput.write(c);
59.19 - }
59.20 - is.close();
59.21 - fileOutput.close();
59.22 - if (file.exists()) {
59.23 - return file.getAbsolutePath();
59.24 + try {
59.25 + File directory = new File(libraryDirectory);
59.26 + directory.mkdirs();
59.27 + File file = File.createTempFile(libname + ".", null,
59.28 + directory);
59.29 + file.deleteOnExit();
59.30 + FileOutputStream fileOutput = new FileOutputStream(file);
59.31 + try {
59.32 + int c;
59.33 + while ((c = is.read()) != -1) {
59.34 + fileOutput.write(c);
59.35 + }
59.36 + } finally {
59.37 + fileOutput.close();
59.38 + }
59.39 + if (file.exists()) {
59.40 + return file.getAbsolutePath();
59.41 + }
59.42 + } finally {
59.43 + is.close();
59.44 }
59.45 }
59.46 } catch (Exception e) {
60.1 --- a/src/share/classes/javax/management/modelmbean/ModelMBeanInfoSupport.java Tue Aug 19 07:50:03 2008 -0700
60.2 +++ b/src/share/classes/javax/management/modelmbean/ModelMBeanInfoSupport.java Thu Aug 21 09:55:18 2008 -0700
60.3 @@ -373,7 +373,7 @@
60.4 "getDescriptors(String)", "Entry");
60.5 }
60.6
60.7 - if ((inDescriptorType == null) || (inDescriptorType.isEmpty())) {
60.8 + if ((inDescriptorType == null) || (inDescriptorType.equals(""))) {
60.9 inDescriptorType = "all";
60.10 }
60.11
60.12 @@ -616,7 +616,7 @@
60.13 inDescriptor = new DescriptorSupport();
60.14 }
60.15
60.16 - if ((inDescriptorType == null) || (inDescriptorType.isEmpty())) {
60.17 + if ((inDescriptorType == null) || (inDescriptorType.equals(""))) {
60.18 inDescriptorType =
60.19 (String) inDescriptor.getFieldValue("descriptorType");
60.20
61.1 --- a/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java Tue Aug 19 07:50:03 2008 -0700
61.2 +++ b/src/share/classes/javax/management/modelmbean/RequiredModelMBean.java Thu Aug 21 09:55:18 2008 -0700
61.3 @@ -1123,7 +1123,7 @@
61.4 if (tracing) {
61.5 MODELMBEAN_LOGGER.logp(Level.FINER,
61.6 RequiredModelMBean.class.getName(),"resolveMethod",
61.7 - "resolving " + targetClass + "." + opMethodName);
61.8 + "resolving " + targetClass.getName() + "." + opMethodName);
61.9 }
61.10
61.11 final Class[] argClasses;
62.1 --- a/src/share/classes/javax/management/openmbean/CompositeDataSupport.java Tue Aug 19 07:50:03 2008 -0700
62.2 +++ b/src/share/classes/javax/management/openmbean/CompositeDataSupport.java Thu Aug 21 09:55:18 2008 -0700
62.3 @@ -355,6 +355,7 @@
62.4 * @return <code>true</code> if the specified object is equal to this
62.5 * <code>CompositeDataSupport</code> instance.
62.6 */
62.7 + @Override
62.8 public boolean equals(Object obj) {
62.9 if (this == obj) {
62.10 return true;
62.11 @@ -419,6 +420,7 @@
62.12 *
62.13 * @return the hash code value for this <code>CompositeDataSupport</code> instance
62.14 */
62.15 + @Override
62.16 public int hashCode() {
62.17 int hashcode = compositeType.hashCode();
62.18
62.19 @@ -457,16 +459,28 @@
62.20 *
62.21 * @return a string representation of this <code>CompositeDataSupport</code> instance
62.22 */
62.23 + @Override
62.24 public String toString() {
62.25 -
62.26 return new StringBuilder()
62.27 .append(this.getClass().getName())
62.28 .append("(compositeType=")
62.29 .append(compositeType.toString())
62.30 .append(",contents=")
62.31 - .append(contents.toString())
62.32 + .append(contentString())
62.33 .append(")")
62.34 .toString();
62.35 }
62.36
62.37 + private String contentString() {
62.38 + StringBuilder sb = new StringBuilder("{");
62.39 + String sep = "";
62.40 + for (Map.Entry<String, Object> entry : contents.entrySet()) {
62.41 + sb.append(sep).append(entry.getKey()).append("=");
62.42 + String s = Arrays.deepToString(new Object[] {entry.getValue()});
62.43 + sb.append(s.substring(1, s.length() - 1));
62.44 + sep = ", ";
62.45 + }
62.46 + sb.append("}");
62.47 + return sb.toString();
62.48 + }
62.49 }
63.1 --- a/src/share/classes/javax/management/openmbean/MXBeanMapping.java Tue Aug 19 07:50:03 2008 -0700
63.2 +++ b/src/share/classes/javax/management/openmbean/MXBeanMapping.java Thu Aug 21 09:55:18 2008 -0700
63.3 @@ -108,6 +108,9 @@
63.4 * <p>If we are unable to modify the {@code MyLinkedList} class,
63.5 * we can define an {@link MXBeanMappingFactory}. See the documentation
63.6 * of that class for further details.</p>
63.7 + *
63.8 + * @see <a href="../MXBean.html#custom">MXBean specification, section
63.9 + * "Custom MXBean type mappings"</a>
63.10 */
63.11 public abstract class MXBeanMapping {
63.12 private final Type javaType;
64.1 --- a/src/share/classes/javax/management/openmbean/MXBeanMappingFactory.java Tue Aug 19 07:50:03 2008 -0700
64.2 +++ b/src/share/classes/javax/management/openmbean/MXBeanMappingFactory.java Thu Aug 21 09:55:18 2008 -0700
64.3 @@ -82,6 +82,9 @@
64.4 * appears in, or we can supply the factory to a {@link
64.5 * javax.management.StandardMBean StandardMBean} constructor or MXBean
64.6 * proxy.</p>
64.7 + *
64.8 + * @see <a href="../MXBean.html#custom">MXBean specification, section
64.9 + * "Custom MXBean type mappings"</a>
64.10 */
64.11 public abstract class MXBeanMappingFactory {
64.12 /**
65.1 --- a/src/share/classes/javax/management/openmbean/TabularDataSupport.java Tue Aug 19 07:50:03 2008 -0700
65.2 +++ b/src/share/classes/javax/management/openmbean/TabularDataSupport.java Thu Aug 21 09:55:18 2008 -0700
65.3 @@ -29,15 +29,18 @@
65.4
65.5 // java import
65.6 //
65.7 +import com.sun.jmx.mbeanserver.GetPropertyAction;
65.8 import java.io.IOException;
65.9 import java.io.ObjectInputStream;
65.10 import java.io.Serializable;
65.11 +import java.security.AccessController;
65.12 import java.util.ArrayList;
65.13 import java.util.Arrays;
65.14 import java.util.Collection;
65.15 import java.util.Collections;
65.16 import java.util.HashMap;
65.17 import java.util.Iterator;
65.18 +import java.util.LinkedHashMap;
65.19 import java.util.List;
65.20 import java.util.Map;
65.21 import java.util.Set;
65.22 @@ -79,12 +82,13 @@
65.23 /**
65.24 * @serial This tabular data instance's contents: a {@link HashMap}
65.25 */
65.26 + // field cannot be final because of clone method
65.27 private Map<Object,CompositeData> dataMap;
65.28
65.29 /**
65.30 * @serial This tabular data instance's tabular type
65.31 */
65.32 - private TabularType tabularType;
65.33 + private final TabularType tabularType;
65.34
65.35 /**
65.36 * The array of item names that define the index used for rows (convenience field)
65.37 @@ -109,7 +113,7 @@
65.38 */
65.39 public TabularDataSupport(TabularType tabularType) {
65.40
65.41 - this(tabularType, 101, 0.75f);
65.42 + this(tabularType, 16, 0.75f);
65.43 }
65.44
65.45 /**
65.46 @@ -141,10 +145,18 @@
65.47 List<String> tmpNames = tabularType.getIndexNames();
65.48 this.indexNamesArray = tmpNames.toArray(new String[tmpNames.size()]);
65.49
65.50 + // Since LinkedHashMap was introduced in SE 1.4, it's conceivable even
65.51 + // if very unlikely that we might be the server of a 1.3 client. In
65.52 + // that case you'll need to set this property. See CR 6334663.
65.53 + String useHashMapProp = AccessController.doPrivileged(
65.54 + new GetPropertyAction("jmx.tabular.data.hash.map"));
65.55 + boolean useHashMap = "true".equalsIgnoreCase(useHashMapProp);
65.56 +
65.57 // Construct the empty contents HashMap
65.58 //
65.59 - this.dataMap =
65.60 - new HashMap<Object,CompositeData>(initialCapacity, loadFactor);
65.61 + this.dataMap = useHashMap ?
65.62 + new HashMap<Object,CompositeData>(initialCapacity, loadFactor) :
65.63 + new LinkedHashMap<Object, CompositeData>(initialCapacity, loadFactor);
65.64 }
65.65
65.66
66.1 --- a/src/share/classes/javax/management/relation/RelationService.java Tue Aug 19 07:50:03 2008 -0700
66.2 +++ b/src/share/classes/javax/management/relation/RelationService.java Thu Aug 21 09:55:18 2008 -0700
66.3 @@ -108,7 +108,7 @@
66.4 // the value HashMap mapping:
66.5 // <relation id> -> ArrayList of <role name>
66.6 // to track where a given MBean is referenced.
66.7 - private Map<ObjectName,Map<String,List<String>>>
66.8 + private final Map<ObjectName,Map<String,List<String>>>
66.9 myRefedMBeanObjName2RelIdsMap =
66.10 new HashMap<ObjectName,Map<String,List<String>>>();
66.11
66.12 @@ -1492,7 +1492,7 @@
66.13 // Clones the list of notifications to be able to still receive new
66.14 // notifications while proceeding those ones
66.15 List<MBeanServerNotification> localUnregNtfList;
66.16 - synchronized(myUnregNtfList) {
66.17 + synchronized(myRefedMBeanObjName2RelIdsMap) {
66.18 localUnregNtfList =
66.19 new ArrayList<MBeanServerNotification>(myUnregNtfList);
66.20 // Resets list
67.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
67.2 +++ b/src/share/classes/javax/management/remote/IdentityMBeanServerForwarder.java Thu Aug 21 09:55:18 2008 -0700
67.3 @@ -0,0 +1,303 @@
67.4 +/*
67.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
67.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
67.7 + *
67.8 + * This code is free software; you can redistribute it and/or modify it
67.9 + * under the terms of the GNU General Public License version 2 only, as
67.10 + * published by the Free Software Foundation. Sun designates this
67.11 + * particular file as subject to the "Classpath" exception as provided
67.12 + * by Sun in the LICENSE file that accompanied this code.
67.13 + *
67.14 + * This code is distributed in the hope that it will be useful, but WITHOUT
67.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
67.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
67.17 + * version 2 for more details (a copy is included in the LICENSE file that
67.18 + * accompanied this code).
67.19 + *
67.20 + * You should have received a copy of the GNU General Public License version
67.21 + * 2 along with this work; if not, write to the Free Software Foundation,
67.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
67.23 + *
67.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
67.25 + * CA 95054 USA or visit www.sun.com if you need additional information or
67.26 + * have any questions.
67.27 + */
67.28 +package javax.management.remote;
67.29 +
67.30 +import java.io.ObjectInputStream;
67.31 +import java.util.Set;
67.32 +import javax.management.Attribute;
67.33 +import javax.management.AttributeList;
67.34 +import javax.management.AttributeNotFoundException;
67.35 +import javax.management.InstanceAlreadyExistsException;
67.36 +import javax.management.InstanceNotFoundException;
67.37 +import javax.management.IntrospectionException;
67.38 +import javax.management.InvalidAttributeValueException;
67.39 +import javax.management.ListenerNotFoundException;
67.40 +import javax.management.MBeanException;
67.41 +import javax.management.MBeanInfo;
67.42 +import javax.management.MBeanRegistrationException;
67.43 +import javax.management.MBeanServer;
67.44 +import javax.management.NotCompliantMBeanException;
67.45 +import javax.management.NotificationFilter;
67.46 +import javax.management.NotificationListener;
67.47 +import javax.management.ObjectInstance;
67.48 +import javax.management.ObjectName;
67.49 +import javax.management.OperationsException;
67.50 +import javax.management.QueryExp;
67.51 +import javax.management.ReflectionException;
67.52 +import javax.management.loading.ClassLoaderRepository;
67.53 +
67.54 +/**
67.55 + * An {@link MBeanServerForwarder} that forwards all {@link MBeanServer}
67.56 + * operations unchanged to the next {@code MBeanServer} in the chain.
67.57 + * This class is typically subclassed to override some but not all methods.
67.58 + */
67.59 +public class IdentityMBeanServerForwarder implements MBeanServerForwarder {
67.60 +
67.61 + private MBeanServer next;
67.62 +
67.63 + /**
67.64 + * <p>Construct a forwarder that has no next {@code MBeanServer}.
67.65 + * The resulting object will be unusable until {@link #setMBeanServer
67.66 + * setMBeanServer} is called to establish the next item in the chain.</p>
67.67 + */
67.68 + public IdentityMBeanServerForwarder() {
67.69 + }
67.70 +
67.71 + /**
67.72 + * <p>Construct a forwarder that forwards to the given {@code MBeanServer}.
67.73 + * It is not an error for {@code next} to be null, but the resulting object
67.74 + * will be unusable until {@link #setMBeanServer setMBeanServer} is called
67.75 + * to establish the next item in the chain.</p>
67.76 + */
67.77 + public IdentityMBeanServerForwarder(MBeanServer next) {
67.78 + this.next = next;
67.79 + }
67.80 +
67.81 + public synchronized MBeanServer getMBeanServer() {
67.82 + return next;
67.83 + }
67.84 +
67.85 + public synchronized void setMBeanServer(MBeanServer mbs) {
67.86 + next = mbs;
67.87 + }
67.88 +
67.89 + private synchronized MBeanServer next() {
67.90 + return next;
67.91 + }
67.92 +
67.93 + public void unregisterMBean(ObjectName name)
67.94 + throws InstanceNotFoundException, MBeanRegistrationException {
67.95 + next().unregisterMBean(name);
67.96 + }
67.97 +
67.98 + public AttributeList setAttributes(ObjectName name,
67.99 + AttributeList attributes)
67.100 + throws InstanceNotFoundException, ReflectionException {
67.101 + return next().setAttributes(name, attributes);
67.102 + }
67.103 +
67.104 + public void setAttribute(ObjectName name, Attribute attribute)
67.105 + throws InstanceNotFoundException, AttributeNotFoundException,
67.106 + InvalidAttributeValueException, MBeanException,
67.107 + ReflectionException {
67.108 + next().setAttribute(name, attribute);
67.109 + }
67.110 +
67.111 + public void removeNotificationListener(ObjectName name,
67.112 + NotificationListener listener,
67.113 + NotificationFilter filter,
67.114 + Object handback)
67.115 + throws InstanceNotFoundException, ListenerNotFoundException {
67.116 + next().removeNotificationListener(name, listener, filter, handback);
67.117 + }
67.118 +
67.119 + public void removeNotificationListener(ObjectName name,
67.120 + NotificationListener listener)
67.121 + throws InstanceNotFoundException, ListenerNotFoundException {
67.122 + next().removeNotificationListener(name, listener);
67.123 + }
67.124 +
67.125 + public void removeNotificationListener(ObjectName name, ObjectName listener,
67.126 + NotificationFilter filter,
67.127 + Object handback)
67.128 + throws InstanceNotFoundException, ListenerNotFoundException {
67.129 + next().removeNotificationListener(name, listener, filter, handback);
67.130 + }
67.131 +
67.132 + public void removeNotificationListener(ObjectName name, ObjectName listener)
67.133 + throws InstanceNotFoundException, ListenerNotFoundException {
67.134 + next().removeNotificationListener(name, listener);
67.135 + }
67.136 +
67.137 + public ObjectInstance registerMBean(Object object, ObjectName name)
67.138 + throws InstanceAlreadyExistsException, MBeanRegistrationException,
67.139 + NotCompliantMBeanException {
67.140 + return next().registerMBean(object, name);
67.141 + }
67.142 +
67.143 + public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
67.144 + return next().queryNames(name, query);
67.145 + }
67.146 +
67.147 + public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
67.148 + return next().queryMBeans(name, query);
67.149 + }
67.150 +
67.151 + public boolean isRegistered(ObjectName name) {
67.152 + return next().isRegistered(name);
67.153 + }
67.154 +
67.155 + public boolean isInstanceOf(ObjectName name, String className)
67.156 + throws InstanceNotFoundException {
67.157 + return next().isInstanceOf(name, className);
67.158 + }
67.159 +
67.160 + public Object invoke(ObjectName name, String operationName, Object[] params,
67.161 + String[] signature)
67.162 + throws InstanceNotFoundException, MBeanException,
67.163 + ReflectionException {
67.164 + return next().invoke(name, operationName, params, signature);
67.165 + }
67.166 +
67.167 + public Object instantiate(String className, ObjectName loaderName,
67.168 + Object[] params, String[] signature)
67.169 + throws ReflectionException, MBeanException,
67.170 + InstanceNotFoundException {
67.171 + return next().instantiate(className, loaderName, params, signature);
67.172 + }
67.173 +
67.174 + public Object instantiate(String className, Object[] params,
67.175 + String[] signature)
67.176 + throws ReflectionException, MBeanException {
67.177 + return next().instantiate(className, params, signature);
67.178 + }
67.179 +
67.180 + public Object instantiate(String className, ObjectName loaderName)
67.181 + throws ReflectionException, MBeanException,
67.182 + InstanceNotFoundException {
67.183 + return next().instantiate(className, loaderName);
67.184 + }
67.185 +
67.186 + public Object instantiate(String className)
67.187 + throws ReflectionException, MBeanException {
67.188 + return next().instantiate(className);
67.189 + }
67.190 +
67.191 + public ObjectInstance getObjectInstance(ObjectName name)
67.192 + throws InstanceNotFoundException {
67.193 + return next().getObjectInstance(name);
67.194 + }
67.195 +
67.196 + public MBeanInfo getMBeanInfo(ObjectName name)
67.197 + throws InstanceNotFoundException, IntrospectionException,
67.198 + ReflectionException {
67.199 + return next().getMBeanInfo(name);
67.200 + }
67.201 +
67.202 + public Integer getMBeanCount() {
67.203 + return next().getMBeanCount();
67.204 + }
67.205 +
67.206 + public String[] getDomains() {
67.207 + return next().getDomains();
67.208 + }
67.209 +
67.210 + public String getDefaultDomain() {
67.211 + return next().getDefaultDomain();
67.212 + }
67.213 +
67.214 + public ClassLoaderRepository getClassLoaderRepository() {
67.215 + return next().getClassLoaderRepository();
67.216 + }
67.217 +
67.218 + public ClassLoader getClassLoaderFor(ObjectName mbeanName)
67.219 + throws InstanceNotFoundException {
67.220 + return next().getClassLoaderFor(mbeanName);
67.221 + }
67.222 +
67.223 + public ClassLoader getClassLoader(ObjectName loaderName)
67.224 + throws InstanceNotFoundException {
67.225 + return next().getClassLoader(loaderName);
67.226 + }
67.227 +
67.228 + public AttributeList getAttributes(ObjectName name, String[] attributes)
67.229 + throws InstanceNotFoundException, ReflectionException {
67.230 + return next().getAttributes(name, attributes);
67.231 + }
67.232 +
67.233 + public Object getAttribute(ObjectName name, String attribute)
67.234 + throws MBeanException, AttributeNotFoundException,
67.235 + InstanceNotFoundException, ReflectionException {
67.236 + return next().getAttribute(name, attribute);
67.237 + }
67.238 +
67.239 + @Deprecated
67.240 + public ObjectInputStream deserialize(String className,
67.241 + ObjectName loaderName,
67.242 + byte[] data)
67.243 + throws InstanceNotFoundException, OperationsException,
67.244 + ReflectionException {
67.245 + return next().deserialize(className, loaderName, data);
67.246 + }
67.247 +
67.248 + @Deprecated
67.249 + public ObjectInputStream deserialize(String className, byte[] data)
67.250 + throws OperationsException, ReflectionException {
67.251 + return next().deserialize(className, data);
67.252 + }
67.253 +
67.254 + @Deprecated
67.255 + public ObjectInputStream deserialize(ObjectName name, byte[] data)
67.256 + throws InstanceNotFoundException, OperationsException {
67.257 + return next().deserialize(name, data);
67.258 + }
67.259 +
67.260 + public ObjectInstance createMBean(String className, ObjectName name,
67.261 + ObjectName loaderName, Object[] params,
67.262 + String[] signature)
67.263 + throws ReflectionException, InstanceAlreadyExistsException,
67.264 + MBeanRegistrationException, MBeanException,
67.265 + NotCompliantMBeanException, InstanceNotFoundException {
67.266 + return next().createMBean(className, name, loaderName, params, signature);
67.267 + }
67.268 +
67.269 + public ObjectInstance createMBean(String className, ObjectName name,
67.270 + Object[] params, String[] signature)
67.271 + throws ReflectionException, InstanceAlreadyExistsException,
67.272 + MBeanRegistrationException, MBeanException,
67.273 + NotCompliantMBeanException {
67.274 + return next().createMBean(className, name, params, signature);
67.275 + }
67.276 +
67.277 + public ObjectInstance createMBean(String className, ObjectName name,
67.278 + ObjectName loaderName)
67.279 + throws ReflectionException, InstanceAlreadyExistsException,
67.280 + MBeanRegistrationException, MBeanException,
67.281 + NotCompliantMBeanException, InstanceNotFoundException {
67.282 + return next().createMBean(className, name, loaderName);
67.283 + }
67.284 +
67.285 + public ObjectInstance createMBean(String className, ObjectName name)
67.286 + throws ReflectionException, InstanceAlreadyExistsException,
67.287 + MBeanRegistrationException, MBeanException,
67.288 + NotCompliantMBeanException {
67.289 + return next().createMBean(className, name);
67.290 + }
67.291 +
67.292 + public void addNotificationListener(ObjectName name, ObjectName listener,
67.293 + NotificationFilter filter,
67.294 + Object handback)
67.295 + throws InstanceNotFoundException {
67.296 + next().addNotificationListener(name, listener, filter, handback);
67.297 + }
67.298 +
67.299 + public void addNotificationListener(ObjectName name,
67.300 + NotificationListener listener,
67.301 + NotificationFilter filter,
67.302 + Object handback)
67.303 + throws InstanceNotFoundException {
67.304 + next().addNotificationListener(name, listener, filter, handback);
67.305 + }
67.306 +}
68.1 --- a/src/share/classes/javax/management/remote/JMXConnector.java Tue Aug 19 07:50:03 2008 -0700
68.2 +++ b/src/share/classes/javax/management/remote/JMXConnector.java Thu Aug 21 09:55:18 2008 -0700
68.3 @@ -57,6 +57,26 @@
68.4 public static final String CREDENTIALS =
68.5 "jmx.remote.credentials";
68.6
68.7 + /**
68.8 + * <p>Name of the attribute that specifies whether to use the
68.9 + * {@linkplain javax.management.event Event Service} to handle
68.10 + * notifications for this connector. The value associated with
68.11 + * this attribute, if any, is a String, which must be equal,
68.12 + * ignoring case, to {@code "true"} or {@code "false"}.</p>
68.13 + *
68.14 + * <p>Not all connectors will understand this attribute, but the
68.15 + * standard {@linkplain javax.management.remote.rmi.RMIConnector
68.16 + * RMI Connector} does.</p>
68.17 + *
68.18 + * <p>If this attribute is not present, then the system property of the
68.19 + * same name (<code>{@value}</code>) is consulted. If that is not set
68.20 + * either, then the Event Service is not used.</p>
68.21 + *
68.22 + * @since 1.7
68.23 + */
68.24 + public static final String USE_EVENT_SERVICE =
68.25 + "jmx.remote.use.event.service";
68.26 +
68.27 /**
68.28 * <p>Establishes the connection to the connector server. This
68.29 * method is equivalent to {@link #connect(Map)
69.1 --- a/src/share/classes/javax/management/remote/JMXConnectorServer.java Tue Aug 19 07:50:03 2008 -0700
69.2 +++ b/src/share/classes/javax/management/remote/JMXConnectorServer.java Thu Aug 21 09:55:18 2008 -0700
69.3 @@ -26,17 +26,21 @@
69.4
69.5 package javax.management.remote;
69.6
69.7 +import com.sun.jmx.remote.util.EnvHelp;
69.8 import java.io.IOException;
69.9 import java.util.ArrayList;
69.10 import java.util.List;
69.11 import java.util.Map;
69.12
69.13 +import java.util.NoSuchElementException;
69.14 +import javax.management.MBeanInfo; // for javadoc
69.15 import javax.management.MBeanNotificationInfo;
69.16 import javax.management.MBeanRegistration;
69.17 import javax.management.MBeanServer;
69.18 import javax.management.Notification;
69.19 import javax.management.NotificationBroadcasterSupport;
69.20 import javax.management.ObjectName;
69.21 +import javax.management.event.EventClientDelegate;
69.22
69.23 /**
69.24 * <p>Superclass of every connector server. A connector server is
69.25 @@ -75,6 +79,48 @@
69.26 public static final String AUTHENTICATOR =
69.27 "jmx.remote.authenticator";
69.28
69.29 + /**
69.30 + * <p>Name of the attribute that specifies whether this connector
69.31 + * server can delegate notification handling to the
69.32 + * {@linkplain javax.management.event Event Service}.
69.33 + * The value associated with
69.34 + * this attribute, if any, is a String, which must be equal,
69.35 + * ignoring case, to {@code "true"} or {@code "false"}.</p>
69.36 + *
69.37 + * <p>Not all connector servers will understand this attribute, but the
69.38 + * standard {@linkplain javax.management.remote.rmi.RMIConnectorServer
69.39 + * RMI Connector Server} does.</p>
69.40 + *
69.41 + * <p>If this attribute is not present, then the system property of the
69.42 + * same name (<code>{@value}</code>) is consulted. If that is not set
69.43 + * either, then the Event Service is used if the connector server
69.44 + * supports it.</p>
69.45 + *
69.46 + * @since 1.7
69.47 + */
69.48 + public static final String DELEGATE_TO_EVENT_SERVICE =
69.49 + "jmx.remote.delegate.event.service";
69.50 +
69.51 + /**
69.52 + * <p>Name of the attribute that specifies whether this connector
69.53 + * server simulates the existence of the {@link EventClientDelegate}
69.54 + * MBean. The value associated with this attribute, if any, must
69.55 + * be a string that is equal to {@code "true"} or {@code "false"},
69.56 + * ignoring case. If it is {@code "true"}, then the connector server
69.57 + * will simulate an EventClientDelegate MBean, as described in {@link
69.58 + * EventClientDelegate#newForwarder}. This MBean is needed for {@link
69.59 + * javax.management.event.EventClient EventClient} to function correctly.</p>
69.60 + *
69.61 + * <p>Not all connector servers will understand this attribute, but the
69.62 + * standard {@linkplain javax.management.remote.rmi.RMIConnectorServer
69.63 + * RMI Connector Server} does. For a connector server that understands
69.64 + * this attribute, the default value is {@code "true"}.</p>
69.65 + *
69.66 + * @since 1.7
69.67 + */
69.68 + public static final String EVENT_CLIENT_DELEGATE_FORWARDER =
69.69 + "jmx.remote.event.client.delegate.forwarder";
69.70 +
69.71 /**
69.72 * <p>Constructs a connector server that will be registered as an
69.73 * MBean in the MBean server it is attached to. This constructor
69.74 @@ -89,34 +135,274 @@
69.75 /**
69.76 * <p>Constructs a connector server that is attached to the given
69.77 * MBean server. A connector server that is created in this way
69.78 - * can be registered in a different MBean server.</p>
69.79 + * can be registered in a different MBean server, or not registered
69.80 + * in any MBean server.</p>
69.81 *
69.82 * @param mbeanServer the MBean server that this connector server
69.83 * is attached to. Null if this connector server will be attached
69.84 * to an MBean server by being registered in it.
69.85 */
69.86 public JMXConnectorServer(MBeanServer mbeanServer) {
69.87 - this.mbeanServer = mbeanServer;
69.88 + insertUserMBeanServer(mbeanServer);
69.89 }
69.90
69.91 /**
69.92 * <p>Returns the MBean server that this connector server is
69.93 - * attached to.</p>
69.94 + * attached to, or the first in a chain of user-added
69.95 + * {@link MBeanServerForwarder}s, if any.</p>
69.96 *
69.97 * @return the MBean server that this connector server is attached
69.98 * to, or null if it is not yet attached to an MBean server.
69.99 + *
69.100 + * @see #setMBeanServerForwarder
69.101 + * @see #getSystemMBeanServer
69.102 */
69.103 public synchronized MBeanServer getMBeanServer() {
69.104 - return mbeanServer;
69.105 + return userMBeanServer;
69.106 }
69.107
69.108 - public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf)
69.109 - {
69.110 + public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
69.111 if (mbsf == null)
69.112 throw new IllegalArgumentException("Invalid null argument: mbsf");
69.113
69.114 - if (mbeanServer != null) mbsf.setMBeanServer(mbeanServer);
69.115 - mbeanServer = mbsf;
69.116 + if (userMBeanServer != null)
69.117 + mbsf.setMBeanServer(userMBeanServer);
69.118 + insertUserMBeanServer(mbsf);
69.119 + }
69.120 +
69.121 + /**
69.122 + * <p>Remove a forwarder from the chain of forwarders. The forwarder can
69.123 + * be in the system chain or the user chain. On successful return from
69.124 + * this method, the first occurrence in the chain of an object that is
69.125 + * {@linkplain Object#equals equal} to {@code mbsf} will have been
69.126 + * removed.</p>
69.127 + * @param mbsf the forwarder to remove
69.128 + * @throws NoSuchElementException if there is no occurrence of {@code mbsf}
69.129 + * in the chain.
69.130 + * @throws IllegalArgumentException if {@code mbsf} is null.
69.131 + */
69.132 + public synchronized void removeMBeanServerForwarder(MBeanServerForwarder mbsf) {
69.133 + if (mbsf == null)
69.134 + throw new IllegalArgumentException("Invalid null argument: mbsf");
69.135 +
69.136 + MBeanServerForwarder prev = null;
69.137 + MBeanServer curr = systemMBeanServer;
69.138 + while (curr instanceof MBeanServerForwarder && !mbsf.equals(curr)) {
69.139 + prev = (MBeanServerForwarder) curr;
69.140 + curr = prev.getMBeanServer();
69.141 + }
69.142 + if (!(curr instanceof MBeanServerForwarder))
69.143 + throw new NoSuchElementException("MBeanServerForwarder not in chain");
69.144 + MBeanServerForwarder deleted = (MBeanServerForwarder) curr;
69.145 + MBeanServer next = deleted.getMBeanServer();
69.146 + if (prev != null)
69.147 + prev.setMBeanServer(next);
69.148 + if (systemMBeanServer == deleted)
69.149 + systemMBeanServer = next;
69.150 + if (userMBeanServer == deleted)
69.151 + userMBeanServer = next;
69.152 + }
69.153 +
69.154 + /*
69.155 + * Set userMBeanServer to mbs and arrange for the end of the chain of
69.156 + * system MBeanServerForwarders to point to it. See the comment before
69.157 + * the systemMBeanServer and userMBeanServer field declarations.
69.158 + */
69.159 + private void insertUserMBeanServer(MBeanServer mbs) {
69.160 + MBeanServerForwarder lastSystemMBSF = null;
69.161 + for (MBeanServer mbsi = systemMBeanServer;
69.162 + mbsi != userMBeanServer;
69.163 + mbsi = lastSystemMBSF.getMBeanServer()) {
69.164 + lastSystemMBSF = (MBeanServerForwarder) mbsi;
69.165 + }
69.166 + userMBeanServer = mbs;
69.167 + if (lastSystemMBSF == null)
69.168 + systemMBeanServer = mbs;
69.169 + else
69.170 + lastSystemMBSF.setMBeanServer(mbs);
69.171 + }
69.172 +
69.173 + /**
69.174 + * <p>Returns the first item in the chain of system and then user
69.175 + * forwarders. In the simplest case, a {@code JMXConnectorServer}
69.176 + * is connected directly to an {@code MBeanServer}. But there can
69.177 + * also be a chain of {@link MBeanServerForwarder}s between the two.
69.178 + * This chain consists of two sub-chains: first the <em>system chain</em>
69.179 + * and then the <em>user chain</em>. Incoming requests are given to the
69.180 + * first forwarder in the system chain. Each forwarder can handle
69.181 + * a request itself, or more usually forward it to the next forwarder,
69.182 + * perhaps with some extra behavior such as logging or security
69.183 + * checking before or after the forwarding. The last forwarder in
69.184 + * the system chain is followed by the first forwarder in the user
69.185 + * chain.</p>
69.186 + *
69.187 + * <p>The <em>system chain</em> is usually
69.188 + * defined by a connector server based on the environment Map;
69.189 + * see {@link JMXConnectorServerFactory#newJMXConnectorServer}. Allowing the
69.190 + * connector server to define its forwarders in this way ensures that
69.191 + * they are in the correct order - some forwarders need to be inserted
69.192 + * before others for correct behavior. It is possible to modify the
69.193 + * system chain, for example using {@link #setSystemMBeanServerForwarder} or
69.194 + * {@link #removeMBeanServerForwarder}, but in that case the system
69.195 + * chain is no longer guaranteed to be correct.</p>
69.196 + *
69.197 + * <p>The <em>user chain</em> is defined by calling {@link
69.198 + * #setMBeanServerForwarder} to insert forwarders at the head of the user
69.199 + * chain.</p>
69.200 + *
69.201 + * <p>If there are no forwarders in either chain, then both
69.202 + * {@link #getMBeanServer()} and {@code getSystemMBeanServer()} will
69.203 + * return the {@code MBeanServer} for this connector server. If there
69.204 + * are forwarders in the user chain but not the system chain, then
69.205 + * both methods will return the first forwarder in the user chain.
69.206 + * If there are forwarders in the system chain but not the user chain,
69.207 + * then {@code getSystemMBeanServer()} will return the first forwarder
69.208 + * in the system chain, and {@code getMBeanServer()} will return the
69.209 + * {@code MBeanServer} for this connector server. Finally, if there
69.210 + * are forwarders in each chain then {@code getSystemMBeanServer()}
69.211 + * will return the first forwarder in the system chain, and {@code
69.212 + * getMBeanServer()} will return the first forwarder in the user chain.</p>
69.213 + *
69.214 + * <p>This code illustrates how the chains can be traversed:</p>
69.215 + *
69.216 + * <pre>
69.217 + * JMXConnectorServer cs;
69.218 + * System.out.println("system chain:");
69.219 + * MBeanServer mbs = cs.getSystemMBeanServer();
69.220 + * while (true) {
69.221 + * if (mbs == cs.getMBeanServer())
69.222 + * System.out.println("user chain:");
69.223 + * if (!(mbs instanceof MBeanServerForwarder))
69.224 + * break;
69.225 + * MBeanServerForwarder mbsf = (MBeanServerForwarder) mbs;
69.226 + * System.out.println("--forwarder: " + mbsf);
69.227 + * mbs = mbsf.getMBeanServer();
69.228 + * }
69.229 + * System.out.println("--MBean Server");
69.230 + * </pre>
69.231 + *
69.232 + * @return the first item in the system chain of forwarders.
69.233 + *
69.234 + * @see #setSystemMBeanServerForwarder
69.235 + */
69.236 + public synchronized MBeanServer getSystemMBeanServer() {
69.237 + return systemMBeanServer;
69.238 + }
69.239 +
69.240 + /**
69.241 + * <p>Inserts an object that intercepts requests for the MBean server
69.242 + * that arrive through this connector server. This object will be
69.243 + * supplied as the <code>MBeanServer</code> for any new connection
69.244 + * created by this connector server. Existing connections are
69.245 + * unaffected.</p>
69.246 + *
69.247 + * <p>This method can be called more than once with different
69.248 + * {@link MBeanServerForwarder} objects. The result is a chain
69.249 + * of forwarders. The last forwarder added is the first in the chain.</p>
69.250 + *
69.251 + * <p>This method modifies the system chain of {@link MBeanServerForwarder}s.
69.252 + * Usually user code should change the user chain instead, via
69.253 + * {@link #setMBeanServerForwarder}.</p>
69.254 + *
69.255 + * <p>Not all connector servers support a system chain of forwarders.
69.256 + * Calling this method on a connector server that does not will produce an
69.257 + * {@link UnsupportedOperationException}.</p>
69.258 + *
69.259 + * <p>Suppose {@code mbs} is the result of {@link #getSystemMBeanServer()}
69.260 + * before calling this method. If {@code mbs} is not null, then
69.261 + * {@code mbsf.setMBeanServer(mbs)} will be called. If doing so
69.262 + * produces an exception, this method throws the same exception without
69.263 + * any other effect. If {@code mbs} is null, or if the call to
69.264 + * {@code mbsf.setMBeanServer(mbs)} succeeds, then this method will
69.265 + * return normally and {@code getSystemMBeanServer()} will then return
69.266 + * {@code mbsf}.</p>
69.267 + *
69.268 + * <p>The result of {@link #getMBeanServer()} is unchanged by this method.</p>
69.269 + *
69.270 + * @param mbsf the new <code>MBeanServerForwarder</code>.
69.271 + *
69.272 + * @throws IllegalArgumentException if the call to {@link
69.273 + * MBeanServerForwarder#setMBeanServer mbsf.setMBeanServer} fails
69.274 + * with <code>IllegalArgumentException</code>, or if
69.275 + * <code>mbsf</code> is null.
69.276 + *
69.277 + * @throws UnsupportedOperationException if
69.278 + * {@link #supportsSystemMBeanServerForwarder} returns false.
69.279 + *
69.280 + * @see #getSystemMBeanServer()
69.281 + */
69.282 + public synchronized void setSystemMBeanServerForwarder(
69.283 + MBeanServerForwarder mbsf) {
69.284 + if (mbsf == null)
69.285 + throw new IllegalArgumentException("Invalid null argument: mbsf");
69.286 + mustSupportSystemMBSF();
69.287 +
69.288 + if (systemMBeanServer != null)
69.289 + mbsf.setMBeanServer(systemMBeanServer);
69.290 + systemMBeanServer = mbsf;
69.291 + }
69.292 +
69.293 + /**
69.294 + * <p>Returns true if this connector server supports a system chain of
69.295 + * {@link MBeanServerForwarder}s. The default implementation of this
69.296 + * method returns false. Connector servers that do support the system
69.297 + * chain must override this method to return true.
69.298 + *
69.299 + * @return true if this connector server supports the system chain of
69.300 + * forwarders.
69.301 + */
69.302 + public boolean supportsSystemMBeanServerForwarder() {
69.303 + return false;
69.304 + }
69.305 +
69.306 + private void mustSupportSystemMBSF() {
69.307 + if (!supportsSystemMBeanServerForwarder()) {
69.308 + throw new UnsupportedOperationException(
69.309 + "System MBeanServerForwarder not supported by this " +
69.310 + "connector server");
69.311 + }
69.312 + }
69.313 +
69.314 + /**
69.315 + * <p>Install {@link MBeanServerForwarder}s in the system chain
69.316 + * based on the attributes in the given {@code Map}. A connector
69.317 + * server that {@linkplain #supportsSystemMBeanServerForwarder supports}
69.318 + * a system chain of {@code MBeanServerForwarder}s can call this method
69.319 + * to add forwarders to that chain based on the contents of {@code env}.
69.320 + * In order:</p>
69.321 + *
69.322 + * <ul>
69.323 + *
69.324 + * <li>If {@link #EVENT_CLIENT_DELEGATE_FORWARDER} is absent, or is
69.325 + * present with the value {@code "true"}, then a forwarder with the
69.326 + * functionality of {@link EventClientDelegate#newForwarder} is inserted
69.327 + * at the start of the system chain.</li>
69.328 + *
69.329 + * </ul>
69.330 + *
69.331 + * <p>For {@code EVENT_CLIENT_DELEGATE_FORWARDER}, if the
69.332 + * attribute is absent from the {@code Map} and a system property
69.333 + * of the same name is defined, then the value of the system
69.334 + * property is used as if it were in the {@code Map}.
69.335 + *
69.336 + * <p>Attributes in {@code env} that are not listed above are ignored
69.337 + * by this method.</p>
69.338 + *
69.339 + * @throws UnsupportedOperationException if {@link
69.340 + * #supportsSystemMBeanServerForwarder} is false.
69.341 + */
69.342 + protected void installStandardForwarders(Map<String, ?> env) {
69.343 + mustSupportSystemMBSF();
69.344 +
69.345 + // Remember that forwarders must be added in reverse order!
69.346 +
69.347 + boolean ecd = EnvHelp.computeBooleanFromString(
69.348 + env, EVENT_CLIENT_DELEGATE_FORWARDER, false, true);
69.349 +
69.350 + if (ecd) {
69.351 + MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
69.352 + setSystemMBeanServerForwarder(mbsf);
69.353 + }
69.354 }
69.355
69.356 public String[] getConnectionIds() {
69.357 @@ -359,8 +645,8 @@
69.358 ObjectName name) {
69.359 if (mbs == null || name == null)
69.360 throw new NullPointerException("Null MBeanServer or ObjectName");
69.361 - if (mbeanServer == null) {
69.362 - mbeanServer = mbs;
69.363 + if (userMBeanServer == null) {
69.364 + insertUserMBeanServer(mbs);
69.365 myName = name;
69.366 }
69.367 return name;
69.368 @@ -394,10 +680,53 @@
69.369 myName = null;
69.370 }
69.371
69.372 - /**
69.373 - * The MBeanServer used by this server to execute a client request.
69.374 + /*
69.375 + * Fields describing the chains of forwarders (MBeanServerForwarders).
69.376 + * In the general case, the forwarders look something like this:
69.377 + *
69.378 + * systemMBeanServer userMBeanServer
69.379 + * | |
69.380 + * v v
69.381 + * mbsf1 -> mbsf2 -> mbsf3 -> mbsf4 -> mbsf5 -> mbs
69.382 + *
69.383 + * Here, each mbsfi is an MBeanServerForwarder, and the arrows
69.384 + * illustrate its getMBeanServer() method. The last MBeanServerForwarder
69.385 + * can point to an MBeanServer that is not instanceof MBeanServerForwarder,
69.386 + * here mbs.
69.387 + *
69.388 + * Initially, the chain can be empty if this JMXConnectorServer was
69.389 + * constructed without an MBeanServer. In this case, both systemMBS
69.390 + * and userMBS will be null. If there is initially an MBeanServer,
69.391 + * then both systemMBS and userMBS will point to it.
69.392 + *
69.393 + * Whenever userMBS is changed, the system chain must be updated. If there
69.394 + * are forwarders in the system chain (between systemMBS and userMBS in the
69.395 + * picture above), then the last one must point to the old value of userMBS
69.396 + * (possibly null). It must be updated to point to the new value. If there
69.397 + * are no forwarders in the system chain, then systemMBS must be updated to
69.398 + * the new value of userMBS. The invariant is that starting from systemMBS
69.399 + * and repeatedly calling MBSF.getMBeanServer() you will end up at
69.400 + * userMBS. The implication is that you will not see any MBeanServer
69.401 + * object on the way that is not also an MBeanServerForwarder.
69.402 + *
69.403 + * The method insertUserMBeanServer contains the logic to change userMBS
69.404 + * and adjust the system chain appropriately.
69.405 + *
69.406 + * If userMBS is null and this JMXConnectorServer is registered in an
69.407 + * MBeanServer, then userMBS becomes that MBeanServer, and the system
69.408 + * chain must be updated as just described.
69.409 + *
69.410 + * When systemMBS is updated, there is no effect on userMBS. The system
69.411 + * chain may contain forwarders even though the user chain is empty
69.412 + * (there is no MBeanServer). In that case an attempt to forward an
69.413 + * incoming request through the chain will fall off the end and fail with a
69.414 + * NullPointerException. Usually a connector server will refuse to start()
69.415 + * if it is not attached to an MBS, so this situation should not arise.
69.416 */
69.417 - private MBeanServer mbeanServer = null;
69.418 +
69.419 + private MBeanServer userMBeanServer;
69.420 +
69.421 + private MBeanServer systemMBeanServer;
69.422
69.423 /**
69.424 * The name used to registered this server in an MBeanServer.
70.1 --- a/src/share/classes/javax/management/remote/JMXConnectorServerFactory.java Tue Aug 19 07:50:03 2008 -0700
70.2 +++ b/src/share/classes/javax/management/remote/JMXConnectorServerFactory.java Thu Aug 21 09:55:18 2008 -0700
70.3 @@ -35,10 +35,8 @@
70.4 import java.util.HashMap;
70.5 import java.util.Iterator;
70.6 import java.util.Map;
70.7 -import java.util.ServiceLoader;
70.8
70.9 import javax.management.MBeanServer;
70.10 -import javax.management.ObjectName;
70.11
70.12 /**
70.13 * <p>Factory to create JMX API connector servers. There
70.14 @@ -172,7 +170,8 @@
70.15 * loader MBean name. This class loader is used to deserialize objects in
70.16 * requests received from the client, possibly after consulting an
70.17 * MBean-specific class loader. The value associated with this
70.18 - * attribute is an instance of {@link ObjectName}.</p>
70.19 + * attribute is an instance of {@link javax.management.ObjectName
70.20 + * ObjectName}.</p>
70.21 */
70.22 public static final String DEFAULT_CLASS_LOADER_NAME =
70.23 "jmx.remote.default.class.loader.name";
71.1 --- a/src/share/classes/javax/management/remote/JMXConnectorServerMBean.java Tue Aug 19 07:50:03 2008 -0700
71.2 +++ b/src/share/classes/javax/management/remote/JMXConnectorServerMBean.java Thu Aug 21 09:55:18 2008 -0700
71.3 @@ -105,23 +105,34 @@
71.4 public boolean isActive();
71.5
71.6 /**
71.7 - * <p>Adds an object that intercepts requests for the MBean server
71.8 + * <p>Inserts an object that intercepts requests for the MBean server
71.9 * that arrive through this connector server. This object will be
71.10 * supplied as the <code>MBeanServer</code> for any new connection
71.11 * created by this connector server. Existing connections are
71.12 * unaffected.</p>
71.13 *
71.14 - * <p>If this connector server is already associated with an
71.15 + * <p>This method can be called more than once with different
71.16 + * {@link MBeanServerForwarder} objects. The result is a chain
71.17 + * of forwarders. The last forwarder added is the first in the chain.
71.18 + * In more detail:</p>
71.19 + *
71.20 + * <ul>
71.21 + * <li><p>If this connector server is already associated with an
71.22 * <code>MBeanServer</code> object, then that object is given to
71.23 * {@link MBeanServerForwarder#setMBeanServer
71.24 * mbsf.setMBeanServer}. If doing so produces an exception, this
71.25 * method throws the same exception without any other effect.</p>
71.26 *
71.27 - * <p>If this connector is not already associated with an
71.28 + * <li><p>If this connector is not already associated with an
71.29 * <code>MBeanServer</code> object, or if the
71.30 * <code>mbsf.setMBeanServer</code> call just mentioned succeeds,
71.31 * then <code>mbsf</code> becomes this connector server's
71.32 * <code>MBeanServer</code>.</p>
71.33 + * </ul>
71.34 + *
71.35 + * <p>A connector server may support two chains of forwarders,
71.36 + * a system chain and a user chain. See {@link
71.37 + * JMXConnectorServer#setSystemMBeanServerForwarder} for details.</p>
71.38 *
71.39 * @param mbsf the new <code>MBeanServerForwarder</code>.
71.40 *
71.41 @@ -129,6 +140,8 @@
71.42 * MBeanServerForwarder#setMBeanServer mbsf.setMBeanServer} fails
71.43 * with <code>IllegalArgumentException</code>. This includes the
71.44 * case where <code>mbsf</code> is null.
71.45 + *
71.46 + * @see JMXConnectorServer#setSystemMBeanServerForwarder
71.47 */
71.48 public void setMBeanServerForwarder(MBeanServerForwarder mbsf);
71.49
72.1 --- a/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java Tue Aug 19 07:50:03 2008 -0700
72.2 +++ b/src/share/classes/javax/management/remote/rmi/RMIConnectionImpl.java Thu Aug 21 09:55:18 2008 -0700
72.3 @@ -25,10 +25,12 @@
72.4
72.5 package javax.management.remote.rmi;
72.6
72.7 +import com.sun.jmx.mbeanserver.Util;
72.8 import static com.sun.jmx.mbeanserver.Util.cast;
72.9 import com.sun.jmx.remote.internal.ServerCommunicatorAdmin;
72.10 import com.sun.jmx.remote.internal.ServerNotifForwarder;
72.11 import com.sun.jmx.remote.security.JMXSubjectDomainCombiner;
72.12 +import com.sun.jmx.remote.security.NotificationAccessController;
72.13 import com.sun.jmx.remote.security.SubjectDelegator;
72.14 import com.sun.jmx.remote.util.ClassLoaderWithRepository;
72.15 import com.sun.jmx.remote.util.ClassLogger;
72.16 @@ -36,6 +38,7 @@
72.17 import com.sun.jmx.remote.util.OrderClassLoaders;
72.18
72.19 import java.io.IOException;
72.20 +import java.lang.reflect.UndeclaredThrowableException;
72.21 import java.rmi.MarshalledObject;
72.22 import java.rmi.UnmarshalException;
72.23 import java.rmi.server.Unreferenced;
72.24 @@ -56,19 +59,24 @@
72.25 import javax.management.InstanceNotFoundException;
72.26 import javax.management.IntrospectionException;
72.27 import javax.management.InvalidAttributeValueException;
72.28 +import javax.management.JMX;
72.29 import javax.management.ListenerNotFoundException;
72.30 import javax.management.MBeanException;
72.31 import javax.management.MBeanInfo;
72.32 import javax.management.MBeanRegistrationException;
72.33 import javax.management.MBeanServer;
72.34 import javax.management.NotCompliantMBeanException;
72.35 +import javax.management.Notification;
72.36 import javax.management.NotificationFilter;
72.37 import javax.management.ObjectInstance;
72.38 import javax.management.ObjectName;
72.39 import javax.management.QueryExp;
72.40 import javax.management.ReflectionException;
72.41 import javax.management.RuntimeOperationsException;
72.42 -import javax.management.loading.ClassLoaderRepository;
72.43 +import javax.management.event.EventClientDelegate;
72.44 +import javax.management.event.EventClientDelegateMBean;
72.45 +import javax.management.event.EventClientNotFoundException;
72.46 +import javax.management.event.FetchingEventForwarder;
72.47 import javax.management.remote.JMXServerErrorException;
72.48 import javax.management.remote.NotificationResult;
72.49 import javax.management.remote.TargetedNotification;
72.50 @@ -149,28 +157,16 @@
72.51 new PrivilegedAction<ClassLoaderWithRepository>() {
72.52 public ClassLoaderWithRepository run() {
72.53 return new ClassLoaderWithRepository(
72.54 - getClassLoaderRepository(),
72.55 - dcl);
72.56 + mbeanServer.getClassLoaderRepository(),
72.57 + dcl);
72.58 }
72.59 });
72.60 -
72.61 serverCommunicatorAdmin = new
72.62 RMIServerCommunicatorAdmin(EnvHelp.getServerConnectionTimeout(env));
72.63
72.64 this.env = env;
72.65 }
72.66
72.67 - private synchronized ServerNotifForwarder getServerNotifFwd() {
72.68 - // Lazily created when first use. Mainly when
72.69 - // addNotificationListener is first called.
72.70 - if (serverNotifForwarder == null)
72.71 - serverNotifForwarder =
72.72 - new ServerNotifForwarder(mbeanServer,
72.73 - env,
72.74 - rmiServer.getNotifBuffer(),
72.75 - connectionId);
72.76 - return serverNotifForwarder;
72.77 - }
72.78
72.79 public String getConnectionId() throws IOException {
72.80 // We should call reqIncomming() here... shouldn't we?
72.81 @@ -181,6 +177,7 @@
72.82 final boolean debug = logger.debugOn();
72.83 final String idstr = (debug?"["+this.toString()+"]":null);
72.84
72.85 + final SubscriptionManager mgr;
72.86 synchronized (this) {
72.87 if (terminated) {
72.88 if (debug) logger.debug("close",idstr + " already terminated.");
72.89 @@ -195,11 +192,12 @@
72.90 serverCommunicatorAdmin.terminate();
72.91 }
72.92
72.93 - if (serverNotifForwarder != null) {
72.94 - serverNotifForwarder.terminate();
72.95 - }
72.96 + mgr = subscriptionManager;
72.97 + subscriptionManager = null;
72.98 }
72.99
72.100 + if (mgr != null) mgr.terminate();
72.101 +
72.102 rmiServer.clientClosed(this);
72.103
72.104 if (debug) logger.debug("close",idstr + " closed.");
72.105 @@ -955,8 +953,7 @@
72.106 int i=0;
72.107 ClassLoader targetCl;
72.108 NotificationFilter[] filterValues =
72.109 - new NotificationFilter[names.length];
72.110 - Object params[];
72.111 + new NotificationFilter[names.length];
72.112 Integer[] ids = new Integer[names.length];
72.113 final boolean debug=logger.debugOn();
72.114
72.115 @@ -991,8 +988,7 @@
72.116 // remove all registered listeners
72.117 for (int j=0; j<i; j++) {
72.118 try {
72.119 - getServerNotifFwd().removeNotificationListener(names[j],
72.120 - ids[j]);
72.121 + doRemoveListener(names[j],ids[j]);
72.122 } catch (Exception eee) {
72.123 // strange
72.124 }
72.125 @@ -1240,22 +1236,299 @@
72.126 final long csn = clientSequenceNumber;
72.127 final int mn = maxNotifications;
72.128 final long t = timeout;
72.129 - PrivilegedAction<NotificationResult> action =
72.130 - new PrivilegedAction<NotificationResult>() {
72.131 - public NotificationResult run() {
72.132 - return getServerNotifFwd().fetchNotifs(csn, t, mn);
72.133 +
72.134 + final PrivilegedExceptionAction<NotificationResult> action =
72.135 + new PrivilegedExceptionAction<NotificationResult>() {
72.136 + public NotificationResult run() throws IOException {
72.137 + return doFetchNotifs(csn, t, mn);
72.138 }
72.139 };
72.140 - if (acc == null)
72.141 - return action.run();
72.142 - else
72.143 - return AccessController.doPrivileged(action, acc);
72.144 + try {
72.145 + if (acc == null)
72.146 + return action.run();
72.147 + else
72.148 + return AccessController.doPrivileged(action, acc);
72.149 + } catch (IOException x) {
72.150 + throw x;
72.151 + } catch (RuntimeException x) {
72.152 + throw x;
72.153 + } catch (Exception x) {
72.154 + // should not happen
72.155 + throw new UndeclaredThrowableException(x);
72.156 + }
72.157 +
72.158 } finally {
72.159 serverCommunicatorAdmin.rspOutgoing();
72.160 }
72.161 }
72.162
72.163 /**
72.164 + * This is an abstraction class that let us use the legacy
72.165 + * ServerNotifForwarder and the new EventClientDelegateMBean
72.166 + * indifferently.
72.167 + **/
72.168 + private static interface SubscriptionManager {
72.169 + public void removeNotificationListener(ObjectName name, Integer id)
72.170 + throws InstanceNotFoundException, ListenerNotFoundException, IOException;
72.171 + public void removeNotificationListener(ObjectName name, Integer[] ids)
72.172 + throws Exception;
72.173 + public NotificationResult fetchNotifications(long csn, long timeout, int maxcount)
72.174 + throws IOException;
72.175 + public Integer addNotificationListener(ObjectName name, NotificationFilter filter)
72.176 + throws InstanceNotFoundException, IOException;
72.177 + public void terminate()
72.178 + throws IOException;
72.179 + }
72.180 +
72.181 + /**
72.182 + * A SubscriptionManager that uses a ServerNotifForwarder.
72.183 + **/
72.184 + private static class LegacySubscriptionManager implements SubscriptionManager {
72.185 + private final ServerNotifForwarder forwarder;
72.186 + LegacySubscriptionManager(ServerNotifForwarder forwarder) {
72.187 + this.forwarder = forwarder;
72.188 + }
72.189 +
72.190 + public void removeNotificationListener(ObjectName name, Integer id)
72.191 + throws InstanceNotFoundException, ListenerNotFoundException,
72.192 + IOException {
72.193 + forwarder.removeNotificationListener(name,id);
72.194 + }
72.195 +
72.196 + public void removeNotificationListener(ObjectName name, Integer[] ids)
72.197 + throws Exception {
72.198 + forwarder.removeNotificationListener(name,ids);
72.199 + }
72.200 +
72.201 + public NotificationResult fetchNotifications(long csn, long timeout, int maxcount) {
72.202 + return forwarder.fetchNotifs(csn,timeout,maxcount);
72.203 + }
72.204 +
72.205 + public Integer addNotificationListener(ObjectName name,
72.206 + NotificationFilter filter)
72.207 + throws InstanceNotFoundException, IOException {
72.208 + return forwarder.addNotificationListener(name,filter);
72.209 + }
72.210 +
72.211 + public void terminate() {
72.212 + forwarder.terminate();
72.213 + }
72.214 + }
72.215 +
72.216 + /**
72.217 + * A SubscriptionManager that uses an EventClientDelegateMBean.
72.218 + **/
72.219 + private static class EventSubscriptionManager
72.220 + implements SubscriptionManager {
72.221 + private final MBeanServer mbeanServer;
72.222 + private final EventClientDelegateMBean delegate;
72.223 + private final NotificationAccessController notifAC;
72.224 + private final boolean checkNotificationEmission;
72.225 + private final String clientId;
72.226 + private final String connectionId;
72.227 +
72.228 + EventSubscriptionManager(
72.229 + MBeanServer mbeanServer,
72.230 + EventClientDelegateMBean delegate,
72.231 + Map<String, ?> env,
72.232 + String clientId,
72.233 + String connectionId) {
72.234 + this.mbeanServer = mbeanServer;
72.235 + this.delegate = delegate;
72.236 + this.notifAC = EnvHelp.getNotificationAccessController(env);
72.237 + this.checkNotificationEmission =
72.238 + EnvHelp.computeBooleanFromString(
72.239 + env, "jmx.remote.x.check.notification.emission", false);
72.240 + this.clientId = clientId;
72.241 + this.connectionId = connectionId;
72.242 + }
72.243 +
72.244 + @SuppressWarnings("serial") // no serialVersionUID
72.245 + private class AccessControlFilter implements NotificationFilter {
72.246 + private final NotificationFilter wrapped;
72.247 + private final ObjectName name;
72.248 +
72.249 + AccessControlFilter(ObjectName name, NotificationFilter wrapped) {
72.250 + this.name = name;
72.251 + this.wrapped = wrapped;
72.252 + }
72.253 +
72.254 + public boolean isNotificationEnabled(Notification notification) {
72.255 + try {
72.256 + if (checkNotificationEmission) {
72.257 + ServerNotifForwarder.checkMBeanPermission(
72.258 + mbeanServer, name, "addNotificationListener");
72.259 + }
72.260 + notifAC.fetchNotification(
72.261 + connectionId, name, notification, getSubject());
72.262 + return (wrapped == null) ? true :
72.263 + wrapped.isNotificationEnabled(notification);
72.264 + } catch (InstanceNotFoundException e) {
72.265 + return false;
72.266 + } catch (SecurityException e) {
72.267 + return false;
72.268 + }
72.269 + }
72.270 +
72.271 + }
72.272 +
72.273 + public Integer addNotificationListener(
72.274 + ObjectName name, NotificationFilter filter)
72.275 + throws InstanceNotFoundException, IOException {
72.276 + if (notifAC != null) {
72.277 + notifAC.addNotificationListener(connectionId, name, getSubject());
72.278 + filter = new AccessControlFilter(name, filter);
72.279 + }
72.280 + try {
72.281 + return delegate.addListener(clientId,name,filter);
72.282 + } catch (EventClientNotFoundException x) {
72.283 + throw new IOException("Unknown clientId: "+clientId,x);
72.284 + }
72.285 + }
72.286 +
72.287 + public void removeNotificationListener(ObjectName name, Integer id)
72.288 + throws InstanceNotFoundException, ListenerNotFoundException,
72.289 + IOException {
72.290 + if (notifAC != null)
72.291 + notifAC.removeNotificationListener(connectionId, name, getSubject());
72.292 + try {
72.293 + delegate.removeListenerOrSubscriber(clientId,id);
72.294 + } catch (EventClientNotFoundException x) {
72.295 + throw new IOException("Unknown clientId: "+clientId,x);
72.296 + }
72.297 + }
72.298 +
72.299 + public void removeNotificationListener(ObjectName name, Integer[] ids)
72.300 + throws InstanceNotFoundException, ListenerNotFoundException,
72.301 + IOException {
72.302 + if (notifAC != null)
72.303 + notifAC.removeNotificationListener(connectionId, name, getSubject());
72.304 + try {
72.305 + for (Integer id : ids)
72.306 + delegate.removeListenerOrSubscriber(clientId,id);
72.307 + } catch (EventClientNotFoundException x) {
72.308 + throw new IOException("Unknown clientId: "+clientId,x);
72.309 + }
72.310 + }
72.311 +
72.312 + public NotificationResult fetchNotifications(long csn, long timeout,
72.313 + int maxcount)
72.314 + throws IOException {
72.315 + try {
72.316 + // For some reason the delegate doesn't accept a negative
72.317 + // sequence number. However legacy clients will always call
72.318 + // fetchNotifications with a negative sequence number, when
72.319 + // they call it for the first time.
72.320 + // In that case, we will use 0 instead.
72.321 + //
72.322 + return delegate.fetchNotifications(
72.323 + clientId, Math.max(csn, 0), maxcount, timeout);
72.324 + } catch (EventClientNotFoundException x) {
72.325 + throw new IOException("Unknown clientId: "+clientId,x);
72.326 + }
72.327 + }
72.328 +
72.329 + public void terminate()
72.330 + throws IOException {
72.331 + try {
72.332 + delegate.removeClient(clientId);
72.333 + } catch (EventClientNotFoundException x) {
72.334 + throw new IOException("Unknown clientId: "+clientId,x);
72.335 + }
72.336 + }
72.337 +
72.338 + private static Subject getSubject() {
72.339 + return Subject.getSubject(AccessController.getContext());
72.340 + }
72.341 + }
72.342 +
72.343 + /**
72.344 + * Creates a SubscriptionManager that uses either the legacy notifications
72.345 + * mechanism (ServerNotifForwarder) or the new event service
72.346 + * (EventClientDelegateMBean) depending on which option was passed in
72.347 + * the connector's map.
72.348 + **/
72.349 + private SubscriptionManager createSubscriptionManager()
72.350 + throws IOException {
72.351 + if (EnvHelp.delegateToEventService(env) &&
72.352 + mbeanServer.isRegistered(EventClientDelegate.OBJECT_NAME)) {
72.353 + final EventClientDelegateMBean mbean =
72.354 + JMX.newMBeanProxy(mbeanServer,
72.355 + EventClientDelegate.OBJECT_NAME,
72.356 + EventClientDelegateMBean.class);
72.357 + String clientId;
72.358 + try {
72.359 + clientId =
72.360 + mbean.addClient(
72.361 + FetchingEventForwarder.class.getName(),
72.362 + new Object[] {EnvHelp.getNotifBufferSize(env)},
72.363 + new String[] {int.class.getName()});
72.364 + } catch (Exception e) {
72.365 + if (e instanceof IOException)
72.366 + throw (IOException) e;
72.367 + else
72.368 + throw new IOException(e);
72.369 + }
72.370 +
72.371 + // we're going to call remove client...
72.372 + try {
72.373 + mbean.lease(clientId, Long.MAX_VALUE);
72.374 + } catch (EventClientNotFoundException x) {
72.375 + throw new IOException("Unknown clientId: "+clientId,x);
72.376 + }
72.377 + return new EventSubscriptionManager(mbeanServer, mbean, env,
72.378 + clientId, connectionId);
72.379 + } else {
72.380 + final ServerNotifForwarder serverNotifForwarder =
72.381 + new ServerNotifForwarder(mbeanServer,
72.382 + env,
72.383 + rmiServer.getNotifBuffer(),
72.384 + connectionId);
72.385 + return new LegacySubscriptionManager(serverNotifForwarder);
72.386 + }
72.387 + }
72.388 +
72.389 + /**
72.390 + * Lazy creation of a SubscriptionManager.
72.391 + **/
72.392 + private synchronized SubscriptionManager getSubscriptionManager()
72.393 + throws IOException {
72.394 + // Lazily created when first use. Mainly when
72.395 + // addNotificationListener is first called.
72.396 +
72.397 + if (subscriptionManager == null) {
72.398 + subscriptionManager = createSubscriptionManager();
72.399 + }
72.400 + return subscriptionManager;
72.401 + }
72.402 +
72.403 + // calls SubscriptionManager.
72.404 + private void doRemoveListener(ObjectName name, Integer id)
72.405 + throws InstanceNotFoundException, ListenerNotFoundException,
72.406 + IOException {
72.407 + getSubscriptionManager().removeNotificationListener(name,id);
72.408 + }
72.409 +
72.410 + // calls SubscriptionManager.
72.411 + private void doRemoveListener(ObjectName name, Integer[] ids)
72.412 + throws Exception {
72.413 + getSubscriptionManager().removeNotificationListener(name,ids);
72.414 + }
72.415 +
72.416 + // calls SubscriptionManager.
72.417 + private NotificationResult doFetchNotifs(long csn, long timeout, int maxcount)
72.418 + throws IOException {
72.419 + return getSubscriptionManager().fetchNotifications(csn, timeout, maxcount);
72.420 + }
72.421 +
72.422 + // calls SubscriptionManager.
72.423 + private Integer doAddListener(ObjectName name, NotificationFilter filter)
72.424 + throws InstanceNotFoundException, IOException {
72.425 + return getSubscriptionManager().addNotificationListener(name,filter);
72.426 + }
72.427 +
72.428 +
72.429 + /**
72.430 * <p>Returns a string representation of this object. In general,
72.431 * the <code>toString</code> method returns a string that
72.432 * "textually represents" this object. The result should be a
72.433 @@ -1313,16 +1586,6 @@
72.434 // private methods
72.435 //------------------------------------------------------------------------
72.436
72.437 - private ClassLoaderRepository getClassLoaderRepository() {
72.438 - return
72.439 - AccessController.doPrivileged(
72.440 - new PrivilegedAction<ClassLoaderRepository>() {
72.441 - public ClassLoaderRepository run() {
72.442 - return mbeanServer.getClassLoaderRepository();
72.443 - }
72.444 - });
72.445 - }
72.446 -
72.447 private ClassLoader getClassLoader(final ObjectName name)
72.448 throws InstanceNotFoundException {
72.449 try {
72.450 @@ -1482,9 +1745,8 @@
72.451 return null;
72.452
72.453 case ADD_NOTIFICATION_LISTENERS:
72.454 - return getServerNotifFwd().addNotificationListener(
72.455 - (ObjectName)params[0],
72.456 - (NotificationFilter)params[1]);
72.457 + return doAddListener((ObjectName)params[0],
72.458 + (NotificationFilter)params[1]);
72.459
72.460 case ADD_NOTIFICATION_LISTENER_OBJECTNAME:
72.461 mbeanServer.addNotificationListener((ObjectName)params[0],
72.462 @@ -1494,9 +1756,7 @@
72.463 return null;
72.464
72.465 case REMOVE_NOTIFICATION_LISTENER:
72.466 - getServerNotifFwd().removeNotificationListener(
72.467 - (ObjectName)params[0],
72.468 - (Integer[])params[1]);
72.469 + doRemoveListener((ObjectName)params[0],(Integer[])params[1]);
72.470 return null;
72.471
72.472 case REMOVE_NOTIFICATION_LISTENER_OBJECTNAME:
72.473 @@ -1709,23 +1969,21 @@
72.474 private final static int
72.475 REMOVE_NOTIFICATION_LISTENER = 19;
72.476 private final static int
72.477 - REMOVE_NOTIFICATION_LISTENER_FILTER_HANDBACK = 20;
72.478 + REMOVE_NOTIFICATION_LISTENER_OBJECTNAME = 20;
72.479 private final static int
72.480 - REMOVE_NOTIFICATION_LISTENER_OBJECTNAME = 21;
72.481 + REMOVE_NOTIFICATION_LISTENER_OBJECTNAME_FILTER_HANDBACK = 21;
72.482 private final static int
72.483 - REMOVE_NOTIFICATION_LISTENER_OBJECTNAME_FILTER_HANDBACK = 22;
72.484 + SET_ATTRIBUTE = 22;
72.485 private final static int
72.486 - SET_ATTRIBUTE = 23;
72.487 + SET_ATTRIBUTES = 23;
72.488 private final static int
72.489 - SET_ATTRIBUTES = 24;
72.490 - private final static int
72.491 - UNREGISTER_MBEAN = 25;
72.492 + UNREGISTER_MBEAN = 24;
72.493
72.494 // SERVER NOTIFICATION
72.495 //--------------------
72.496
72.497 - private ServerNotifForwarder serverNotifForwarder;
72.498 - private Map env;
72.499 + private SubscriptionManager subscriptionManager;
72.500 + private Map<String, ?> env;
72.501
72.502 // TRACES & DEBUG
72.503 //---------------
73.1 --- a/src/share/classes/javax/management/remote/rmi/RMIConnector.java Tue Aug 19 07:50:03 2008 -0700
73.2 +++ b/src/share/classes/javax/management/remote/rmi/RMIConnector.java Thu Aug 21 09:55:18 2008 -0700
73.3 @@ -25,6 +25,9 @@
73.4
73.5 package javax.management.remote.rmi;
73.6
73.7 +import com.sun.jmx.event.DaemonThreadFactory;
73.8 +import com.sun.jmx.event.EventConnection;
73.9 +import com.sun.jmx.mbeanserver.PerThreadGroupPool;
73.10 import com.sun.jmx.remote.internal.ClientCommunicatorAdmin;
73.11 import com.sun.jmx.remote.internal.ClientListenerInfo;
73.12 import com.sun.jmx.remote.internal.ClientNotifForwarder;
73.13 @@ -68,6 +71,12 @@
73.14 import java.util.Properties;
73.15 import java.util.Set;
73.16 import java.util.WeakHashMap;
73.17 +import java.util.concurrent.ArrayBlockingQueue;
73.18 +import java.util.concurrent.Executor;
73.19 +import java.util.concurrent.LinkedBlockingDeque;
73.20 +import java.util.concurrent.ThreadFactory;
73.21 +import java.util.concurrent.ThreadPoolExecutor;
73.22 +import java.util.concurrent.TimeUnit;
73.23 import javax.management.Attribute;
73.24 import javax.management.AttributeList;
73.25 import javax.management.AttributeNotFoundException;
73.26 @@ -75,6 +84,7 @@
73.27 import javax.management.InstanceNotFoundException;
73.28 import javax.management.IntrospectionException;
73.29 import javax.management.InvalidAttributeValueException;
73.30 +import javax.management.JMX;
73.31 import javax.management.ListenerNotFoundException;
73.32 import javax.management.MBeanException;
73.33 import javax.management.MBeanInfo;
73.34 @@ -92,6 +102,8 @@
73.35 import javax.management.ObjectName;
73.36 import javax.management.QueryExp;
73.37 import javax.management.ReflectionException;
73.38 +import javax.management.event.EventClient;
73.39 +import javax.management.event.EventClientDelegateMBean;
73.40 import javax.management.remote.JMXConnectionNotification;
73.41 import javax.management.remote.JMXConnector;
73.42 import javax.management.remote.JMXConnectorFactory;
73.43 @@ -280,8 +292,8 @@
73.44 // client-side environment property is set to "true".
73.45 //
73.46 boolean checkStub = EnvHelp.computeBooleanFromString(
73.47 - usemap,
73.48 - "jmx.remote.x.check.stub");
73.49 + usemap,
73.50 + "jmx.remote.x.check.stub",false);
73.51 if (checkStub) checkStub(stub, rmiServerImplStubClass);
73.52
73.53 // Connect IIOP Stub if needed.
73.54 @@ -318,6 +330,8 @@
73.55 //
73.56 connectionId = getConnectionId();
73.57
73.58 + eventServiceEnabled = EnvHelp.eventServiceEnabled(env);
73.59 +
73.60 Notification connectedNotif =
73.61 new JMXConnectionNotification(JMXConnectionNotification.OPENED,
73.62 this,
73.63 @@ -327,6 +341,8 @@
73.64 null);
73.65 sendNotification(connectedNotif);
73.66
73.67 + // whether or not event service
73.68 +
73.69 if (tracing) logger.trace("connect",idstr + " done...");
73.70 } catch (IOException e) {
73.71 if (tracing)
73.72 @@ -378,13 +394,42 @@
73.73 throw new IOException("Not connected");
73.74 }
73.75
73.76 - MBeanServerConnection mbsc = rmbscMap.get(delegationSubject);
73.77 - if (mbsc != null)
73.78 - return mbsc;
73.79 + MBeanServerConnection rmbsc = rmbscMap.get(delegationSubject);
73.80 + if (rmbsc != null) {
73.81 + return rmbsc;
73.82 + }
73.83
73.84 - mbsc = new RemoteMBeanServerConnection(delegationSubject);
73.85 - rmbscMap.put(delegationSubject, mbsc);
73.86 - return mbsc;
73.87 + rmbsc = new RemoteMBeanServerConnection(delegationSubject);
73.88 + if (eventServiceEnabled) {
73.89 + EventClientDelegateMBean ecd = JMX.newMBeanProxy(
73.90 + rmbsc, EventClientDelegateMBean.OBJECT_NAME,
73.91 + EventClientDelegateMBean.class);
73.92 + EventClient ec = new EventClient(ecd, null, defaultExecutor(), null,
73.93 + EventClient.DEFAULT_LEASE_TIMEOUT);
73.94 +
73.95 + rmbsc = EventConnection.Factory.make(rmbsc, ec);
73.96 + ec.addEventClientListener(
73.97 + lostNotifListener, null, null);
73.98 + }
73.99 + rmbscMap.put(delegationSubject, rmbsc);
73.100 + return rmbsc;
73.101 + }
73.102 +
73.103 + private static Executor defaultExecutor() {
73.104 + PerThreadGroupPool.Create<ThreadPoolExecutor> create =
73.105 + new PerThreadGroupPool.Create<ThreadPoolExecutor>() {
73.106 + public ThreadPoolExecutor createThreadPool(ThreadGroup group) {
73.107 + ThreadFactory daemonThreadFactory = new DaemonThreadFactory(
73.108 + "RMIConnector listener dispatch %d");
73.109 + ThreadPoolExecutor exec = new ThreadPoolExecutor(
73.110 + 1, 10, 1, TimeUnit.SECONDS,
73.111 + new LinkedBlockingDeque<Runnable>(),
73.112 + daemonThreadFactory);
73.113 + exec.allowCoreThreadTimeOut(true);
73.114 + return exec;
73.115 + }
73.116 + };
73.117 + return listenerDispatchThreadPool.getThreadPoolExecutor(create);
73.118 }
73.119
73.120 public void
73.121 @@ -466,6 +511,17 @@
73.122 communicatorAdmin.terminate();
73.123 }
73.124
73.125 + // close all EventClient
73.126 + for (MBeanServerConnection rmbsc : rmbscMap.values()) {
73.127 + if (rmbsc instanceof EventConnection) {
73.128 + try {
73.129 + ((EventConnection)rmbsc).getEventClient().close();
73.130 + } catch (Exception e) {
73.131 + // OK
73.132 + }
73.133 + }
73.134 + }
73.135 +
73.136 if (rmiNotifClient != null) {
73.137 try {
73.138 rmiNotifClient.terminate();
73.139 @@ -592,17 +648,18 @@
73.140 }
73.141
73.142 if (debug) logger.debug("addListenersWithSubjects","registered "
73.143 - + listenerIDs.length + " listener(s)");
73.144 + + ((listenerIDs==null)?0:listenerIDs.length)
73.145 + + " listener(s)");
73.146 return listenerIDs;
73.147 }
73.148
73.149 //--------------------------------------------------------------------
73.150 // Implementation of MBeanServerConnection
73.151 //--------------------------------------------------------------------
73.152 - private class RemoteMBeanServerConnection
73.153 - implements MBeanServerConnection {
73.154 + private class RemoteMBeanServerConnection implements MBeanServerConnection {
73.155 + private Subject delegationSubject;
73.156
73.157 - private Subject delegationSubject;
73.158 + public EventClient eventClient = null;
73.159
73.160 public RemoteMBeanServerConnection() {
73.161 this(null);
73.162 @@ -1205,6 +1262,7 @@
73.163 IOException {
73.164
73.165 final boolean debug = logger.debugOn();
73.166 +
73.167 if (debug)
73.168 logger.debug("addNotificationListener" +
73.169 "(ObjectName,NotificationListener,"+
73.170 @@ -1226,8 +1284,9 @@
73.171 public void removeNotificationListener(ObjectName name,
73.172 NotificationListener listener)
73.173 throws InstanceNotFoundException,
73.174 - ListenerNotFoundException,
73.175 - IOException {
73.176 + ListenerNotFoundException,
73.177 + IOException {
73.178 +
73.179 final boolean debug = logger.debugOn();
73.180
73.181 if (debug) logger.debug("removeNotificationListener"+
73.182 @@ -1804,6 +1863,26 @@
73.183 terminated = false;
73.184
73.185 connectionBroadcaster = new NotificationBroadcasterSupport();
73.186 +
73.187 + lostNotifListener =
73.188 + new NotificationListener() {
73.189 + public void handleNotification(Notification n, Object hb) {
73.190 + if (n != null && EventClient.NOTIFS_LOST.equals(n.getType())) {
73.191 + Long lost = (Long)n.getUserData();
73.192 + final String msg =
73.193 + "May have lost up to " + lost +
73.194 + " notification" + (lost.longValue() == 1 ? "" : "s");
73.195 + sendNotification(new JMXConnectionNotification(
73.196 + JMXConnectionNotification.NOTIFS_LOST,
73.197 + RMIConnector.this,
73.198 + connectionId,
73.199 + clientNotifCounter++,
73.200 + msg,
73.201 + lost));
73.202 +
73.203 + }
73.204 + }
73.205 + };
73.206 }
73.207
73.208 //--------------------------------------------------------------------
73.209 @@ -2528,6 +2607,11 @@
73.210
73.211 private transient ClientCommunicatorAdmin communicatorAdmin;
73.212
73.213 + private boolean eventServiceEnabled;
73.214 +// private transient EventRelay eventRelay;
73.215 +
73.216 + private transient NotificationListener lostNotifListener;
73.217 +
73.218 /**
73.219 * A static WeakReference to an {@link org.omg.CORBA.ORB ORB} to
73.220 * connect unconnected stubs.
73.221 @@ -2546,4 +2630,7 @@
73.222 private static String strings(final String[] strs) {
73.223 return objects(strs);
73.224 }
73.225 +
73.226 + private static final PerThreadGroupPool<ThreadPoolExecutor> listenerDispatchThreadPool =
73.227 + PerThreadGroupPool.make();
73.228 }
74.1 --- a/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java Tue Aug 19 07:50:03 2008 -0700
74.2 +++ b/src/share/classes/javax/management/remote/rmi/RMIConnectorServer.java Thu Aug 21 09:55:18 2008 -0700
74.3 @@ -230,6 +230,8 @@
74.4
74.5 this.address = url;
74.6 this.rmiServerImpl = rmiServerImpl;
74.7 +
74.8 + installStandardForwarders(this.attributes);
74.9 }
74.10
74.11 /**
74.12 @@ -380,8 +382,8 @@
74.13
74.14 try {
74.15 if (tracing) logger.trace("start", "setting default class loader");
74.16 - defaultClassLoader =
74.17 - EnvHelp.resolveServerClassLoader(attributes, getMBeanServer());
74.18 + defaultClassLoader = EnvHelp.resolveServerClassLoader(
74.19 + attributes, getSystemMBeanServer());
74.20 } catch (InstanceNotFoundException infc) {
74.21 IllegalArgumentException x = new
74.22 IllegalArgumentException("ClassLoader not found: "+infc);
74.23 @@ -396,7 +398,7 @@
74.24 else
74.25 rmiServer = newServer();
74.26
74.27 - rmiServer.setMBeanServer(getMBeanServer());
74.28 + rmiServer.setMBeanServer(getSystemMBeanServer());
74.29 rmiServer.setDefaultClassLoader(defaultClassLoader);
74.30 rmiServer.setRMIConnectorServer(this);
74.31 rmiServer.export();
74.32 @@ -413,7 +415,7 @@
74.33
74.34 final boolean rebind = EnvHelp.computeBooleanFromString(
74.35 attributes,
74.36 - JNDI_REBIND_ATTRIBUTE);
74.37 + JNDI_REBIND_ATTRIBUTE,false);
74.38
74.39 if (tracing)
74.40 logger.trace("start", JNDI_REBIND_ATTRIBUTE + "=" + rebind);
74.41 @@ -590,11 +592,39 @@
74.42 return Collections.unmodifiableMap(map);
74.43 }
74.44
74.45 - public synchronized
74.46 - void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
74.47 + @Override
74.48 + public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
74.49 + MBeanServer oldSMBS = getSystemMBeanServer();
74.50 super.setMBeanServerForwarder(mbsf);
74.51 + if (oldSMBS != getSystemMBeanServer())
74.52 + updateMBeanServer();
74.53 + // If the system chain of MBeanServerForwarders is not empty, then
74.54 + // there is no need to call rmiServerImpl.setMBeanServer, because
74.55 + // it is pointing to the head of the system chain and that has not
74.56 + // changed. (The *end* of the system chain will have been changed
74.57 + // to point to mbsf.)
74.58 + }
74.59 +
74.60 + private void updateMBeanServer() {
74.61 if (rmiServerImpl != null)
74.62 - rmiServerImpl.setMBeanServer(getMBeanServer());
74.63 + rmiServerImpl.setMBeanServer(getSystemMBeanServer());
74.64 + }
74.65 +
74.66 + @Override
74.67 + public synchronized void setSystemMBeanServerForwarder(
74.68 + MBeanServerForwarder mbsf) {
74.69 + super.setSystemMBeanServerForwarder(mbsf);
74.70 + updateMBeanServer();
74.71 + }
74.72 +
74.73 + /**
74.74 + * {@inheritDoc}
74.75 + * @return true, since this connector server does support a system chain
74.76 + * of forwarders.
74.77 + */
74.78 + @Override
74.79 + public boolean supportsSystemMBeanServerForwarder() {
74.80 + return true;
74.81 }
74.82
74.83 /* We repeat the definitions of connection{Opened,Closed,Failed}
75.1 --- a/src/share/classes/sun/tools/jconsole/inspector/TableSorter.java Tue Aug 19 07:50:03 2008 -0700
75.2 +++ b/src/share/classes/sun/tools/jconsole/inspector/TableSorter.java Thu Aug 21 09:55:18 2008 -0700
75.3 @@ -25,71 +25,78 @@
75.4
75.5 package sun.tools.jconsole.inspector;
75.6
75.7 -import java.util.*;
75.8 -import java.awt.event.*;
75.9 -import javax.swing.table.*;
75.10 -import javax.swing.event.*;
75.11
75.12 // Imports for picking up mouse events from the JTable.
75.13
75.14 -import java.awt.event.MouseAdapter;
75.15 import java.awt.event.MouseEvent;
75.16 -import java.awt.event.InputEvent;
75.17 +import java.awt.event.MouseListener;
75.18 +import java.util.Vector;
75.19 import javax.swing.JTable;
75.20 +import javax.swing.event.TableModelEvent;
75.21 +import javax.swing.event.TableModelListener;
75.22 +import javax.swing.table.DefaultTableModel;
75.23 import javax.swing.table.JTableHeader;
75.24 import javax.swing.table.TableColumnModel;
75.25 +import sun.tools.jconsole.JConsole;
75.26
75.27 @SuppressWarnings("serial")
75.28 public class TableSorter extends DefaultTableModel implements MouseListener {
75.29 private boolean ascending = true;
75.30 private TableColumnModel columnModel;
75.31 private JTable tableView;
75.32 - private Vector<TableModelListener> listenerList;
75.33 + private Vector<TableModelListener> evtListenerList;
75.34 private int sortColumn = 0;
75.35
75.36 private int[] invertedIndex;
75.37
75.38 public TableSorter() {
75.39 super();
75.40 - listenerList = new Vector<TableModelListener>();
75.41 + evtListenerList = new Vector<TableModelListener>();
75.42 }
75.43
75.44 public TableSorter(Object[] columnNames, int numRows) {
75.45 super(columnNames,numRows);
75.46 - listenerList = new Vector<TableModelListener>();
75.47 + evtListenerList = new Vector<TableModelListener>();
75.48 }
75.49
75.50 + @Override
75.51 public void newDataAvailable(TableModelEvent e) {
75.52 super.newDataAvailable(e);
75.53 invertedIndex = new int[getRowCount()];
75.54 - for (int i=0;i<invertedIndex.length;i++) {
75.55 + for (int i = 0; i < invertedIndex.length; i++) {
75.56 invertedIndex[i] = i;
75.57 }
75.58 - sort(this.sortColumn);
75.59 + sort(this.sortColumn, this.ascending);
75.60 }
75.61
75.62 + @Override
75.63 public void addTableModelListener(TableModelListener l) {
75.64 - listenerList.add(l);
75.65 + evtListenerList.add(l);
75.66 super.addTableModelListener(l);
75.67 }
75.68
75.69 + @Override
75.70 public void removeTableModelListener(TableModelListener l) {
75.71 - listenerList.remove(l);
75.72 + evtListenerList.remove(l);
75.73 super.removeTableModelListener(l);
75.74 }
75.75
75.76 private void removeListeners() {
75.77 - for(TableModelListener tnl : listenerList)
75.78 + for(TableModelListener tnl : evtListenerList)
75.79 super.removeTableModelListener(tnl);
75.80 }
75.81
75.82 private void restoreListeners() {
75.83 - for(TableModelListener tnl : listenerList)
75.84 + for(TableModelListener tnl : evtListenerList)
75.85 super.addTableModelListener(tnl);
75.86 }
75.87
75.88 @SuppressWarnings("unchecked")
75.89 - public int compare(Object o1, Object o2) {
75.90 + private int compare(Object o1, Object o2) {
75.91 + // take care of the case where both o1 & o2 are null. Needed to keep
75.92 + // the method symetric. Without this quickSort gives surprising results.
75.93 + if (o1 == o2)
75.94 + return 0;
75.95 if (o1==null)
75.96 return 1;
75.97 if (o2==null)
75.98 @@ -104,18 +111,40 @@
75.99 }
75.100 }
75.101
75.102 - public void sort(int column) {
75.103 + private void sort(int column, boolean isAscending) {
75.104 + final XMBeanAttributes attrs =
75.105 + (tableView instanceof XMBeanAttributes)
75.106 + ?(XMBeanAttributes) tableView
75.107 + :null;
75.108 +
75.109 + // We cannot sort rows when a cell is being
75.110 + // edited - so we're going to cancel cell editing here if needed.
75.111 + // This might happen when the user is editing a row, and clicks on
75.112 + // another row without validating. In that case there are two events
75.113 + // that compete: one is the validation of the value that was previously
75.114 + // edited, the other is the mouse click that opens the new editor.
75.115 + //
75.116 + // When we reach here the previous value is already validated, and the
75.117 + // old editor is closed, but the new editor might have opened.
75.118 + // It's this new editor that wil be cancelled here, if needed.
75.119 + //
75.120 + if (attrs != null && attrs.isEditing())
75.121 + attrs.cancelCellEditing();
75.122 +
75.123 // remove registered listeners
75.124 removeListeners();
75.125 // do the sort
75.126 - //n2sort(column);
75.127 - quickSort(0,getRowCount()-1,column);
75.128 +
75.129 + if (JConsole.isDebug()) {
75.130 + System.err.println("sorting table against column="+column
75.131 + +" ascending="+isAscending);
75.132 + }
75.133 + quickSort(0,getRowCount()-1,column,isAscending);
75.134 // restore registered listeners
75.135 restoreListeners();
75.136 - this.sortColumn = column;
75.137 +
75.138 // update row heights in XMBeanAttributes (required by expandable cells)
75.139 - if (tableView instanceof XMBeanAttributes) {
75.140 - XMBeanAttributes attrs = (XMBeanAttributes) tableView;
75.141 + if (attrs != null) {
75.142 for (int i = 0; i < getRowCount(); i++) {
75.143 Vector data = (Vector) dataVector.elementAt(i);
75.144 attrs.updateRowHeight(data.elementAt(1), i);
75.145 @@ -123,21 +152,21 @@
75.146 }
75.147 }
75.148
75.149 - private synchronized boolean compareS(Object s1, Object s2) {
75.150 - if (ascending)
75.151 + private boolean compareS(Object s1, Object s2, boolean isAscending) {
75.152 + if (isAscending)
75.153 return (compare(s1,s2) > 0);
75.154 else
75.155 return (compare(s1,s2) < 0);
75.156 }
75.157
75.158 - private synchronized boolean compareG(Object s1, Object s2) {
75.159 - if (ascending)
75.160 + private boolean compareG(Object s1, Object s2, boolean isAscending) {
75.161 + if (isAscending)
75.162 return (compare(s1,s2) < 0);
75.163 else
75.164 return (compare(s1,s2) > 0);
75.165 }
75.166
75.167 - private synchronized void quickSort(int lo0,int hi0, int key) {
75.168 + private void quickSort(int lo0,int hi0, int key, boolean isAscending) {
75.169 int lo = lo0;
75.170 int hi = hi0;
75.171 Object mid;
75.172 @@ -153,14 +182,14 @@
75.173 * from the left Index.
75.174 */
75.175 while( ( lo < hi0 ) &&
75.176 - ( compareS(mid,getValueAt(lo,key)) ))
75.177 + ( compareS(mid,getValueAt(lo,key), isAscending) ))
75.178 ++lo;
75.179
75.180 /* find an element that is smaller than or equal to
75.181 * the partition element starting from the right Index.
75.182 */
75.183 while( ( hi > lo0 ) &&
75.184 - ( compareG(mid,getValueAt(hi,key)) ))
75.185 + ( compareG(mid,getValueAt(hi,key), isAscending) ))
75.186 --hi;
75.187
75.188 // if the indexes have not crossed, swap
75.189 @@ -177,27 +206,17 @@
75.190 * must now sort the left partition.
75.191 */
75.192 if( lo0 < hi )
75.193 - quickSort(lo0, hi , key);
75.194 + quickSort(lo0, hi , key, isAscending);
75.195
75.196 /* If the left index has not reached the right
75.197 * side of array
75.198 * must now sort the right partition.
75.199 */
75.200 if( lo <= hi0 )
75.201 - quickSort(lo, hi0 , key);
75.202 + quickSort(lo, hi0 , key, isAscending);
75.203 }
75.204 }
75.205
75.206 - public void n2sort(int column) {
75.207 - for (int i = 0; i < getRowCount(); i++) {
75.208 - for (int j = i+1; j < getRowCount(); j++) {
75.209 - if (compare(getValueAt(i,column),getValueAt(j,column)) == -1) {
75.210 - swap(i, j, column);
75.211 - }
75.212 - }
75.213 - }
75.214 - }
75.215 -
75.216 private Vector getRow(int row) {
75.217 return (Vector) dataVector.elementAt(row);
75.218 }
75.219 @@ -207,7 +226,7 @@
75.220 dataVector.setElementAt(data,row);
75.221 }
75.222
75.223 - public void swap(int i, int j, int column) {
75.224 + private void swap(int i, int j, int column) {
75.225 Vector data = getRow(i);
75.226 setRow(getRow(j),i);
75.227 setRow(data,j);
75.228 @@ -223,11 +242,12 @@
75.229
75.230 public void sortByColumn(int column, boolean ascending) {
75.231 this.ascending = ascending;
75.232 - sort(column);
75.233 + this.sortColumn = column;
75.234 + sort(column,ascending);
75.235 }
75.236
75.237 - public int[] getInvertedIndex() {
75.238 - return invertedIndex;
75.239 + public int getIndexOfRow(int row) {
75.240 + return invertedIndex[row];
75.241 }
75.242
75.243 // Add a mouse listener to the Table to trigger a table sort
75.244 @@ -243,6 +263,14 @@
75.245 int viewColumn = columnModel.getColumnIndexAtX(e.getX());
75.246 int column = tableView.convertColumnIndexToModel(viewColumn);
75.247 if (e.getClickCount() == 1 && column != -1) {
75.248 + if (tableView instanceof XTable) {
75.249 + XTable attrs = (XTable) tableView;
75.250 + // inform the table view that the rows are going to be sorted
75.251 + // against the values in a given column. This gives the
75.252 + // chance to the table view to close its editor - if needed.
75.253 + //
75.254 + attrs.sortRequested(column);
75.255 + }
75.256 tableView.invalidate();
75.257 sortByColumn(column);
75.258 tableView.validate();
76.1 --- a/src/share/classes/sun/tools/jconsole/inspector/XMBeanAttributes.java Tue Aug 19 07:50:03 2008 -0700
76.2 +++ b/src/share/classes/sun/tools/jconsole/inspector/XMBeanAttributes.java Thu Aug 21 09:55:18 2008 -0700
76.3 @@ -25,31 +25,49 @@
76.4
76.5 package sun.tools.jconsole.inspector;
76.6
76.7 -import javax.swing.*;
76.8 -import javax.swing.event.*;
76.9 -import javax.swing.table.*;
76.10 -import javax.swing.tree.*;
76.11 -import java.awt.BorderLayout;
76.12 -import java.awt.Color;
76.13 -import java.awt.GridLayout;
76.14 -import java.awt.FlowLayout;
76.15 +
76.16 import java.awt.Component;
76.17 import java.awt.EventQueue;
76.18 -import java.awt.event.*;
76.19 -import java.awt.Insets;
76.20 import java.awt.Dimension;
76.21 -import java.util.*;
76.22 -import java.io.*;
76.23 +import java.awt.event.MouseAdapter;
76.24 +import java.awt.event.MouseEvent;
76.25 +import java.io.IOException;
76.26
76.27 import java.lang.reflect.Array;
76.28
76.29 -import javax.management.*;
76.30 +import java.util.EventObject;
76.31 +import java.util.HashMap;
76.32 +import java.util.WeakHashMap;
76.33 +
76.34 +import java.util.concurrent.ExecutionException;
76.35 +import java.util.logging.Level;
76.36 +import java.util.logging.Logger;
76.37 +import javax.management.JMException;
76.38 +import javax.management.MBeanInfo;
76.39 +import javax.management.MBeanAttributeInfo;
76.40 +import javax.management.AttributeList;
76.41 +import javax.management.Attribute;
76.42 import javax.management.openmbean.CompositeData;
76.43 import javax.management.openmbean.TabularData;
76.44
76.45 +import javax.swing.JComponent;
76.46 +import javax.swing.JOptionPane;
76.47 +import javax.swing.JTable;
76.48 +import javax.swing.JTextField;
76.49 +import javax.swing.SwingWorker;
76.50 +import javax.swing.event.ChangeEvent;
76.51 +import javax.swing.event.TableModelEvent;
76.52 +import javax.swing.event.TableModelListener;
76.53 +import javax.swing.table.DefaultTableCellRenderer;
76.54 +import javax.swing.table.DefaultTableModel;
76.55 +import javax.swing.table.TableCellEditor;
76.56 +import javax.swing.table.TableCellRenderer;
76.57 +import javax.swing.table.TableColumn;
76.58 +import javax.swing.table.TableColumnModel;
76.59 +import javax.swing.table.TableModel;
76.60 +
76.61 import sun.tools.jconsole.Resources;
76.62 import sun.tools.jconsole.MBeansTab;
76.63 -import sun.tools.jconsole.Plotter;
76.64 import sun.tools.jconsole.JConsole;
76.65 import sun.tools.jconsole.ProxyClient.SnapshotMBeanServerConnection;
76.66
76.67 @@ -61,12 +79,14 @@
76.68 COMPULSORY to not call the JMX world in synchronized blocks */
76.69 @SuppressWarnings("serial")
76.70 public class XMBeanAttributes extends XTable {
76.71 +
76.72 + final Logger LOGGER =
76.73 + Logger.getLogger(XMBeanAttributes.class.getPackage().getName());
76.74 +
76.75 private final static String[] columnNames =
76.76 {Resources.getText("Name"),
76.77 Resources.getText("Value")};
76.78
76.79 - private boolean editable = true;
76.80 -
76.81 private XMBean mbean;
76.82 private MBeanInfo mbeanInfo;
76.83 private MBeanAttributeInfo[] attributesInfo;
76.84 @@ -75,9 +95,8 @@
76.85 private HashMap<String, Object> viewableAttributes;
76.86 private WeakHashMap<XMBean, HashMap<String, ZoomedCell>> viewersCache =
76.87 new WeakHashMap<XMBean, HashMap<String, ZoomedCell>>();
76.88 - private TableModelListener attributesListener;
76.89 + private final TableModelListener attributesListener;
76.90 private MBeansTab mbeansTab;
76.91 - private XTable table;
76.92 private TableCellEditor valueCellEditor = new ValueCellEditor();
76.93 private int rowMinHeight = -1;
76.94 private AttributesMouseListener mouseListener = new AttributesMouseListener();
76.95 @@ -89,8 +108,8 @@
76.96 super();
76.97 this.mbeansTab = mbeansTab;
76.98 ((DefaultTableModel)getModel()).setColumnIdentifiers(columnNames);
76.99 - getModel().addTableModelListener(attributesListener =
76.100 - new AttributesListener(this));
76.101 + attributesListener = new AttributesListener(this);
76.102 + getModel().addTableModelListener(attributesListener);
76.103 getColumnModel().getColumn(NAME_COLUMN).setPreferredWidth(40);
76.104
76.105 addMouseListener(mouseListener);
76.106 @@ -99,6 +118,7 @@
76.107 addKeyListener(new Utils.CopyKeyAdapter());
76.108 }
76.109
76.110 + @Override
76.111 public synchronized Component prepareRenderer(TableCellRenderer renderer,
76.112 int row, int column) {
76.113 //In case we have a repaint thread that is in the process of
76.114 @@ -124,6 +144,7 @@
76.115 setRowHeight(row, rowMinHeight);
76.116 }
76.117
76.118 + @Override
76.119 public synchronized TableCellRenderer getCellRenderer(int row,
76.120 int column) {
76.121 //In case we have a repaint thread that is in the process of
76.122 @@ -169,26 +190,40 @@
76.123 }
76.124
76.125 public void cancelCellEditing() {
76.126 - TableCellEditor editor = getCellEditor();
76.127 - if (editor != null) {
76.128 - editor.cancelCellEditing();
76.129 + if (LOGGER.isLoggable(Level.FINER)) {
76.130 + LOGGER.finer("Cancel Editing Row: "+getEditingRow());
76.131 + }
76.132 + final TableCellEditor tableCellEditor = getCellEditor();
76.133 + if (tableCellEditor != null) {
76.134 + tableCellEditor.cancelCellEditing();
76.135 }
76.136 }
76.137
76.138 public void stopCellEditing() {
76.139 - TableCellEditor editor = getCellEditor();
76.140 - if (editor != null) {
76.141 - editor.stopCellEditing();
76.142 + if (LOGGER.isLoggable(Level.FINER)) {
76.143 + LOGGER.finer("Stop Editing Row: "+getEditingRow());
76.144 + }
76.145 + final TableCellEditor tableCellEditor = getCellEditor();
76.146 + if (tableCellEditor != null) {
76.147 + tableCellEditor.stopCellEditing();
76.148 }
76.149 }
76.150
76.151 - public final boolean editCellAt(int row, int column, EventObject e) {
76.152 + @Override
76.153 + public final boolean editCellAt(final int row, final int column, EventObject e) {
76.154 + if (LOGGER.isLoggable(Level.FINER)) {
76.155 + LOGGER.finer("editCellAt(row="+row+", col="+column+
76.156 + ", e="+e+")");
76.157 + }
76.158 + if (JConsole.isDebug()) {
76.159 + System.err.println("edit: "+getValueName(row)+"="+getValue(row));
76.160 + }
76.161 boolean retVal = super.editCellAt(row, column, e);
76.162 if (retVal) {
76.163 - TableCellEditor editor =
76.164 + final TableCellEditor tableCellEditor =
76.165 getColumnModel().getColumn(column).getCellEditor();
76.166 - if (editor == valueCellEditor) {
76.167 - ((JComponent) editor).requestFocus();
76.168 + if (tableCellEditor == valueCellEditor) {
76.169 + ((JComponent) tableCellEditor).requestFocus();
76.170 }
76.171 }
76.172 return retVal;
76.173 @@ -213,6 +248,10 @@
76.174 public void setValueAt(Object value, int row, int column) {
76.175 if (!isCellError(row, column) && isColumnEditable(column) &&
76.176 isWritable(row) && Utils.isEditableType(getClassName(row))) {
76.177 + if (JConsole.isDebug()) {
76.178 + System.err.println("validating [row="+row+", column="+column+
76.179 + "]: "+getValueName(row)+"="+value);
76.180 + }
76.181 super.setValueAt(value, row, column);
76.182 }
76.183 }
76.184 @@ -256,12 +295,14 @@
76.185 }
76.186 }
76.187
76.188 -
76.189 public Object getValue(int row) {
76.190 - return ((DefaultTableModel) getModel()).getValueAt(row, VALUE_COLUMN);
76.191 + final Object val = ((DefaultTableModel) getModel())
76.192 + .getValueAt(row, VALUE_COLUMN);
76.193 + return val;
76.194 }
76.195
76.196 //tool tip only for editable column
76.197 + @Override
76.198 public String getToolTip(int row, int column) {
76.199 if (isCellError(row, column)) {
76.200 return (String) unavailableAttributes.get(getValueName(row));
76.201 @@ -302,6 +343,7 @@
76.202 * Override JTable method in order to make any call to this method
76.203 * atomic with TableModel elements.
76.204 */
76.205 + @Override
76.206 public synchronized int getRowCount() {
76.207 return super.getRowCount();
76.208 }
76.209 @@ -332,24 +374,67 @@
76.210 return isViewable;
76.211 }
76.212
76.213 - public void loadAttributes(final XMBean mbean, MBeanInfo mbeanInfo) {
76.214 + // Call this in EDT
76.215 + public void loadAttributes(final XMBean mbean, final MBeanInfo mbeanInfo) {
76.216 +
76.217 + final SwingWorker<Runnable,Void> load =
76.218 + new SwingWorker<Runnable,Void>() {
76.219 + @Override
76.220 + protected Runnable doInBackground() throws Exception {
76.221 + return doLoadAttributes(mbean,mbeanInfo);
76.222 + }
76.223 +
76.224 + @Override
76.225 + protected void done() {
76.226 + try {
76.227 + final Runnable updateUI = get();
76.228 + if (updateUI != null) updateUI.run();
76.229 + } catch (RuntimeException x) {
76.230 + throw x;
76.231 + } catch (ExecutionException x) {
76.232 + if(JConsole.isDebug()) {
76.233 + System.err.println(
76.234 + "Exception raised while loading attributes: "
76.235 + +x.getCause());
76.236 + x.printStackTrace();
76.237 + }
76.238 + } catch (InterruptedException x) {
76.239 + if(JConsole.isDebug()) {
76.240 + System.err.println(
76.241 + "Interrupted while loading attributes: "+x);
76.242 + x.printStackTrace();
76.243 + }
76.244 + }
76.245 + }
76.246 +
76.247 + };
76.248 + mbeansTab.workerAdd(load);
76.249 + }
76.250 +
76.251 + // Don't call this in EDT, but execute returned Runnable inside
76.252 + // EDT - typically in the done() method of a SwingWorker
76.253 + // This method can return null.
76.254 + private Runnable doLoadAttributes(final XMBean mbean, MBeanInfo infoOrNull)
76.255 + throws JMException, IOException {
76.256 // To avoid deadlock with events coming from the JMX side,
76.257 // we retrieve all JMX stuff in a non synchronized block.
76.258
76.259 - if(mbean == null) return;
76.260 + if(mbean == null) return null;
76.261 + final MBeanInfo curMBeanInfo =
76.262 + (infoOrNull==null)?mbean.getMBeanInfo():infoOrNull;
76.263
76.264 - final MBeanAttributeInfo[] attributesInfo = mbeanInfo.getAttributes();
76.265 - final HashMap<String, Object> attributes =
76.266 - new HashMap<String, Object>(attributesInfo.length);
76.267 - final HashMap<String, Object> unavailableAttributes =
76.268 - new HashMap<String, Object>(attributesInfo.length);
76.269 - final HashMap<String, Object> viewableAttributes =
76.270 - new HashMap<String, Object>(attributesInfo.length);
76.271 + final MBeanAttributeInfo[] attrsInfo = curMBeanInfo.getAttributes();
76.272 + final HashMap<String, Object> attrs =
76.273 + new HashMap<String, Object>(attrsInfo.length);
76.274 + final HashMap<String, Object> unavailableAttrs =
76.275 + new HashMap<String, Object>(attrsInfo.length);
76.276 + final HashMap<String, Object> viewableAttrs =
76.277 + new HashMap<String, Object>(attrsInfo.length);
76.278 AttributeList list = null;
76.279
76.280 try {
76.281 - list = mbean.getAttributes(attributesInfo);
76.282 - } catch (Exception e) {
76.283 + list = mbean.getAttributes(attrsInfo);
76.284 + }catch(Exception e) {
76.285 if (JConsole.isDebug()) {
76.286 System.err.println("Error calling getAttributes() on MBean \"" +
76.287 mbean.getObjectName() + "\". JConsole will " +
76.288 @@ -359,18 +444,18 @@
76.289 }
76.290 list = new AttributeList();
76.291 //Can't load all attributes, do it one after each other.
76.292 - for(int i = 0; i < attributesInfo.length; i++) {
76.293 + for(int i = 0; i < attrsInfo.length; i++) {
76.294 String name = null;
76.295 try {
76.296 - name = attributesInfo[i].getName();
76.297 + name = attrsInfo[i].getName();
76.298 Object value =
76.299 - mbean.getMBeanServerConnection().getAttribute(mbean.getObjectName(), name);
76.300 + mbean.getMBeanServerConnection().
76.301 + getAttribute(mbean.getObjectName(), name);
76.302 list.add(new Attribute(name, value));
76.303 }catch(Exception ex) {
76.304 - if(attributesInfo[i].isReadable()) {
76.305 - unavailableAttributes.put(name,
76.306 - Utils.getActualException(ex).
76.307 - toString());
76.308 + if(attrsInfo[i].isReadable()) {
76.309 + unavailableAttrs.put(name,
76.310 + Utils.getActualException(ex).toString());
76.311 }
76.312 }
76.313 }
76.314 @@ -380,22 +465,22 @@
76.315 for (int i=0;i<att_length;i++) {
76.316 Attribute attribute = (Attribute) list.get(i);
76.317 if(isViewable(attribute)) {
76.318 - viewableAttributes.put(attribute.getName(),
76.319 + viewableAttrs.put(attribute.getName(),
76.320 attribute.getValue());
76.321 }
76.322 else
76.323 - attributes.put(attribute.getName(),attribute.getValue());
76.324 + attrs.put(attribute.getName(),attribute.getValue());
76.325
76.326 }
76.327 // if not all attributes are accessible,
76.328 // check them one after the other.
76.329 - if (att_length < attributesInfo.length) {
76.330 - for (int i=0;i<attributesInfo.length;i++) {
76.331 - MBeanAttributeInfo attributeInfo = attributesInfo[i];
76.332 - if (!attributes.containsKey(attributeInfo.getName()) &&
76.333 - !viewableAttributes.containsKey(attributeInfo.
76.334 + if (att_length < attrsInfo.length) {
76.335 + for (int i=0;i<attrsInfo.length;i++) {
76.336 + MBeanAttributeInfo attributeInfo = attrsInfo[i];
76.337 + if (!attrs.containsKey(attributeInfo.getName()) &&
76.338 + !viewableAttrs.containsKey(attributeInfo.
76.339 getName()) &&
76.340 - !unavailableAttributes.containsKey(attributeInfo.
76.341 + !unavailableAttrs.containsKey(attributeInfo.
76.342 getName())) {
76.343 if (attributeInfo.isReadable()) {
76.344 // getAttributes didn't help resolving the
76.345 @@ -408,16 +493,13 @@
76.346 mbean.getObjectName(), attributeInfo.getName());
76.347 //What happens if now it is ok?
76.348 // Be pragmatic, add it to readable...
76.349 - attributes.put(attributeInfo.getName(),
76.350 + attrs.put(attributeInfo.getName(),
76.351 v);
76.352 }catch(Exception e) {
76.353 //Put the exception that will be displayed
76.354 // in tooltip
76.355 - unavailableAttributes.put(attributeInfo.
76.356 - getName(),
76.357 - Utils.
76.358 - getActualException(e)
76.359 - .toString());
76.360 + unavailableAttrs.put(attributeInfo.getName(),
76.361 + Utils.getActualException(e).toString());
76.362 }
76.363 }
76.364 }
76.365 @@ -426,10 +508,10 @@
76.366 }
76.367 catch(Exception e) {
76.368 //sets all attributes unavailable except the writable ones
76.369 - for (int i=0;i<attributesInfo.length;i++) {
76.370 - MBeanAttributeInfo attributeInfo = attributesInfo[i];
76.371 + for (int i=0;i<attrsInfo.length;i++) {
76.372 + MBeanAttributeInfo attributeInfo = attrsInfo[i];
76.373 if (attributeInfo.isReadable()) {
76.374 - unavailableAttributes.put(attributeInfo.getName(),
76.375 + unavailableAttrs.put(attributeInfo.getName(),
76.376 Utils.getActualException(e).
76.377 toString());
76.378 }
76.379 @@ -438,40 +520,36 @@
76.380 //end of retrieval
76.381
76.382 //one update at a time
76.383 - synchronized(this) {
76.384 + return new Runnable() {
76.385 + public void run() {
76.386 + synchronized (XMBeanAttributes.this) {
76.387 + XMBeanAttributes.this.mbean = mbean;
76.388 + XMBeanAttributes.this.mbeanInfo = curMBeanInfo;
76.389 + XMBeanAttributes.this.attributesInfo = attrsInfo;
76.390 + XMBeanAttributes.this.attributes = attrs;
76.391 + XMBeanAttributes.this.unavailableAttributes = unavailableAttrs;
76.392 + XMBeanAttributes.this.viewableAttributes = viewableAttrs;
76.393
76.394 - this.mbean = mbean;
76.395 - this.mbeanInfo = mbeanInfo;
76.396 - this.attributesInfo = attributesInfo;
76.397 - this.attributes = attributes;
76.398 - this.unavailableAttributes = unavailableAttributes;
76.399 - this.viewableAttributes = viewableAttributes;
76.400 -
76.401 - EventQueue.invokeLater(new Runnable() {
76.402 - public void run() {
76.403 DefaultTableModel tableModel =
76.404 (DefaultTableModel) getModel();
76.405
76.406 - // don't listen to these events
76.407 - tableModel.removeTableModelListener(attributesListener);
76.408 -
76.409 // add attribute information
76.410 - emptyTable();
76.411 + emptyTable(tableModel);
76.412
76.413 addTableData(tableModel,
76.414 - mbean,
76.415 - attributesInfo,
76.416 - attributes,
76.417 - unavailableAttributes,
76.418 - viewableAttributes);
76.419 + mbean,
76.420 + attrsInfo,
76.421 + attrs,
76.422 + unavailableAttrs,
76.423 + viewableAttrs);
76.424
76.425 // update the model with the new data
76.426 tableModel.newDataAvailable(new TableModelEvent(tableModel));
76.427 // re-register for change events
76.428 tableModel.addTableModelListener(attributesListener);
76.429 }
76.430 - });
76.431 - }
76.432 + }
76.433 + };
76.434 }
76.435
76.436 void collapse(String attributeName, final Component c) {
76.437 @@ -534,21 +612,79 @@
76.438 return cell;
76.439 }
76.440
76.441 - public void refreshAttributes() {
76.442 - SnapshotMBeanServerConnection mbsc = mbeansTab.getSnapshotMBeanServerConnection();
76.443 - mbsc.flush();
76.444 - stopCellEditing();
76.445 - loadAttributes(mbean, mbeanInfo);
76.446 + // This is called by XSheet when the "refresh" button is pressed.
76.447 + // In this case we will commit any pending attribute values by
76.448 + // calling 'stopCellEditing'.
76.449 + //
76.450 + public void refreshAttributes() {
76.451 + refreshAttributes(true);
76.452 + }
76.453 +
76.454 + // refreshAttributes(false) is called by tableChanged().
76.455 + // in this case we must not call stopCellEditing, because it's already
76.456 + // been called - e.g.
76.457 + // lostFocus/mousePressed -> stopCellEditing -> setValueAt -> tableChanged
76.458 + // -> refreshAttributes(false)
76.459 + //
76.460 + // Can be called in EDT - as long as the implementation of
76.461 + // mbeansTab.getCachedMBeanServerConnection() and mbsc.flush() doesn't
76.462 + // change
76.463 + //
76.464 + private void refreshAttributes(final boolean stopCellEditing) {
76.465 + SwingWorker<Void,Void> sw = new SwingWorker<Void,Void>() {
76.466 +
76.467 + @Override
76.468 + protected Void doInBackground() throws Exception {
76.469 + SnapshotMBeanServerConnection mbsc =
76.470 + mbeansTab.getSnapshotMBeanServerConnection();
76.471 + mbsc.flush();
76.472 + return null;
76.473 + }
76.474 +
76.475 + @Override
76.476 + protected void done() {
76.477 + try {
76.478 + get();
76.479 + if (stopCellEditing) stopCellEditing();
76.480 + loadAttributes(mbean, mbeanInfo);
76.481 + } catch (Exception x) {
76.482 + if (JConsole.isDebug()) {
76.483 + x.printStackTrace();
76.484 + }
76.485 + }
76.486 + }
76.487 + };
76.488 + mbeansTab.workerAdd(sw);
76.489 + }
76.490 + // We need to call stop editing here - otherwise edits are lost
76.491 + // when resizing the table.
76.492 + //
76.493 + @Override
76.494 + public void columnMarginChanged(ChangeEvent e) {
76.495 + if (isEditing()) stopCellEditing();
76.496 + super.columnMarginChanged(e);
76.497 + }
76.498 +
76.499 + // We need to call stop editing here - otherwise the edited value
76.500 + // is transferred to the wrong row...
76.501 + //
76.502 + @Override
76.503 + void sortRequested(int column) {
76.504 + if (isEditing()) stopCellEditing();
76.505 + super.sortRequested(column);
76.506 + }
76.507 +
76.508 +
76.509 + @Override
76.510 + public synchronized void emptyTable() {
76.511 + emptyTable((DefaultTableModel)getModel());
76.512 }
76.513
76.514 -
76.515 - public void emptyTable() {
76.516 - synchronized(this) {
76.517 - ((DefaultTableModel) getModel()).
76.518 - removeTableModelListener(attributesListener);
76.519 - super.emptyTable();
76.520 - }
76.521 - }
76.522 + // Call this in synchronized block.
76.523 + private void emptyTable(DefaultTableModel model) {
76.524 + model.removeTableModelListener(attributesListener);
76.525 + super.emptyTable();
76.526 + }
76.527
76.528 private boolean isViewable(Attribute attribute) {
76.529 Object data = attribute.getValue();
76.530 @@ -659,6 +795,7 @@
76.531
76.532 class AttributesMouseListener extends MouseAdapter {
76.533
76.534 + @Override
76.535 public void mousePressed(MouseEvent e) {
76.536 if(e.getButton() == MouseEvent.BUTTON1) {
76.537 if(e.getClickCount() >= 2) {
76.538 @@ -674,8 +811,10 @@
76.539 }
76.540 }
76.541
76.542 + @SuppressWarnings("serial")
76.543 class ValueCellEditor extends XTextFieldEditor {
76.544 // implements javax.swing.table.TableCellEditor
76.545 + @Override
76.546 public Component getTableCellEditorComponent(JTable table,
76.547 Object value,
76.548 boolean isSelected,
76.549 @@ -727,6 +866,7 @@
76.550 }
76.551 }
76.552
76.553 + @SuppressWarnings("serial")
76.554 class MaximizedCellRenderer extends DefaultTableCellRenderer {
76.555 Component comp;
76.556 MaximizedCellRenderer(Component comp) {
76.557 @@ -736,6 +876,7 @@
76.558 comp.setPreferredSize(new Dimension((int) d.getWidth(), 200));
76.559 }
76.560 }
76.561 + @Override
76.562 public Component getTableCellRendererComponent(JTable table,
76.563 Object value,
76.564 boolean isSelected,
76.565 @@ -818,6 +959,7 @@
76.566 return minHeight;
76.567 }
76.568
76.569 + @Override
76.570 public String toString() {
76.571
76.572 if(value == null) return null;
76.573 @@ -854,45 +996,82 @@
76.574 this.component = component;
76.575 }
76.576
76.577 + // Call this in EDT
76.578 public void tableChanged(final TableModelEvent e) {
76.579 - final TableModel model = (TableModel)e.getSource();
76.580 // only post changes to the draggable column
76.581 if (isColumnEditable(e.getColumn())) {
76.582 - mbeansTab.workerAdd(new Runnable() {
76.583 - public void run() {
76.584 - try {
76.585 - Object tableValue =
76.586 - model.getValueAt(e.getFirstRow(),
76.587 - e.getColumn());
76.588 - // if it's a String, try construct new value
76.589 - // using the defined type.
76.590 - if (tableValue instanceof String) {
76.591 - tableValue =
76.592 - Utils.createObjectFromString(getClassName(e.getFirstRow()), // type
76.593 - (String)tableValue);// value
76.594 - }
76.595 - String attributeName =
76.596 - getValueName(e.getFirstRow());
76.597 - Attribute attribute =
76.598 - new Attribute(attributeName,tableValue);
76.599 - mbean.setAttribute(attribute);
76.600 - }
76.601 - catch (Throwable ex) {
76.602 - if (JConsole.isDebug()) {
76.603 - ex.printStackTrace();
76.604 - }
76.605 - ex = Utils.getActualException(ex);
76.606 + final TableModel model = (TableModel)e.getSource();
76.607 + Object tableValue = model.getValueAt(e.getFirstRow(),
76.608 + e.getColumn());
76.609
76.610 - String message = (ex.getMessage() != null) ? ex.getMessage() : ex.toString();
76.611 - EventQueue.invokeLater(new ThreadDialog(component,
76.612 - message+"\n",
76.613 - Resources.getText("Problem setting attribute"),
76.614 - JOptionPane.ERROR_MESSAGE));
76.615 - }
76.616 - refreshAttributes();
76.617 + if (LOGGER.isLoggable(Level.FINER)) {
76.618 + LOGGER.finer("tableChanged: firstRow="+e.getFirstRow()+
76.619 + ", lastRow="+e.getLastRow()+", column="+e.getColumn()+
76.620 + ", value="+tableValue);
76.621 + }
76.622 + // if it's a String, try construct new value
76.623 + // using the defined type.
76.624 + if (tableValue instanceof String) {
76.625 + try {
76.626 + tableValue =
76.627 + Utils.createObjectFromString(getClassName(e.getFirstRow()), // type
76.628 + (String)tableValue);// value
76.629 + } catch (Throwable ex) {
76.630 + popupAndLog(ex,"tableChanged",
76.631 + "Problem setting attribute");
76.632 + }
76.633 + }
76.634 + final String attributeName = getValueName(e.getFirstRow());
76.635 + final Attribute attribute =
76.636 + new Attribute(attributeName,tableValue);
76.637 + setAttribute(attribute, "tableChanged");
76.638 + }
76.639 + }
76.640 +
76.641 + // Call this in EDT
76.642 + private void setAttribute(final Attribute attribute, final String method) {
76.643 + final SwingWorker<Void,Void> setAttribute =
76.644 + new SwingWorker<Void,Void>() {
76.645 + @Override
76.646 + protected Void doInBackground() throws Exception {
76.647 + try {
76.648 + if (JConsole.isDebug()) {
76.649 + System.err.println("setAttribute("+
76.650 + attribute.getName()+
76.651 + "="+attribute.getValue()+")");
76.652 }
76.653 - });
76.654 - }
76.655 + mbean.setAttribute(attribute);
76.656 + } catch (Throwable ex) {
76.657 + popupAndLog(ex,method,"Problem setting attribute");
76.658 + }
76.659 + return null;
76.660 + }
76.661 + @Override
76.662 + protected void done() {
76.663 + try {
76.664 + get();
76.665 + } catch (Exception x) {
76.666 + if (JConsole.isDebug())
76.667 + x.printStackTrace();
76.668 + }
76.669 + refreshAttributes(false);
76.670 + }
76.671 +
76.672 + };
76.673 + mbeansTab.workerAdd(setAttribute);
76.674 + }
76.675 +
76.676 + // Call this outside EDT
76.677 + private void popupAndLog(Throwable ex, String method, String key) {
76.678 + ex = Utils.getActualException(ex);
76.679 + if (JConsole.isDebug()) ex.printStackTrace();
76.680 +
76.681 + String message = (ex.getMessage() != null) ? ex.getMessage()
76.682 + : ex.toString();
76.683 + EventQueue.invokeLater(
76.684 + new ThreadDialog(component, message+"\n",
76.685 + Resources.getText(key),
76.686 + JOptionPane.ERROR_MESSAGE));
76.687 }
76.688 }
76.689 }
77.1 --- a/src/share/classes/sun/tools/jconsole/inspector/XPlotter.java Tue Aug 19 07:50:03 2008 -0700
77.2 +++ b/src/share/classes/sun/tools/jconsole/inspector/XPlotter.java Thu Aug 21 09:55:18 2008 -0700
77.3 @@ -37,6 +37,7 @@
77.4 super(unit);
77.5 this.table = table;
77.6 }
77.7 + @Override
77.8 public void addValues(long time, long... values) {
77.9 super.addValues(time, values);
77.10 table.repaint();
78.1 --- a/src/share/classes/sun/tools/jconsole/inspector/XSheet.java Tue Aug 19 07:50:03 2008 -0700
78.2 +++ b/src/share/classes/sun/tools/jconsole/inspector/XSheet.java Thu Aug 21 09:55:18 2008 -0700
78.3 @@ -25,18 +25,39 @@
78.4
78.5 package sun.tools.jconsole.inspector;
78.6
78.7 -import java.awt.*;
78.8 -import java.awt.event.*;
78.9 -import java.io.*;
78.10 -import javax.management.*;
78.11 -import javax.swing.*;
78.12 -import javax.swing.border.*;
78.13 -import javax.swing.tree.*;
78.14 +
78.15 +import java.awt.BorderLayout;
78.16 +import java.awt.Color;
78.17 +import java.awt.Component;
78.18 +import java.awt.Dimension;
78.19 +import java.awt.event.ActionEvent;
78.20 +import java.awt.event.ActionListener;
78.21 +import java.io.IOException;
78.22 +
78.23 +import javax.management.IntrospectionException;
78.24 +import javax.management.NotificationListener;
78.25 +import javax.management.MBeanInfo;
78.26 +import javax.management.InstanceNotFoundException;
78.27 +import javax.management.ReflectionException;
78.28 +import javax.management.MBeanAttributeInfo;
78.29 +import javax.management.MBeanOperationInfo;
78.30 +import javax.management.MBeanNotificationInfo;
78.31 +import javax.management.Notification;
78.32 +import javax.swing.BorderFactory;
78.33 +import javax.swing.JButton;
78.34 +import javax.swing.JOptionPane;
78.35 +import javax.swing.JPanel;
78.36 +import javax.swing.JScrollPane;
78.37 +import javax.swing.JTextArea;
78.38 +import javax.swing.SwingWorker;
78.39 +import javax.swing.border.LineBorder;
78.40 +import javax.swing.tree.DefaultMutableTreeNode;
78.41 +import javax.swing.tree.DefaultTreeModel;
78.42 +
78.43 import sun.tools.jconsole.*;
78.44 import sun.tools.jconsole.inspector.XNodeInfo.Type;
78.45
78.46 import static sun.tools.jconsole.Resources.*;
78.47 -import static sun.tools.jconsole.Utilities.*;
78.48
78.49 @SuppressWarnings("serial")
78.50 public class XSheet extends JPanel
78.51 @@ -344,34 +365,41 @@
78.52 return;
78.53 }
78.54 mbean = (XMBean) uo.getData();
78.55 - SwingWorker<Void, Void> sw = new SwingWorker<Void, Void>() {
78.56 + final XMBean xmb = mbean;
78.57 + SwingWorker<MBeanInfo,Void> sw = new SwingWorker<MBeanInfo,Void>() {
78.58 @Override
78.59 - public Void doInBackground() throws InstanceNotFoundException,
78.60 + public MBeanInfo doInBackground() throws InstanceNotFoundException,
78.61 IntrospectionException, ReflectionException, IOException {
78.62 - mbeanAttributes.loadAttributes(mbean, mbean.getMBeanInfo());
78.63 - return null;
78.64 + MBeanInfo mbi = xmb.getMBeanInfo();
78.65 + return mbi;
78.66 }
78.67 @Override
78.68 protected void done() {
78.69 try {
78.70 - get();
78.71 - if (!isSelectedNode(node, currentNode)) {
78.72 - return;
78.73 + MBeanInfo mbi = get();
78.74 + if (mbi != null && mbi.getAttributes() != null &&
78.75 + mbi.getAttributes().length > 0) {
78.76 +
78.77 + mbeanAttributes.loadAttributes(xmb, mbi);
78.78 +
78.79 + if (!isSelectedNode(node, currentNode)) {
78.80 + return;
78.81 + }
78.82 + invalidate();
78.83 + mainPanel.removeAll();
78.84 + JPanel borderPanel = new JPanel(new BorderLayout());
78.85 + borderPanel.setBorder(BorderFactory.createTitledBorder(
78.86 + Resources.getText("Attribute values")));
78.87 + borderPanel.add(new JScrollPane(mbeanAttributes));
78.88 + mainPanel.add(borderPanel, BorderLayout.CENTER);
78.89 + // add the refresh button to the south panel
78.90 + southPanel.removeAll();
78.91 + southPanel.add(refreshButton, BorderLayout.SOUTH);
78.92 + southPanel.setVisible(true);
78.93 + refreshButton.setEnabled(true);
78.94 + validate();
78.95 + repaint();
78.96 }
78.97 - invalidate();
78.98 - mainPanel.removeAll();
78.99 - JPanel borderPanel = new JPanel(new BorderLayout());
78.100 - borderPanel.setBorder(BorderFactory.createTitledBorder(
78.101 - Resources.getText("Attribute values")));
78.102 - borderPanel.add(new JScrollPane(mbeanAttributes));
78.103 - mainPanel.add(borderPanel, BorderLayout.CENTER);
78.104 - // add the refresh button to the south panel
78.105 - southPanel.removeAll();
78.106 - southPanel.add(refreshButton, BorderLayout.SOUTH);
78.107 - southPanel.setVisible(true);
78.108 - refreshButton.setEnabled(true);
78.109 - validate();
78.110 - repaint();
78.111 } catch (Exception e) {
78.112 Throwable t = Utils.getActualException(e);
78.113 if (JConsole.isDebug()) {
78.114 @@ -704,13 +732,7 @@
78.115 JButton button = (JButton) e.getSource();
78.116 // Refresh button
78.117 if (button == refreshButton) {
78.118 - new SwingWorker<Void, Void>() {
78.119 - @Override
78.120 - public Void doInBackground() {
78.121 - refreshAttributes();
78.122 - return null;
78.123 - }
78.124 - }.execute();
78.125 + refreshAttributes();
78.126 return;
78.127 }
78.128 // Clear button
79.1 --- a/src/share/classes/sun/tools/jconsole/inspector/XTable.java Tue Aug 19 07:50:03 2008 -0700
79.2 +++ b/src/share/classes/sun/tools/jconsole/inspector/XTable.java Thu Aug 21 09:55:18 2008 -0700
79.3 @@ -25,10 +25,13 @@
79.4
79.5 package sun.tools.jconsole.inspector;
79.6
79.7 -import javax.swing.*;
79.8 -import javax.swing.table.*;
79.9 -import java.awt.*;
79.10 -import java.io.*;
79.11 +import java.awt.Color;
79.12 +import java.awt.Component;
79.13 +import java.awt.Font;
79.14 +import javax.swing.JTable;
79.15 +import javax.swing.table.DefaultTableCellRenderer;
79.16 +import javax.swing.table.DefaultTableModel;
79.17 +import javax.swing.table.TableCellRenderer;
79.18
79.19 public abstract class XTable extends JTable {
79.20 static final int NAME_COLUMN = 0;
79.21 @@ -38,8 +41,9 @@
79.22
79.23 public XTable () {
79.24 super();
79.25 - TableSorter sorter;
79.26 - setModel(sorter = new TableSorter());
79.27 + @SuppressWarnings("serial")
79.28 + final TableSorter sorter = new TableSorter();
79.29 + setModel(sorter);
79.30 sorter.addMouseListenerToHeaderInTable(this);
79.31 setRowSelectionAllowed(false);
79.32 setColumnSelectionAllowed(false);
79.33 @@ -55,6 +59,14 @@
79.34 }
79.35
79.36 /**
79.37 + * Called by TableSorter if a mouse event requests to sort the rows.
79.38 + * @param column the column against which the rows are sorted
79.39 + */
79.40 + void sortRequested(int column) {
79.41 + // This is a hook for subclasses
79.42 + }
79.43 +
79.44 + /**
79.45 * This returns the select index as the table was at initialization
79.46 */
79.47 public int getSelectedIndex() {
79.48 @@ -67,7 +79,7 @@
79.49 public int convertRowToIndex(int row) {
79.50 if (row == -1) return row;
79.51 if (getModel() instanceof TableSorter) {
79.52 - return (((TableSorter) getModel()).getInvertedIndex()[row]);
79.53 + return ((TableSorter) getModel()).getIndexOfRow(row);
79.54 } else {
79.55 return row;
79.56 }
79.57 @@ -97,6 +109,7 @@
79.58 //JTable re-implementation
79.59
79.60 //attribute can be editable even if unavailable
79.61 + @Override
79.62 public boolean isCellEditable(int row, int col) {
79.63 return ((isTableEditable() && isColumnEditable(col)
79.64 && isWritable(row)
79.65 @@ -118,6 +131,7 @@
79.66 * This method sets read write rows to be blue, and other rows to be their
79.67 * default rendered colour.
79.68 */
79.69 + @Override
79.70 public TableCellRenderer getCellRenderer(int row, int column) {
79.71 DefaultTableCellRenderer tcr =
79.72 (DefaultTableCellRenderer) super.getCellRenderer(row,column);
79.73 @@ -146,6 +160,7 @@
79.74 return tcr;
79.75 }
79.76
79.77 + @Override
79.78 public Component prepareRenderer(TableCellRenderer renderer,
79.79 int row, int column) {
79.80 Component comp = super.prepareRenderer(renderer, row, column);
80.1 --- a/src/share/classes/sun/tools/jconsole/inspector/XTextFieldEditor.java Tue Aug 19 07:50:03 2008 -0700
80.2 +++ b/src/share/classes/sun/tools/jconsole/inspector/XTextFieldEditor.java Thu Aug 21 09:55:18 2008 -0700
80.3 @@ -26,22 +26,30 @@
80.4 package sun.tools.jconsole.inspector;
80.5
80.6 import java.awt.Component;
80.7 +import java.awt.event.ActionEvent;
80.8 +import java.awt.event.FocusAdapter;
80.9 +import java.awt.event.FocusEvent;
80.10 +import java.awt.event.FocusListener;
80.11 import java.util.EventObject;
80.12 -import java.awt.event.*;
80.13 -import java.awt.dnd.DragSourceDropEvent;
80.14 -import javax.swing.*;
80.15 -import javax.swing.event.*;
80.16 -import javax.swing.table.*;
80.17 +import javax.swing.JMenuItem;
80.18 +import javax.swing.JTable;
80.19 +import javax.swing.JTextField;
80.20 +import javax.swing.event.CellEditorListener;
80.21 +import javax.swing.event.ChangeEvent;
80.22 +import javax.swing.event.EventListenerList;
80.23 +import javax.swing.table.TableCellEditor;
80.24
80.25 @SuppressWarnings("serial")
80.26 public class XTextFieldEditor extends XTextField implements TableCellEditor {
80.27
80.28 - protected EventListenerList listenerList = new EventListenerList();
80.29 + protected EventListenerList evtListenerList = new EventListenerList();
80.30 protected ChangeEvent changeEvent = new ChangeEvent(this);
80.31
80.32 private FocusListener editorFocusListener = new FocusAdapter() {
80.33 + @Override
80.34 public void focusLost(FocusEvent e) {
80.35 - fireEditingStopped();
80.36 + // fireEditingStopped();
80.37 + // must not call fireEditingStopped() here!
80.38 }
80.39 };
80.40
80.41 @@ -51,6 +59,7 @@
80.42 }
80.43
80.44 //edition stopped ou JMenuItem selection & JTextField selection
80.45 + @Override
80.46 public void actionPerformed(ActionEvent e) {
80.47 super.actionPerformed(e);
80.48 if ((e.getSource() instanceof JMenuItem) ||
80.49 @@ -67,16 +76,16 @@
80.50 //TableCellEditor implementation
80.51
80.52 public void addCellEditorListener(CellEditorListener listener) {
80.53 - listenerList.add(CellEditorListener.class,listener);
80.54 + evtListenerList.add(CellEditorListener.class,listener);
80.55 }
80.56
80.57 public void removeCellEditorListener(CellEditorListener listener) {
80.58 - listenerList.remove(CellEditorListener.class, listener);
80.59 + evtListenerList.remove(CellEditorListener.class, listener);
80.60 }
80.61
80.62 protected void fireEditingStopped() {
80.63 CellEditorListener listener;
80.64 - Object[] listeners = listenerList.getListenerList();
80.65 + Object[] listeners = evtListenerList.getListenerList();
80.66 for (int i=0;i< listeners.length;i++) {
80.67 if (listeners[i] == CellEditorListener.class) {
80.68 listener = (CellEditorListener) listeners[i+1];
80.69 @@ -87,7 +96,7 @@
80.70
80.71 protected void fireEditingCanceled() {
80.72 CellEditorListener listener;
80.73 - Object[] listeners = listenerList.getListenerList();
80.74 + Object[] listeners = evtListenerList.getListenerList();
80.75 for (int i=0;i< listeners.length;i++) {
80.76 if (listeners[i] == CellEditorListener.class) {
80.77 listener = (CellEditorListener) listeners[i+1];
81.1 --- a/test/com/sun/jdi/Solaris32AndSolaris64Test.sh Tue Aug 19 07:50:03 2008 -0700
81.2 +++ b/test/com/sun/jdi/Solaris32AndSolaris64Test.sh Thu Aug 21 09:55:18 2008 -0700
81.3 @@ -25,7 +25,7 @@
81.4
81.5 #
81.6 # @test Solaris32AndSolaris64Test.sh
81.7 -# @bug 4478312 4780570 4913748
81.8 +# @bug 4478312 4780570 4913748 6730273
81.9 # @summary Test debugging with mixed 32/64bit VMs.
81.10 # @author Tim Bell
81.11 # Based on test/java/awt/TEMPLATE/AutomaticShellTest.sh
81.12 @@ -177,8 +177,14 @@
81.13 if [ ! -r ${filename} ] ; then
81.14 filename=$TESTCLASSES/../@debuggeeVMOptions
81.15 fi
81.16 +# Remove -d32, -d64 if present, and remove -XX:[+-]UseCompressedOops
81.17 +# if present since it is illegal in 32 bit mode.
81.18 if [ -r ${filename} ] ; then
81.19 - DEBUGGEEFLAGS=`cat ${filename} | sed -e 's/-d32//g' -e 's/-d64//g'`
81.20 + DEBUGGEEFLAGS=`cat ${filename} | sed \
81.21 + -e 's/-d32//g' \
81.22 + -e 's/-d64//g' \
81.23 + -e 's/-XX:.UseCompressedOops//g' \
81.24 + `
81.25 fi
81.26
81.27 #
82.1 --- a/test/java/lang/management/ManagementFactory/ThreadMXBeanProxy.java Tue Aug 19 07:50:03 2008 -0700
82.2 +++ b/test/java/lang/management/ManagementFactory/ThreadMXBeanProxy.java Thu Aug 21 09:55:18 2008 -0700
82.3 @@ -60,8 +60,8 @@
82.4 thread.setDaemon(true);
82.5 thread.start();
82.6
82.7 - // wait until myThread acquires mutex
82.8 - while (!mutex.isLocked()) {
82.9 + // wait until myThread acquires mutex and lock owner is set.
82.10 + while (!(mutex.isLocked() && mutex.getLockOwner() == thread)) {
82.11 try {
82.12 Thread.sleep(100);
82.13 } catch (InterruptedException e) {
82.14 @@ -73,17 +73,17 @@
82.15
82.16 // validate the local access
82.17 ThreadInfo[] infos = getThreadMXBean().getThreadInfo(ids, true, true);
82.18 - if (ids.length != 1) {
82.19 + if (infos.length != 1) {
82.20 throw new RuntimeException("Returned ThreadInfo[] of length=" +
82.21 - ids.length + ". Expected to be 1.");
82.22 + infos.length + ". Expected to be 1.");
82.23 }
82.24 thread.checkThreadInfo(infos[0]);
82.25
82.26 // validate the remote access
82.27 infos = mbean.getThreadInfo(ids, true, true);
82.28 - if (ids.length != 1) {
82.29 + if (infos.length != 1) {
82.30 throw new RuntimeException("Returned ThreadInfo[] of length=" +
82.31 - ids.length + ". Expected to be 1.");
82.32 + infos.length + ". Expected to be 1.");
82.33 }
82.34 thread.checkThreadInfo(infos[0]);
82.35
82.36 @@ -160,8 +160,7 @@
82.37 LockInfo[] syncs = info.getLockedSynchronizers();
82.38 if (syncs.length != OWNED_SYNCS) {
82.39 throw new RuntimeException("Number of locked syncs = " +
82.40 - syncs.length +
82.41 - " not matched. Expected: " + OWNED_SYNCS);
82.42 + syncs.length + " not matched. Expected: " + OWNED_SYNCS);
82.43 }
82.44 AbstractOwnableSynchronizer s = mutex.getSync();
82.45 String lockName = s.getClass().getName();
82.46 @@ -174,7 +173,6 @@
82.47 throw new RuntimeException("LockInfo: " + syncs[0] +
82.48 " IdentityHashCode not matched. Expected: " + hcode);
82.49 }
82.50 -
82.51 }
82.52 }
82.53 static class Mutex implements Lock, java.io.Serializable {
82.54 @@ -214,6 +212,10 @@
82.55 s.defaultReadObject();
82.56 setState(0); // reset to unlocked state
82.57 }
82.58 +
82.59 + protected Thread getLockOwner() {
82.60 + return getExclusiveOwnerThread();
82.61 + }
82.62 }
82.63
82.64 // The sync object does all the hard work. We just forward to it.
82.65 @@ -232,6 +234,8 @@
82.66 return sync.tryAcquireNanos(1, unit.toNanos(timeout));
82.67 }
82.68
82.69 + public Thread getLockOwner() { return sync.getLockOwner(); }
82.70 +
82.71 public AbstractOwnableSynchronizer getSync() { return sync; }
82.72 }
82.73 }
83.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
83.2 +++ b/test/java/util/Timer/DelayOverflow.java Thu Aug 21 09:55:18 2008 -0700
83.3 @@ -0,0 +1,115 @@
83.4 +/*
83.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
83.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
83.7 + *
83.8 + * This code is free software; you can redistribute it and/or modify it
83.9 + * under the terms of the GNU General Public License version 2 only, as
83.10 + * published by the Free Software Foundation.
83.11 + *
83.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
83.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
83.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
83.15 + * version 2 for more details (a copy is included in the LICENSE file that
83.16 + * accompanied this code).
83.17 + *
83.18 + * You should have received a copy of the GNU General Public License version
83.19 + * 2 along with this work; if not, write to the Free Software Foundation,
83.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
83.21 + *
83.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
83.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
83.24 + * have any questions.
83.25 + */
83.26 +
83.27 +/*
83.28 + * @test
83.29 + * @bug 6730507
83.30 + * @summary java.util.Timer schedule delay Long.MAX_VALUE causes task to execute multiple times
83.31 + * @author Chris Hegarty
83.32 + * @author Martin Buchholz
83.33 + */
83.34 +
83.35 +import java.util.Date;
83.36 +import java.util.Timer;
83.37 +import java.util.TimerTask;
83.38 +import java.util.concurrent.*;
83.39 +import java.util.concurrent.atomic.AtomicInteger;
83.40 +
83.41 +public class DelayOverflow
83.42 +{
83.43 + void scheduleNow(Timer timer, TimerTask task, int how) {
83.44 + switch (how) {
83.45 + case 0 :
83.46 + timer.schedule(task, new Date(), Long.MAX_VALUE);
83.47 + break;
83.48 + case 1:
83.49 + timer.schedule(task, 0L, Long.MAX_VALUE);
83.50 + break;
83.51 + case 2:
83.52 + timer.scheduleAtFixedRate(task, new Date(), Long.MAX_VALUE);
83.53 + break;
83.54 + case 3:
83.55 + timer.scheduleAtFixedRate(task, 0L, Long.MAX_VALUE);
83.56 + break;
83.57 + default:
83.58 + fail(String.valueOf(how));
83.59 + }
83.60 + }
83.61 +
83.62 + void sleep(long millis) {
83.63 + try { Thread.sleep(millis); }
83.64 + catch (Throwable t) { unexpected(t); }
83.65 + }
83.66 +
83.67 + /** Checks that scheduledExecutionTime returns a "recent" time. */
83.68 + void checkScheduledExecutionTime(TimerTask task) {
83.69 + long t = System.currentTimeMillis()
83.70 + - task.scheduledExecutionTime();
83.71 + check(t >= 0 && t < 1000 * 600);
83.72 + }
83.73 +
83.74 + void test(String[] args) throws Throwable {
83.75 + for (int how=0; how<4; how++) {
83.76 + final CountDownLatch done = new CountDownLatch(1);
83.77 + final AtomicInteger count = new AtomicInteger(0);
83.78 + final Timer timer = new Timer();
83.79 + final TimerTask task = new TimerTask() {
83.80 + @Override
83.81 + public void run() {
83.82 + checkScheduledExecutionTime(this);
83.83 + count.incrementAndGet();
83.84 + done.countDown();
83.85 + }};
83.86 +
83.87 + scheduleNow(timer, task, how);
83.88 + done.await();
83.89 + equal(count.get(), 1);
83.90 + checkScheduledExecutionTime(task);
83.91 + if (new java.util.Random().nextBoolean())
83.92 + sleep(10);
83.93 + check(task.cancel());
83.94 + timer.cancel();
83.95 + checkScheduledExecutionTime(task);
83.96 + }
83.97 + }
83.98 +
83.99 + //--------------------- Infrastructure ---------------------------
83.100 + volatile int passed = 0, failed = 0;
83.101 + void pass() {passed++;}
83.102 + void fail() {failed++; Thread.dumpStack();}
83.103 + void fail(String msg) {System.err.println(msg); fail();}
83.104 + void unexpected(Throwable t) {failed++; t.printStackTrace();}
83.105 + void check(boolean cond) {if (cond) pass(); else fail();}
83.106 + void equal(Object x, Object y) {
83.107 + if (x == null ? y == null : x.equals(y)) pass();
83.108 + else fail(x + " not equal to " + y);}
83.109 + public static void main(String[] args) throws Throwable {
83.110 + Class<?> k = new Object(){}.getClass().getEnclosingClass();
83.111 + try {k.getMethod("instanceMain",String[].class)
83.112 + .invoke( k.newInstance(), (Object) args);}
83.113 + catch (Throwable e) {throw e.getCause();}}
83.114 + public void instanceMain(String[] args) throws Throwable {
83.115 + try {test(args);} catch (Throwable t) {unexpected(t);}
83.116 + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
83.117 + if (failed > 0) throw new AssertionError("Some tests failed");}
83.118 +}
84.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
84.2 +++ b/test/java/util/concurrent/ScheduledThreadPoolExecutor/DelayOverflow.java Thu Aug 21 09:55:18 2008 -0700
84.3 @@ -0,0 +1,161 @@
84.4 +/*
84.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
84.6 + *
84.7 + * This code is free software; you can redistribute it and/or modify it
84.8 + * under the terms of the GNU General Public License version 2 only, as
84.9 + * published by the Free Software Foundation.
84.10 + *
84.11 + * This code is distributed in the hope that it will be useful, but WITHOUT
84.12 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
84.13 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
84.14 + * version 2 for more details (a copy is included in the LICENSE file that
84.15 + * accompanied this code).
84.16 + *
84.17 + * You should have received a copy of the GNU General Public License version
84.18 + * 2 along with this work; if not, write to the Free Software Foundation,
84.19 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
84.20 + *
84.21 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
84.22 + * CA 95054 USA or visit www.sun.com if you need additional information or
84.23 + * have any questions.
84.24 + */
84.25 +
84.26 +/*
84.27 + * @test
84.28 + * @bug 6725789
84.29 + * @summary Check for long overflow in task time comparison.
84.30 + */
84.31 +
84.32 +import java.util.concurrent.*;
84.33 +
84.34 +public class DelayOverflow {
84.35 + static void waitForNanoTimeTick() {
84.36 + for (long t0 = System.nanoTime(); t0 == System.nanoTime(); )
84.37 + ;
84.38 + }
84.39 +
84.40 + void scheduleNow(ScheduledThreadPoolExecutor pool,
84.41 + Runnable r, int how) {
84.42 + switch (how) {
84.43 + case 0:
84.44 + pool.schedule(r, 0, TimeUnit.MILLISECONDS);
84.45 + break;
84.46 + case 1:
84.47 + pool.schedule(Executors.callable(r), 0, TimeUnit.DAYS);
84.48 + break;
84.49 + case 2:
84.50 + pool.scheduleWithFixedDelay(r, 0, 1000, TimeUnit.NANOSECONDS);
84.51 + break;
84.52 + case 3:
84.53 + pool.scheduleAtFixedRate(r, 0, 1000, TimeUnit.MILLISECONDS);
84.54 + break;
84.55 + default:
84.56 + fail(String.valueOf(how));
84.57 + }
84.58 + }
84.59 +
84.60 + void scheduleAtTheEndOfTime(ScheduledThreadPoolExecutor pool,
84.61 + Runnable r, int how) {
84.62 + switch (how) {
84.63 + case 0:
84.64 + pool.schedule(r, Long.MAX_VALUE, TimeUnit.MILLISECONDS);
84.65 + break;
84.66 + case 1:
84.67 + pool.schedule(Executors.callable(r), Long.MAX_VALUE, TimeUnit.DAYS);
84.68 + break;
84.69 + case 2:
84.70 + pool.scheduleWithFixedDelay(r, Long.MAX_VALUE, 1000, TimeUnit.NANOSECONDS);
84.71 + break;
84.72 + case 3:
84.73 + pool.scheduleAtFixedRate(r, Long.MAX_VALUE, 1000, TimeUnit.MILLISECONDS);
84.74 + break;
84.75 + default:
84.76 + fail(String.valueOf(how));
84.77 + }
84.78 + }
84.79 +
84.80 + /**
84.81 + * Attempts to test exhaustively and deterministically, all 20
84.82 + * possible ways that one task can be scheduled in the maximal
84.83 + * distant future, while at the same time an existing tasks's time
84.84 + * has already expired.
84.85 + */
84.86 + void test(String[] args) throws Throwable {
84.87 + for (int nowHow = 0; nowHow < 4; nowHow++) {
84.88 + for (int thenHow = 0; thenHow < 4; thenHow++) {
84.89 +
84.90 + final ScheduledThreadPoolExecutor pool
84.91 + = new ScheduledThreadPoolExecutor(1);
84.92 + final CountDownLatch runLatch = new CountDownLatch(1);
84.93 + final CountDownLatch busyLatch = new CountDownLatch(1);
84.94 + final CountDownLatch proceedLatch = new CountDownLatch(1);
84.95 + final Runnable notifier = new Runnable() {
84.96 + public void run() { runLatch.countDown(); }};
84.97 + final Runnable neverRuns = new Runnable() {
84.98 + public void run() { fail(); }};
84.99 + final Runnable keepPoolBusy = new Runnable() {
84.100 + public void run() {
84.101 + try {
84.102 + busyLatch.countDown();
84.103 + proceedLatch.await();
84.104 + } catch (Throwable t) { unexpected(t); }
84.105 + }};
84.106 + pool.schedule(keepPoolBusy, 0, TimeUnit.SECONDS);
84.107 + busyLatch.await();
84.108 + scheduleNow(pool, notifier, nowHow);
84.109 + waitForNanoTimeTick();
84.110 + scheduleAtTheEndOfTime(pool, neverRuns, thenHow);
84.111 + proceedLatch.countDown();
84.112 +
84.113 + check(runLatch.await(10L, TimeUnit.SECONDS));
84.114 + equal(runLatch.getCount(), 0L);
84.115 +
84.116 + pool.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
84.117 + pool.shutdown();
84.118 + }
84.119 +
84.120 + final int nowHowCopy = nowHow;
84.121 + final ScheduledThreadPoolExecutor pool
84.122 + = new ScheduledThreadPoolExecutor(1);
84.123 + final CountDownLatch runLatch = new CountDownLatch(1);
84.124 + final Runnable notifier = new Runnable() {
84.125 + public void run() { runLatch.countDown(); }};
84.126 + final Runnable scheduleNowScheduler = new Runnable() {
84.127 + public void run() {
84.128 + try {
84.129 + scheduleNow(pool, notifier, nowHowCopy);
84.130 + waitForNanoTimeTick();
84.131 + } catch (Throwable t) { unexpected(t); }
84.132 + }};
84.133 + pool.scheduleWithFixedDelay(scheduleNowScheduler,
84.134 + 0, Long.MAX_VALUE,
84.135 + TimeUnit.NANOSECONDS);
84.136 +
84.137 + check(runLatch.await(10L, TimeUnit.SECONDS));
84.138 + equal(runLatch.getCount(), 0L);
84.139 +
84.140 + pool.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
84.141 + pool.shutdown();
84.142 + }
84.143 + }
84.144 +
84.145 + //--------------------- Infrastructure ---------------------------
84.146 + volatile int passed = 0, failed = 0;
84.147 + void pass() {passed++;}
84.148 + void fail() {failed++; Thread.dumpStack();}
84.149 + void fail(String msg) {System.err.println(msg); fail();}
84.150 + void unexpected(Throwable t) {failed++; t.printStackTrace();}
84.151 + void check(boolean cond) {if (cond) pass(); else fail();}
84.152 + void equal(Object x, Object y) {
84.153 + if (x == null ? y == null : x.equals(y)) pass();
84.154 + else fail(x + " not equal to " + y);}
84.155 + public static void main(String[] args) throws Throwable {
84.156 + Class<?> k = new Object(){}.getClass().getEnclosingClass();
84.157 + try {k.getMethod("instanceMain",String[].class)
84.158 + .invoke( k.newInstance(), (Object) args);}
84.159 + catch (Throwable e) {throw e.getCause();}}
84.160 + public void instanceMain(String[] args) throws Throwable {
84.161 + try {test(args);} catch (Throwable t) {unexpected(t);}
84.162 + System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
84.163 + if (failed > 0) throw new AssertionError("Some tests failed");}
84.164 +}
85.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
85.2 +++ b/test/javax/management/MBeanServer/DynamicWrapperMBeanTest.java Thu Aug 21 09:55:18 2008 -0700
85.3 @@ -0,0 +1,164 @@
85.4 +/*
85.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
85.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
85.7 + *
85.8 + * This code is free software; you can redistribute it and/or modify it
85.9 + * under the terms of the GNU General Public License version 2 only, as
85.10 + * published by the Free Software Foundation.
85.11 + *
85.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
85.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
85.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
85.15 + * version 2 for more details (a copy is included in the LICENSE file that
85.16 + * accompanied this code).
85.17 + *
85.18 + * You should have received a copy of the GNU General Public License version
85.19 + * 2 along with this work; if not, write to the Free Software Foundation,
85.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
85.21 + *
85.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
85.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
85.24 + * have any questions.
85.25 + */
85.26 +
85.27 +/*
85.28 + * @test DynamicWrapperMBeanTest
85.29 + * @bug 6624232
85.30 + * @summary Test the DynamicWrapperMBean interface
85.31 + * @author Eamonn McManus
85.32 + */
85.33 +
85.34 +import java.lang.management.ManagementFactory;
85.35 +import javax.management.MBeanServer;
85.36 +import javax.management.ObjectName;
85.37 +import javax.management.StandardMBean;
85.38 +import javax.management.modelmbean.ModelMBeanInfo;
85.39 +import javax.management.modelmbean.ModelMBeanInfoSupport;
85.40 +import javax.management.modelmbean.ModelMBeanOperationInfo;
85.41 +import javax.management.modelmbean.RequiredModelMBean;
85.42 +import static javax.management.StandardMBean.Options;
85.43 +
85.44 +public class DynamicWrapperMBeanTest {
85.45 + public static interface WrappedMBean {
85.46 + public void sayHello();
85.47 + }
85.48 + public static class Wrapped implements WrappedMBean {
85.49 + public void sayHello() {
85.50 + System.out.println("Hello");
85.51 + }
85.52 + }
85.53 +
85.54 + private static String failure;
85.55 +
85.56 + public static void main(String[] args) throws Exception {
85.57 + if (Wrapped.class.getClassLoader() ==
85.58 + StandardMBean.class.getClassLoader()) {
85.59 + throw new Exception(
85.60 + "TEST ERROR: Resource and StandardMBean have same ClassLoader");
85.61 + }
85.62 +
85.63 + Options wrappedVisOpts = new Options();
85.64 + wrappedVisOpts.setWrappedObjectVisible(true);
85.65 + Options wrappedInvisOpts = new Options();
85.66 + wrappedInvisOpts.setWrappedObjectVisible(false);
85.67 + assertEquals("Options withWrappedObjectVisible(false)",
85.68 + new Options(), wrappedInvisOpts);
85.69 +
85.70 + Wrapped resource = new Wrapped();
85.71 +
85.72 + StandardMBean visible =
85.73 + new StandardMBean(resource, WrappedMBean.class, wrappedVisOpts);
85.74 + StandardMBean invisible =
85.75 + new StandardMBean(resource, WrappedMBean.class, wrappedInvisOpts);
85.76 +
85.77 + assertEquals("getResource withWrappedObjectVisible(true)",
85.78 + resource, visible.getWrappedObject());
85.79 + assertEquals("getResource withWrappedObjectVisible(false)",
85.80 + invisible, invisible.getWrappedObject());
85.81 +
85.82 + System.out.println("===Testing StandardMBean===");
85.83 +
85.84 + ObjectName visibleName = new ObjectName("a:type=visible");
85.85 + ObjectName invisibleName = new ObjectName("a:type=invisible");
85.86 + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
85.87 + mbs.registerMBean(visible, visibleName);
85.88 + mbs.registerMBean(invisible, invisibleName);
85.89 +
85.90 + assertEquals("ClassLoader for visible resource",
85.91 + Wrapped.class.getClassLoader(),
85.92 + mbs.getClassLoaderFor(visibleName));
85.93 + assertEquals("ClassLoader for invisible resource",
85.94 + StandardMBean.class.getClassLoader(),
85.95 + mbs.getClassLoaderFor(invisibleName));
85.96 +
85.97 + assertEquals("isInstanceOf(WrappedMBean) for visible wrapped",
85.98 + true, mbs.isInstanceOf(visibleName, WrappedMBean.class.getName()));
85.99 + assertEquals("isInstanceOf(WrappedMBean) for invisible wrapped",
85.100 + false, mbs.isInstanceOf(invisibleName, WrappedMBean.class.getName()));
85.101 + assertEquals("isInstanceOf(StandardMBean) for visible wrapped",
85.102 + false, mbs.isInstanceOf(visibleName, StandardMBean.class.getName()));
85.103 + assertEquals("isInstanceOf(StandardMBean) for invisible wrapped",
85.104 + true, mbs.isInstanceOf(invisibleName, StandardMBean.class.getName()));
85.105 +
85.106 + mbs.unregisterMBean(visibleName);
85.107 + mbs.unregisterMBean(invisibleName);
85.108 +
85.109 + System.out.println("===Testing RequiredModelMBean===");
85.110 +
85.111 + // Godawful Model MBeans...
85.112 + ModelMBeanOperationInfo mmboi = new ModelMBeanOperationInfo(
85.113 + "say hello to the nice man", Wrapped.class.getMethod("sayHello"));
85.114 + ModelMBeanInfo visibleMmbi = new ModelMBeanInfoSupport(
85.115 + Wrapped.class.getName(), "Visible wrapped", null, null,
85.116 + new ModelMBeanOperationInfo[] {mmboi}, null);
85.117 + ModelMBeanInfo invisibleMmbi = new ModelMBeanInfoSupport(
85.118 + Wrapped.class.getName(), "Invisible wrapped", null, null,
85.119 + new ModelMBeanOperationInfo[] {mmboi}, null);
85.120 + RequiredModelMBean visibleRmmb = new RequiredModelMBean(visibleMmbi);
85.121 + RequiredModelMBean invisibleRmmb = new RequiredModelMBean(invisibleMmbi);
85.122 + visibleRmmb.setManagedResource(resource, "VisibleObjectReference");
85.123 + invisibleRmmb.setManagedResource(resource, "ObjectReference");
85.124 +
85.125 + mbs.registerMBean(visibleRmmb, visibleName);
85.126 + mbs.registerMBean(invisibleRmmb, invisibleName);
85.127 +
85.128 + assertEquals("ClassLoader for visible wrapped",
85.129 + Wrapped.class.getClassLoader(),
85.130 + mbs.getClassLoaderFor(visibleName));
85.131 + assertEquals("ClassLoader for invisible wrapped",
85.132 + StandardMBean.class.getClassLoader(),
85.133 + mbs.getClassLoaderFor(invisibleName));
85.134 +
85.135 + assertEquals("isInstanceOf(WrappedMBean) for visible resource",
85.136 + true, mbs.isInstanceOf(visibleName, WrappedMBean.class.getName()));
85.137 + assertEquals("isInstanceOf(WrappedMBean) for invisible resource",
85.138 + false, mbs.isInstanceOf(invisibleName, WrappedMBean.class.getName()));
85.139 + assertEquals("isInstanceOf(RequiredModelMBean) for visible resource",
85.140 + false, mbs.isInstanceOf(visibleName, RequiredModelMBean.class.getName()));
85.141 + assertEquals("isInstanceOf(RequiredModelMBean) for invisible resource",
85.142 + true, mbs.isInstanceOf(invisibleName, RequiredModelMBean.class.getName()));
85.143 +
85.144 + if (failure != null)
85.145 + throw new Exception("TEST FAILED: " + failure);
85.146 + }
85.147 +
85.148 + private static void assertEquals(String what, Object expect, Object actual) {
85.149 + if (equal(expect, actual))
85.150 + System.out.println("OK: " + what + " = " + expect);
85.151 + else
85.152 + fail(what + " should be " + expect + ", is " + actual);
85.153 + }
85.154 +
85.155 + private static boolean equal(Object x, Object y) {
85.156 + if (x == y)
85.157 + return true;
85.158 + if (x == null || y == null)
85.159 + return false;
85.160 + return x.equals(y);
85.161 + }
85.162 +
85.163 + private static void fail(String why) {
85.164 + failure = why;
85.165 + System.out.println("FAIL: " + why);
85.166 + }
85.167 +}
86.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
86.2 +++ b/test/javax/management/MBeanServer/MBeanServerNotificationTest.java Thu Aug 21 09:55:18 2008 -0700
86.3 @@ -0,0 +1,132 @@
86.4 +/*
86.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
86.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
86.7 + *
86.8 + * This code is free software; you can redistribute it and/or modify it
86.9 + * under the terms of the GNU General Public License version 2 only, as
86.10 + * published by the Free Software Foundation.
86.11 + *
86.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
86.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
86.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
86.15 + * version 2 for more details (a copy is included in the LICENSE file that
86.16 + * accompanied this code).
86.17 + *
86.18 + * You should have received a copy of the GNU General Public License version
86.19 + * 2 along with this work; if not, write to the Free Software Foundation,
86.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
86.21 + *
86.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
86.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
86.24 + * have any questions.
86.25 + */
86.26 +
86.27 +/*
86.28 + * @test
86.29 + * @bug 6689505
86.30 + * @summary Checks that MBeanServerNotification.toString contains the
86.31 + * MBean name.
86.32 + * @author Daniel Fuchs
86.33 + * @compile MBeanServerNotificationTest.java
86.34 + * @run main MBeanServerNotificationTest
86.35 + */
86.36 +
86.37 +import com.sun.jmx.mbeanserver.Util;
86.38 +import javax.management.*;
86.39 +import java.util.concurrent.*;
86.40 +
86.41 +public class MBeanServerNotificationTest {
86.42 + final static String[] names = {
86.43 + ":type=Wombat", "wombat:type=Wombat",null,
86.44 + };
86.45 + public static void main(String[] args) throws Exception {
86.46 + System.out.println("Test that MBeanServerNotification.toString " +
86.47 + "contains the name of the MBean being registered " +
86.48 + "or unregistered.");
86.49 + int failures = 0;
86.50 + final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
86.51 + for (String str:names) {
86.52 + try {
86.53 + final ObjectName name = (str==null)?null:new ObjectName(str);
86.54 + failures+=test(mbs, name, name!=null);
86.55 + } catch(Exception x) {
86.56 + x.printStackTrace(System.out);
86.57 + System.out.println("Test failed for: "+str);
86.58 + failures++;
86.59 + }
86.60 + }
86.61 + if (failures == 0)
86.62 + System.out.println("Test passed");
86.63 + else {
86.64 + System.out.println("TEST FAILED: " + failures + " failure(s)");
86.65 + System.exit(1);
86.66 + }
86.67 + }
86.68 +
86.69 + private static enum Registration {
86.70 + REGISTER(MBeanServerNotification.REGISTRATION_NOTIFICATION),
86.71 + UNREGISTER(MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
86.72 + final String type;
86.73 + private Registration(String type) {this.type = type;}
86.74 + public int test(MBeanServerNotification n, ObjectName name) {
86.75 + int failures = 0;
86.76 + System.out.println("Testing: "+n);
86.77 + if (!n.toString().endsWith("[type="+type+
86.78 + "][message="+n.getMessage()+
86.79 + "][mbeanName="+name+"]")) {
86.80 + System.err.println("Test failed for "+ type+
86.81 + " ["+name+"]: "+n);
86.82 + failures++;
86.83 + }
86.84 + return failures;
86.85 + }
86.86 + public MBeanServerNotification create(ObjectName name) {
86.87 + return new MBeanServerNotification(type,
86.88 + MBeanServerDelegate.DELEGATE_NAME, next(), name);
86.89 + }
86.90 + private static long next = 0;
86.91 + private static synchronized long next() {return next++;}
86.92 +
86.93 + }
86.94 +
86.95 + private static int test(MBeanServer mbs, ObjectName name,
86.96 + boolean register)
86.97 + throws Exception {
86.98 + System.out.println("--------" + name + "--------");
86.99 +
86.100 + int failures = 0;
86.101 + for (Registration reg : Registration.values()) {
86.102 + failures = reg.test(reg.create(name), name);
86.103 + }
86.104 + if (!register) return failures;
86.105 +
86.106 + final ArrayBlockingQueue<Notification> queue =
86.107 + new ArrayBlockingQueue<Notification>(10);
86.108 + final NotificationListener listener = new NotificationListener() {
86.109 + public void handleNotification(Notification notification,
86.110 + Object handback) {
86.111 + try {
86.112 + queue.put(notification);
86.113 + } catch(Exception x) {
86.114 + x.printStackTrace(System.out);
86.115 + }
86.116 + }
86.117 + };
86.118 + mbs.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME,
86.119 + listener, null, name);
86.120 + final ObjectInstance oi = mbs.registerMBean(new Wombat(), name);
86.121 + try {
86.122 + failures+=Registration.REGISTER.test((MBeanServerNotification)
86.123 + queue.poll(2, TimeUnit.SECONDS), oi.getObjectName());
86.124 + } finally {
86.125 + mbs.unregisterMBean(oi.getObjectName());
86.126 + failures+=Registration.UNREGISTER.test((MBeanServerNotification)
86.127 + queue.poll(2, TimeUnit.SECONDS), oi.getObjectName());
86.128 + }
86.129 + return failures;
86.130 + }
86.131 +
86.132 + public static interface WombatMBean {}
86.133 + public static class Wombat implements WombatMBean {}
86.134 +
86.135 +}
87.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
87.2 +++ b/test/javax/management/MBeanServer/OldMBeanServerTest.java Thu Aug 21 09:55:18 2008 -0700
87.3 @@ -0,0 +1,1410 @@
87.4 +/*
87.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
87.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
87.7 + *
87.8 + * This code is free software; you can redistribute it and/or modify it
87.9 + * under the terms of the GNU General Public License version 2 only, as
87.10 + * published by the Free Software Foundation.
87.11 + *
87.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
87.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
87.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
87.15 + * version 2 for more details (a copy is included in the LICENSE file that
87.16 + * accompanied this code).
87.17 + *
87.18 + * You should have received a copy of the GNU General Public License version
87.19 + * 2 along with this work; if not, write to the Free Software Foundation,
87.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
87.21 + *
87.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
87.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
87.24 + * have any questions.
87.25 + */
87.26 +
87.27 +import java.io.IOException;
87.28 +import java.io.ObjectInputStream;
87.29 +import java.io.Serializable;
87.30 +import java.lang.annotation.Retention;
87.31 +import java.lang.annotation.RetentionPolicy;
87.32 +import java.lang.management.ManagementFactory;
87.33 +import java.lang.ref.WeakReference;
87.34 +import java.lang.reflect.AccessibleObject;
87.35 +import java.lang.reflect.Constructor;
87.36 +import java.lang.reflect.InvocationTargetException;
87.37 +import java.lang.reflect.Method;
87.38 +import java.lang.reflect.Modifier;
87.39 +import java.util.ArrayList;
87.40 +import java.util.Arrays;
87.41 +import java.util.HashMap;
87.42 +import java.util.HashSet;
87.43 +import java.util.Iterator;
87.44 +import java.util.List;
87.45 +import java.util.Map;
87.46 +import java.util.Set;
87.47 +import java.util.WeakHashMap;
87.48 +import java.util.concurrent.Callable;
87.49 +import java.util.concurrent.ConcurrentHashMap;
87.50 +import java.util.concurrent.ConcurrentMap;
87.51 +import javax.management.Attribute;
87.52 +import javax.management.AttributeList;
87.53 +import javax.management.AttributeNotFoundException;
87.54 +import javax.management.DynamicMBean;
87.55 +import javax.management.InstanceAlreadyExistsException;
87.56 +import javax.management.InstanceNotFoundException;
87.57 +import javax.management.IntrospectionException;
87.58 +import javax.management.InvalidAttributeValueException;
87.59 +import javax.management.ListenerNotFoundException;
87.60 +import javax.management.MBeanAttributeInfo;
87.61 +import javax.management.MBeanConstructorInfo;
87.62 +import javax.management.MBeanException;
87.63 +import javax.management.MBeanInfo;
87.64 +import javax.management.MBeanNotificationInfo;
87.65 +import javax.management.MBeanOperationInfo;
87.66 +import javax.management.MBeanRegistration;
87.67 +import javax.management.MBeanRegistrationException;
87.68 +import javax.management.MBeanServer;
87.69 +import javax.management.MBeanServerBuilder;
87.70 +import javax.management.MBeanServerConnection;
87.71 +import javax.management.MBeanServerDelegate;
87.72 +import javax.management.MBeanServerFactory;
87.73 +import javax.management.MBeanServerNotification;
87.74 +import javax.management.MalformedObjectNameException;
87.75 +import javax.management.NotCompliantMBeanException;
87.76 +import javax.management.Notification;
87.77 +import javax.management.NotificationBroadcaster;
87.78 +import javax.management.NotificationBroadcasterSupport;
87.79 +import javax.management.NotificationEmitter;
87.80 +import javax.management.NotificationFilter;
87.81 +import javax.management.NotificationListener;
87.82 +import javax.management.ObjectInstance;
87.83 +import javax.management.ObjectName;
87.84 +import javax.management.OperationsException;
87.85 +import javax.management.QueryEval;
87.86 +import javax.management.QueryExp;
87.87 +import javax.management.ReflectionException;
87.88 +import javax.management.RuntimeErrorException;
87.89 +import javax.management.RuntimeMBeanException;
87.90 +import javax.management.StandardMBean;
87.91 +import javax.management.loading.ClassLoaderRepository;
87.92 +import javax.management.remote.JMXConnector;
87.93 +import javax.management.remote.JMXConnectorFactory;
87.94 +import javax.management.remote.JMXConnectorServer;
87.95 +import javax.management.remote.JMXConnectorServerFactory;
87.96 +import javax.management.remote.JMXServiceURL;
87.97 +
87.98 +/*
87.99 + * @test OldMBeanServerTest.java
87.100 + * @bug 5072268
87.101 + * @summary Test that nothing assumes a post-1.2 MBeanServer
87.102 + * @author Eamonn McManus
87.103 + * @run main/othervm -ea OldMBeanServerTest
87.104 + */
87.105 +
87.106 +/*
87.107 + * We defined the MBeanServerBuilder class and the associated system
87.108 + * property javax.management.builder.initial in version 1.2 of the JMX
87.109 + * spec. That amounts to a guarantee that someone can set the property
87.110 + * to an MBeanServer that only knows about JMX 1.2 semantics, and if they
87.111 + * only do JMX 1.2 operations, everything should work. This test is a
87.112 + * sanity check that ensures we don't inadvertently make any API changes
87.113 + * that stop that from being true. It includes a complete (if slow)
87.114 + * MBeanServer implementation. That implementation doesn't replicate the
87.115 + * mandated exception behaviour everywhere, though, since there's lots of
87.116 + * arbitrary cruft in that. Also, the behaviour of concurrent unregisterMBean
87.117 + * calls is incorrect in detail.
87.118 + */
87.119 +
87.120 +public class OldMBeanServerTest {
87.121 + private static MBeanServerConnection mbsc;
87.122 + private static String failure;
87.123 +
87.124 + public static void main(String[] args) throws Exception {
87.125 + if (!OldMBeanServerTest.class.desiredAssertionStatus())
87.126 + throw new Exception("Test must be run with -ea");
87.127 +
87.128 + System.setProperty("javax.management.builder.initial",
87.129 + OldMBeanServerBuilder.class.getName());
87.130 + assert MBeanServerFactory.newMBeanServer() instanceof OldMBeanServer;
87.131 +
87.132 + System.out.println("=== RUNNING TESTS WITH LOCAL MBEANSERVER ===");
87.133 + runTests(new Callable<MBeanServerConnection>() {
87.134 + public MBeanServerConnection call() {
87.135 + return MBeanServerFactory.newMBeanServer();
87.136 + }
87.137 + }, null);
87.138 +
87.139 + System.out.println("=== RUNNING TESTS THROUGH CONNECTOR ===");
87.140 + ConnectionBuilder builder = new ConnectionBuilder();
87.141 + runTests(builder, builder);
87.142 +
87.143 + if (failure == null)
87.144 + System.out.println("TEST PASSED");
87.145 + else
87.146 + throw new Exception("TEST FAILED: " + failure);
87.147 + }
87.148 +
87.149 + private static class ConnectionBuilder
87.150 + implements Callable<MBeanServerConnection>, Runnable {
87.151 + private JMXConnector connector;
87.152 + public MBeanServerConnection call() {
87.153 + MBeanServer mbs = MBeanServerFactory.newMBeanServer();
87.154 + try {
87.155 + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
87.156 + JMXConnectorServer cs =
87.157 + JMXConnectorServerFactory.newJMXConnectorServer(
87.158 + url, null, mbs);
87.159 + cs.start();
87.160 + JMXServiceURL addr = cs.getAddress();
87.161 + connector = JMXConnectorFactory.connect(addr);
87.162 + return connector.getMBeanServerConnection();
87.163 + } catch (IOException e) {
87.164 + throw new RuntimeException(e);
87.165 + }
87.166 + }
87.167 + public void run() {
87.168 + if (connector != null) {
87.169 + try {
87.170 + connector.close();
87.171 + } catch (IOException e) {
87.172 + throw new RuntimeException(e);
87.173 + }
87.174 + }
87.175 + }
87.176 + }
87.177 +
87.178 + private static void runTests(
87.179 + Callable<MBeanServerConnection> maker, Runnable breaker)
87.180 + throws Exception {
87.181 + for (Method m : OldMBeanServerTest.class.getDeclaredMethods()) {
87.182 + if (Modifier.isStatic(m.getModifiers()) &&
87.183 + m.getName().startsWith("test") &&
87.184 + m.getParameterTypes().length == 0) {
87.185 + ExpectException expexc = m.getAnnotation(ExpectException.class);
87.186 + mbsc = maker.call();
87.187 + try {
87.188 + m.invoke(null);
87.189 + if (expexc != null) {
87.190 + failure =
87.191 + m.getName() + " did not got expected exception " +
87.192 + expexc.value().getName();
87.193 + System.out.println(failure);
87.194 + } else
87.195 + System.out.println(m.getName() + " OK");
87.196 + } catch (InvocationTargetException ite) {
87.197 + Throwable t = ite.getCause();
87.198 + String prob = null;
87.199 + if (expexc != null) {
87.200 + if (expexc.value().isInstance(t)) {
87.201 + System.out.println(m.getName() + " OK (got expected " +
87.202 + expexc.value().getName() + ")");
87.203 + } else
87.204 + prob = "got wrong exception";
87.205 + } else
87.206 + prob = "got exception";
87.207 + if (prob != null) {
87.208 + failure = m.getName() + ": " + prob + " " +
87.209 + t.getClass().getName();
87.210 + System.out.println(failure);
87.211 + t.printStackTrace(System.out);
87.212 + }
87.213 + } finally {
87.214 + if (breaker != null)
87.215 + breaker.run();
87.216 + }
87.217 + }
87.218 + }
87.219 + }
87.220 +
87.221 + @Retention(RetentionPolicy.RUNTIME)
87.222 + private static @interface ExpectException {
87.223 + Class<? extends Exception> value();
87.224 + }
87.225 +
87.226 + public static interface BoringMBean {
87.227 + public String getName();
87.228 + public int add(int x, int y);
87.229 + }
87.230 +
87.231 + // This class is Serializable so we can createMBean a StandardMBean
87.232 + // that contains it. Not recommended practice in general --
87.233 + // should we have a StandardMBean constructor that takes a class
87.234 + // name and constructor parameters?
87.235 + public static class Boring implements BoringMBean, Serializable {
87.236 + public String getName() {
87.237 + return "Jessica";
87.238 + }
87.239 +
87.240 + public int add(int x, int y) {
87.241 + return x + y;
87.242 + }
87.243 + }
87.244 +
87.245 + public static interface BoringNotifierMBean extends BoringMBean {
87.246 + public void send();
87.247 + }
87.248 +
87.249 + public static class BoringNotifier
87.250 + extends Boring implements BoringNotifierMBean, NotificationBroadcaster {
87.251 + private final NotificationBroadcasterSupport nbs =
87.252 + new NotificationBroadcasterSupport();
87.253 +
87.254 + public void addNotificationListener(
87.255 + NotificationListener listener, NotificationFilter filter, Object handback)
87.256 + throws IllegalArgumentException {
87.257 + nbs.addNotificationListener(listener, filter, handback);
87.258 + }
87.259 +
87.260 + public void removeNotificationListener(NotificationListener listener)
87.261 + throws ListenerNotFoundException {
87.262 + nbs.removeNotificationListener(listener);
87.263 + }
87.264 +
87.265 + public MBeanNotificationInfo[] getNotificationInfo() {
87.266 + return null;
87.267 + }
87.268 +
87.269 + public void send() {
87.270 + Notification n = new Notification("type.type", this, 0L);
87.271 + nbs.sendNotification(n);
87.272 + }
87.273 + }
87.274 +
87.275 + private static class CountListener implements NotificationListener {
87.276 + volatile int count;
87.277 + public void handleNotification(Notification n, Object h) {
87.278 + if (h == null)
87.279 + h = 1;
87.280 + count += (Integer) h;
87.281 + }
87.282 + void waitForCount(int expect) throws InterruptedException {
87.283 + long deadline = System.currentTimeMillis() + 2000L;
87.284 + while (count < expect && System.currentTimeMillis() < deadline)
87.285 + Thread.sleep(1);
87.286 + assert count == expect;
87.287 + }
87.288 + }
87.289 +
87.290 + private static void testBasic() throws Exception {
87.291 + CountListener countListener = new CountListener();
87.292 + mbsc.addNotificationListener(
87.293 + MBeanServerDelegate.DELEGATE_NAME, countListener, null, null);
87.294 + assert countListener.count == 0;
87.295 + ObjectName name = new ObjectName("a:b=c");
87.296 + if (mbsc instanceof MBeanServer)
87.297 + ((MBeanServer) mbsc).registerMBean(new Boring(), name);
87.298 + else
87.299 + mbsc.createMBean(Boring.class.getName(), name);
87.300 + countListener.waitForCount(1);
87.301 + assert mbsc.isRegistered(name);
87.302 + assert mbsc.queryNames(null, null).contains(name);
87.303 + assert mbsc.getAttribute(name, "Name").equals("Jessica");
87.304 + assert mbsc.invoke(
87.305 + name, "add", new Object[] {2, 3}, new String[] {"int", "int"})
87.306 + .equals(5);
87.307 + mbsc.unregisterMBean(name);
87.308 + countListener.waitForCount(2);
87.309 + assert !mbsc.isRegistered(name);
87.310 + assert !mbsc.queryNames(null, null).contains(name);
87.311 +
87.312 + mbsc.createMBean(BoringNotifier.class.getName(), name);
87.313 + countListener.waitForCount(3);
87.314 + CountListener boringListener = new CountListener();
87.315 + class AlwaysNotificationFilter implements NotificationFilter {
87.316 + public boolean isNotificationEnabled(Notification notification) {
87.317 + return true;
87.318 + }
87.319 + }
87.320 + mbsc.addNotificationListener(
87.321 + name, boringListener, new AlwaysNotificationFilter(), 5);
87.322 + mbsc.invoke(name, "send", null, null);
87.323 + boringListener.waitForCount(5);
87.324 + }
87.325 +
87.326 + private static void testPrintAttrs() throws Exception {
87.327 + printAttrs(mbsc, null);
87.328 + }
87.329 +
87.330 + private static void testPlatformMBeanServer() throws Exception {
87.331 + MBeanServer pmbs = ManagementFactory.getPlatformMBeanServer();
87.332 + assert pmbs instanceof OldMBeanServer;
87.333 + // Preceding assertion could be violated if at some stage we wrap
87.334 + // the Platform MBeanServer. In that case we can still check that
87.335 + // it is ultimately an OldMBeanServer for example by adding a
87.336 + // counter to getAttribute and checking that it is incremented
87.337 + // when we call pmbs.getAttribute.
87.338 +
87.339 + printAttrs(pmbs, UnsupportedOperationException.class);
87.340 + ObjectName memoryMXBeanName =
87.341 + new ObjectName(ManagementFactory.MEMORY_MXBEAN_NAME);
87.342 + pmbs.invoke(memoryMXBeanName, "gc", null, null);
87.343 + }
87.344 +
87.345 + private static void printAttrs(
87.346 + MBeanServerConnection mbsc1, Class<? extends Exception> expectX)
87.347 + throws Exception {
87.348 + Set<ObjectName> names = mbsc1.queryNames(null, null);
87.349 + for (ObjectName name : names) {
87.350 + System.out.println(name + ":");
87.351 + MBeanInfo mbi = mbsc1.getMBeanInfo(name);
87.352 + MBeanAttributeInfo[] mbais = mbi.getAttributes();
87.353 + for (MBeanAttributeInfo mbai : mbais) {
87.354 + String attr = mbai.getName();
87.355 + Object value;
87.356 + try {
87.357 + value = mbsc1.getAttribute(name, attr);
87.358 + } catch (Exception e) {
87.359 + if (expectX != null && expectX.isInstance(e))
87.360 + value = "<" + e + ">";
87.361 + else
87.362 + throw e;
87.363 + }
87.364 + String s = " " + attr + " = " + value;
87.365 + if (s.length() > 80)
87.366 + s = s.substring(0, 77) + "...";
87.367 + System.out.println(s);
87.368 + }
87.369 + }
87.370 + }
87.371 +
87.372 + private static void testJavaxManagementStandardMBean() throws Exception {
87.373 + ObjectName name = new ObjectName("a:b=c");
87.374 + Object mbean = new StandardMBean(new Boring(), BoringMBean.class);
87.375 + mbsc.createMBean(
87.376 + StandardMBean.class.getName(), name,
87.377 + new Object[] {new Boring(), BoringMBean.class},
87.378 + new String[] {Object.class.getName(), Class.class.getName()});
87.379 + assert mbsc.getAttribute(name, "Name").equals("Jessica");
87.380 + assert mbsc.invoke(
87.381 + name, "add", new Object[] {2, 3}, new String[] {"int", "int"})
87.382 + .equals(5);
87.383 + mbsc.unregisterMBean(name);
87.384 + }
87.385 +
87.386 + private static void testConnector() throws Exception {
87.387 + }
87.388 +
87.389 + public static class OldMBeanServerBuilder extends MBeanServerBuilder {
87.390 + public MBeanServer newMBeanServer(
87.391 + String defaultDomain, MBeanServer outer, MBeanServerDelegate delegate) {
87.392 + return new OldMBeanServer(defaultDomain, delegate);
87.393 + }
87.394 + }
87.395 +
87.396 + public static class OldMBeanServer implements MBeanServer {
87.397 + // We pretend there's a ClassLoader MBean representing the Class Loader
87.398 + // Repository and intercept references to it where necessary to keep up
87.399 + // the pretence. This allows us to fake the right behaviour for
87.400 + // the omitted-ClassLoader versions of createMBean and instantiate
87.401 + // (which are not the same as passing a null for the ClassLoader parameter
87.402 + // of the versions that have one).
87.403 + private static final ObjectName clrName;
87.404 + static {
87.405 + try {
87.406 + clrName =
87.407 + new ObjectName("JMImplementation:type=ClassLoaderRepository");
87.408 + } catch (MalformedObjectNameException e) {
87.409 + throw new RuntimeException(e);
87.410 + }
87.411 + }
87.412 +
87.413 + private final ConcurrentMap<ObjectName, DynamicMBean> mbeans =
87.414 + new ConcurrentHashMap<ObjectName, DynamicMBean>();
87.415 + private final ConcurrentMap<ObjectName, ListenerTable> listenerMap =
87.416 + new ConcurrentHashMap<ObjectName, ListenerTable>();
87.417 + private final String defaultDomain;
87.418 + private final MBeanServerDelegate delegate;
87.419 + private final ClassLoaderRepositoryImpl clr =
87.420 + new ClassLoaderRepositoryImpl();
87.421 +
87.422 + OldMBeanServer(String defaultDomain, MBeanServerDelegate delegate) {
87.423 + this.defaultDomain = defaultDomain;
87.424 + this.delegate = delegate;
87.425 + try {
87.426 + registerMBean(delegate, MBeanServerDelegate.DELEGATE_NAME);
87.427 + } catch (Exception e) {
87.428 + throw new RuntimeException(e);
87.429 + }
87.430 + }
87.431 +
87.432 + public ObjectInstance createMBean(String className, ObjectName name)
87.433 + throws ReflectionException, InstanceAlreadyExistsException,
87.434 + MBeanRegistrationException, MBeanException,
87.435 + NotCompliantMBeanException {
87.436 + return createMBean(className, name, null, null);
87.437 + }
87.438 +
87.439 + public ObjectInstance createMBean(
87.440 + String className, ObjectName name, ObjectName loaderName)
87.441 + throws ReflectionException, InstanceAlreadyExistsException,
87.442 + MBeanRegistrationException, MBeanException,
87.443 + NotCompliantMBeanException, InstanceNotFoundException {
87.444 + return createMBean(className, name, loaderName, null, null);
87.445 + }
87.446 +
87.447 + public ObjectInstance createMBean(
87.448 + String className, ObjectName name, Object[] params, String[] signature)
87.449 + throws ReflectionException, InstanceAlreadyExistsException,
87.450 + MBeanRegistrationException, MBeanException,
87.451 + NotCompliantMBeanException {
87.452 + try {
87.453 + return createMBean(className, name, clrName, params, signature);
87.454 + } catch (InstanceNotFoundException ex) {
87.455 + throw new RuntimeException(ex); // can't happen
87.456 + }
87.457 + }
87.458 +
87.459 + public ObjectInstance createMBean(
87.460 + String className, ObjectName name, ObjectName loaderName,
87.461 + Object[] params, String[] signature)
87.462 + throws ReflectionException, InstanceAlreadyExistsException,
87.463 + MBeanRegistrationException, MBeanException,
87.464 + NotCompliantMBeanException, InstanceNotFoundException {
87.465 + Object mbean = instantiate(className, loaderName, params, signature);
87.466 + return registerMBean(mbean, name);
87.467 + }
87.468 +
87.469 + private void forbidJMImpl(ObjectName name) {
87.470 + if (name.getDomain().equals("JMImplementation") &&
87.471 + mbeans.containsKey(MBeanServerDelegate.DELEGATE_NAME))
87.472 + throw new IllegalArgumentException("JMImplementation reserved");
87.473 + }
87.474 +
87.475 + public ObjectInstance registerMBean(Object object, ObjectName name)
87.476 + throws InstanceAlreadyExistsException, MBeanRegistrationException,
87.477 + NotCompliantMBeanException {
87.478 + forbidJMImpl(name);
87.479 + if (name.isPattern())
87.480 + throw new IllegalArgumentException(name.toString());
87.481 + // This is the only place we check for wildcards. Since you
87.482 + // can't register a wildcard name, other operations that supply
87.483 + // one will get InstanceNotFoundException when they look it up.
87.484 +
87.485 + DynamicMBean mbean;
87.486 + if (object instanceof DynamicMBean)
87.487 + mbean = (DynamicMBean) object;
87.488 + else
87.489 + mbean = standardToDynamic(object);
87.490 + MBeanRegistration reg = mbeanRegistration(object);
87.491 + try {
87.492 + name = reg.preRegister(this, name);
87.493 + } catch (Exception e) {
87.494 + throw new MBeanRegistrationException(e);
87.495 + }
87.496 + DynamicMBean put = mbeans.putIfAbsent(name, mbean);
87.497 + if (put != null) {
87.498 + reg.postRegister(false);
87.499 + throw new InstanceAlreadyExistsException(name.toString());
87.500 + }
87.501 + reg.postRegister(true);
87.502 +
87.503 + if (object instanceof ClassLoader)
87.504 + clr.addLoader((ClassLoader) object);
87.505 +
87.506 + Notification n = new MBeanServerNotification(
87.507 + MBeanServerNotification.REGISTRATION_NOTIFICATION,
87.508 + MBeanServerDelegate.DELEGATE_NAME,
87.509 + 0,
87.510 + name);
87.511 + delegate.sendNotification(n);
87.512 +
87.513 + String className = mbean.getMBeanInfo().getClassName();
87.514 + return new ObjectInstance(name, className);
87.515 + }
87.516 +
87.517 + public void unregisterMBean(ObjectName name)
87.518 + throws InstanceNotFoundException, MBeanRegistrationException {
87.519 +
87.520 + forbidJMImpl(name);
87.521 +
87.522 + DynamicMBean mbean = getMBean(name);
87.523 + if (mbean == null)
87.524 + throw new InstanceNotFoundException(name.toString());
87.525 +
87.526 + MBeanRegistration reg = mbeanRegistration(mbean);
87.527 + try {
87.528 + reg.preDeregister();
87.529 + } catch (Exception e) {
87.530 + throw new MBeanRegistrationException(e);
87.531 + }
87.532 + if (!mbeans.remove(name, mbean))
87.533 + throw new InstanceNotFoundException(name.toString());
87.534 + // This is incorrect because we've invoked preDeregister
87.535 +
87.536 + Object userMBean = getUserMBean(mbean);
87.537 + if (userMBean instanceof ClassLoader)
87.538 + clr.removeLoader((ClassLoader) userMBean);
87.539 +
87.540 + Notification n = new MBeanServerNotification(
87.541 + MBeanServerNotification.REGISTRATION_NOTIFICATION,
87.542 + MBeanServerDelegate.DELEGATE_NAME,
87.543 + 0,
87.544 + name);
87.545 + delegate.sendNotification(n);
87.546 +
87.547 + reg.postDeregister();
87.548 + }
87.549 +
87.550 + public ObjectInstance getObjectInstance(ObjectName name)
87.551 + throws InstanceNotFoundException {
87.552 + DynamicMBean mbean = getMBean(name);
87.553 + return new ObjectInstance(name, mbean.getMBeanInfo().getClassName());
87.554 + }
87.555 +
87.556 + private static class TrueQueryExp implements QueryExp {
87.557 + public boolean apply(ObjectName name) {
87.558 + return true;
87.559 + }
87.560 +
87.561 + public void setMBeanServer(MBeanServer s) {}
87.562 + }
87.563 + private static final QueryExp trueQuery = new TrueQueryExp();
87.564 +
87.565 + public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
87.566 + Set<ObjectInstance> instances = newSet();
87.567 + if (name == null)
87.568 + name = ObjectName.WILDCARD;
87.569 + if (query == null)
87.570 + query = trueQuery;
87.571 + MBeanServer oldMBS = QueryEval.getMBeanServer();
87.572 + try {
87.573 + query.setMBeanServer(this);
87.574 + for (ObjectName n : mbeans.keySet()) {
87.575 + if (name.apply(n)) {
87.576 + try {
87.577 + if (query.apply(n))
87.578 + instances.add(getObjectInstance(n));
87.579 + } catch (Exception e) {
87.580 + // OK: Ignore this MBean in the result
87.581 + }
87.582 + }
87.583 + }
87.584 + } finally {
87.585 + query.setMBeanServer(oldMBS);
87.586 + }
87.587 + return instances;
87.588 + }
87.589 +
87.590 + public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
87.591 + Set<ObjectInstance> instances = queryMBeans(name, query);
87.592 + Set<ObjectName> names = newSet();
87.593 + for (ObjectInstance instance : instances)
87.594 + names.add(instance.getObjectName());
87.595 + return names;
87.596 + }
87.597 +
87.598 + public boolean isRegistered(ObjectName name) {
87.599 + return mbeans.containsKey(name);
87.600 + }
87.601 +
87.602 + public Integer getMBeanCount() {
87.603 + return mbeans.size();
87.604 + }
87.605 +
87.606 + public Object getAttribute(ObjectName name, String attribute)
87.607 + throws MBeanException, AttributeNotFoundException,
87.608 + InstanceNotFoundException, ReflectionException {
87.609 + return getMBean(name).getAttribute(attribute);
87.610 + }
87.611 +
87.612 + public AttributeList getAttributes(ObjectName name, String[] attributes)
87.613 + throws InstanceNotFoundException, ReflectionException {
87.614 + return getMBean(name).getAttributes(attributes);
87.615 + }
87.616 +
87.617 + public void setAttribute(ObjectName name, Attribute attribute)
87.618 + throws InstanceNotFoundException, AttributeNotFoundException,
87.619 + InvalidAttributeValueException, MBeanException,
87.620 + ReflectionException {
87.621 + getMBean(name).setAttribute(attribute);
87.622 + }
87.623 +
87.624 + public AttributeList setAttributes(
87.625 + ObjectName name, AttributeList attributes)
87.626 + throws InstanceNotFoundException, ReflectionException {
87.627 + return getMBean(name).setAttributes(attributes);
87.628 + }
87.629 +
87.630 + public Object invoke(
87.631 + ObjectName name, String operationName, Object[] params,
87.632 + String[] signature)
87.633 + throws InstanceNotFoundException, MBeanException, ReflectionException {
87.634 + return getMBean(name).invoke(operationName, params, signature);
87.635 + }
87.636 +
87.637 + public String getDefaultDomain() {
87.638 + return defaultDomain;
87.639 + }
87.640 +
87.641 + public String[] getDomains() {
87.642 + Set<String> domains = newSet();
87.643 + for (ObjectName name : mbeans.keySet())
87.644 + domains.add(name.getDomain());
87.645 + return domains.toArray(new String[0]);
87.646 + }
87.647 +
87.648 + // ClassCastException if MBean is not a NotificationBroadcaster
87.649 + public void addNotificationListener(
87.650 + ObjectName name, NotificationListener listener,
87.651 + NotificationFilter filter, Object handback)
87.652 + throws InstanceNotFoundException {
87.653 + NotificationBroadcaster userMBean =
87.654 + (NotificationBroadcaster) getUserMBean(name);
87.655 + NotificationListener wrappedListener =
87.656 + wrappedListener(name, userMBean, listener);
87.657 + userMBean.addNotificationListener(wrappedListener, filter, handback);
87.658 + }
87.659 +
87.660 + public void addNotificationListener(
87.661 + ObjectName name, ObjectName listener,
87.662 + NotificationFilter filter, Object handback)
87.663 + throws InstanceNotFoundException {
87.664 + NotificationListener nl =
87.665 + (NotificationListener) getUserMBean(listener);
87.666 + addNotificationListener(name, nl, filter, handback);
87.667 + }
87.668 +
87.669 + public void removeNotificationListener(
87.670 + ObjectName name, ObjectName listener)
87.671 + throws InstanceNotFoundException, ListenerNotFoundException {
87.672 + NotificationListener nl =
87.673 + (NotificationListener) getUserMBean(listener);
87.674 + removeNotificationListener(name, nl);
87.675 + }
87.676 +
87.677 + public void removeNotificationListener(
87.678 + ObjectName name, ObjectName listener,
87.679 + NotificationFilter filter, Object handback)
87.680 + throws InstanceNotFoundException, ListenerNotFoundException {
87.681 + NotificationListener nl =
87.682 + (NotificationListener) getUserMBean(listener);
87.683 + removeNotificationListener(name, nl, filter, handback);
87.684 + }
87.685 +
87.686 + public void removeNotificationListener(
87.687 + ObjectName name, NotificationListener listener)
87.688 + throws InstanceNotFoundException, ListenerNotFoundException {
87.689 + NotificationBroadcaster userMBean =
87.690 + (NotificationBroadcaster) getUserMBean(name);
87.691 + NotificationListener wrappedListener =
87.692 + wrappedListener(name, userMBean, listener);
87.693 + userMBean.removeNotificationListener(wrappedListener);
87.694 + }
87.695 +
87.696 + public void removeNotificationListener(
87.697 + ObjectName name, NotificationListener listener,
87.698 + NotificationFilter filter, Object handback)
87.699 + throws InstanceNotFoundException, ListenerNotFoundException {
87.700 + NotificationEmitter userMBean =
87.701 + (NotificationEmitter) getMBean(name);
87.702 + NotificationListener wrappedListener =
87.703 + wrappedListener(name, userMBean, listener);
87.704 + userMBean.removeNotificationListener(wrappedListener, filter, handback);
87.705 + }
87.706 +
87.707 + public MBeanInfo getMBeanInfo(ObjectName name)
87.708 + throws InstanceNotFoundException, IntrospectionException,
87.709 + ReflectionException {
87.710 + return getMBean(name).getMBeanInfo();
87.711 + }
87.712 +
87.713 + public boolean isInstanceOf(ObjectName name, String className)
87.714 + throws InstanceNotFoundException {
87.715 + DynamicMBean mbean = getMBean(name);
87.716 + String mbeanClassName = mbean.getMBeanInfo().getClassName();
87.717 + if (className.equals(mbeanClassName))
87.718 + return true;
87.719 + ClassLoader loader = getUserMBean(mbean).getClass().getClassLoader();
87.720 + try {
87.721 + Class<?> mbeanClass = Class.forName(mbeanClassName, false, loader);
87.722 + Class<?> isInstClass = Class.forName(className, false, loader);
87.723 + return isInstClass.isAssignableFrom(mbeanClass);
87.724 + } catch (ClassNotFoundException e) {
87.725 + return false;
87.726 + }
87.727 + }
87.728 +
87.729 + public Object instantiate(String className)
87.730 + throws ReflectionException, MBeanException {
87.731 + return instantiate(className, null, null);
87.732 + }
87.733 +
87.734 + public Object instantiate(String className, ObjectName loaderName)
87.735 + throws ReflectionException, MBeanException, InstanceNotFoundException {
87.736 + return instantiate(className, loaderName, null, null);
87.737 + }
87.738 +
87.739 + public Object instantiate(
87.740 + String className, Object[] params, String[] signature)
87.741 + throws ReflectionException, MBeanException {
87.742 + try {
87.743 + return instantiate(className, clrName, params, signature);
87.744 + } catch (InstanceNotFoundException e) {
87.745 + throw new RuntimeException(e); // can't happen
87.746 + }
87.747 + }
87.748 +
87.749 + public Object instantiate(
87.750 + String className, ObjectName loaderName,
87.751 + Object[] params, String[] signature)
87.752 + throws ReflectionException, MBeanException, InstanceNotFoundException {
87.753 +
87.754 + if (params == null)
87.755 + params = new Object[0];
87.756 + if (signature == null)
87.757 + signature = new String[0];
87.758 +
87.759 + ClassLoader loader;
87.760 + if (loaderName == null)
87.761 + loader = this.getClass().getClassLoader();
87.762 + else if (loaderName.equals(clrName))
87.763 + loader = clr;
87.764 + else
87.765 + loader = (ClassLoader) getMBean(loaderName);
87.766 +
87.767 + Class<?> c;
87.768 + try {
87.769 + c = Class.forName(className, false, loader);
87.770 + } catch (ClassNotFoundException e) {
87.771 + throw new ReflectionException(e);
87.772 + }
87.773 +
87.774 + Constructor[] constrs = c.getConstructors();
87.775 + Constructor found = null;
87.776 + findconstr:
87.777 + for (Constructor constr : constrs) {
87.778 + Class<?>[] cTypes = constr.getParameterTypes();
87.779 + if (cTypes.length == signature.length) {
87.780 + for (int i = 0; i < cTypes.length; i++) {
87.781 + if (!cTypes[i].getName().equals(signature[i]))
87.782 + continue findconstr;
87.783 + }
87.784 + found = constr;
87.785 + break findconstr;
87.786 + }
87.787 + }
87.788 + if (found == null) {
87.789 + Exception x = new NoSuchMethodException(
87.790 + className + Arrays.toString(signature));
87.791 + throw new ReflectionException(x);
87.792 + }
87.793 + return invokeSomething(found, null, params);
87.794 + }
87.795 +
87.796 + @Deprecated
87.797 + public ObjectInputStream deserialize(ObjectName name, byte[] data)
87.798 + throws InstanceNotFoundException, OperationsException {
87.799 + throw new UnsupportedOperationException();
87.800 + }
87.801 +
87.802 + @Deprecated
87.803 + public ObjectInputStream deserialize(String className, byte[] data)
87.804 + throws OperationsException, ReflectionException {
87.805 + throw new UnsupportedOperationException();
87.806 + }
87.807 +
87.808 + @Deprecated
87.809 + public ObjectInputStream deserialize(
87.810 + String className, ObjectName loaderName, byte[] data)
87.811 + throws InstanceNotFoundException, OperationsException, ReflectionException {
87.812 + throw new UnsupportedOperationException();
87.813 + }
87.814 +
87.815 + public ClassLoader getClassLoaderFor(ObjectName mbeanName)
87.816 + throws InstanceNotFoundException {
87.817 + DynamicMBean mbean = getMBean(mbeanName);
87.818 + Object userMBean = getUserMBean(mbean);
87.819 + return userMBean.getClass().getClassLoader();
87.820 + }
87.821 +
87.822 + public ClassLoader getClassLoader(ObjectName loaderName)
87.823 + throws InstanceNotFoundException {
87.824 + return (ClassLoader) getMBean(loaderName);
87.825 + }
87.826 +
87.827 + public ClassLoaderRepository getClassLoaderRepository() {
87.828 + return new ClassLoaderRepository() {
87.829 + public Class<?> loadClass(String className)
87.830 + throws ClassNotFoundException {
87.831 + return clr.loadClass(className);
87.832 + }
87.833 +
87.834 + public Class<?> loadClassWithout(
87.835 + ClassLoader exclude, String className)
87.836 + throws ClassNotFoundException {
87.837 + return clr.loadClassWithout(exclude, className);
87.838 + }
87.839 +
87.840 + public Class<?> loadClassBefore(
87.841 + ClassLoader stop, String className)
87.842 + throws ClassNotFoundException {
87.843 + return clr.loadClassBefore(stop, className);
87.844 + }
87.845 + };
87.846 + }
87.847 +
87.848 + private static class ClassLoaderRepositoryImpl
87.849 + extends ClassLoader implements ClassLoaderRepository {
87.850 + private List<ClassLoader> loaders = newList();
87.851 + {
87.852 + loaders.add(this.getClass().getClassLoader());
87.853 + // We also behave as if the system class loader were in
87.854 + // the repository, since we do nothing to stop delegation
87.855 + // to the parent, which is the system class loader, and
87.856 + // that delegation happens before our findClass is called.
87.857 + }
87.858 +
87.859 + void addLoader(ClassLoader loader) {
87.860 + loaders.add(loader);
87.861 + }
87.862 +
87.863 + void removeLoader(ClassLoader loader) {
87.864 + if (!loaders.remove(loader))
87.865 + throw new RuntimeException("Loader was not in CLR!");
87.866 + }
87.867 +
87.868 + public Class<?> loadClassWithout(
87.869 + ClassLoader exclude, String className)
87.870 + throws ClassNotFoundException {
87.871 + return loadClassWithoutBefore(exclude, null, className);
87.872 + }
87.873 +
87.874 + public Class<?> loadClassBefore(ClassLoader stop, String className)
87.875 + throws ClassNotFoundException {
87.876 + return loadClassWithoutBefore(null, stop, className);
87.877 + }
87.878 +
87.879 + private Class<?> loadClassWithoutBefore(
87.880 + ClassLoader exclude, ClassLoader stop, String className)
87.881 + throws ClassNotFoundException {
87.882 + for (ClassLoader loader : loaders) {
87.883 + if (loader == exclude)
87.884 + continue;
87.885 + if (loader == stop)
87.886 + break;
87.887 + try {
87.888 + return Class.forName(className, false, loader);
87.889 + } catch (ClassNotFoundException e) {
87.890 + // OK: try others
87.891 + }
87.892 + }
87.893 + throw new ClassNotFoundException(className);
87.894 + }
87.895 +
87.896 + @Override
87.897 + protected Class<?> findClass(String className)
87.898 + throws ClassNotFoundException {
87.899 + return loadClassWithout(null, className);
87.900 + }
87.901 + }
87.902 +
87.903 + /* There is zero or one ListenerTable per MBean.
87.904 + * The ListenerTable stuff is complicated. We want to rewrite the
87.905 + * source of notifications so that if the source of a notification
87.906 + * from the MBean X is a reference to X itself, it gets replaced
87.907 + * by X's ObjectName. To do this, we wrap the user's listener in
87.908 + * a RewriteListener. But if the same listener is added a second
87.909 + * time (perhaps with a different filter or handback) we must
87.910 + * reuse the same RewriteListener so that the two-argument
87.911 + * removeNotificationListener(ObjectName,NotificationListener) will
87.912 + * correctly remove both listeners. This means we must remember the
87.913 + * mapping from listener to WrappedListener. But if the MBean
87.914 + * discards its listeners (as a result of removeNL or spontaneously)
87.915 + * then we don't want to keep a reference to the WrappedListener.
87.916 + * So we have tons of WeakReferences. The key in the ListenerTable
87.917 + * is an IdentityListener, which wraps the user's listener to ensure
87.918 + * that identity and not equality is used during the lookup, even if
87.919 + * the user's listener has an equals method. The value in the
87.920 + * ListenerTable is a WeakReference wrapping a RewriteListener wrapping
87.921 + * the same IdentityListener. Since the RewriteListener is what is
87.922 + * added to the user's MBean, the WeakReference won't disappear as long
87.923 + * as the MBean still has this listener. And since it references the
87.924 + * IdentityListener, that won't disappear either. But once the
87.925 + * RewriteListener is no longer referenced by the user's MBean,
87.926 + * there's nothing to stop its WeakReference from being cleared,
87.927 + * and then corresponding IdentityListener that is now only weakly
87.928 + * referenced from the key in the table.
87.929 + */
87.930 + private static class ListenerTable
87.931 + extends WeakHashMap<NotificationListener,
87.932 + WeakReference<NotificationListener>> {
87.933 + }
87.934 +
87.935 + private static class IdentityListener implements NotificationListener {
87.936 + private final NotificationListener userListener;
87.937 +
87.938 + IdentityListener(NotificationListener userListener) {
87.939 + this.userListener = userListener;
87.940 + }
87.941 +
87.942 + public void handleNotification(
87.943 + Notification notification, Object handback) {
87.944 + userListener.handleNotification(notification, handback);
87.945 + }
87.946 +
87.947 + @Override
87.948 + public boolean equals(Object o) {
87.949 + return (this == o);
87.950 + }
87.951 +
87.952 + @Override
87.953 + public int hashCode() {
87.954 + return System.identityHashCode(this);
87.955 + }
87.956 + }
87.957 +
87.958 + private static class RewriteListener implements NotificationListener {
87.959 + private final ObjectName name;
87.960 + private final Object userMBean;
87.961 + private final NotificationListener userListener;
87.962 +
87.963 + RewriteListener(
87.964 + ObjectName name, Object userMBean,
87.965 + NotificationListener userListener) {
87.966 + this.name = name;
87.967 + this.userMBean = userMBean;
87.968 + this.userListener = userListener;
87.969 + }
87.970 +
87.971 + public void handleNotification(
87.972 + Notification notification, Object handback) {
87.973 + if (notification.getSource() == userMBean)
87.974 + notification.setSource(name);
87.975 + userListener.handleNotification(notification, handback);
87.976 + }
87.977 + }
87.978 +
87.979 + private NotificationListener wrappedListener(
87.980 + ObjectName name, Object userMBean, NotificationListener userListener)
87.981 + throws InstanceNotFoundException {
87.982 + ListenerTable table = new ListenerTable();
87.983 + ListenerTable oldTable = listenerMap.putIfAbsent(name, table);
87.984 + if (oldTable != null)
87.985 + table = oldTable;
87.986 + NotificationListener identityListener =
87.987 + new IdentityListener(userListener);
87.988 + synchronized (table) {
87.989 + NotificationListener rewriteListener = null;
87.990 + WeakReference<NotificationListener> wr =
87.991 + table.get(identityListener);
87.992 + if (wr != null)
87.993 + rewriteListener = wr.get();
87.994 + if (rewriteListener == null) {
87.995 + rewriteListener = new RewriteListener(
87.996 + name, userMBean, identityListener);
87.997 + wr = new WeakReference<NotificationListener>(rewriteListener);
87.998 + table.put(identityListener, wr);
87.999 + }
87.1000 + return rewriteListener;
87.1001 + }
87.1002 + }
87.1003 +
87.1004 + private DynamicMBean getMBean(ObjectName name)
87.1005 + throws InstanceNotFoundException {
87.1006 + DynamicMBean mbean = mbeans.get(name);
87.1007 + if (mbean == null)
87.1008 + throw new InstanceNotFoundException(name.toString());
87.1009 + return mbean;
87.1010 + }
87.1011 +
87.1012 + private static interface WrapDynamicMBean extends DynamicMBean {
87.1013 + public Object getWrappedMBean();
87.1014 + }
87.1015 +
87.1016 + private static class StandardWrapper
87.1017 + implements WrapDynamicMBean, MBeanRegistration {
87.1018 + private final Map<String, AttrMethods> attrMap = newMap();
87.1019 + private final Map<String, List<Method>> opMap = newMap();
87.1020 + private static class AttrMethods {
87.1021 + Method getter, setter;
87.1022 + }
87.1023 +
87.1024 + private final Object std;
87.1025 +
87.1026 + StandardWrapper(Object std) throws NotCompliantMBeanException {
87.1027 + this.std = std;
87.1028 + Class<?> intf = mbeanInterface(std.getClass());
87.1029 + try {
87.1030 + initMaps(intf);
87.1031 + } catch (NotCompliantMBeanException e) {
87.1032 + throw e;
87.1033 + } catch (Exception e) {
87.1034 + NotCompliantMBeanException x =
87.1035 + new NotCompliantMBeanException(e.getMessage());
87.1036 + x.initCause(e);
87.1037 + throw x;
87.1038 + }
87.1039 + }
87.1040 +
87.1041 + private static Class<?> mbeanInterface(Class<?> c)
87.1042 + throws NotCompliantMBeanException {
87.1043 + do {
87.1044 + Class<?>[] intfs = c.getInterfaces();
87.1045 + String intfName = c.getName() + "MBean";
87.1046 + for (Class<?> intf : intfs) {
87.1047 + if (intf.getName().equals(intfName))
87.1048 + return intf;
87.1049 + }
87.1050 + c = c.getSuperclass();
87.1051 + } while (c != null);
87.1052 + throw new NotCompliantMBeanException(
87.1053 + "Does not match Standard or Dynamic MBean patterns: " +
87.1054 + c.getName());
87.1055 + }
87.1056 +
87.1057 + private void initMaps(Class<?> intf) throws NotCompliantMBeanException {
87.1058 + Method[] methods = intf.getMethods();
87.1059 +
87.1060 + for (Method m : methods) {
87.1061 + final String name = m.getName();
87.1062 + final int nParams = m.getParameterTypes().length;
87.1063 +
87.1064 + String attrName = "";
87.1065 + if (name.startsWith("get"))
87.1066 + attrName = name.substring(3);
87.1067 + else if (name.startsWith("is")
87.1068 + && m.getReturnType() == boolean.class)
87.1069 + attrName = name.substring(2);
87.1070 +
87.1071 + if (attrName.length() != 0 && m.getParameterTypes().length == 0
87.1072 + && m.getReturnType() != void.class) {
87.1073 + // It's a getter
87.1074 + // Check we don't have both isX and getX
87.1075 + AttrMethods am = attrMap.get(attrName);
87.1076 + if (am == null)
87.1077 + am = new AttrMethods();
87.1078 + else {
87.1079 + if (am.getter != null) {
87.1080 + final String msg = "Attribute " + attrName +
87.1081 + " has more than one getter";
87.1082 + throw new NotCompliantMBeanException(msg);
87.1083 + }
87.1084 + }
87.1085 + am.getter = m;
87.1086 + attrMap.put(attrName, am);
87.1087 + } else if (name.startsWith("set") && name.length() > 3
87.1088 + && m.getParameterTypes().length == 1 &&
87.1089 + m.getReturnType() == void.class) {
87.1090 + // It's a setter
87.1091 + attrName = name.substring(3);
87.1092 + AttrMethods am = attrMap.get(attrName);
87.1093 + if (am == null)
87.1094 + am = new AttrMethods();
87.1095 + else if (am.setter != null) {
87.1096 + final String msg = "Attribute " + attrName +
87.1097 + " has more than one setter";
87.1098 + throw new NotCompliantMBeanException(msg);
87.1099 + }
87.1100 + am.setter = m;
87.1101 + attrMap.put(attrName, am);
87.1102 + } else {
87.1103 + // It's an operation
87.1104 + List<Method> ops = opMap.get(name);
87.1105 + if (ops == null)
87.1106 + ops = newList();
87.1107 + ops.add(m);
87.1108 + opMap.put(name, ops);
87.1109 + }
87.1110 + }
87.1111 + /* Check that getters and setters are consistent. */
87.1112 + for (Map.Entry<String, AttrMethods> entry : attrMap.entrySet()) {
87.1113 + AttrMethods am = entry.getValue();
87.1114 + if (am.getter != null && am.setter != null &&
87.1115 + am.getter.getReturnType() != am.setter.getParameterTypes()[0]) {
87.1116 + final String msg = "Getter and setter for " + entry.getKey() +
87.1117 + " have inconsistent types";
87.1118 + throw new NotCompliantMBeanException(msg);
87.1119 + }
87.1120 + }
87.1121 + }
87.1122 +
87.1123 + public Object getAttribute(String attribute)
87.1124 + throws AttributeNotFoundException, MBeanException, ReflectionException {
87.1125 + AttrMethods am = attrMap.get(attribute);
87.1126 + if (am == null || am.getter == null)
87.1127 + throw new AttributeNotFoundException(attribute);
87.1128 + return invokeMethod(am.getter);
87.1129 + }
87.1130 +
87.1131 + public void setAttribute(Attribute attribute)
87.1132 + throws AttributeNotFoundException, InvalidAttributeValueException,
87.1133 + MBeanException, ReflectionException {
87.1134 + String name = attribute.getName();
87.1135 + AttrMethods am = attrMap.get(name);
87.1136 + if (am == null || am.setter == null)
87.1137 + throw new AttributeNotFoundException(name);
87.1138 + invokeMethod(am.setter, attribute.getValue());
87.1139 + }
87.1140 +
87.1141 + public AttributeList getAttributes(String[] attributes) {
87.1142 + AttributeList list = new AttributeList();
87.1143 + for (String attr : attributes) {
87.1144 + try {
87.1145 + list.add(new Attribute(attr, getAttribute(attr)));
87.1146 + } catch (Exception e) {
87.1147 + // OK: ignore per spec
87.1148 + }
87.1149 + }
87.1150 + return list;
87.1151 + }
87.1152 +
87.1153 + public AttributeList setAttributes(AttributeList attributes) {
87.1154 + AttributeList list = new AttributeList();
87.1155 + // We carefully avoid using any new stuff from AttributeList here!
87.1156 + for (Iterator<?> it = attributes.iterator(); it.hasNext(); ) {
87.1157 + Attribute attr = (Attribute) it.next();
87.1158 + try {
87.1159 + setAttribute(attr);
87.1160 + list.add(attr);
87.1161 + } catch (Exception e) {
87.1162 + // OK: ignore per spec
87.1163 + }
87.1164 + }
87.1165 + return list;
87.1166 + }
87.1167 +
87.1168 + public Object invoke(String actionName, Object[] params, String[] signature)
87.1169 + throws MBeanException, ReflectionException {
87.1170 + if (params == null)
87.1171 + params = new Object[0];
87.1172 + if (signature == null)
87.1173 + signature = new String[0];
87.1174 + List<Method> methods = opMap.get(actionName);
87.1175 + if (methods == null) {
87.1176 + Exception x = new NoSuchMethodException(actionName);
87.1177 + throw new MBeanException(x);
87.1178 + }
87.1179 + Method found = null;
87.1180 + methodloop:
87.1181 + for (Method m : methods) {
87.1182 + Class<?>[] msig = m.getParameterTypes();
87.1183 + if (msig.length != signature.length)
87.1184 + continue methodloop;
87.1185 + for (int i = 0; i < msig.length; i++) {
87.1186 + if (!msig[i].getName().equals(signature[i]))
87.1187 + continue methodloop;
87.1188 + }
87.1189 + found = m;
87.1190 + break methodloop;
87.1191 + }
87.1192 + if (found == null) {
87.1193 + Exception x = new NoSuchMethodException(
87.1194 + actionName + Arrays.toString(signature));
87.1195 + throw new MBeanException(x);
87.1196 + }
87.1197 + return invokeMethod(found, params);
87.1198 + }
87.1199 +
87.1200 + public MBeanInfo getMBeanInfo() {
87.1201 + // Attributes
87.1202 + List<MBeanAttributeInfo> attrs = newList();
87.1203 + for (Map.Entry<String, AttrMethods> attr : attrMap.entrySet()) {
87.1204 + String name = attr.getKey();
87.1205 + AttrMethods am = attr.getValue();
87.1206 + try {
87.1207 + attrs.add(new MBeanAttributeInfo(
87.1208 + name, name, am.getter, am.setter));
87.1209 + } catch (IntrospectionException e) { // grrr
87.1210 + throw new RuntimeException(e);
87.1211 + }
87.1212 + }
87.1213 +
87.1214 + // Operations
87.1215 + List<MBeanOperationInfo> ops = newList();
87.1216 + for (Map.Entry<String, List<Method>> op : opMap.entrySet()) {
87.1217 + String name = op.getKey();
87.1218 + List<Method> methods = op.getValue();
87.1219 + for (Method m : methods)
87.1220 + ops.add(new MBeanOperationInfo(name, m));
87.1221 + }
87.1222 +
87.1223 + // Constructors
87.1224 + List<MBeanConstructorInfo> constrs = newList();
87.1225 + for (Constructor constr : std.getClass().getConstructors())
87.1226 + constrs.add(new MBeanConstructorInfo("Constructor", constr));
87.1227 +
87.1228 + // Notifications
87.1229 + MBeanNotificationInfo[] notifs;
87.1230 + if (std instanceof NotificationBroadcaster)
87.1231 + notifs = ((NotificationBroadcaster) std).getNotificationInfo();
87.1232 + else
87.1233 + notifs = null;
87.1234 +
87.1235 + String className = std.getClass().getName();
87.1236 + return new MBeanInfo(
87.1237 + className, className,
87.1238 + attrs.toArray(new MBeanAttributeInfo[0]),
87.1239 + constrs.toArray(new MBeanConstructorInfo[0]),
87.1240 + ops.toArray(new MBeanOperationInfo[0]),
87.1241 + notifs);
87.1242 + }
87.1243 +
87.1244 + private Object invokeMethod(Method m, Object... args)
87.1245 + throws MBeanException, ReflectionException {
87.1246 + return invokeSomething(m, std,args);
87.1247 + }
87.1248 +
87.1249 + public ObjectName preRegister(MBeanServer server, ObjectName name)
87.1250 + throws Exception {
87.1251 + return mbeanRegistration(std).preRegister(server, name);
87.1252 + }
87.1253 +
87.1254 + public void postRegister(Boolean registrationDone) {
87.1255 + mbeanRegistration(std).postRegister(registrationDone);
87.1256 + }
87.1257 +
87.1258 + public void preDeregister() throws Exception {
87.1259 + mbeanRegistration(std).preDeregister();
87.1260 + }
87.1261 +
87.1262 + public void postDeregister() {
87.1263 + mbeanRegistration(std).postDeregister();
87.1264 + }
87.1265 +
87.1266 + public Object getWrappedMBean() {
87.1267 + return std;
87.1268 + }
87.1269 + }
87.1270 +
87.1271 + private DynamicMBean standardToDynamic(Object std)
87.1272 + throws NotCompliantMBeanException {
87.1273 + return new StandardWrapper(std);
87.1274 + }
87.1275 +
87.1276 +// private static class NotifWrapper
87.1277 +// implements WrapDynamicMBean, NotificationEmitter {
87.1278 +// private final DynamicMBean mbean;
87.1279 +//
87.1280 +// NotifWrapper(DynamicMBean mbean) {
87.1281 +// this.mbean = mbean;
87.1282 +// }
87.1283 +//
87.1284 +// public Object getAttribute(String attribute)
87.1285 +// throws AttributeNotFoundException, MBeanException, ReflectionException {
87.1286 +// return mbean.getAttribute(attribute);
87.1287 +// }
87.1288 +//
87.1289 +// public void setAttribute(Attribute attribute)
87.1290 +// throws AttributeNotFoundException, InvalidAttributeValueException,
87.1291 +// MBeanException, ReflectionException {
87.1292 +// mbean.setAttribute(attribute);
87.1293 +// }
87.1294 +//
87.1295 +// public AttributeList getAttributes(String[] attributes) {
87.1296 +// return mbean.getAttributes(attributes);
87.1297 +// }
87.1298 +//
87.1299 +// public AttributeList setAttributes(AttributeList attributes) {
87.1300 +// return mbean.setAttributes(attributes);
87.1301 +// }
87.1302 +//
87.1303 +// public Object invoke(
87.1304 +// String actionName, Object[] params, String[] signature)
87.1305 +// throws MBeanException, ReflectionException {
87.1306 +// return mbean.invoke(actionName, params, signature);
87.1307 +// }
87.1308 +//
87.1309 +// public MBeanInfo getMBeanInfo() {
87.1310 +// return mbean.getMBeanInfo();
87.1311 +// }
87.1312 +//
87.1313 +// public void removeNotificationListener(
87.1314 +// NotificationListener listener, NotificationFilter filter, Object handback)
87.1315 +// throws ListenerNotFoundException {
87.1316 +// ((NotificationEmitter) mbean).removeNotificationListener(
87.1317 +// listener, filter, handback);
87.1318 +// // ClassCastException if MBean is not an emitter
87.1319 +// }
87.1320 +//
87.1321 +// public void addNotificationListener(
87.1322 +// NotificationListener listener, NotificationFilter filter, Object handback)
87.1323 +// throws IllegalArgumentException {
87.1324 +// ((NotificationBroadcaster) mbean).addNotificationListener(
87.1325 +// listener, filter, handback);
87.1326 +// }
87.1327 +//
87.1328 +// public void removeNotificationListener(NotificationListener listener)
87.1329 +// throws ListenerNotFoundException {
87.1330 +// ((NotificationBroadcaster) mbean).removeNotificationListener(listener);
87.1331 +// }
87.1332 +//
87.1333 +// public MBeanNotificationInfo[] getNotificationInfo() {
87.1334 +// return ((NotificationBroadcaster) mbean).getNotificationInfo();
87.1335 +// }
87.1336 +//
87.1337 +// public Object getWrappedMBean() {
87.1338 +// return getUserMBean(mbean);
87.1339 +// }
87.1340 +// }
87.1341 +
87.1342 + private static Object invokeSomething(
87.1343 + AccessibleObject ao, Object target, Object[] args)
87.1344 + throws MBeanException, ReflectionException {
87.1345 + try {
87.1346 + if (ao instanceof Method)
87.1347 + return ((Method) ao).invoke(target, args);
87.1348 + else
87.1349 + return ((Constructor) ao).newInstance(args);
87.1350 + } catch (InvocationTargetException e) {
87.1351 + try {
87.1352 + throw e.getCause();
87.1353 + } catch (RuntimeException x) {
87.1354 + throw new RuntimeMBeanException(x);
87.1355 + } catch (Error x) {
87.1356 + throw new RuntimeErrorException(x);
87.1357 + } catch (Exception x) {
87.1358 + throw new MBeanException(x);
87.1359 + } catch (Throwable x) {
87.1360 + throw new RuntimeException(x); // neither Error nor Exception!
87.1361 + }
87.1362 + } catch (Exception e) {
87.1363 + throw new ReflectionException(e);
87.1364 + }
87.1365 + }
87.1366 +
87.1367 + private static Object getUserMBean(DynamicMBean mbean) {
87.1368 + if (mbean instanceof WrapDynamicMBean)
87.1369 + return ((WrapDynamicMBean) mbean).getWrappedMBean();
87.1370 + return mbean;
87.1371 + }
87.1372 +
87.1373 + private Object getUserMBean(ObjectName name)
87.1374 + throws InstanceNotFoundException {
87.1375 + return getUserMBean(getMBean(name));
87.1376 + }
87.1377 +
87.1378 + private static final MBeanRegistration noRegistration =
87.1379 + new MBeanRegistration() {
87.1380 + public ObjectName preRegister(MBeanServer server, ObjectName name) {
87.1381 + return name;
87.1382 + }
87.1383 +
87.1384 + public void postRegister(Boolean registrationDone) {
87.1385 + }
87.1386 +
87.1387 + public void preDeregister() throws Exception {
87.1388 + }
87.1389 +
87.1390 + public void postDeregister() {
87.1391 + }
87.1392 + };
87.1393 +
87.1394 + private static MBeanRegistration mbeanRegistration(Object object) {
87.1395 + if (object instanceof MBeanRegistration)
87.1396 + return (MBeanRegistration) object;
87.1397 + else
87.1398 + return noRegistration;
87.1399 + }
87.1400 +
87.1401 + private static <E> List<E> newList() {
87.1402 + return new ArrayList<E>();
87.1403 + }
87.1404 +
87.1405 + private static <K, V> Map<K, V> newMap() {
87.1406 + return new HashMap<K, V>();
87.1407 + }
87.1408 +
87.1409 + private static <E> Set<E> newSet() {
87.1410 + return new HashSet<E>();
87.1411 + }
87.1412 + }
87.1413 +}
88.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
88.2 +++ b/test/javax/management/MBeanServer/PostExceptionTest.java Thu Aug 21 09:55:18 2008 -0700
88.3 @@ -0,0 +1,516 @@
88.4 +/*
88.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
88.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
88.7 + *
88.8 + * This code is free software; you can redistribute it and/or modify it
88.9 + * under the terms of the GNU General Public License version 2 only, as
88.10 + * published by the Free Software Foundation.
88.11 + *
88.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
88.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
88.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
88.15 + * version 2 for more details (a copy is included in the LICENSE file that
88.16 + * accompanied this code).
88.17 + *
88.18 + * You should have received a copy of the GNU General Public License version
88.19 + * 2 along with this work; if not, write to the Free Software Foundation,
88.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
88.21 + *
88.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
88.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
88.24 + * have any questions.
88.25 + */
88.26 +
88.27 +/*
88.28 + * @test
88.29 + * @bug 6730926
88.30 + * @summary Check behaviour of MBeanServer when postRegister and postDeregister
88.31 + * throw exceptions.
88.32 + * @author Daniel Fuchs
88.33 + * @compile PostExceptionTest.java
88.34 + * @run main PostExceptionTest
88.35 + */
88.36 +
88.37 +import javax.management.*;
88.38 +import java.io.Serializable;
88.39 +import java.net.URL;
88.40 +import java.util.EnumSet;
88.41 +import javax.management.loading.MLet;
88.42 +
88.43 +public class PostExceptionTest {
88.44 +
88.45 + /**
88.46 + * A test case where we instantiate an ExceptionalWombatMBean (or a
88.47 + * subclass of it) which will throw the exception {@code t} from within
88.48 + * the methods indicated by {@code where}
88.49 + */
88.50 + public static class Case {
88.51 + public final Throwable t;
88.52 + public final EnumSet<WHERE> where;
88.53 + public Case(Throwable t,EnumSet<WHERE> where) {
88.54 + this.t=t; this.where=where;
88.55 + }
88.56 + }
88.57 +
88.58 + // Various methods to create an instance of Case in a single line
88.59 + // --------------------------------------------------------------
88.60 +
88.61 + public static Case caze(Throwable t, WHERE w) {
88.62 + return new Case(t,EnumSet.of(w));
88.63 + }
88.64 + public static Case caze(Throwable t, EnumSet<WHERE> where) {
88.65 + return new Case(t,where);
88.66 + }
88.67 + public static Case caze(Throwable t, WHERE w, WHERE... rest) {
88.68 + return new Case(t,EnumSet.of(w,rest));
88.69 + }
88.70 +
88.71 + /**
88.72 + * Here is the list of our test cases:
88.73 + */
88.74 + public static Case[] cases ={
88.75 + caze(new RuntimeException(),WHERE.PREREGISTER),
88.76 + caze(new RuntimeException(),WHERE.POSTREGISTER),
88.77 + caze(new RuntimeException(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER),
88.78 + caze(new RuntimeException(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER),
88.79 + caze(new Exception(),WHERE.PREREGISTER),
88.80 + caze(new Exception(),WHERE.POSTREGISTER),
88.81 + caze(new Exception(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER),
88.82 + caze(new Exception(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER),
88.83 + caze(new Error(),WHERE.PREREGISTER),
88.84 + caze(new Error(),WHERE.POSTREGISTER),
88.85 + caze(new Error(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER),
88.86 + caze(new Error(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER),
88.87 + caze(new RuntimeException(),EnumSet.allOf(WHERE.class)),
88.88 + caze(new Exception(),EnumSet.allOf(WHERE.class)),
88.89 + caze(new Error(),EnumSet.allOf(WHERE.class)),
88.90 + };
88.91 +
88.92 + public static void main(String[] args) throws Exception {
88.93 + System.out.println("Test behaviour of MBeanServer when postRegister " +
88.94 + "or postDeregister throw exceptions");
88.95 + MBeanServer mbs = MBeanServerFactory.newMBeanServer();
88.96 + int failures = 0;
88.97 + final ObjectName n = new ObjectName("test:type=Wombat");
88.98 +
88.99 + // We're going to test each cases, using each of the 4 createMBean
88.100 + // forms + registerMBean in turn to create the MBean.
88.101 + // Wich method is used to create the MBean is indicated by "how"
88.102 + //
88.103 + for (Case caze:cases) {
88.104 + for (CREATE how : CREATE.values()) {
88.105 + failures+=test(mbs,n,how,caze.t,caze.where);
88.106 + }
88.107 + }
88.108 + if (failures == 0)
88.109 + System.out.println("Test passed");
88.110 + else {
88.111 + System.out.println("TEST FAILED: " + failures + " failure(s)");
88.112 + System.exit(1);
88.113 + }
88.114 + }
88.115 +
88.116 + // Execute a test case composed of:
88.117 + // mbs: The MBeanServer where the MBean will be registered,
88.118 + // name: The name of that MBean
88.119 + // how: How will the MBean be created/registered (which MBeanServer
88.120 + // method)
88.121 + // t: The exception/error that the MBean will throw
88.122 + // where: In which pre/post register/deregister method the exception/error
88.123 + // will be thrown
88.124 + //
88.125 + private static int test(MBeanServer mbs, ObjectName name, CREATE how,
88.126 + Throwable t, EnumSet<WHERE> where)
88.127 + throws Exception {
88.128 + System.out.println("-------<"+how+"> / <"+t+"> / "+ where + "-------");
88.129 +
88.130 + int failures = 0;
88.131 + ObjectInstance oi = null;
88.132 + Exception reg = null; // exception thrown by create/register
88.133 + Exception unreg = null; // exception thrown by unregister
88.134 + try {
88.135 + // Create the MBean
88.136 + oi = how.create(t, where, mbs, name);
88.137 + } catch (Exception xx) {
88.138 + reg=xx;
88.139 + }
88.140 + final ObjectName n = (oi==null)?name:oi.getObjectName();
88.141 + final boolean isRegistered = mbs.isRegistered(n);
88.142 + try {
88.143 + // If the MBean is registered, unregister it
88.144 + if (isRegistered) mbs.unregisterMBean(n);
88.145 + } catch (Exception xxx) {
88.146 + unreg=xxx;
88.147 + }
88.148 + final boolean isUnregistered = !mbs.isRegistered(n);
88.149 + if (!isUnregistered) {
88.150 + // if the MBean is still registered (preDeregister threw an
88.151 + // exception) signify to the MBean that it now should stop
88.152 + // throwing anaything and unregister it.
88.153 + JMX.newMBeanProxy(mbs, n, ExceptionalWombatMBean.class).end();
88.154 + mbs.unregisterMBean(n);
88.155 + }
88.156 +
88.157 + // Now analyze the result. If we didn't ask the MBean to throw any
88.158 + // exception then reg should be null.
88.159 + if (where.isEmpty() && reg!=null) {
88.160 + System.out.println("Unexpected registration exception: "+
88.161 + reg);
88.162 + throw new RuntimeException("Unexpected registration exception: "+
88.163 + reg,reg);
88.164 + }
88.165 +
88.166 + // If we didn't ask the MBean to throw any exception then unreg should
88.167 + // also be null.
88.168 + if (where.isEmpty() && unreg!=null) {
88.169 + System.out.println("Unexpected unregistration exception: "+
88.170 + unreg);
88.171 + throw new RuntimeException("Unexpected unregistration exception: "+
88.172 + unreg,unreg);
88.173 + }
88.174 +
88.175 + // If we asked the MBean to throw an exception in either of preRegister
88.176 + // or postRegister, then reg should not be null.
88.177 + if ((where.contains(WHERE.PREREGISTER)
88.178 + || where.contains(WHERE.POSTREGISTER))&& reg==null) {
88.179 + System.out.println("Expected registration exception not " +
88.180 + "thrown by "+where);
88.181 + throw new RuntimeException("Expected registration exception not " +
88.182 + "thrown by "+where);
88.183 + }
88.184 +
88.185 + // If we asked the MBean not to throw any exception in preRegister
88.186 + // then the MBean should have been registered, unregisterMBean should
88.187 + // have been called.
88.188 + // If we asked the MBean to throw an exception in either of preDeregister
88.189 + // or postDeregister, then unreg should not be null.
88.190 + if ((where.contains(WHERE.PREDEREGISTER)
88.191 + || where.contains(WHERE.POSTDEREGISTER))&& unreg==null
88.192 + && !where.contains(WHERE.PREREGISTER)) {
88.193 + System.out.println("Expected unregistration exception not " +
88.194 + "thrown by "+where);
88.195 + throw new RuntimeException("Expected unregistration exception not " +
88.196 + "thrown by "+where);
88.197 + }
88.198 +
88.199 + // If we asked the MBean to throw an exception in preRegister
88.200 + // then the MBean should not have been registered.
88.201 + if (where.contains(WHERE.PREREGISTER)) {
88.202 + if (isRegistered) {
88.203 + System.out.println("MBean is still registered [" +
88.204 + where+
88.205 + "]: "+name+" / "+reg);
88.206 + throw new RuntimeException("MBean is still registered [" +
88.207 + where+
88.208 + "]: "+name+" / "+reg,reg);
88.209 + }
88.210 + }
88.211 +
88.212 + // If we asked the MBean not to throw an exception in preRegister,
88.213 + // but to throw an exception in postRegister, then the MBean should
88.214 + // have been registered.
88.215 + if (where.contains(WHERE.POSTREGISTER) &&
88.216 + !where.contains(WHERE.PREREGISTER)) {
88.217 + if (!isRegistered) {
88.218 + System.out.println("MBean is already unregistered [" +
88.219 + where+
88.220 + "]: "+name+" / "+reg);
88.221 + throw new RuntimeException("MBean is already unregistered [" +
88.222 + where+
88.223 + "]: "+name+" / "+reg,reg);
88.224 + }
88.225 + }
88.226 +
88.227 + // If we asked the MBean to throw an exception in preRegister,
88.228 + // check that the exception we caught was as expected.
88.229 + //
88.230 + if (where.contains(WHERE.PREREGISTER)) {
88.231 + WHERE.PREREGISTER.check(reg, t);
88.232 + } else if (where.contains(WHERE.POSTREGISTER)) {
88.233 + // If we asked the MBean to throw an exception in postRegister,
88.234 + // check that the exception we caught was as expected.
88.235 + // We don't do this check if we asked the MBean to also throw an
88.236 + // exception in pre register, because postRegister will not have
88.237 + // been called.
88.238 + WHERE.POSTREGISTER.check(reg, t);
88.239 + }
88.240 +
88.241 + if (!isRegistered) return failures;
88.242 +
88.243 + // The MBean was registered, so unregisterMBean was called. Check
88.244 + // unregisterMBean exceptions...
88.245 + //
88.246 +
88.247 + // If we asked the MBean to throw an exception in preDeregister
88.248 + // then the MBean should not have been deregistered.
88.249 + if (where.contains(WHERE.PREDEREGISTER)) {
88.250 + if (isUnregistered) {
88.251 + System.out.println("MBean is already unregistered [" +
88.252 + where+
88.253 + "]: "+name+" / "+unreg);
88.254 + throw new RuntimeException("MBean is already unregistered [" +
88.255 + where+
88.256 + "]: "+name+" / "+unreg,unreg);
88.257 + }
88.258 + }
88.259 +
88.260 + // If we asked the MBean not to throw an exception in preDeregister,
88.261 + // but to throw an exception in postDeregister, then the MBean should
88.262 + // have been deregistered.
88.263 + if (where.contains(WHERE.POSTDEREGISTER) &&
88.264 + !where.contains(WHERE.PREDEREGISTER)) {
88.265 + if (!isUnregistered) {
88.266 + System.out.println("MBean is not unregistered [" +
88.267 + where+
88.268 + "]: "+name+" / "+unreg);
88.269 + throw new RuntimeException("MBean is not unregistered [" +
88.270 + where+
88.271 + "]: "+name+" / "+unreg,unreg);
88.272 + }
88.273 + }
88.274 +
88.275 + // If we asked the MBean to throw an exception in preDeregister,
88.276 + // check that the exception we caught was as expected.
88.277 + //
88.278 + if (where.contains(WHERE.PREDEREGISTER)) {
88.279 + WHERE.PREDEREGISTER.check(unreg, t);
88.280 + } else if (where.contains(WHERE.POSTDEREGISTER)) {
88.281 + // If we asked the MBean to throw an exception in postDeregister,
88.282 + // check that the exception we caught was as expected.
88.283 + // We don't do this check if we asked the MBean to also throw an
88.284 + // exception in pre register, because postRegister will not have
88.285 + // been called.
88.286 + WHERE.POSTDEREGISTER.check(unreg, t);
88.287 + }
88.288 + return failures;
88.289 + }
88.290 +
88.291 + /**
88.292 + * This enum lists the 4 methods in MBeanRegistration.
88.293 + */
88.294 + public static enum WHERE {
88.295 +
88.296 + PREREGISTER, POSTREGISTER, PREDEREGISTER, POSTDEREGISTER;
88.297 +
88.298 + // Checks that an exception thrown by the MBeanServer correspond to
88.299 + // what is expected when an MBean throws an exception in this
88.300 + // MBeanRegistration method ("this" is one of the 4 enum values above)
88.301 + //
88.302 + public void check(Exception thrown, Throwable t)
88.303 + throws Exception {
88.304 + if (t instanceof RuntimeException) {
88.305 + if (!(thrown instanceof RuntimeMBeanException)) {
88.306 + System.out.println("Expected RuntimeMBeanException, got "+
88.307 + thrown);
88.308 + throw new Exception("Expected RuntimeMBeanException, got "+
88.309 + thrown);
88.310 + }
88.311 + } else if (t instanceof Error) {
88.312 + if (!(thrown instanceof RuntimeErrorException)) {
88.313 + System.out.println("Expected RuntimeErrorException, got "+
88.314 + thrown);
88.315 + throw new Exception("Expected RuntimeErrorException, got "+
88.316 + thrown);
88.317 + }
88.318 + } else if (t instanceof Exception) {
88.319 + if (EnumSet.of(POSTDEREGISTER,POSTREGISTER).contains(this)) {
88.320 + if (!(thrown instanceof RuntimeMBeanException)) {
88.321 + System.out.println("Expected RuntimeMBeanException, got "+
88.322 + thrown);
88.323 + throw new Exception("Expected RuntimeMBeanException, got "+
88.324 + thrown);
88.325 + }
88.326 + if (! (thrown.getCause() instanceof RuntimeException)) {
88.327 + System.out.println("Bad cause: " +
88.328 + "expected RuntimeException, " +
88.329 + "got <"+thrown.getCause()+">");
88.330 + throw new Exception("Bad cause: " +
88.331 + "expected RuntimeException, " +
88.332 + "got <"+thrown.getCause()+">");
88.333 + }
88.334 + }
88.335 + if (EnumSet.of(PREDEREGISTER,PREREGISTER).contains(this)) {
88.336 + if (!(thrown instanceof MBeanRegistrationException)) {
88.337 + System.out.println("Expected " +
88.338 + "MBeanRegistrationException, got "+
88.339 + thrown);
88.340 + throw new Exception("Expected " +
88.341 + "MBeanRegistrationException, got "+
88.342 + thrown);
88.343 + }
88.344 + if (! (thrown.getCause() instanceof Exception)) {
88.345 + System.out.println("Bad cause: " +
88.346 + "expected Exception, " +
88.347 + "got <"+thrown.getCause()+">");
88.348 + throw new Exception("Bad cause: " +
88.349 + "expected Exception, " +
88.350 + "got <"+thrown.getCause()+">");
88.351 + }
88.352 + }
88.353 + }
88.354 +
88.355 + }
88.356 + }
88.357 +
88.358 + /**
88.359 + * This enum lists the 5 methods to create and register an
88.360 + * ExceptionalWombat MBean
88.361 + */
88.362 + public static enum CREATE {
88.363 +
88.364 + CREATE1() {
88.365 + // Creates an ExceptionalWombat MBean using createMBean form #1
88.366 + public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
88.367 + MBeanServer server, ObjectName name) throws Exception {
88.368 + ExceptionallyHackyWombat.t = t;
88.369 + ExceptionallyHackyWombat.w = where;
88.370 + return server.createMBean(
88.371 + ExceptionallyHackyWombat.class.getName(),
88.372 + name);
88.373 + }
88.374 + },
88.375 + CREATE2() {
88.376 + // Creates an ExceptionalWombat MBean using createMBean form #2
88.377 + public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
88.378 + MBeanServer server, ObjectName name) throws Exception {
88.379 + ExceptionallyHackyWombat.t = t;
88.380 + ExceptionallyHackyWombat.w = where;
88.381 + final ObjectName loaderName = registerMLet(server);
88.382 + return server.createMBean(
88.383 + ExceptionallyHackyWombat.class.getName(),
88.384 + name, loaderName);
88.385 + }
88.386 + },
88.387 + CREATE3() {
88.388 + // Creates an ExceptionalWombat MBean using createMBean form #3
88.389 + public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
88.390 + MBeanServer server, ObjectName name) throws Exception {
88.391 + final Object[] params = {t, where};
88.392 + final String[] signature = {Throwable.class.getName(),
88.393 + EnumSet.class.getName()
88.394 + };
88.395 + return server.createMBean(
88.396 + ExceptionalWombat.class.getName(), name,
88.397 + params, signature);
88.398 + }
88.399 + },
88.400 + CREATE4() {
88.401 + // Creates an ExceptionalWombat MBean using createMBean form #4
88.402 + public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
88.403 + MBeanServer server, ObjectName name) throws Exception {
88.404 + final Object[] params = {t, where};
88.405 + final String[] signature = {Throwable.class.getName(),
88.406 + EnumSet.class.getName()
88.407 + };
88.408 + return server.createMBean(
88.409 + ExceptionalWombat.class.getName(), name,
88.410 + registerMLet(server), params, signature);
88.411 + }
88.412 + },
88.413 + REGISTER() {
88.414 + // Creates an ExceptionalWombat MBean using registerMBean
88.415 + public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
88.416 + MBeanServer server, ObjectName name) throws Exception {
88.417 + final ExceptionalWombat wombat =
88.418 + new ExceptionalWombat(t, where);
88.419 + return server.registerMBean(wombat, name);
88.420 + }
88.421 + };
88.422 +
88.423 + // Creates an ExceptionalWombat MBean using the method denoted by this
88.424 + // Enum value - one of CREATE1, CREATE2, CREATE3, CREATE4, or REGISTER.
88.425 + public abstract ObjectInstance create(Throwable t, EnumSet<WHERE> where,
88.426 + MBeanServer server, ObjectName name) throws Exception;
88.427 +
88.428 + // This is a bit of a hack - we use an MLet that delegates to the
88.429 + // System ClassLoader so that we can use createMBean form #2 and #3
88.430 + // while still using the same class loader (system).
88.431 + // This is necessary to make the ExceptionallyHackyWombatMBean work ;-)
88.432 + //
88.433 + public ObjectName registerMLet(MBeanServer server) throws Exception {
88.434 + final ObjectName name = new ObjectName("test:type=MLet");
88.435 + if (server.isRegistered(name)) {
88.436 + return name;
88.437 + }
88.438 + final MLet mlet = new MLet(new URL[0],
88.439 + ClassLoader.getSystemClassLoader());
88.440 + return server.registerMBean(mlet, name).getObjectName();
88.441 + }
88.442 + }
88.443 +
88.444 + /**
88.445 + *A Wombat MBean that can throw exceptions or errors in any of the
88.446 + * MBeanRegistration methods.
88.447 + */
88.448 + public static interface ExceptionalWombatMBean {
88.449 + // Tells the MBean to stop throwing exceptions - we sometime
88.450 + // need to call this at the end of the test so that we can
88.451 + // actually unregister the MBean.
88.452 + public void end();
88.453 + }
88.454 +
88.455 + /**
88.456 + *A Wombat MBean that can throw exceptions or errors in any of the
88.457 + * MBeanRegistration methods.
88.458 + */
88.459 + public static class ExceptionalWombat
88.460 + implements ExceptionalWombatMBean, MBeanRegistration {
88.461 +
88.462 + private final Throwable throwable;
88.463 + private final EnumSet<WHERE> where;
88.464 + private volatile boolean end=false;
88.465 +
88.466 + public ExceptionalWombat(Throwable t, EnumSet<WHERE> where) {
88.467 + this.throwable=t; this.where=where;
88.468 + }
88.469 + private Exception doThrow() {
88.470 + if (throwable instanceof Error)
88.471 + throw (Error)throwable;
88.472 + if (throwable instanceof RuntimeException)
88.473 + throw (RuntimeException)throwable;
88.474 + return (Exception)throwable;
88.475 + }
88.476 + public ObjectName preRegister(MBeanServer server, ObjectName name)
88.477 + throws Exception {
88.478 + if (!end && where.contains(WHERE.PREREGISTER))
88.479 + throw doThrow();
88.480 + return name;
88.481 + }
88.482 +
88.483 + public void postRegister(Boolean registrationDone) {
88.484 + if (!end && where.contains(WHERE.POSTREGISTER))
88.485 + throw new RuntimeException(doThrow());
88.486 + }
88.487 +
88.488 + public void preDeregister() throws Exception {
88.489 + if (!end && where.contains(WHERE.PREDEREGISTER))
88.490 + throw doThrow();
88.491 + }
88.492 +
88.493 + public void postDeregister() {
88.494 + if (!end && where.contains(WHERE.POSTREGISTER))
88.495 + throw new RuntimeException(doThrow());
88.496 + }
88.497 +
88.498 + public void end() {
88.499 + this.end=true;
88.500 + }
88.501 + }
88.502 +
88.503 + /**
88.504 + * This is a big ugly hack to call createMBean form #1 and #2 - where
88.505 + * the empty constructor is used. Since we still want to supply parameters
88.506 + * to the ExceptionalWombat super class, we temporarily store these
88.507 + * parameter value in a static volatile before calling create MBean.
88.508 + * Of course this only works because our test is sequential and single
88.509 + * threaded, and nobody but our test uses this ExceptionallyHackyWombat.
88.510 + */
88.511 + public static class ExceptionallyHackyWombat extends ExceptionalWombat {
88.512 + public static volatile Throwable t;
88.513 + public static volatile EnumSet<WHERE> w;
88.514 + public ExceptionallyHackyWombat() {
88.515 + super(t,w);
88.516 + }
88.517 + }
88.518 +
88.519 +}
89.1 --- a/test/javax/management/ObjectName/SerialCompatTest.java Tue Aug 19 07:50:03 2008 -0700
89.2 +++ b/test/javax/management/ObjectName/SerialCompatTest.java Thu Aug 21 09:55:18 2008 -0700
89.3 @@ -23,9 +23,9 @@
89.4
89.5 /*
89.6 * @test
89.7 - * @bug 6211220
89.8 + * @bug 6211220 6616825
89.9 * @summary Test that jmx.serial.form=1.0 works for ObjectName
89.10 - * @author Eamonn McManus
89.11 + * @author Eamonn McManus, Daniel Fuchs
89.12 * @run clean SerialCompatTest
89.13 * @run build SerialCompatTest
89.14 * @run main/othervm SerialCompatTest
89.15 @@ -36,19 +36,8 @@
89.16 import javax.management.ObjectName;
89.17
89.18 public class SerialCompatTest {
89.19 - public static void main(String[] args) throws Exception {
89.20 - System.setProperty("jmx.serial.form", "1.0");
89.21
89.22 - /* Check that we really are in jmx.serial.form=1.0 mode.
89.23 - The property is frozen the first time the ObjectName class
89.24 - is referenced so checking that it is set to the correct
89.25 - value now is not enough. */
89.26 - ObjectStreamClass osc = ObjectStreamClass.lookup(ObjectName.class);
89.27 - if (osc.getFields().length != 6) {
89.28 - throw new Exception("Not using old serial form: fields: " +
89.29 - Arrays.asList(osc.getFields()));
89.30 - // new serial form has no fields, uses writeObject
89.31 - }
89.32 + public static void check6211220() throws Exception {
89.33
89.34 ObjectName on = new ObjectName("a:b=c");
89.35 ByteArrayOutputStream bos = new ByteArrayOutputStream();
89.36 @@ -62,56 +51,214 @@
89.37
89.38 // if the bug is present, these will get NullPointerException
89.39 for (int i = 0; i <= 11; i++) {
89.40 + String msg = "6211220 case(" + i + ")";
89.41 try {
89.42 switch (i) {
89.43 - case 0:
89.44 - check(on1.getDomain().equals("a")); break;
89.45 - case 1:
89.46 - check(on1.getCanonicalName().equals("a:b=c")); break;
89.47 - case 2:
89.48 - check(on1.getKeyPropertyListString().equals("b=c")); break;
89.49 - case 3:
89.50 - check(on1.getCanonicalKeyPropertyListString().equals("b=c"));
89.51 - break;
89.52 - case 4:
89.53 - check(on1.getKeyProperty("b").equals("c")); break;
89.54 - case 5:
89.55 - check(on1.getKeyPropertyList()
89.56 - .equals(Collections.singletonMap("b", "c"))); break;
89.57 - case 6:
89.58 - check(!on1.isDomainPattern()); break;
89.59 - case 7:
89.60 - check(!on1.isPattern()); break;
89.61 - case 8:
89.62 - check(!on1.isPropertyPattern()); break;
89.63 - case 9:
89.64 - check(on1.equals(on)); break;
89.65 - case 10:
89.66 - check(on.equals(on1)); break;
89.67 - case 11:
89.68 - check(on1.apply(on)); break;
89.69 - default:
89.70 - throw new Exception("Test incorrect: case: " + i);
89.71 + case 0:
89.72 + check(msg, on1.getDomain().equals("a"));
89.73 + break;
89.74 + case 1:
89.75 + check(msg, on1.getCanonicalName().equals("a:b=c"));
89.76 + break;
89.77 + case 2:
89.78 + check(msg, on1.getKeyPropertyListString()
89.79 + .equals("b=c"));
89.80 + break;
89.81 + case 3:
89.82 + check(msg, on1.getCanonicalKeyPropertyListString()
89.83 + .equals("b=c"));
89.84 + break;
89.85 + case 4:
89.86 + check(msg, on1.getKeyProperty("b").equals("c"));
89.87 + break;
89.88 + case 5:
89.89 + check(msg, on1.getKeyPropertyList()
89.90 + .equals(Collections.singletonMap("b", "c")));
89.91 + break;
89.92 + case 6:
89.93 + check(msg, !on1.isDomainPattern());
89.94 + break;
89.95 + case 7:
89.96 + check(msg, !on1.isPattern());
89.97 + break;
89.98 + case 8:
89.99 + check(msg, !on1.isPropertyPattern());
89.100 + break;
89.101 + case 9:
89.102 + check(msg, on1.equals(on));
89.103 + break;
89.104 + case 10:
89.105 + check(msg, on.equals(on1));
89.106 + break;
89.107 + case 11:
89.108 + check(msg, on1.apply(on));
89.109 + break;
89.110 + default:
89.111 + throw new Exception(msg + ": Test incorrect");
89.112 }
89.113 } catch (Exception e) {
89.114 - System.out.println("Test failed with exception:");
89.115 + System.out.println(msg + ": Test failed with exception:");
89.116 e.printStackTrace(System.out);
89.117 failed = true;
89.118 }
89.119 }
89.120
89.121 - if (failed)
89.122 - throw new Exception("Some tests failed");
89.123 - else
89.124 - System.out.println("All tests passed");
89.125 + if (failed) {
89.126 + throw new Exception("Some tests for 6211220 failed");
89.127 + } else {
89.128 + System.out.println("All tests for 6211220 passed");
89.129 + }
89.130 }
89.131
89.132 - private static void check(boolean condition) {
89.133 + static void checkName(String testname, ObjectName on)
89.134 + throws Exception {
89.135 + ByteArrayOutputStream bos = new ByteArrayOutputStream();
89.136 + ObjectOutputStream oos = new ObjectOutputStream(bos);
89.137 + oos.writeObject(on);
89.138 + oos.close();
89.139 + byte[] bytes = bos.toByteArray();
89.140 + ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
89.141 + ObjectInputStream ois = new ObjectInputStream(bis);
89.142 + ObjectName on1 = (ObjectName) ois.readObject();
89.143 + // if the bug is present, these will get NullPointerException
89.144 + for (int i = 0; i <= 11; i++) {
89.145 + String msg = testname + " case(" + i + ")";
89.146 + try {
89.147 + switch (i) {
89.148 + case 0:
89.149 + check(msg, on1.getDomain().equals(on.getDomain()));
89.150 + break;
89.151 + case 1:
89.152 + check(msg, on1.getCanonicalName().
89.153 + equals(on.getCanonicalName()));
89.154 + break;
89.155 + case 2:
89.156 + check(msg, on1.getKeyPropertyListString().
89.157 + equals(on.getKeyPropertyListString()));
89.158 + break;
89.159 + case 3:
89.160 + check(msg, on1.getCanonicalKeyPropertyListString().
89.161 + equals(on.getCanonicalKeyPropertyListString()));
89.162 + break;
89.163 + case 4:
89.164 + for (Object ko : on1.getKeyPropertyList().keySet()) {
89.165 + final String key = (String) ko;
89.166 + check(msg, on1.getKeyProperty(key).
89.167 + equals(on.getKeyProperty(key)));
89.168 + }
89.169 + for (Object ko : on.getKeyPropertyList().keySet()) {
89.170 + final String key = (String) ko;
89.171 + check(msg, on1.getKeyProperty(key).
89.172 + equals(on.getKeyProperty(key)));
89.173 + }
89.174 + case 5:
89.175 + check(msg, on1.getKeyPropertyList()
89.176 + .equals(on.getKeyPropertyList()));
89.177 + break;
89.178 + case 6:
89.179 + check(msg, on1.isDomainPattern()==on.isDomainPattern());
89.180 + break;
89.181 + case 7:
89.182 + check(msg, on1.isPattern() == on.isPattern());
89.183 + break;
89.184 + case 8:
89.185 + check(msg,
89.186 + on1.isPropertyPattern()==on.isPropertyPattern());
89.187 + break;
89.188 + case 9:
89.189 + check(msg, on1.equals(on));
89.190 + break;
89.191 + case 10:
89.192 + check(msg, on.equals(on1));
89.193 + break;
89.194 + case 11:
89.195 + if (!on.isPattern()) {
89.196 + check(msg, on1.apply(on));
89.197 + }
89.198 + break;
89.199 + default:
89.200 + throw new Exception("Test incorrect: case: " + i);
89.201 + }
89.202 + } catch (Exception e) {
89.203 + System.out.println("Test (" + i + ") failed with exception:");
89.204 + e.printStackTrace(System.out);
89.205 + failed = true;
89.206 + }
89.207 + }
89.208 +
89.209 + }
89.210 + private static String[] names6616825 = {
89.211 + "a:b=c", "a:b=c,*", "*:*", ":*", ":b=c", ":b=c,*",
89.212 + "a:*,b=c", ":*", ":*,b=c", "*x?:k=\"x\\*z\"", "*x?:k=\"x\\*z\",*",
89.213 + "*x?:*,k=\"x\\*z\"", "*x?:k=\"x\\*z\",*,b=c"
89.214 + };
89.215 +
89.216 + static void check6616825() throws Exception {
89.217 + System.out.println("Testing 616825");
89.218 + for (String n : names6616825) {
89.219 + final ObjectName on;
89.220 + try {
89.221 + on = new ObjectName(n);
89.222 + } catch (Exception x) {
89.223 + failed = true;
89.224 + System.out.println("Unexpected failure for 6616825 [" + n +
89.225 + "]: " + x);
89.226 + x.printStackTrace(System.out);
89.227 + continue;
89.228 + }
89.229 + try {
89.230 + checkName("616825 " + n, on);
89.231 + } catch (Exception x) {
89.232 + failed = true;
89.233 + System.out.println("6616825 failed for [" + n + "]: " + x);
89.234 + x.printStackTrace(System.out);
89.235 + }
89.236 + }
89.237 +
89.238 + if (failed) {
89.239 + throw new Exception("Some tests for 6616825 failed");
89.240 + } else {
89.241 + System.out.println("All tests for 6616825 passed");
89.242 + }
89.243 + }
89.244 +
89.245 + public static void main(String[] args) throws Exception {
89.246 + System.setProperty("jmx.serial.form", "1.0");
89.247 +
89.248 + /* Check that we really are in jmx.serial.form=1.0 mode.
89.249 + The property is frozen the first time the ObjectName class
89.250 + is referenced so checking that it is set to the correct
89.251 + value now is not enough. */
89.252 + ObjectStreamClass osc = ObjectStreamClass.lookup(ObjectName.class);
89.253 + if (osc.getFields().length != 6) {
89.254 + throw new Exception("Not using old serial form: fields: " +
89.255 + Arrays.asList(osc.getFields()));
89.256 + // new serial form has no fields, uses writeObject
89.257 + }
89.258 +
89.259 + try {
89.260 + check6211220();
89.261 + } catch (Exception x) {
89.262 + System.err.println(x.getMessage());
89.263 + }
89.264 + try {
89.265 + check6616825();
89.266 + } catch (Exception x) {
89.267 + System.err.println(x.getMessage());
89.268 + }
89.269 +
89.270 + if (failed) {
89.271 + throw new Exception("Some tests failed");
89.272 + } else {
89.273 + System.out.println("All tests passed");
89.274 + }
89.275 + }
89.276 +
89.277 + private static void check(String msg, boolean condition) {
89.278 if (!condition) {
89.279 - new Throwable("Test failed").printStackTrace(System.out);
89.280 + new Throwable("Test failed " + msg).printStackTrace(System.out);
89.281 failed = true;
89.282 }
89.283 }
89.284 -
89.285 private static boolean failed;
89.286 }
90.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
90.2 +++ b/test/javax/management/eventService/AddRemoveListenerTest.java Thu Aug 21 09:55:18 2008 -0700
90.3 @@ -0,0 +1,371 @@
90.4 +/*
90.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
90.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
90.7 + *
90.8 + * This code is free software; you can redistribute it and/or modify it
90.9 + * under the terms of the GNU General Public License version 2 only, as
90.10 + * published by the Free Software Foundation.
90.11 + *
90.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
90.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
90.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
90.15 + * version 2 for more details (a copy is included in the LICENSE file that
90.16 + * accompanied this code).
90.17 + *
90.18 + * You should have received a copy of the GNU General Public License version
90.19 + * 2 along with this work; if not, write to the Free Software Foundation,
90.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
90.21 + *
90.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
90.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
90.24 + * have any questions.
90.25 + */
90.26 +
90.27 +/*
90.28 + * @test AddRemoveListenerTest.java
90.29 + * @bug 5108776
90.30 + * @summary Basic test for EventClient to see internal thread management.
90.31 + * @author Shanliang JIANG
90.32 + * @run clean AddRemoveListenerTest
90.33 + * @run build AddRemoveListenerTest
90.34 + * @run main AddRemoveListenerTest
90.35 + */
90.36 +
90.37 +import java.io.IOException;
90.38 +import javax.management.MBeanServer;
90.39 +import javax.management.MBeanServerFactory;
90.40 +import javax.management.Notification;
90.41 +import javax.management.NotificationBroadcasterSupport;
90.42 +import javax.management.NotificationFilter;
90.43 +import javax.management.NotificationListener;
90.44 +import javax.management.ObjectName;
90.45 +import javax.management.event.EventClient;
90.46 +import javax.management.event.EventClientDelegate;
90.47 +import javax.management.event.EventClientDelegateMBean;
90.48 +import javax.management.event.FetchingEventRelay;
90.49 +import javax.management.event.RMIPushEventRelay;
90.50 +import javax.management.remote.JMXConnector;
90.51 +import javax.management.remote.JMXConnectorFactory;
90.52 +import javax.management.remote.JMXConnectorServer;
90.53 +import javax.management.remote.JMXConnectorServerFactory;
90.54 +import javax.management.remote.JMXServiceURL;
90.55 +
90.56 +
90.57 +// This thread creates a single MBean that emits a number of parallel
90.58 +// sequences of notifications. Each sequence is distinguished by an id
90.59 +// and each id corresponds to a thread that is filtering the notifications
90.60 +// so it only sees its own ones. The notifications for a given id have
90.61 +// contiguous sequence numbers and each thread checks that the notifications
90.62 +// it receives do indeed have these numbers. If notifications are lost or
90.63 +// if the different sequences interfere with each other then the test will
90.64 +// fail. As an added tweak, a "noise" thread periodically causes notifications
90.65 +// to be emitted that do not correspond to any sequence and do not have any id.
90.66 +public class AddRemoveListenerTest {
90.67 +
90.68 + private static MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer();
90.69 + private static ObjectName emitter;
90.70 + private static NotificationSender emitterImpl;
90.71 + private static JMXServiceURL url;
90.72 + private static JMXConnectorServer server;
90.73 +
90.74 + private static int toSend = 100;
90.75 + private static final long bigWaiting = 10000;
90.76 + private static int counter = 0;
90.77 + private static int jobs = 10;
90.78 + private static int endedJobs = 0;
90.79 +
90.80 + private static volatile String failure;
90.81 +
90.82 + public static void main(String[] args) throws Exception {
90.83 + System.out.println(">>> Test on multiple adding/removing listeners.");
90.84 +
90.85 + // for 1.5
90.86 + if (System.getProperty("java.version").startsWith("1.5") &&
90.87 + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
90.88 + System.out.print("Working on "+System.getProperty("java.version")+
90.89 + " register "+EventClientDelegateMBean.OBJECT_NAME);
90.90 +
90.91 + mbeanServer.registerMBean(EventClientDelegate.
90.92 + getEventClientDelegate(mbeanServer),
90.93 + EventClientDelegateMBean.OBJECT_NAME);
90.94 + }
90.95 +
90.96 + emitter = new ObjectName("Default:name=NotificationSender");
90.97 + emitterImpl = new NotificationSender();
90.98 + mbeanServer.registerMBean(emitterImpl, emitter);
90.99 +
90.100 + String[] types = new String[]{"PushEventRelay", "FetchingEventRelay"};
90.101 + String[] protos = new String[]{"rmi", "iiop", "jmxmp"};
90.102 + for (String prot : protos) {
90.103 + url = new JMXServiceURL(prot, null, 0);
90.104 +
90.105 + try {
90.106 + server =
90.107 + JMXConnectorServerFactory.newJMXConnectorServer(url,
90.108 + null, mbeanServer);
90.109 + server.start();
90.110 + } catch (Exception e) {
90.111 + System.out.println(">>> Skip "+prot+", not supported.");
90.112 + continue;
90.113 + }
90.114 +
90.115 + url = server.getAddress();
90.116 +
90.117 + // noise
90.118 + Thread noise = new Thread(new Runnable() {
90.119 + public void run() {
90.120 + while (true) {
90.121 + emitterImpl.sendNotif(1, null);
90.122 + try {
90.123 + Thread.sleep(10);
90.124 + } catch (Exception e) {
90.125 + // OK
90.126 + }
90.127 + }
90.128 + }
90.129 + });
90.130 + noise.setDaemon(true);
90.131 + noise.start();
90.132 +
90.133 + try {
90.134 + for (String type: types) {
90.135 + System.out.println("\n\n>>> Testing "+type+" on "+url+" ...");
90.136 + JMXConnector conn = newConn();
90.137 + try {
90.138 + testType(type, conn);
90.139 + } finally {
90.140 + conn.close();
90.141 + System.out.println(">>> Testing "+type+" on "+url+" ... done");
90.142 + }
90.143 + }
90.144 + } finally {
90.145 + server.stop();
90.146 + }
90.147 + }
90.148 + }
90.149 +
90.150 + private static void testType(String type, JMXConnector conn) throws Exception {
90.151 + Thread[] threads = new Thread[jobs];
90.152 + for (int i=0; i<jobs; i++) {
90.153 + threads[i] = new Thread(new Job(type, conn));
90.154 + threads[i].setDaemon(true);
90.155 + threads[i].start();
90.156 + }
90.157 +
90.158 + // to wait
90.159 + long toWait = bigWaiting*jobs;
90.160 + long stopTime = System.currentTimeMillis() + toWait;
90.161 +
90.162 + synchronized(AddRemoveListenerTest.class) {
90.163 + while (endedJobs < jobs && toWait > 0 && failure == null) {
90.164 + AddRemoveListenerTest.class.wait(toWait);
90.165 + toWait = stopTime - System.currentTimeMillis();
90.166 + }
90.167 + }
90.168 +
90.169 + if (endedJobs != jobs && failure == null) {
90.170 + throw new RuntimeException("Need to set bigger waiting timeout?");
90.171 + }
90.172 +
90.173 + endedJobs = 0;
90.174 + }
90.175 +
90.176 + public static class Job implements Runnable {
90.177 + public Job(String type, JMXConnector conn) {
90.178 + this.type = type;
90.179 + this.conn = conn;
90.180 + }
90.181 + public void run() {
90.182 + try {
90.183 + test(type, conn);
90.184 +
90.185 + synchronized(AddRemoveListenerTest.class) {
90.186 + endedJobs++;
90.187 + if (endedJobs>=jobs) {
90.188 + AddRemoveListenerTest.class.notify();
90.189 + }
90.190 + }
90.191 + } catch (RuntimeException re) {
90.192 + throw re;
90.193 + } catch (Exception e) {
90.194 + throw new RuntimeException(e);
90.195 + }
90.196 + }
90.197 +
90.198 + private final String type;
90.199 + private final JMXConnector conn;
90.200 + }
90.201 +
90.202 + private static void test(String type, JMXConnector conn) throws Exception {
90.203 + EventClient ec = newEventClient(type, conn);
90.204 + try {
90.205 + test(type, conn, ec);
90.206 + } finally {
90.207 + ec.close();
90.208 + }
90.209 + }
90.210 +
90.211 + private static void test(String type, JMXConnector conn, EventClient ec)
90.212 + throws Exception {
90.213 + String id = getId();
90.214 +
90.215 + Listener listener = new Listener(id);
90.216 + Filter filter = new Filter(id);
90.217 +
90.218 + System.out.println(">>> ("+id+") To receive notifications "+toSend);
90.219 + ec.addNotificationListener(emitter,
90.220 + listener, filter, null);
90.221 +
90.222 + emitterImpl.sendNotif(toSend, id);
90.223 + listener.waitNotifs(bigWaiting, toSend);
90.224 + if (listener.received != toSend) {
90.225 + throw new RuntimeException(">>> ("+id+") Expected to receive: "
90.226 + +toSend+", but got: "+listener.received);
90.227 + }
90.228 +
90.229 + listener.clear();
90.230 + ec.removeNotificationListener(emitter, listener, filter, null);
90.231 +
90.232 + System.out.println(">>> ("+id+") Repeat adding and removing ...");
90.233 + for (int j=0; j<10; j++) {
90.234 + ec.addNotificationListener(emitter, dummyListener, null, id);
90.235 + Thread.yield(); // allow to start listening
90.236 + ec.removeNotificationListener(emitter, dummyListener, null, id);
90.237 + }
90.238 +
90.239 + System.out.println(">>> ("+id+") To receive again notifications "+toSend);
90.240 + ec.addNotificationListener(emitter,
90.241 + listener, filter, null);
90.242 +
90.243 + emitterImpl.sendNotif(toSend, id);
90.244 + listener.waitNotifs(bigWaiting, toSend);
90.245 + Thread.yield(); //any duplicated?
90.246 + if (listener.received != toSend) {
90.247 + throw new RuntimeException("("+id+") Expected to receive: "
90.248 + +toSend+", but got: "+listener.received);
90.249 + }
90.250 + }
90.251 +
90.252 +//--------------------------
90.253 +// private classes
90.254 +//--------------------------
90.255 +
90.256 + private static class Listener implements NotificationListener {
90.257 + public Listener(String id) {
90.258 + this.id = id;
90.259 + }
90.260 + public void handleNotification(Notification notif, Object handback) {
90.261 + if (!id.equals(notif.getUserData())) {
90.262 + System.out.println("("+id+") Filter error, my id is: "+id+
90.263 + ", but got "+notif.getUserData());
90.264 + System.exit(1);
90.265 + }
90.266 +
90.267 + synchronized (this) {
90.268 + received++;
90.269 +
90.270 + if(++sequenceNB != notif.getSequenceNumber()) {
90.271 + fail("(" + id + ") Wrong sequence number, expected: "
90.272 + +sequenceNB+", but got: "+notif.getSequenceNumber());
90.273 + }
90.274 + if (received >= toSend || failure != null) {
90.275 + this.notify();
90.276 + }
90.277 + }
90.278 + }
90.279 +
90.280 + public void waitNotifs(long timeout, int nb) throws Exception {
90.281 + long toWait = timeout;
90.282 + long stopTime = System.currentTimeMillis() + timeout;
90.283 + synchronized(this) {
90.284 + while (received < nb && toWait > 0 && failure == null) {
90.285 + this.wait(toWait);
90.286 + toWait = stopTime - System.currentTimeMillis();
90.287 + }
90.288 + }
90.289 + }
90.290 +
90.291 + public void clear() {
90.292 + synchronized(this) {
90.293 + received = 0;
90.294 + sequenceNB = -1;
90.295 + }
90.296 + }
90.297 +
90.298 + private String id;
90.299 + private int received = 0;
90.300 +
90.301 + private long sequenceNB = -1;
90.302 + }
90.303 +
90.304 + private static class Filter implements NotificationFilter {
90.305 + public Filter(String id) {
90.306 + this.id = id;
90.307 + }
90.308 +
90.309 + public boolean isNotificationEnabled(Notification n) {
90.310 + return id.equals(n.getUserData());
90.311 + }
90.312 + private String id;
90.313 + }
90.314 +
90.315 + private static NotificationListener dummyListener = new NotificationListener() {
90.316 + public void handleNotification(Notification notif, Object handback) {
90.317 + }
90.318 + };
90.319 +
90.320 + public static class NotificationSender extends NotificationBroadcasterSupport
90.321 + implements NotificationSenderMBean {
90.322 +
90.323 + /**
90.324 + * Send Notification objects.
90.325 + *
90.326 + * @param nb The number of notifications to send
90.327 + */
90.328 + public void sendNotif(int nb, String userData) {
90.329 + long sequenceNumber = 0;
90.330 + for (int i = 0; i<nb; i++) {
90.331 + Notification notif = new Notification(myType, this, sequenceNumber++);
90.332 + notif.setUserData(userData);
90.333 + sendNotification(notif);
90.334 + }
90.335 + }
90.336 +
90.337 +
90.338 + private final String myType = "notification.my_notification";
90.339 + }
90.340 +
90.341 + public interface NotificationSenderMBean {
90.342 + public void sendNotif(int nb, String userData);
90.343 + }
90.344 +
90.345 + private static JMXConnector newConn() throws IOException {
90.346 + return JMXConnectorFactory.connect(url);
90.347 + }
90.348 +
90.349 + private static EventClient newEventClient(String type, JMXConnector conn)
90.350 + throws Exception {
90.351 + EventClientDelegateMBean proxy =
90.352 + EventClientDelegate.getProxy(conn.getMBeanServerConnection());
90.353 + if (type.equals("PushEventRelay")) {
90.354 + return new EventClient(proxy,
90.355 + new RMIPushEventRelay(proxy), null, null, 60000);
90.356 + } else if (type.equals("FetchingEventRelay")) {
90.357 + return new EventClient(proxy,
90.358 + new FetchingEventRelay(proxy), null, null, 60000);
90.359 + } else {
90.360 + throw new RuntimeException("Wrong event client type: "+type);
90.361 + }
90.362 + }
90.363 +
90.364 + private static String getId() {
90.365 + synchronized(AddRemoveListenerTest.class) {
90.366 + return String.valueOf(counter++);
90.367 + }
90.368 + }
90.369 +
90.370 + private static void fail(String msg) {
90.371 + System.out.println("FAIL: " + msg);
90.372 + failure = msg;
90.373 + }
90.374 +}
91.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
91.2 +++ b/test/javax/management/eventService/CustomForwarderTest.java Thu Aug 21 09:55:18 2008 -0700
91.3 @@ -0,0 +1,348 @@
91.4 +/*
91.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
91.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
91.7 + *
91.8 + * This code is free software; you can redistribute it and/or modify it
91.9 + * under the terms of the GNU General Public License version 2 only, as
91.10 + * published by the Free Software Foundation.
91.11 + *
91.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
91.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
91.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
91.15 + * version 2 for more details (a copy is included in the LICENSE file that
91.16 + * accompanied this code).
91.17 + *
91.18 + * You should have received a copy of the GNU General Public License version
91.19 + * 2 along with this work; if not, write to the Free Software Foundation,
91.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
91.21 + *
91.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
91.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
91.24 + * have any questions.
91.25 + */
91.26 +
91.27 +/*
91.28 + * @test CustomForwarderTest
91.29 + * @bug 5108776
91.30 + * @summary Test that a custom EventForwarder can be added
91.31 + * @author Eamonn McManus
91.32 + */
91.33 +
91.34 +import java.io.ByteArrayInputStream;
91.35 +import java.io.ByteArrayOutputStream;
91.36 +import java.io.IOException;
91.37 +import java.io.ObjectInputStream;
91.38 +import java.io.ObjectOutputStream;
91.39 +import java.lang.management.ManagementFactory;
91.40 +import java.net.DatagramPacket;
91.41 +import java.net.DatagramSocket;
91.42 +import java.net.SocketAddress;
91.43 +import java.util.Map;
91.44 +import java.util.concurrent.ArrayBlockingQueue;
91.45 +import java.util.concurrent.BlockingQueue;
91.46 +import java.util.concurrent.Semaphore;
91.47 +import java.util.concurrent.TimeUnit;
91.48 +import java.util.concurrent.atomic.AtomicBoolean;
91.49 +import java.util.concurrent.atomic.AtomicInteger;
91.50 +import java.util.concurrent.atomic.AtomicLong;
91.51 +import javax.management.MBeanServer;
91.52 +import javax.management.MBeanServerInvocationHandler;
91.53 +import javax.management.Notification;
91.54 +import javax.management.NotificationBroadcasterSupport;
91.55 +import javax.management.NotificationFilter;
91.56 +import javax.management.NotificationListener;
91.57 +import javax.management.ObjectName;
91.58 +import javax.management.event.EventClient;
91.59 +import javax.management.event.EventClientDelegate;
91.60 +import javax.management.event.EventClientDelegateMBean;
91.61 +import javax.management.event.EventForwarder;
91.62 +import javax.management.event.EventReceiver;
91.63 +import javax.management.event.EventRelay;
91.64 +import javax.management.remote.MBeanServerForwarder;
91.65 +import javax.management.remote.NotificationResult;
91.66 +import javax.management.remote.TargetedNotification;
91.67 +
91.68 +public class CustomForwarderTest {
91.69 + public static class UdpEventRelay implements EventRelay {
91.70 + private final EventClientDelegateMBean delegate;
91.71 + private final DatagramSocket socket;
91.72 + private final AtomicBoolean closed = new AtomicBoolean();
91.73 + private final String clientId;
91.74 + private EventReceiver receiver;
91.75 +
91.76 + public UdpEventRelay(EventClientDelegateMBean delegate)
91.77 + throws IOException {
91.78 + this.delegate = delegate;
91.79 + this.socket = new DatagramSocket();
91.80 + try {
91.81 + clientId = delegate.addClient(
91.82 + UdpEventForwarder.class.getName(),
91.83 + new Object[] {socket.getLocalSocketAddress()},
91.84 + new String[] {SocketAddress.class.getName()});
91.85 + } catch (IOException e) {
91.86 + throw e;
91.87 + } catch (RuntimeException e) {
91.88 + throw e;
91.89 + } catch (Exception e) {
91.90 + final IOException ioe =
91.91 + new IOException("Exception creating EventForwarder");
91.92 + ioe.initCause(e);
91.93 + throw ioe;
91.94 + }
91.95 + Thread t = new Thread(new Receiver());
91.96 + t.setDaemon(true);
91.97 + t.start();
91.98 + }
91.99 +
91.100 + public String getClientId() throws IOException {
91.101 + return clientId;
91.102 + }
91.103 +
91.104 + public void setEventReceiver(EventReceiver eventReceiver) {
91.105 + this.receiver = eventReceiver;
91.106 + }
91.107 +
91.108 + public void stop() throws IOException {
91.109 + closed.set(true);
91.110 + socket.close();
91.111 + }
91.112 +
91.113 + private class Receiver implements Runnable {
91.114 + public void run() {
91.115 + byte[] buf = new byte[1024];
91.116 + DatagramPacket packet = new DatagramPacket(buf, buf.length);
91.117 + while (true) {
91.118 + try {
91.119 + socket.receive(packet);
91.120 + } catch (IOException e) {
91.121 + if (closed.get()) {
91.122 + System.out.println("Receiver got exception: " + e);
91.123 + System.out.println("Normal because it has been closed");
91.124 + return;
91.125 + } else {
91.126 + System.err.println("UNEXPECTED EXCEPTION IN RECEIVER:");
91.127 + e.printStackTrace();
91.128 + System.exit(1);
91.129 + }
91.130 + }
91.131 + try {
91.132 + ByteArrayInputStream bin = new ByteArrayInputStream(buf);
91.133 + ObjectInputStream oin = new ObjectInputStream(bin);
91.134 + NotificationResult nr = (NotificationResult)
91.135 + oin.readObject();
91.136 + receiver.receive(nr);
91.137 + } catch (Exception e) {
91.138 + System.err.println("UNEXPECTED EXCEPTION IN RECEIVER:");
91.139 + e.printStackTrace();
91.140 + System.exit(1);
91.141 + }
91.142 + }
91.143 + }
91.144 + }
91.145 + }
91.146 +
91.147 + public static class UdpEventForwarder implements EventForwarder {
91.148 + private final DatagramSocket socket;
91.149 + private final AtomicLong seqNo = new AtomicLong(0);
91.150 + private static volatile boolean drop;
91.151 +
91.152 + public UdpEventForwarder(SocketAddress addr) throws IOException {
91.153 + this.socket = new DatagramSocket();
91.154 + socket.connect(addr);
91.155 + }
91.156 +
91.157 + public static void setDrop(boolean drop) {
91.158 + UdpEventForwarder.drop = drop;
91.159 + }
91.160 +
91.161 + public void forward(Notification n, Integer listenerId) throws IOException {
91.162 + long nextSeqNo = seqNo.incrementAndGet();
91.163 + long thisSeqNo = nextSeqNo - 1;
91.164 + TargetedNotification tn = new TargetedNotification(n, listenerId);
91.165 + NotificationResult nr = new NotificationResult(
91.166 + thisSeqNo, nextSeqNo, new TargetedNotification[] {tn});
91.167 + ByteArrayOutputStream bout = new ByteArrayOutputStream();
91.168 + ObjectOutputStream oout = new ObjectOutputStream(bout);
91.169 + oout.writeObject(nr);
91.170 + oout.close();
91.171 + byte[] bytes = bout.toByteArray();
91.172 + DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
91.173 + if (!drop)
91.174 + socket.send(packet);
91.175 + }
91.176 +
91.177 + public void close() throws IOException {
91.178 + socket.close();
91.179 + }
91.180 +
91.181 + public void setClientId(String clientId) throws IOException {
91.182 + // Nothing to do.
91.183 + }
91.184 + }
91.185 +
91.186 + public static interface EmptyMBean {}
91.187 +
91.188 + public static class Empty
91.189 + extends NotificationBroadcasterSupport implements EmptyMBean {
91.190 + public void send(Notification n) {
91.191 + super.sendNotification(n);
91.192 + }
91.193 + }
91.194 +
91.195 + public static void main(String[] args) throws Exception {
91.196 + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
91.197 + MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
91.198 + mbsf.setMBeanServer(mbs);
91.199 + mbs = mbsf;
91.200 +
91.201 + // for 1.5
91.202 + if (System.getProperty("java.version").startsWith("1.5") &&
91.203 + !mbs.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
91.204 + System.out.print("Working on "+System.getProperty("java.version")+
91.205 + " register "+EventClientDelegateMBean.OBJECT_NAME);
91.206 +
91.207 + mbs.registerMBean(EventClientDelegate.
91.208 + getEventClientDelegate(mbs),
91.209 + EventClientDelegateMBean.OBJECT_NAME);
91.210 + }
91.211 +
91.212 + ObjectName name = new ObjectName("a:b=c");
91.213 + Empty mbean = new Empty();
91.214 + mbs.registerMBean(mbean, name);
91.215 +
91.216 + EventClientDelegateMBean delegate = (EventClientDelegateMBean)
91.217 + MBeanServerInvocationHandler.newProxyInstance(
91.218 + mbs,
91.219 + EventClientDelegateMBean.OBJECT_NAME,
91.220 + EventClientDelegateMBean.class,
91.221 + false);
91.222 + EventRelay relay = new UdpEventRelay(delegate);
91.223 + EventClient client = new EventClient(delegate, relay, null, null, 0L);
91.224 +
91.225 + final Semaphore lostCountSema = new Semaphore(0);
91.226 + NotificationListener lostListener = new NotificationListener() {
91.227 + public void handleNotification(Notification notification, Object handback) {
91.228 + if (notification.getType().equals(EventClient.NOTIFS_LOST)) {
91.229 + System.out.println("Got lost-notifs notif: count=" +
91.230 + notification.getUserData());
91.231 + lostCountSema.release(((Long) notification.getUserData()).intValue());
91.232 + } else
91.233 + System.out.println("Mysterious EventClient notif: " + notification);
91.234 + }
91.235 + };
91.236 + client.addEventClientListener(lostListener, null, null);
91.237 +
91.238 + final BlockingQueue<Notification> notifQueue =
91.239 + new ArrayBlockingQueue<Notification>(10);
91.240 + NotificationListener countListener = new NotificationListener() {
91.241 + public void handleNotification(Notification notification, Object handback) {
91.242 + System.out.println("Received: " + notification);
91.243 + notifQueue.add(notification);
91.244 + if (!"tiddly".equals(handback)) {
91.245 + System.err.println("TEST FAILED: bad handback: " + handback);
91.246 + System.exit(1);
91.247 + }
91.248 + }
91.249 + };
91.250 +
91.251 + final AtomicInteger filterCount = new AtomicInteger(0);
91.252 + NotificationFilter countFilter = new NotificationFilter() {
91.253 + private static final long serialVersionUID = 1234L;
91.254 +
91.255 + public boolean isNotificationEnabled(Notification notification) {
91.256 + System.out.println("Filter called for: " + notification);
91.257 + filterCount.incrementAndGet();
91.258 + return true;
91.259 + }
91.260 + };
91.261 +
91.262 + client.addNotificationListener(name, countListener, countFilter, "tiddly");
91.263 +
91.264 + assertEquals("Initial notif count", 0, notifQueue.size());
91.265 + assertEquals("Initial filter count", 0, filterCount.get());
91.266 +
91.267 + Notification n = nextNotif(name);
91.268 + mbean.send(n);
91.269 +
91.270 + System.out.println("Waiting for notification to arrive...");
91.271 +
91.272 + Notification n1 = notifQueue.poll(10, TimeUnit.SECONDS);
91.273 +
91.274 + assertEquals("Received notif", n, n1);
91.275 + assertEquals("Notif queue size after receive", 0, notifQueue.size());
91.276 + assertEquals("Filter count after notif", 1, filterCount.get());
91.277 + assertEquals("Lost notif count", 0, lostCountSema.availablePermits());
91.278 +
91.279 + System.out.println("Dropping notifs");
91.280 +
91.281 + UdpEventForwarder.setDrop(true);
91.282 + for (int i = 0; i < 3; i++)
91.283 + mbean.send(nextNotif(name));
91.284 + UdpEventForwarder.setDrop(false);
91.285 +
91.286 + Thread.sleep(2);
91.287 + assertEquals("Notif queue size after drops", 0, notifQueue.size());
91.288 +
91.289 + System.out.println("Turning off dropping and sending a notif");
91.290 + n = nextNotif(name);
91.291 + mbean.send(n);
91.292 +
91.293 + System.out.println("Waiting for dropped notifications to be detected...");
91.294 + boolean acquired = lostCountSema.tryAcquire(3, 5, TimeUnit.SECONDS);
91.295 + assertEquals("Correct count of lost notifs", true, acquired);
91.296 +
91.297 + n1 = notifQueue.poll(10, TimeUnit.SECONDS);
91.298 + assertEquals("Received non-dropped notif", n, n1);
91.299 +
91.300 + assertEquals("Notif queue size", 0, notifQueue.size());
91.301 + assertEquals("Filter count after drops", 5, filterCount.get());
91.302 +
91.303 + Thread.sleep(10);
91.304 + assertEquals("Further lost-notifs", 0, lostCountSema.availablePermits());
91.305 +
91.306 + client.close();
91.307 +
91.308 + System.out.println("TEST PASSED");
91.309 + }
91.310 +
91.311 + private static AtomicLong nextSeqNo = new AtomicLong(0);
91.312 + private static Notification nextNotif(ObjectName name) {
91.313 + long n = nextSeqNo.incrementAndGet();
91.314 + return new Notification("type", name, n, "" + n);
91.315 + }
91.316 +
91.317 + private static void assertEquals(String what, Object expected, Object got) {
91.318 + if (equals(expected, got))
91.319 + System.out.println(what + " = " + expected + ", as expected");
91.320 + else {
91.321 + Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
91.322 + for (Thread t : traces.keySet()) {
91.323 + System.out.println(t.getName());
91.324 + for (StackTraceElement elmt : traces.get(t)) {
91.325 + System.out.println(" " + elmt);
91.326 + }
91.327 + }
91.328 + throw new RuntimeException(
91.329 + "TEST FAILED: " + what + " is " + got + "; should be " +
91.330 + expected);
91.331 + }
91.332 + }
91.333 +
91.334 + private static boolean equals(Object expected, Object got) {
91.335 + if (!(expected instanceof Notification))
91.336 + return expected.equals(got);
91.337 + if (expected.getClass() != got.getClass())
91.338 + return false;
91.339 + // Notification doesn't override Object.equals so two distinct
91.340 + // notifs are never equal even if they have the same contents.
91.341 + // Although the test doesn't serialize the notifs, if at some
91.342 + // stage it did then it would fail because the deserialized notif
91.343 + // was not equal to the original one. Therefore we compare enough
91.344 + // notif fields to detect when notifs really are different.
91.345 + Notification en = (Notification) expected;
91.346 + Notification gn = (Notification) got;
91.347 + return (en.getType().equals(gn.getType()) &&
91.348 + en.getSource().equals(gn.getSource()) &&
91.349 + en.getSequenceNumber() == gn.getSequenceNumber());
91.350 + }
91.351 +}
92.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
92.2 +++ b/test/javax/management/eventService/EventClientExecutorTest.java Thu Aug 21 09:55:18 2008 -0700
92.3 @@ -0,0 +1,191 @@
92.4 +/*
92.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
92.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
92.7 + *
92.8 + * This code is free software; you can redistribute it and/or modify it
92.9 + * under the terms of the GNU General Public License version 2 only, as
92.10 + * published by the Free Software Foundation.
92.11 + *
92.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
92.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
92.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
92.15 + * version 2 for more details (a copy is included in the LICENSE file that
92.16 + * accompanied this code).
92.17 + *
92.18 + * You should have received a copy of the GNU General Public License version
92.19 + * 2 along with this work; if not, write to the Free Software Foundation,
92.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
92.21 + *
92.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
92.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
92.24 + * have any questions.
92.25 + */
92.26 +
92.27 +/*
92.28 + * @test
92.29 + * @bug 5108776
92.30 + * @summary Test that the various Executor parameters in an EventClient do
92.31 + * what they are supposed to.
92.32 + * @author Eamonn McManus
92.33 + */
92.34 +
92.35 +import java.lang.reflect.InvocationHandler;
92.36 +import java.lang.reflect.InvocationTargetException;
92.37 +import java.lang.reflect.Method;
92.38 +import java.lang.reflect.Proxy;
92.39 +import java.util.HashSet;
92.40 +import java.util.Set;
92.41 +import java.util.concurrent.Executor;
92.42 +import java.util.concurrent.Executors;
92.43 +import java.util.concurrent.ScheduledExecutorService;
92.44 +import java.util.concurrent.ThreadFactory;
92.45 +import javax.management.MBeanServer;
92.46 +import javax.management.MBeanServerFactory;
92.47 +import javax.management.Notification;
92.48 +import javax.management.NotificationBroadcasterSupport;
92.49 +import javax.management.NotificationListener;
92.50 +import javax.management.ObjectName;
92.51 +import javax.management.event.EventClient;
92.52 +import javax.management.event.EventClientDelegate;
92.53 +import javax.management.event.EventClientDelegateMBean;
92.54 +import javax.management.event.FetchingEventRelay;
92.55 +import javax.management.remote.MBeanServerForwarder;
92.56 +
92.57 +public class EventClientExecutorTest {
92.58 + private static volatile String failure;
92.59 + private static final Set testedPrefixes = new HashSet();
92.60 +
92.61 + public static void main(String[] args) throws Exception {
92.62 + Executor fetchExecutor = Executors.newSingleThreadExecutor(
92.63 + new NamedThreadFactory("FETCH"));
92.64 + Executor listenerExecutor = Executors.newSingleThreadExecutor(
92.65 + new NamedThreadFactory("LISTENER"));
92.66 + ScheduledExecutorService leaseScheduler =
92.67 + Executors.newSingleThreadScheduledExecutor(
92.68 + new NamedThreadFactory("LEASE"));
92.69 +
92.70 + MBeanServer mbs = MBeanServerFactory.newMBeanServer();
92.71 + MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
92.72 + mbsf.setMBeanServer(mbs);
92.73 + mbs = mbsf;
92.74 +
92.75 + EventClientDelegateMBean ecd = EventClientDelegate.getProxy(mbs);
92.76 + ecd = (EventClientDelegateMBean) Proxy.newProxyInstance(
92.77 + EventClientDelegateMBean.class.getClassLoader(),
92.78 + new Class<?>[] {EventClientDelegateMBean.class},
92.79 + new DelegateCheckIH(ecd));
92.80 +
92.81 + ObjectName mbeanName = new ObjectName("d:type=Notifier");
92.82 + Notifier notifier = new Notifier();
92.83 + mbs.registerMBean(notifier, mbeanName);
92.84 +
92.85 + FetchingEventRelay eventRelay = new FetchingEventRelay(
92.86 + ecd, fetchExecutor);
92.87 + EventClient ec = new EventClient(
92.88 + ecd, eventRelay, listenerExecutor, leaseScheduler, 1000L);
92.89 + NotificationListener checkListener = new NotificationListener() {
92.90 + public void handleNotification(Notification notification,
92.91 + Object handback) {
92.92 + assertThreadName("listener dispatch", "LISTENER");
92.93 + }
92.94 + };
92.95 + ec.addNotificationListener(mbeanName, checkListener, null, null);
92.96 +
92.97 + mbs.invoke(mbeanName, "send", null, null);
92.98 +
92.99 + // Now wait until we have seen all three thread types.
92.100 + long deadline = System.currentTimeMillis() + 5000;
92.101 + synchronized (testedPrefixes) {
92.102 + while (testedPrefixes.size() < 3 && failure == null) {
92.103 + long remain = deadline - System.currentTimeMillis();
92.104 + if (remain <= 0) {
92.105 + fail("Timed out waiting for all three thread types to show, " +
92.106 + "saw only " + testedPrefixes);
92.107 + break;
92.108 + }
92.109 + try {
92.110 + testedPrefixes.wait(remain);
92.111 + } catch (InterruptedException e) {
92.112 + fail("Unexpected InterruptedException");
92.113 + break;
92.114 + }
92.115 + }
92.116 + }
92.117 +
92.118 + // We deliberately don't close the EventClient to check that it has
92.119 + // not created any non-daemon threads.
92.120 +
92.121 + if (failure != null)
92.122 + throw new Exception("TEST FAILED: " + failure);
92.123 + else
92.124 + System.out.println("TEST PASSED");
92.125 + }
92.126 +
92.127 + public static interface NotifierMBean {
92.128 + public void send();
92.129 + }
92.130 +
92.131 + public static class Notifier extends NotificationBroadcasterSupport
92.132 + implements NotifierMBean {
92.133 + public void send() {
92.134 + Notification n = new Notification("a.b.c", this, 0L);
92.135 + sendNotification(n);
92.136 + }
92.137 + }
92.138 +
92.139 + static void fail(String why) {
92.140 + System.out.println("FAIL: " + why);
92.141 + failure = why;
92.142 + }
92.143 +
92.144 + static void assertThreadName(String what, String prefix) {
92.145 + String name = Thread.currentThread().getName();
92.146 + if (!name.startsWith(prefix)) {
92.147 + fail("Wrong thread for " + what + ": " + name);
92.148 + return;
92.149 + }
92.150 +
92.151 + synchronized (testedPrefixes) {
92.152 + if (testedPrefixes.add(prefix))
92.153 + testedPrefixes.notify();
92.154 + }
92.155 + }
92.156 +
92.157 + private static class DelegateCheckIH implements InvocationHandler {
92.158 + private final EventClientDelegateMBean ecd;
92.159 +
92.160 + public DelegateCheckIH(EventClientDelegateMBean ecd) {
92.161 + this.ecd = ecd;
92.162 + }
92.163 +
92.164 + public Object invoke(Object proxy, Method method, Object[] args)
92.165 + throws Throwable {
92.166 + String methodName = method.getName();
92.167 + if (methodName.equals("fetchNotifications"))
92.168 + assertThreadName("fetchNotifications", "FETCH");
92.169 + else if (methodName.equals("lease"))
92.170 + assertThreadName("lease renewal", "LEASE");
92.171 + try {
92.172 + return method.invoke(ecd, args);
92.173 + } catch (InvocationTargetException e) {
92.174 + throw e.getCause();
92.175 + }
92.176 + }
92.177 + }
92.178 +
92.179 + private static class NamedThreadFactory implements ThreadFactory {
92.180 + private final String namePrefix;
92.181 + private int count;
92.182 +
92.183 + NamedThreadFactory(String namePrefix) {
92.184 + this.namePrefix = namePrefix;
92.185 + }
92.186 +
92.187 + public synchronized Thread newThread(Runnable r) {
92.188 + Thread t = new Thread(r);
92.189 + t.setName(namePrefix + " " + ++count);
92.190 + t.setDaemon(true);
92.191 + return t;
92.192 + }
92.193 + }
92.194 +}
93.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
93.2 +++ b/test/javax/management/eventService/EventDelegateSecurityTest.java Thu Aug 21 09:55:18 2008 -0700
93.3 @@ -0,0 +1,289 @@
93.4 +/*
93.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
93.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
93.7 + *
93.8 + * This code is free software; you can redistribute it and/or modify it
93.9 + * under the terms of the GNU General Public License version 2 only, as
93.10 + * published by the Free Software Foundation.
93.11 + *
93.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
93.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
93.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
93.15 + * version 2 for more details (a copy is included in the LICENSE file that
93.16 + * accompanied this code).
93.17 + *
93.18 + * You should have received a copy of the GNU General Public License version
93.19 + * 2 along with this work; if not, write to the Free Software Foundation,
93.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
93.21 + *
93.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
93.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
93.24 + * have any questions.
93.25 + */
93.26 +
93.27 +/*
93.28 + * @test
93.29 + * @bug 5108776
93.30 + * @summary Test that the EventClientDelegate MBean does not require extra
93.31 + * permissions compared with plain addNotificationListener.
93.32 + * @author Eamonn McManus
93.33 + * @run main/othervm -Dxjava.security.debug=policy,access,failure EventDelegateSecurityTest
93.34 + */
93.35 +
93.36 +import java.io.File;
93.37 +import java.io.IOException;
93.38 +import java.io.PrintWriter;
93.39 +import java.lang.management.ManagementFactory;
93.40 +import java.lang.reflect.InvocationHandler;
93.41 +import java.lang.reflect.InvocationTargetException;
93.42 +import java.lang.reflect.Method;
93.43 +import java.lang.reflect.Proxy;
93.44 +import java.security.AccessControlContext;
93.45 +import java.security.AccessController;
93.46 +import java.security.AllPermission;
93.47 +import java.security.PrivilegedExceptionAction;
93.48 +import java.util.Arrays;
93.49 +import java.util.HashMap;
93.50 +import java.util.HashSet;
93.51 +import java.util.Map;
93.52 +import java.util.Set;
93.53 +import java.util.concurrent.BlockingQueue;
93.54 +import java.util.concurrent.SynchronousQueue;
93.55 +import java.util.concurrent.TimeUnit;
93.56 +import javax.management.MBeanPermission;
93.57 +import javax.management.MBeanServer;
93.58 +import javax.management.MBeanServerConnection;
93.59 +import javax.management.Notification;
93.60 +import javax.management.NotificationBroadcasterSupport;
93.61 +import javax.management.NotificationListener;
93.62 +import javax.management.ObjectName;
93.63 +import javax.management.event.EventClient;
93.64 +import javax.management.remote.JMXAuthenticator;
93.65 +import javax.management.remote.JMXConnector;
93.66 +import javax.management.remote.JMXConnectorFactory;
93.67 +import javax.management.remote.JMXConnectorServer;
93.68 +import javax.management.remote.JMXConnectorServerFactory;
93.69 +import javax.management.remote.JMXPrincipal;
93.70 +import javax.management.remote.JMXServiceURL;
93.71 +import javax.management.remote.MBeanServerForwarder;
93.72 +import javax.security.auth.Subject;
93.73 +
93.74 +public class EventDelegateSecurityTest {
93.75 + private static final BlockingQueue<Notification> notifQ =
93.76 + new SynchronousQueue<Notification>();
93.77 +
93.78 + private static volatile long seqNo;
93.79 + private static volatile long expectSeqNo;
93.80 +
93.81 + private static class QueueListener implements NotificationListener {
93.82 + public void handleNotification(Notification notification,
93.83 + Object handback) {
93.84 + try {
93.85 + notifQ.put(notification);
93.86 + } catch (InterruptedException e) {
93.87 + throw new AssertionError(e);
93.88 + }
93.89 + }
93.90 + }
93.91 + private static final NotificationListener queueListener = new QueueListener();
93.92 +
93.93 + public static interface SenderMBean {
93.94 + public void send();
93.95 + }
93.96 +
93.97 + public static class Sender
93.98 + extends NotificationBroadcasterSupport implements SenderMBean {
93.99 + public void send() {
93.100 + Notification n = new Notification("x", this, seqNo++);
93.101 + sendNotification(n);
93.102 + }
93.103 + }
93.104 +
93.105 + private static class LimitInvocationHandler implements InvocationHandler {
93.106 + private MBeanServer nextMBS;
93.107 + private final Set<String> allowedMethods = new HashSet<String>();
93.108 +
93.109 + void allow(String... names) {
93.110 + synchronized (allowedMethods) {
93.111 + allowedMethods.addAll(Arrays.asList(names));
93.112 + }
93.113 + }
93.114 +
93.115 + public Object invoke(Object proxy, Method m, Object[] args)
93.116 + throws Throwable {
93.117 + System.out.println(
93.118 + "filter: " + m.getName() +
93.119 + ((args == null) ? "[]" : Arrays.deepToString(args)));
93.120 + String name = m.getName();
93.121 +
93.122 + if (name.equals("getMBeanServer"))
93.123 + return nextMBS;
93.124 +
93.125 + if (name.equals("setMBeanServer")) {
93.126 + nextMBS = (MBeanServer) args[0];
93.127 + return null;
93.128 + }
93.129 +
93.130 + if (m.getDeclaringClass() == Object.class ||
93.131 + allowedMethods.contains(name)) {
93.132 + try {
93.133 + return m.invoke(nextMBS, args);
93.134 + } catch (InvocationTargetException e) {
93.135 + throw e.getCause();
93.136 + }
93.137 + } else {
93.138 + System.out.println("...refused");
93.139 + throw new SecurityException(
93.140 + "Method refused: " + m.getDeclaringClass().getName() +
93.141 + "." + m.getName() +
93.142 + ((args == null) ? "[]" : Arrays.deepToString(args)));
93.143 + }
93.144 + }
93.145 +
93.146 + }
93.147 +
93.148 + private static interface MakeConnectorServer {
93.149 + public JMXConnectorServer make(JMXServiceURL url) throws IOException;
93.150 + }
93.151 +
93.152 +
93.153 + public static void main(String[] args) throws Exception {
93.154 + JMXPrincipal rootPrincipal = new JMXPrincipal("root");
93.155 + Subject rootSubject = new Subject();
93.156 + rootSubject.getPrincipals().add(rootPrincipal);
93.157 + Subject.doAsPrivileged(rootSubject, new PrivilegedExceptionAction<Void>() {
93.158 + public Void run() throws Exception {
93.159 + mainAsRoot();
93.160 + return null;
93.161 + }
93.162 + }, null);
93.163 + }
93.164 +
93.165 + private static void mainAsRoot() throws Exception {
93.166 + AccessControlContext acc = AccessController.getContext();
93.167 + Subject subject = Subject.getSubject(acc);
93.168 + System.out.println("Subject: " + subject);
93.169 + final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
93.170 + ObjectName name = new ObjectName("a:b=c");
93.171 + mbs.registerMBean(new Sender(), name);
93.172 +
93.173 + System.out.println("Test with no installed security");
93.174 + test(mbs, name, new MakeConnectorServer() {
93.175 + public JMXConnectorServer make(JMXServiceURL url) throws IOException {
93.176 + return
93.177 + JMXConnectorServerFactory.newJMXConnectorServer(url, null, null);
93.178 + }
93.179 + });
93.180 +
93.181 + System.out.println("Test with filtering MBeanServerForwarder");
93.182 + LimitInvocationHandler limitIH = new LimitInvocationHandler();
93.183 + // We allow getClassLoaderRepository because the ConnectorServer
93.184 + // calls it so any real checking MBeanServerForwarder must accept it.
93.185 + limitIH.allow(
93.186 + "addNotificationListener", "removeNotificationListener",
93.187 + "getClassLoaderRepository"
93.188 + );
93.189 + final MBeanServerForwarder limitMBSF = (MBeanServerForwarder)
93.190 + Proxy.newProxyInstance(
93.191 + MBeanServerForwarder.class.getClassLoader(),
93.192 + new Class<?>[] {MBeanServerForwarder.class},
93.193 + limitIH);
93.194 + // We go to considerable lengths to ensure that the ConnectorServer has
93.195 + // no MBeanServer when the EventClientDelegate forwarder is activated,
93.196 + // so that the calls it makes when it is later linked to an MBeanServer
93.197 + // go through the limitMBSF.
93.198 + test(mbs, name, new MakeConnectorServer() {
93.199 + public JMXConnectorServer make(JMXServiceURL url) throws IOException {
93.200 + JMXConnectorServer cs =
93.201 + JMXConnectorServerFactory.newJMXConnectorServer(url, null, null);
93.202 + limitMBSF.setMBeanServer(mbs);
93.203 + cs.setMBeanServerForwarder(limitMBSF);
93.204 + return cs;
93.205 + }
93.206 + });
93.207 +
93.208 + final File policyFile =
93.209 + File.createTempFile("EventDelegateSecurityTest", ".policy");
93.210 + PrintWriter pw = new PrintWriter(policyFile);
93.211 + String JMXPrincipal = JMXPrincipal.class.getName();
93.212 + String AllPermission = AllPermission.class.getName();
93.213 + String MBeanPermission = MBeanPermission.class.getName();
93.214 + pw.println("grant principal " + JMXPrincipal + " \"root\" {");
93.215 + pw.println(" permission " + AllPermission + ";");
93.216 + pw.println("};");
93.217 + pw.println("grant principal " + JMXPrincipal + " \"user\" {");
93.218 + pw.println(" permission " + MBeanPermission + " \"*\", " +
93.219 + " \"addNotificationListener\";");
93.220 + pw.println(" permission " + MBeanPermission + " \"*\", " +
93.221 + " \"removeNotificationListener\";");
93.222 + pw.println("};");
93.223 + pw.close();
93.224 + Runtime.getRuntime().addShutdownHook(new Thread() {
93.225 + @Override
93.226 + public void run() {
93.227 + policyFile.delete();
93.228 + }
93.229 + });
93.230 + System.setProperty("java.security.policy", policyFile.getAbsolutePath());
93.231 + System.setSecurityManager(new SecurityManager());
93.232 + test(mbs, name, new MakeConnectorServer() {
93.233 + public JMXConnectorServer make(JMXServiceURL url) throws IOException {
93.234 + Map<String, Object> env = new HashMap<String, Object>();
93.235 + env.put(JMXConnectorServer.AUTHENTICATOR, new JMXAuthenticator() {
93.236 + public Subject authenticate(Object credentials) {
93.237 + Subject s = new Subject();
93.238 + s.getPrincipals().add(new JMXPrincipal("user"));
93.239 + return s;
93.240 + }
93.241 + });
93.242 + return
93.243 + JMXConnectorServerFactory.newJMXConnectorServer(url, env, null);
93.244 + }
93.245 + });
93.246 + }
93.247 +
93.248 + private static void test(MBeanServer mbs, ObjectName name) throws Exception {
93.249 + test(mbs, name, null);
93.250 + }
93.251 +
93.252 + private static void test(
93.253 + MBeanServer mbs, ObjectName name, MakeConnectorServer make)
93.254 + throws Exception {
93.255 + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///");
93.256 + JMXConnectorServer cs = make.make(url);
93.257 + ObjectName csName = new ObjectName("a:type=ConnectorServer");
93.258 + mbs.registerMBean(cs, csName);
93.259 + cs.start();
93.260 + try {
93.261 + JMXServiceURL addr = cs.getAddress();
93.262 + JMXConnector cc = JMXConnectorFactory.connect(addr);
93.263 + MBeanServerConnection mbsc = cc.getMBeanServerConnection();
93.264 + test(mbs, mbsc, name);
93.265 + cc.close();
93.266 + mbs.unregisterMBean(csName);
93.267 + } finally {
93.268 + cs.stop();
93.269 + }
93.270 + }
93.271 +
93.272 + private static void test(
93.273 + MBeanServer mbs, MBeanServerConnection mbsc, ObjectName name)
93.274 + throws Exception {
93.275 + EventClient ec = new EventClient(mbsc);
93.276 + ec.addNotificationListener(name, queueListener, null, null);
93.277 + mbs.invoke(name, "send", null, null);
93.278 +
93.279 + Notification n = notifQ.poll(5, TimeUnit.SECONDS);
93.280 + if (n == null)
93.281 + throw new Exception("FAILED: notif not delivered");
93.282 + if (n.getSequenceNumber() != expectSeqNo) {
93.283 + throw new Exception(
93.284 + "FAILED: notif seqno " + n.getSequenceNumber() +
93.285 + " should be " + expectSeqNo);
93.286 + }
93.287 + expectSeqNo++;
93.288 +
93.289 + ec.removeNotificationListener(name, queueListener);
93.290 + ec.close();
93.291 + }
93.292 +}
94.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
94.2 +++ b/test/javax/management/eventService/EventManagerTest.java Thu Aug 21 09:55:18 2008 -0700
94.3 @@ -0,0 +1,221 @@
94.4 +/*
94.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
94.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
94.7 + *
94.8 + * This code is free software; you can redistribute it and/or modify it
94.9 + * under the terms of the GNU General Public License version 2 only, as
94.10 + * published by the Free Software Foundation.
94.11 + *
94.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
94.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
94.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
94.15 + * version 2 for more details (a copy is included in the LICENSE file that
94.16 + * accompanied this code).
94.17 + *
94.18 + * You should have received a copy of the GNU General Public License version
94.19 + * 2 along with this work; if not, write to the Free Software Foundation,
94.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
94.21 + *
94.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
94.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
94.24 + * have any questions.
94.25 + */
94.26 +
94.27 +/*
94.28 + * @test EventManagerTest.java 1.8 08/01/22
94.29 + * @bug 5108776
94.30 + * @summary Basic test for EventManager.
94.31 + * @author Shanliang JIANG
94.32 + * @run clean EventManagerTest
94.33 + * @run build EventManagerTest
94.34 + * @run main EventManagerTest
94.35 + */
94.36 +
94.37 +import javax.management.MBeanNotificationInfo;
94.38 +import javax.management.MBeanServer;
94.39 +import javax.management.MBeanServerConnection;
94.40 +import javax.management.MBeanServerFactory;
94.41 +import javax.management.Notification;
94.42 +import javax.management.NotificationBroadcasterSupport;
94.43 +import javax.management.NotificationListener;
94.44 +import javax.management.ObjectName;
94.45 +import javax.management.event.*;
94.46 +import javax.management.remote.JMXConnector;
94.47 +import javax.management.remote.JMXConnectorFactory;
94.48 +import javax.management.remote.JMXConnectorServer;
94.49 +import javax.management.remote.JMXConnectorServerFactory;
94.50 +import javax.management.remote.JMXServiceURL;
94.51 +
94.52 +/**
94.53 + *
94.54 + */
94.55 +public class EventManagerTest {
94.56 + private static MBeanServer mbeanServer;
94.57 + private static ObjectName emitter;
94.58 + private static JMXServiceURL url;
94.59 + private static JMXConnectorServer server;
94.60 + private static JMXConnector conn;
94.61 + private static MBeanServerConnection client;
94.62 +
94.63 + /**
94.64 + * @param args the command line arguments
94.65 + */
94.66 + public static void main(String[] args) throws Exception {
94.67 + System.out.println(">>> EventManagerTest-main basic tests ...");
94.68 + mbeanServer = MBeanServerFactory.createMBeanServer();
94.69 +
94.70 + // for 1.5
94.71 + if (System.getProperty("java.version").startsWith("1.5") &&
94.72 + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
94.73 + System.out.print("Working on "+System.getProperty("java.version")+
94.74 + " register "+EventClientDelegateMBean.OBJECT_NAME);
94.75 +
94.76 + mbeanServer.registerMBean(EventClientDelegate.
94.77 + getEventClientDelegate(mbeanServer),
94.78 + EventClientDelegateMBean.OBJECT_NAME);
94.79 + }
94.80 +
94.81 + emitter = new ObjectName("Default:name=NotificationEmitter");
94.82 +
94.83 + url = new JMXServiceURL("rmi", null, 0) ;
94.84 + server =
94.85 + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer);
94.86 + server.start();
94.87 +
94.88 + url = server.getAddress();
94.89 + conn = JMXConnectorFactory.connect(url, null);
94.90 + client = conn.getMBeanServerConnection();
94.91 +
94.92 + mbeanServer.registerMBean(new NotificationEmitter(), emitter);
94.93 +
94.94 + boolean succeed;
94.95 +
94.96 + System.out.println(">>> EventManagerTest-main: using the fetching EventRelay...");
94.97 + succeed = test(new EventClient(client));
94.98 +
94.99 + System.out.println(">>> EventManagerTest-main: using the pushing EventRelay...");
94.100 + EventClientDelegateMBean ecd = EventClientDelegate.getProxy(client);
94.101 + succeed &= test(new EventClient(ecd,
94.102 + new RMIPushEventRelay(ecd),
94.103 + null, null,
94.104 + EventClient.DEFAULT_LEASE_TIMEOUT));
94.105 +
94.106 + conn.close();
94.107 + server.stop();
94.108 +
94.109 + if (succeed) {
94.110 + System.out.println(">>> EventManagerTest-main: PASSE!");
94.111 + } else {
94.112 + System.out.println("\n>>> EventManagerTest-main: FAILED!");
94.113 + System.exit(1);
94.114 + }
94.115 + }
94.116 +
94.117 + public static boolean test(EventClient efClient) throws Exception {
94.118 + // add listener from the client side
94.119 + Listener listener = new Listener();
94.120 + efClient.subscribe(emitter, listener, null, null);
94.121 +
94.122 + // ask to send notifs
94.123 + Object[] params = new Object[] {new Integer(sendNB)};
94.124 + String[] signatures = new String[] {"java.lang.Integer"};
94.125 + client.invoke(emitter, "sendNotifications", params, signatures);
94.126 +
94.127 + // waiting
94.128 + long toWait = 6000;
94.129 + long stopTime = System.currentTimeMillis() + toWait;
94.130 +
94.131 + synchronized(listener) {
94.132 + while(listener.received < sendNB && toWait > 0) {
94.133 + listener.wait(toWait);
94.134 + toWait = stopTime - System.currentTimeMillis();
94.135 + }
94.136 + }
94.137 +
94.138 + // clean
94.139 + System.out.println(">>> EventManagerTest-test: cleaning...");
94.140 + efClient.unsubscribe(emitter, listener);
94.141 + efClient.close();
94.142 +
94.143 + if (listener.received != sendNB) {
94.144 + System.out.println(">>> EventManagerTest-test: FAILED! Expected to receive "+sendNB+", but got "+listener.received);
94.145 +
94.146 + return false;
94.147 + } else if (listener.seqErr > 0) {
94.148 + System.out.println(">>> EventManagerTest-test: FAILED! The receiving sequence is not correct.");
94.149 +
94.150 + return false;
94.151 + } else {
94.152 + System.out.println(">>> EventManagerTest-test: got all expected "+listener.received);
94.153 + return true;
94.154 + }
94.155 + }
94.156 +
94.157 + private static class Listener implements NotificationListener {
94.158 + public int received = 0;
94.159 + public int seqErr = 0;
94.160 +
94.161 + private long lastSeq = -1;
94.162 +
94.163 + public void handleNotification(Notification notif, Object handback) {
94.164 + if (!myType.equals(notif.getType())) {
94.165 + System.out.println(">>> EventManagerTest-Listener: got unexpected notif: "+notif);
94.166 + System.exit(1);
94.167 + }
94.168 +
94.169 + if (lastSeq == -1) {
94.170 + lastSeq = notif.getSequenceNumber();
94.171 + } else if (notif.getSequenceNumber() - lastSeq++ != 1) {
94.172 + seqErr++;
94.173 + }
94.174 +
94.175 + //System.out.println(">>> EventManagerTest-Listener: got notif "+notif.getSequenceNumber());
94.176 +
94.177 + synchronized(this) {
94.178 + if (++received >= sendNB) {
94.179 + this.notify();
94.180 + }
94.181 + }
94.182 + }
94.183 + }
94.184 +
94.185 + public static class NotificationEmitter extends NotificationBroadcasterSupport
94.186 + implements NotificationEmitterMBean {
94.187 +
94.188 + public MBeanNotificationInfo[] getNotificationInfo() {
94.189 + final String[] ntfTypes = {myType};
94.190 +
94.191 + final MBeanNotificationInfo[] ntfInfoArray = {
94.192 + new MBeanNotificationInfo(ntfTypes,
94.193 + "javax.management.Notification",
94.194 + "Notifications sent by the NotificationEmitter")};
94.195 +
94.196 + return ntfInfoArray;
94.197 + }
94.198 +
94.199 + /**
94.200 + * Send Notification objects.
94.201 + *
94.202 + * @param nb The number of notifications to send
94.203 + */
94.204 + public void sendNotifications(Integer nb) {
94.205 + Notification notif;
94.206 + for (int i=1; i<=nb.intValue(); i++) {
94.207 + notif = new Notification(myType, this, count++);
94.208 + notif.setUserData("jsl");
94.209 + //System.out.println(">>> EventManagerService-NotificationEmitter-sendNotifications: "+i);
94.210 +
94.211 + sendNotification(notif);
94.212 + }
94.213 + }
94.214 + }
94.215 +
94.216 + public interface NotificationEmitterMBean {
94.217 + public void sendNotifications(Integer nb);
94.218 + }
94.219 +
94.220 + private static int sendNB = 120;
94.221 + private static long count = 0;
94.222 +
94.223 + private static final String myType = "notification.my_notification";
94.224 +}
95.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
95.2 +++ b/test/javax/management/eventService/FetchingTest.java Thu Aug 21 09:55:18 2008 -0700
95.3 @@ -0,0 +1,276 @@
95.4 +/*
95.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
95.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
95.7 + *
95.8 + * This code is free software; you can redistribute it and/or modify it
95.9 + * under the terms of the GNU General Public License version 2 only, as
95.10 + * published by the Free Software Foundation.
95.11 + *
95.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
95.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
95.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
95.15 + * version 2 for more details (a copy is included in the LICENSE file that
95.16 + * accompanied this code).
95.17 + *
95.18 + * You should have received a copy of the GNU General Public License version
95.19 + * 2 along with this work; if not, write to the Free Software Foundation,
95.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
95.21 + *
95.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
95.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
95.24 + * have any questions.
95.25 + */
95.26 +
95.27 +/*
95.28 + * @test
95.29 + * @bug 5108776
95.30 + * @summary Basic test for EventClient.
95.31 + * @author Shanliang JIANG
95.32 + * @run clean FetchingTest MyFetchingEventForwarder
95.33 + * @run build FetchingTest MyFetchingEventForwarder
95.34 + * @run main FetchingTest MyFetchingEventForwarder
95.35 + */
95.36 +
95.37 +import javax.management.MBeanServer;
95.38 +import javax.management.MBeanServerConnection;
95.39 +import javax.management.MBeanServerFactory;
95.40 +import javax.management.Notification;
95.41 +import javax.management.NotificationBroadcasterSupport;
95.42 +import javax.management.NotificationListener;
95.43 +import javax.management.ObjectName;
95.44 +import javax.management.event.EventClient;
95.45 +import javax.management.event.EventClientDelegate;
95.46 +import javax.management.event.EventClientDelegateMBean;
95.47 +import javax.management.event.FetchingEventRelay;
95.48 +import javax.management.event.RMIPushEventForwarder;
95.49 +import javax.management.event.RMIPushServer;
95.50 +import javax.management.remote.JMXConnector;
95.51 +import javax.management.remote.JMXConnectorFactory;
95.52 +import javax.management.remote.JMXConnectorServer;
95.53 +import javax.management.remote.JMXConnectorServerFactory;
95.54 +import javax.management.remote.JMXServiceURL;
95.55 +
95.56 +public class FetchingTest {
95.57 + private static MBeanServer mbeanServer;
95.58 + private static ObjectName emitter;
95.59 + private static JMXServiceURL url;
95.60 + private static JMXConnectorServer server;
95.61 + private static JMXConnector conn;
95.62 + private static MBeanServerConnection client;
95.63 + private static long WAITING_TIME = 6000;
95.64 +
95.65 + /**
95.66 + * @param args the command line arguments
95.67 + */
95.68 + public static void main(String[] args) throws Exception {
95.69 +
95.70 + System.out.println(">>> FetchingTest-main basic tests ...");
95.71 + mbeanServer = MBeanServerFactory.createMBeanServer();
95.72 +
95.73 + // for 1.5
95.74 + if (System.getProperty("java.version").startsWith("1.5") &&
95.75 + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
95.76 + System.out.print("Working on "+System.getProperty("java.version")+
95.77 + " register "+EventClientDelegateMBean.OBJECT_NAME);
95.78 +
95.79 + mbeanServer.registerMBean(EventClientDelegate.
95.80 + getEventClientDelegate(mbeanServer),
95.81 + EventClientDelegateMBean.OBJECT_NAME);
95.82 + }
95.83 +
95.84 + emitter = new ObjectName("Default:name=NotificationEmitter");
95.85 + mbeanServer.registerMBean(new NotificationEmitter(), emitter);
95.86 + boolean succeed = true;
95.87 +
95.88 + final String[] protos = new String[] {"rmi", "iiop", "jmxmp"};
95.89 + for (String proto : protos) {
95.90 + System.out.println(">>> FetchingTest-main: testing on "+proto);
95.91 +
95.92 + try {
95.93 + url = new JMXServiceURL(proto, null, 0) ;
95.94 + server = JMXConnectorServerFactory.
95.95 + newJMXConnectorServer(url, null, mbeanServer);
95.96 + server.start();
95.97 + } catch (Exception e) {
95.98 + // OK
95.99 + System.out.println(">>> FetchingTest-main: skip the proto "+proto);
95.100 + continue;
95.101 + }
95.102 +
95.103 + url = server.getAddress();
95.104 + conn = JMXConnectorFactory.connect(url, null);
95.105 + client = conn.getMBeanServerConnection();
95.106 +
95.107 + succeed &= test();
95.108 +
95.109 + conn.close();
95.110 + server.stop();
95.111 +
95.112 + System.out.println(
95.113 + ">>> FetchingTest-main: testing on "+proto+" done.");
95.114 + }
95.115 +
95.116 + if (succeed) {
95.117 + System.out.println(">>> FetchingTest-main: PASSED!");
95.118 + } else {
95.119 + System.out.println("\n>>> FetchingTest-main: FAILED!");
95.120 + System.exit(1);
95.121 + }
95.122 + }
95.123 +
95.124 + public static boolean test() throws Exception {
95.125 + System.out.println(">>> FetchingTest-test: " +
95.126 + "using the default fetching forwarder ...");
95.127 + EventClient eventClient =
95.128 + new EventClient(client);
95.129 +
95.130 + Listener listener = new Listener();
95.131 + eventClient.addNotificationListener(emitter, listener, null, null);
95.132 +
95.133 + // ask to send notifs
95.134 + Object[] params = new Object[] {new Integer(sendNB)};
95.135 + String[] signatures = new String[] {"java.lang.Integer"};
95.136 + conn.getMBeanServerConnection().invoke(emitter,
95.137 + "sendNotifications", params, signatures);
95.138 +
95.139 + if (listener.waitNotif(WAITING_TIME) != sendNB) {
95.140 + System.out.println(
95.141 + ">>> FetchingTest-test: FAILED! Expected to receive "+
95.142 + sendNB+", but got "+listener.received);
95.143 +
95.144 + return false;
95.145 + }
95.146 +
95.147 + System.out.println(
95.148 + ">>> ListenerTest-test: got all expected "+listener.received);
95.149 + //eventClient.removeNotificationListener(emitter, listener);
95.150 + eventClient.close();
95.151 +
95.152 + System.out.println(">>> FetchingTest-test: " +
95.153 + "using a user specific List ...");
95.154 +
95.155 + FetchingEventRelay fer = new FetchingEventRelay(
95.156 + EventClientDelegate.getProxy(client),
95.157 + 1000, 1000L, 1000, null,
95.158 + MyFetchingEventForwarder.class.getName(),
95.159 + null, null);
95.160 +
95.161 + eventClient = new EventClient(
95.162 + EventClientDelegate.getProxy(client), fer, null, null, 10000);
95.163 +
95.164 + eventClient.addNotificationListener(emitter, listener, null, null);
95.165 + listener.received = 0;
95.166 +
95.167 + conn.getMBeanServerConnection().invoke(emitter,
95.168 + "sendNotifications", params, signatures);
95.169 +
95.170 + if (listener.waitNotif(WAITING_TIME) != sendNB) {
95.171 + System.out.println(
95.172 + ">>> FetchingTest-test: FAILED! Expected to receive "+
95.173 + sendNB+", but got "+listener.received);
95.174 +
95.175 + return false;
95.176 + }
95.177 +
95.178 + System.out.println(
95.179 + ">>> FetchingTest-test: got all expected "+listener.received);
95.180 +
95.181 + if (!MyFetchingEventForwarder.shared.isUsed()) {
95.182 + System.out.println(
95.183 + ">>> FetchingTest-test: FAILED! The user specific list" +
95.184 + "is not used!");
95.185 +
95.186 + return false;
95.187 + }
95.188 +
95.189 + System.out.println(">>> Negative test to add an EventClient" +
95.190 + " with a non EventForwarder object.");
95.191 + try {
95.192 + MyFetchingEventForwarder.shared.setAgain();
95.193 +
95.194 + System.out.println(
95.195 + ">>> FetchingTest-test: FAILED! No expected exception" +
95.196 + "when setting the list after the forwarder started.");
95.197 +
95.198 + return false;
95.199 + } catch (IllegalStateException ise) {
95.200 + // OK
95.201 + System.out.println(
95.202 + ">>> FetchingTest-test: Got expected exception: " + ise);
95.203 + }
95.204 +
95.205 + eventClient.close();
95.206 +
95.207 + try {
95.208 + fer = new FetchingEventRelay(
95.209 + EventClientDelegate.getProxy(client),
95.210 + 1000, 1000L, 1000, null,
95.211 + Object.class.getName(),
95.212 + null, null);
95.213 +
95.214 + eventClient = new EventClient(
95.215 + EventClientDelegate.getProxy(client), fer, null, null, 10000);
95.216 +
95.217 + System.out.println(
95.218 + ">>> FetchingTest-test: FAILED! No expected exception" +
95.219 + "when creating an illegal EventForwarder");
95.220 + } catch (IllegalArgumentException iae) {
95.221 + // OK
95.222 + // iae.printStackTrace();
95.223 + }
95.224 +
95.225 + return true;
95.226 + }
95.227 +
95.228 + private static class Listener implements NotificationListener {
95.229 + public void handleNotification(Notification notif, Object handback) {
95.230 + synchronized(this) {
95.231 + if (++received >= sendNB) {
95.232 + this.notify();
95.233 + }
95.234 + }
95.235 +
95.236 + //System.out.println(">>> FetchingTest-Listener: received = "+received);
95.237 + }
95.238 +
95.239 + public int waitNotif(long timeout) throws Exception {
95.240 + synchronized(this) {
95.241 + long stopTime = System.currentTimeMillis() + timeout;
95.242 + long toWait = timeout;
95.243 + while (toWait > 0 && received < sendNB) {
95.244 + this.wait(toWait);
95.245 + toWait = stopTime - System.currentTimeMillis();
95.246 + }
95.247 + }
95.248 +
95.249 + return received;
95.250 + }
95.251 +
95.252 + public static int received = 0;
95.253 + }
95.254 +
95.255 + public static class NotificationEmitter extends NotificationBroadcasterSupport
95.256 + implements NotificationEmitterMBean {
95.257 +
95.258 + public void sendNotifications(Integer nb) {
95.259 + System.out.println(
95.260 + ">>> FetchingTest-NotificationEmitter-sendNotifications: "+nb);
95.261 + Notification notif;
95.262 + for (int i=1; i<=nb.intValue(); i++) {
95.263 + notif = new Notification(myType, this, count++);
95.264 + sendNotification(notif);
95.265 + }
95.266 + }
95.267 + }
95.268 +
95.269 + public interface NotificationEmitterMBean {
95.270 + public void sendNotifications(Integer nb);
95.271 + }
95.272 +
95.273 +
95.274 +
95.275 + private static int sendNB = 20;
95.276 + private static int count = 0;
95.277 +
95.278 + private static final String myType = "notification.my_notification";
95.279 +}
96.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
96.2 +++ b/test/javax/management/eventService/LeaseManagerDeadlockTest.java Thu Aug 21 09:55:18 2008 -0700
96.3 @@ -0,0 +1,96 @@
96.4 +/*
96.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
96.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
96.7 + *
96.8 + * This code is free software; you can redistribute it and/or modify it
96.9 + * under the terms of the GNU General Public License version 2 only, as
96.10 + * published by the Free Software Foundation.
96.11 + *
96.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
96.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
96.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
96.15 + * version 2 for more details (a copy is included in the LICENSE file that
96.16 + * accompanied this code).
96.17 + *
96.18 + * You should have received a copy of the GNU General Public License version
96.19 + * 2 along with this work; if not, write to the Free Software Foundation,
96.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
96.21 + *
96.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
96.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
96.24 + * have any questions.
96.25 + */
96.26 +
96.27 +/*
96.28 + * @test
96.29 + * @bug 6717789
96.30 + * @summary Check that a lock is not held when a LeaseManager expires.
96.31 + * @author Eamonn McManus
96.32 + */
96.33 +
96.34 +import com.sun.jmx.event.LeaseManager;
96.35 +import java.lang.management.ManagementFactory;
96.36 +import java.lang.management.ThreadMXBean;
96.37 +import java.util.Arrays;
96.38 +import java.util.concurrent.Semaphore;
96.39 +import java.util.concurrent.TimeUnit;
96.40 +
96.41 +public class LeaseManagerDeadlockTest {
96.42 + public static String failure;
96.43 + public static LeaseManager leaseManager;
96.44 + public static Semaphore callbackThreadCompleted = new Semaphore(0);
96.45 + public static Object lock = new Object();
96.46 +
96.47 + public static Runnable triggerDeadlock = new Runnable() {
96.48 + public void run() {
96.49 + Runnable pingLeaseManager = new Runnable() {
96.50 + public void run() {
96.51 + System.out.println("Ping thread starts");
96.52 + synchronized (lock) {
96.53 + leaseManager.lease(1);
96.54 + }
96.55 + System.out.println("Ping thread completes");
96.56 + }
96.57 + };
96.58 + Thread t = new Thread(pingLeaseManager);
96.59 + t.start();
96.60 + try {
96.61 + Thread.sleep(10); // enough time for ping thread to grab lock
96.62 + synchronized (lock) {
96.63 + t.join();
96.64 + }
96.65 + } catch (InterruptedException e) {
96.66 + fail(e.toString());
96.67 + }
96.68 + System.out.println("Callback thread completes");
96.69 + callbackThreadCompleted.release();
96.70 + }
96.71 + };
96.72 +
96.73 + public static void main(String[] args) throws Exception {
96.74 + // Also test that we can shorten the lease from its initial value.
96.75 + leaseManager = new LeaseManager(triggerDeadlock, 1000000);
96.76 + leaseManager.lease(1L);
96.77 +
96.78 + boolean callbackRan =
96.79 + callbackThreadCompleted.tryAcquire(3, TimeUnit.SECONDS);
96.80 +
96.81 + if (!callbackRan) {
96.82 + fail("Callback did not complete - probable deadlock");
96.83 + ThreadMXBean threads = ManagementFactory.getThreadMXBean();
96.84 + System.out.println(Arrays.toString(threads.findDeadlockedThreads()));
96.85 + System.out.println("PRESS RETURN");
96.86 + System.in.read();
96.87 + }
96.88 +
96.89 + if (failure == null)
96.90 + System.out.println("TEST PASSED");
96.91 + else
96.92 + throw new Exception("TEST FAILED: " + failure);
96.93 + }
96.94 +
96.95 + public static void fail(String why) {
96.96 + System.out.println("TEST FAILS: " + why);
96.97 + failure = why;
96.98 + }
96.99 +}
97.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
97.2 +++ b/test/javax/management/eventService/LeaseTest.java Thu Aug 21 09:55:18 2008 -0700
97.3 @@ -0,0 +1,361 @@
97.4 +/*
97.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
97.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
97.7 + *
97.8 + * This code is free software; you can redistribute it and/or modify it
97.9 + * under the terms of the GNU General Public License version 2 only, as
97.10 + * published by the Free Software Foundation.
97.11 + *
97.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
97.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
97.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
97.15 + * version 2 for more details (a copy is included in the LICENSE file that
97.16 + * accompanied this code).
97.17 + *
97.18 + * You should have received a copy of the GNU General Public License version
97.19 + * 2 along with this work; if not, write to the Free Software Foundation,
97.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
97.21 + *
97.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
97.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
97.24 + * have any questions.
97.25 + */
97.26 +
97.27 +/*
97.28 + * @test LeaseTest.java 1.6 08/01/22
97.29 + * @bug 5108776
97.30 + * @summary Basic test for Event service leasing.
97.31 + * @author Shanliang JIANG
97.32 + * @run clean LeaseTest
97.33 + * @run build LeaseTest
97.34 + * @run main LeaseTest
97.35 + */
97.36 +
97.37 +
97.38 +import java.io.IOException;
97.39 +import java.util.ArrayList;
97.40 +import java.util.List;
97.41 +import javax.management.ListenerNotFoundException;
97.42 +import javax.management.MBeanNotificationInfo;
97.43 +import javax.management.MBeanServer;
97.44 +import javax.management.MBeanServerFactory;
97.45 +import javax.management.Notification;
97.46 +import javax.management.NotificationBroadcasterSupport;
97.47 +import javax.management.NotificationFilter;
97.48 +import javax.management.NotificationListener;
97.49 +import javax.management.ObjectName;
97.50 +import javax.management.event.EventClient;
97.51 +import javax.management.event.EventClientDelegate;
97.52 +import javax.management.event.EventClientDelegateMBean;
97.53 +import javax.management.event.EventClientNotFoundException;
97.54 +import javax.management.event.FetchingEventRelay;
97.55 +import javax.management.remote.JMXConnector;
97.56 +import javax.management.remote.JMXConnectorFactory;
97.57 +import javax.management.remote.JMXConnectorServer;
97.58 +import javax.management.remote.JMXConnectorServerFactory;
97.59 +import javax.management.remote.JMXServiceURL;
97.60 +
97.61 +public class LeaseTest {
97.62 +
97.63 + private static MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer();
97.64 + private static List<Notification> notifList = new ArrayList<Notification>();
97.65 + private static ObjectName emitter;
97.66 + private static NotificationEmitter emitterImpl;
97.67 + private static JMXServiceURL url;
97.68 + private static JMXConnectorServer server;
97.69 + private static JMXConnector conn;
97.70 + private static Listener listener = new Listener();
97.71 +
97.72 + private static long leaseTime = 100;
97.73 + private static final int multiple = 5;
97.74 + private static final long bigWaiting = 6000;
97.75 +
97.76 + public static void main(String[] args) throws Exception {
97.77 + System.out.println(">>> Test the event service lease");
97.78 +
97.79 + // for 1.5
97.80 + if (System.getProperty("java.version").startsWith("1.5") &&
97.81 + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
97.82 + System.out.print("Working on "+System.getProperty("java.version")+
97.83 + " register "+EventClientDelegateMBean.OBJECT_NAME);
97.84 +
97.85 + mbeanServer.registerMBean(EventClientDelegate.
97.86 + getEventClientDelegate(mbeanServer),
97.87 + EventClientDelegateMBean.OBJECT_NAME);
97.88 + }
97.89 +
97.90 + System.setProperty("com.sun.event.lease.time",
97.91 + String.valueOf(leaseTime));
97.92 + emitter = new ObjectName("Default:name=NotificationEmitter");
97.93 + emitterImpl = new NotificationEmitter();
97.94 + mbeanServer.registerMBean(emitterImpl, emitter);
97.95 +
97.96 + String[] types = new String[]{"PushingEventRelay", "FetchingEventRelay"};
97.97 + String[] protos = new String[]{"rmi", "iiop", "jmxmp"};
97.98 + for (String prot : protos) {
97.99 + url = new JMXServiceURL(prot, null, 0);
97.100 +
97.101 + try {
97.102 + server =
97.103 + JMXConnectorServerFactory.newJMXConnectorServer(url,
97.104 + null, mbeanServer);
97.105 + server.start();
97.106 + } catch (Exception e) {
97.107 + System.out.println(">>> Skip "+prot+", not support.");
97.108 + continue;
97.109 + }
97.110 +
97.111 + url = server.getAddress();
97.112 +
97.113 + try {
97.114 + for (String type: types) {
97.115 + test(type);
97.116 + }
97.117 + } finally {
97.118 + server.stop();
97.119 + }
97.120 + }
97.121 + }
97.122 +
97.123 + private static void test(String type) throws Exception {
97.124 + System.out.println("\n\n>>> Testing "+type+" on "+url+" ...");
97.125 + newConn();
97.126 + EventClient ec = newEventClient(type);
97.127 +
97.128 + ec.addNotificationListener(emitter,
97.129 + listener, null, null);
97.130 +
97.131 + System.out.println(">>> Send a notification and should receive it.");
97.132 + emitterImpl.sendNotif(++counter);
97.133 +
97.134 + if (!waitNotif(bigWaiting, counter)) {
97.135 + throw new RuntimeException(">>> Failed to receive notif.");
97.136 + }
97.137 +
97.138 + System.out.println(">>> Sleep 3 times of requested lease time.");
97.139 + Thread.sleep(leaseTime*3);
97.140 + System.out.println(">>> Send again a notification and should receive it.");
97.141 + emitterImpl.sendNotif(++counter);
97.142 +
97.143 + if (!waitNotif(bigWaiting, counter)) {
97.144 + throw new RuntimeException(">>> Failed to receive notif.");
97.145 + }
97.146 +
97.147 + System.out.println(">>> Close the client connection: "+
97.148 + conn.getConnectionId());
97.149 + conn.close();
97.150 +
97.151 + System.out.println(">>> Waiting lease timeout to do clean.");
97.152 +
97.153 + if (!emitterImpl.waitingClean(leaseTime*multiple)) {
97.154 + throw new RuntimeException(
97.155 + ">>> The event lease failed to do clean: "+
97.156 + emitterImpl.listenerSize);
97.157 + } else {
97.158 + System.out.println(">>> The listener has been removed.");
97.159 + }
97.160 +
97.161 + // Check that the client id has indeed been removed, by trying to
97.162 + // remove it again, which should fail.
97.163 + newConn();
97.164 + try {
97.165 + EventClientDelegateMBean proxy =
97.166 + EventClientDelegate.getProxy(conn.getMBeanServerConnection());
97.167 + proxy.removeClient(ec.getEventRelay().getClientId());
97.168 +
97.169 + throw new RuntimeException(
97.170 + ">>> The client id is not removed.");
97.171 + } catch (EventClientNotFoundException ecnfe) {
97.172 + // OK
97.173 + System.out.println(">>> The client id has been removed.");
97.174 + }
97.175 + conn.close();
97.176 +
97.177 + System.out.println(">>> Reconnect to the server.");
97.178 + newConn();
97.179 +
97.180 + System.out.println(">>> Create a new EventClient and add the listeners" +
97.181 + " in the failed EventClient into new EventClient");
97.182 + EventClient newEC = newEventClient(type);
97.183 + newEC.addListeners(ec.getListeners());
97.184 + // We expect ec.close() to get IOException because we closed the
97.185 + // underlying connection.
97.186 + try {
97.187 + ec.close();
97.188 + throw new RuntimeException(">>> EventClient.close did not throw " +
97.189 + "expected IOException");
97.190 + } catch (IOException e) {
97.191 + System.out.println(">>> EventClient.close threw expected exception: " + e);
97.192 + }
97.193 +
97.194 + emitterImpl.sendNotif(++counter);
97.195 +
97.196 + if (!waitNotif(bigWaiting, counter)) {
97.197 + throw new RuntimeException(">>> The event client failed to add " +
97.198 + "all old registered listeners after re-connection.");
97.199 + } else {
97.200 + System.out.println(">>> Successfully received notification from" +
97.201 + " new EventClient.");
97.202 + }
97.203 +
97.204 + System.out.println(">>> Clean the failed EventClient.");
97.205 + ec.close();
97.206 + if (ec.getListeners().size() != 0) {
97.207 + throw new RuntimeException(">>> The event client fails to do clean.");
97.208 + }
97.209 +
97.210 + System.out.println(">>> Clean the new EventClient.");
97.211 + newEC.close();
97.212 + if (newEC.getListeners().size() != 0) {
97.213 + throw new RuntimeException(">>> The event client fails to do clean.");
97.214 + }
97.215 +
97.216 + conn.close();
97.217 + System.out.println(">>> Testing "+type+" on "+url+" ... done");
97.218 + }
97.219 +
97.220 + private static boolean waitNotif(long time, int sequenceNumber)
97.221 + throws Exception {
97.222 + synchronized(notifList) {
97.223 + if (search(sequenceNumber)) {
97.224 + return true;
97.225 + }
97.226 +
97.227 + long stopTime = System.currentTimeMillis() + time;
97.228 + long toWait = time;
97.229 + while (toWait > 0) {
97.230 + notifList.wait(toWait);
97.231 +
97.232 + if (search(sequenceNumber)) {
97.233 + return true;
97.234 + }
97.235 +
97.236 + toWait = stopTime - System.currentTimeMillis();
97.237 + }
97.238 +
97.239 + return false;
97.240 + }
97.241 + }
97.242 +
97.243 + private static boolean search(int sequenceNumber) {
97.244 + while(notifList.size() > 0) {
97.245 + Notification n = notifList.remove(0);
97.246 + if (n.getSequenceNumber() == sequenceNumber) {
97.247 + return true;
97.248 + }
97.249 + }
97.250 +
97.251 + return false;
97.252 + }
97.253 +
97.254 +//--------------------------
97.255 +// private classes
97.256 +//--------------------------
97.257 +
97.258 + private static class Listener implements NotificationListener {
97.259 + public void handleNotification(Notification notif, Object handback) {
97.260 + synchronized (notifList) {
97.261 + notifList.add(notif);
97.262 + notifList.notify();
97.263 + }
97.264 + }
97.265 + }
97.266 +
97.267 + public static class NotificationEmitter extends NotificationBroadcasterSupport
97.268 + implements NotificationEmitterMBean {
97.269 +
97.270 + public MBeanNotificationInfo[] getNotificationInfo() {
97.271 + final String[] ntfTypes = {myType};
97.272 +
97.273 + final MBeanNotificationInfo[] ntfInfoArray = {
97.274 + new MBeanNotificationInfo(ntfTypes,
97.275 + "javax.management.Notification",
97.276 + "Notifications sent by the NotificationEmitter")};
97.277 +
97.278 + return ntfInfoArray;
97.279 + }
97.280 +
97.281 + /**
97.282 + * Send Notification objects.
97.283 + *
97.284 + * @param nb The number of notifications to send
97.285 + */
97.286 + public void sendNotif(int sequenceNumber) {
97.287 + Notification notif = new Notification(myType, this, sequenceNumber);
97.288 + sendNotification(notif);
97.289 + }
97.290 +
97.291 + public void addNotificationListener(NotificationListener listener,
97.292 + NotificationFilter filter, Object handback) {
97.293 + super.addNotificationListener(listener, filter, handback);
97.294 +
97.295 + listenerSize++;
97.296 + }
97.297 +
97.298 + public void removeNotificationListener(NotificationListener listener)
97.299 + throws ListenerNotFoundException {
97.300 + super.removeNotificationListener(listener);
97.301 + listenerSize--;
97.302 +
97.303 + synchronized(this) {
97.304 + if (listenerSize == 0) {
97.305 + this.notifyAll();
97.306 + }
97.307 + }
97.308 + }
97.309 +
97.310 + public void removeNotificationListener(NotificationListener listener,
97.311 + NotificationFilter filter, Object handback)
97.312 + throws ListenerNotFoundException {
97.313 + super.removeNotificationListener(listener, filter, handback);
97.314 + listenerSize--;
97.315 +
97.316 + synchronized(this) {
97.317 + if (listenerSize == 0) {
97.318 + this.notifyAll();
97.319 + }
97.320 + }
97.321 + }
97.322 +
97.323 + public boolean waitingClean(long timeout) throws Exception {
97.324 + synchronized(this) {
97.325 + long stopTime = System.currentTimeMillis() + timeout;
97.326 + long toWait = timeout;
97.327 + while (listenerSize != 0 && toWait > 0) {
97.328 + this.wait(toWait);
97.329 + toWait = stopTime - System.currentTimeMillis();
97.330 + }
97.331 + }
97.332 +
97.333 + return listenerSize == 0;
97.334 + }
97.335 +
97.336 + public int listenerSize = 0;
97.337 +
97.338 + private final String myType = "notification.my_notification";
97.339 + }
97.340 +
97.341 + public interface NotificationEmitterMBean {
97.342 + public void sendNotif(int sequenceNumber);
97.343 + }
97.344 +
97.345 + private static void newConn() throws IOException {
97.346 + conn = JMXConnectorFactory.connect(url);
97.347 + }
97.348 +
97.349 + private static EventClient newEventClient(String type) throws Exception {
97.350 + EventClientDelegateMBean proxy =
97.351 + EventClientDelegate.getProxy(conn.getMBeanServerConnection());
97.352 + if (type.equals("PushingEventRelay")) {
97.353 + return new EventClient(proxy,
97.354 + new FetchingEventRelay(proxy), null, null, leaseTime);
97.355 + } else if (type.equals("FetchingEventRelay")) {
97.356 + return new EventClient(proxy,
97.357 + new FetchingEventRelay(proxy), null, null, leaseTime);
97.358 + } else {
97.359 + throw new RuntimeException("Wrong event client type: "+type);
97.360 + }
97.361 + }
97.362 +
97.363 + private static int counter = 0;
97.364 +}
98.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
98.2 +++ b/test/javax/management/eventService/ListenerTest.java Thu Aug 21 09:55:18 2008 -0700
98.3 @@ -0,0 +1,224 @@
98.4 +/*
98.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
98.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
98.7 + *
98.8 + * This code is free software; you can redistribute it and/or modify it
98.9 + * under the terms of the GNU General Public License version 2 only, as
98.10 + * published by the Free Software Foundation.
98.11 + *
98.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
98.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
98.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
98.15 + * version 2 for more details (a copy is included in the LICENSE file that
98.16 + * accompanied this code).
98.17 + *
98.18 + * You should have received a copy of the GNU General Public License version
98.19 + * 2 along with this work; if not, write to the Free Software Foundation,
98.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
98.21 + *
98.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
98.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
98.24 + * have any questions.
98.25 + */
98.26 +
98.27 +/*
98.28 + * @test ListenerTest.java 1.7 08/01/22
98.29 + * @bug 5108776
98.30 + * @summary Basic test for EventClient.
98.31 + * @author Shanliang JIANG
98.32 + * @run clean ListenerTest
98.33 + * @run build ListenerTest
98.34 + * @run main ListenerTest
98.35 + */
98.36 +
98.37 +import javax.management.MBeanNotificationInfo;
98.38 +import javax.management.MBeanServer;
98.39 +import javax.management.MBeanServerConnection;
98.40 +import javax.management.MBeanServerFactory;
98.41 +import javax.management.Notification;
98.42 +import javax.management.NotificationBroadcasterSupport;
98.43 +import javax.management.NotificationListener;
98.44 +import javax.management.ObjectName;
98.45 +import javax.management.event.*;
98.46 +import javax.management.remote.JMXConnector;
98.47 +import javax.management.remote.JMXConnectorFactory;
98.48 +import javax.management.remote.JMXConnectorServer;
98.49 +import javax.management.remote.JMXConnectorServerFactory;
98.50 +import javax.management.remote.JMXServiceURL;
98.51 +
98.52 +/**
98.53 + *
98.54 + */
98.55 +public class ListenerTest {
98.56 + private static MBeanServer mbeanServer;
98.57 + private static ObjectName emitter;
98.58 + private static JMXServiceURL url;
98.59 + private static JMXConnectorServer server;
98.60 + private static JMXConnector conn;
98.61 + private static MBeanServerConnection client;
98.62 +
98.63 + /**
98.64 + * @param args the command line arguments
98.65 + */
98.66 + public static void main(String[] args) throws Exception {
98.67 +
98.68 + System.out.println(">>> ListenerTest-main basic tests ...");
98.69 + mbeanServer = MBeanServerFactory.createMBeanServer();
98.70 +
98.71 + // for 1.5
98.72 + if (System.getProperty("java.version").startsWith("1.5") &&
98.73 + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
98.74 + System.out.print("Working on "+System.getProperty("java.version")+
98.75 + " register "+EventClientDelegateMBean.OBJECT_NAME);
98.76 +
98.77 + mbeanServer.registerMBean(EventClientDelegate.
98.78 + getEventClientDelegate(mbeanServer),
98.79 + EventClientDelegateMBean.OBJECT_NAME);
98.80 + }
98.81 +
98.82 + emitter = new ObjectName("Default:name=NotificationEmitter");
98.83 +
98.84 + url = new JMXServiceURL("rmi", null, 0) ;
98.85 + server =
98.86 + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer);
98.87 + server.start();
98.88 +
98.89 + url = server.getAddress();
98.90 + conn = JMXConnectorFactory.connect(url, null);
98.91 + client = conn.getMBeanServerConnection();
98.92 +
98.93 + mbeanServer.registerMBean(new NotificationEmitter(), emitter);
98.94 +
98.95 + boolean succeed;
98.96 +
98.97 + System.out.println(">>> ListenerTest-main: using the fetching EventRelay...");
98.98 + succeed = test(new EventClient(client));
98.99 +
98.100 + System.out.println(">>> ListenerTest-main: using the pushing EventRelay...");
98.101 + EventClientDelegateMBean ecd = EventClientDelegate.getProxy(client);
98.102 + succeed &= test(new EventClient(ecd,
98.103 + new RMIPushEventRelay(ecd),
98.104 + null, null,
98.105 + EventClient.DEFAULT_LEASE_TIMEOUT));
98.106 +
98.107 + conn.close();
98.108 + server.stop();
98.109 +
98.110 + if (succeed) {
98.111 + System.out.println(">>> ListenerTest-main: PASSED!");
98.112 + } else {
98.113 + System.out.println("\n>>> ListenerTest-main: FAILED!");
98.114 + System.exit(1);
98.115 + }
98.116 + }
98.117 +
98.118 + public static boolean test(EventClient efClient) throws Exception {
98.119 + // add listener from the client side
98.120 + Listener listener = new Listener();
98.121 + efClient.addNotificationListener(emitter, listener, null, null);
98.122 +
98.123 + // ask to send notifs
98.124 + Object[] params = new Object[] {new Integer(sendNB)};
98.125 + String[] signatures = new String[] {"java.lang.Integer"};
98.126 + client.invoke(emitter, "sendNotifications", params, signatures);
98.127 +
98.128 + // waiting
98.129 + long toWait = 6000;
98.130 + long stopTime = System.currentTimeMillis() + toWait;
98.131 +
98.132 + synchronized(listener) {
98.133 + while(listener.received < sendNB && toWait > 0) {
98.134 + listener.wait(toWait);
98.135 + toWait = stopTime - System.currentTimeMillis();
98.136 + }
98.137 + }
98.138 +
98.139 + // clean
98.140 + efClient.removeNotificationListener(emitter, listener, null, null);
98.141 + efClient.close();
98.142 +
98.143 + if (listener.received != sendNB) {
98.144 + System.out.println(">>> ListenerTest-test: FAILED! Expected to receive "+sendNB+", but got "+listener.received);
98.145 +
98.146 + return false;
98.147 + } else if (listener.seqErr > 0) {
98.148 + System.out.println(">>> ListenerTest-test: FAILED! The receiving sequence is not correct.");
98.149 +
98.150 + return false;
98.151 + } else {
98.152 + System.out.println(">>> ListenerTest-test: got all expected "+listener.received);
98.153 + return true;
98.154 + }
98.155 + }
98.156 +
98.157 + private static class Listener implements NotificationListener {
98.158 + public int received = 0;
98.159 + public int seqErr = 0;
98.160 +
98.161 + private long lastSeq = -1;
98.162 +
98.163 + public void handleNotification(Notification notif, Object handback) {
98.164 + if (!myType.equals(notif.getType())) {
98.165 + System.out.println(">>> EventManagerTest-Listener: got unexpected notif: "+notif);
98.166 + System.exit(1);
98.167 + }
98.168 +
98.169 + if (lastSeq == -1) {
98.170 + lastSeq = notif.getSequenceNumber();
98.171 + } else if (notif.getSequenceNumber() - lastSeq++ != 1) {
98.172 + seqErr++;
98.173 + }
98.174 +
98.175 + System.out.println(">>> ListenerTest-Listener: got notif "+notif.getSequenceNumber());
98.176 +
98.177 + synchronized(this) {
98.178 + if (++received >= sendNB) {
98.179 + this.notify();
98.180 + }
98.181 + }
98.182 +
98.183 + System.out.println(">>> ListenerTest-Listener: received = "+received);
98.184 + }
98.185 + }
98.186 +
98.187 + public static class NotificationEmitter extends NotificationBroadcasterSupport
98.188 + implements NotificationEmitterMBean {
98.189 +
98.190 + public MBeanNotificationInfo[] getNotificationInfo() {
98.191 + final String[] ntfTypes = {myType};
98.192 +
98.193 + final MBeanNotificationInfo[] ntfInfoArray = {
98.194 + new MBeanNotificationInfo(ntfTypes,
98.195 + "javax.management.Notification",
98.196 + "Notifications sent by the NotificationEmitter")};
98.197 +
98.198 + return ntfInfoArray;
98.199 + }
98.200 +
98.201 + /**
98.202 + * Send Notification objects.
98.203 + *
98.204 + * @param nb The number of notifications to send
98.205 + */
98.206 + public void sendNotifications(Integer nb) {
98.207 + Notification notif;
98.208 + for (int i=1; i<=nb.intValue(); i++) {
98.209 + notif = new Notification(myType, this, count++);
98.210 + //System.out.println(">>> ListenerTest-NotificationEmitter-sendNotifications: "+i);
98.211 +
98.212 + sendNotification(notif);
98.213 + }
98.214 + }
98.215 +
98.216 +
98.217 + }
98.218 +
98.219 + public interface NotificationEmitterMBean {
98.220 + public void sendNotifications(Integer nb);
98.221 + }
98.222 +
98.223 + private static int sendNB = 20;
98.224 + private static int count = 0;
98.225 +
98.226 + private static final String myType = "notification.my_notification";
98.227 +}
99.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
99.2 +++ b/test/javax/management/eventService/MyFetchingEventForwarder.java Thu Aug 21 09:55:18 2008 -0700
99.3 @@ -0,0 +1,53 @@
99.4 +/*
99.5 + * MyList.java
99.6 + *
99.7 + * Created on Oct 23, 2007, 2:45:57 PM
99.8 + *
99.9 + * To change this template, choose Tools | Templates
99.10 + * and open the template in the editor.
99.11 + */
99.12 +
99.13 +/**
99.14 + *
99.15 + * @author sjiang
99.16 + */
99.17 +
99.18 +import java.io.IOException;
99.19 +import java.util.ArrayList;
99.20 +import javax.management.event.FetchingEventForwarder;
99.21 +
99.22 +public class MyFetchingEventForwarder extends FetchingEventForwarder {
99.23 +
99.24 + public MyFetchingEventForwarder() {
99.25 + super(1000);
99.26 + shared = this;
99.27 + setList(myList);
99.28 + }
99.29 +
99.30 + public void setAgain() {
99.31 + setList(myList);
99.32 + }
99.33 +
99.34 + public void setClientId(String clientId) throws IOException {
99.35 + used = true;
99.36 + super.setClientId(clientId);
99.37 + }
99.38 +
99.39 + public boolean isUsed() {
99.40 + return used;
99.41 + }
99.42 +
99.43 + private class MyList<TargetedNotification>
99.44 + extends ArrayList<TargetedNotification> {
99.45 +
99.46 + public boolean add(TargetedNotification e) {
99.47 + used = true;
99.48 +
99.49 + return super.add(e);
99.50 + }
99.51 + }
99.52 +
99.53 + public MyList myList = new MyList();
99.54 + public static MyFetchingEventForwarder shared;
99.55 + private boolean used = false;
99.56 +}
100.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
100.2 +++ b/test/javax/management/eventService/NotSerializableNotifTest.java Thu Aug 21 09:55:18 2008 -0700
100.3 @@ -0,0 +1,227 @@
100.4 +/*
100.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
100.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
100.7 + *
100.8 + * This code is free software; you can redistribute it and/or modify it
100.9 + * under the terms of the GNU General Public License version 2 only, as
100.10 + * published by the Free Software Foundation.
100.11 + *
100.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
100.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
100.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
100.15 + * version 2 for more details (a copy is included in the LICENSE file that
100.16 + * accompanied this code).
100.17 + *
100.18 + * You should have received a copy of the GNU General Public License version
100.19 + * 2 along with this work; if not, write to the Free Software Foundation,
100.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
100.21 + *
100.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
100.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
100.24 + * have any questions.
100.25 + */
100.26 +
100.27 +
100.28 +/*
100.29 + * @test NotSerializableNotifTest.java 1.5 08/01/22
100.30 + * @bug 5108776
100.31 + * @summary Basic test for EventClient.
100.32 + * @author Shanliang JIANG
100.33 + * @run clean NotSerializableNotifTest
100.34 + * @run build NotSerializableNotifTest
100.35 + * @run main NotSerializableNotifTest
100.36 + */
100.37 +
100.38 +
100.39 +// JMX imports
100.40 +//
100.41 +import javax.management.* ;
100.42 +import javax.management.event.EventClient;
100.43 +import javax.management.event.EventClientDelegate;
100.44 +import javax.management.event.EventClientDelegateMBean;
100.45 +import javax.management.event.EventRelay;
100.46 +import javax.management.event.FetchingEventRelay;
100.47 +
100.48 +import javax.management.remote.*;
100.49 +import javax.management.remote.JMXServiceURL;
100.50 +
100.51 +public class NotSerializableNotifTest {
100.52 + private static MBeanServer mbeanServer =
100.53 + MBeanServerFactory.createMBeanServer();
100.54 + private static ObjectName emitter;
100.55 + private static int port = 2468;
100.56 +
100.57 + private static String[] protocols;
100.58 +
100.59 + private static final int sentNotifs = 50;
100.60 +
100.61 + public static void main(String[] args) throws Exception {
100.62 + System.out.println(">>> Test to send a not serializable notification");
100.63 +
100.64 + // for 1.5
100.65 + if (System.getProperty("java.version").startsWith("1.5") &&
100.66 + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
100.67 + System.out.print("Working on "+System.getProperty("java.version")+
100.68 + " register "+EventClientDelegateMBean.OBJECT_NAME);
100.69 +
100.70 + mbeanServer.registerMBean(EventClientDelegate.
100.71 + getEventClientDelegate(mbeanServer),
100.72 + EventClientDelegateMBean.OBJECT_NAME);
100.73 + }
100.74 +
100.75 + NotificationEmitter nm = new NotificationEmitter();
100.76 + emitter = new ObjectName("Default:name=NotificationEmitter");
100.77 + mbeanServer.registerMBean(nm, emitter);
100.78 + String proto = "rmi";
100.79 +
100.80 + System.out.println(">>> Test for protocol " + proto);
100.81 +
100.82 + JMXServiceURL url = new JMXServiceURL(proto, null, 0);
100.83 +
100.84 + JMXConnectorServer server =
100.85 + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer);
100.86 +
100.87 + server.start();
100.88 +
100.89 + url = server.getAddress();
100.90 + JMXConnector conn = JMXConnectorFactory.connect(url, null);
100.91 + MBeanServerConnection client = conn.getMBeanServerConnection();
100.92 +
100.93 + EventClientDelegateMBean ecd = EventClientDelegate.getProxy(client);
100.94 + EventRelay eventRelay = new FetchingEventRelay(
100.95 + ecd,
100.96 + FetchingEventRelay.DEFAULT_BUFFER_SIZE,
100.97 + 10,
100.98 + FetchingEventRelay.DEFAULT_MAX_NOTIFICATIONS,
100.99 + null);
100.100 + EventClient ec = new EventClient(ecd, eventRelay, null, null,
100.101 + EventClient.DEFAULT_LEASE_TIMEOUT);
100.102 +
100.103 + // add listener from the client side
100.104 + Listener listener = new Listener();
100.105 + ec.addNotificationListener(emitter, listener, null, null);
100.106 +
100.107 + LostListener lostListener = new LostListener();
100.108 + ec.addEventClientListener(lostListener, null, null);
100.109 +
100.110 + // ask to send one not serializable notif
100.111 + System.out.println(">>> sending not serializable notifs ...");
100.112 +
100.113 + Object[] params = new Object[] {new Integer(sentNotifs)};
100.114 + String[] signatures = new String[] {"java.lang.Integer"};
100.115 + client.invoke(emitter, "sendNotserializableNotifs", params, signatures);
100.116 +
100.117 +// nm.sendNotserializableNotifs(sentNotifs);
100.118 +// nm.sendNotifications(1);
100.119 +
100.120 + // waiting
100.121 + synchronized(lostListener) {
100.122 + if (lostListener.lostCount != sentNotifs) {
100.123 + lostListener.wait(6000);
100.124 + }
100.125 + }
100.126 +
100.127 + Thread.sleep(100);
100.128 +
100.129 + if (lostListener.lostCount != sentNotifs) {
100.130 + System.out.println(">>> FAILED. Expected "+sentNotifs+", but got "+lostListener.lostCount);
100.131 + System.exit(1);
100.132 + }
100.133 +
100.134 + System.out.println(">>> Passed.");
100.135 +
100.136 + ec.close();
100.137 + conn.close();
100.138 + server.stop();
100.139 + }
100.140 +
100.141 +
100.142 +//--------------------------
100.143 +// private classes
100.144 +//--------------------------
100.145 + private static class Listener implements NotificationListener {
100.146 + public void handleNotification(Notification n, Object handback) {
100.147 + System.out.println(">>> Listener: receive: "+n);
100.148 + }
100.149 + }
100.150 +
100.151 +
100.152 + private static class LostListener implements NotificationListener {
100.153 + public void handleNotification(Notification n, Object handback) {
100.154 + if (!EventClient.NOTIFS_LOST.equals(n.getType())) {
100.155 + return;
100.156 + }
100.157 +
100.158 + if (!(n.getUserData() instanceof Long)) {
100.159 + System.out.println(">>> Listener: JMXConnectionNotification userData " +
100.160 + "not a Long: " + n.getUserData());
100.161 + System.exit(1);
100.162 + } else {
100.163 + int lost = ((Long) n.getUserData()).intValue();
100.164 + lostCount += lost;
100.165 + if (lostCount >= sentNotifs) {
100.166 + synchronized(this) {
100.167 + this.notifyAll();
100.168 + }
100.169 + }
100.170 + }
100.171 +
100.172 + }
100.173 +
100.174 +
100.175 + private int lostCount = 0;
100.176 + }
100.177 +
100.178 + public static class NotificationEmitter extends NotificationBroadcasterSupport
100.179 + implements NotificationEmitterMBean {
100.180 +
100.181 + public MBeanNotificationInfo[] getNotificationInfo() {
100.182 + final String[] ntfTypes = {myType};
100.183 +
100.184 + final MBeanNotificationInfo[] ntfInfoArray = {
100.185 + new MBeanNotificationInfo(ntfTypes,
100.186 + "javax.management.Notification",
100.187 + "Notifications sent by the NotificationEmitter")};
100.188 +
100.189 + return ntfInfoArray;
100.190 + }
100.191 +
100.192 + /**
100.193 + * Send not serializable Notifications.
100.194 + *
100.195 + * @param nb The number of notifications to send
100.196 + */
100.197 + public void sendNotserializableNotifs(Integer nb) {
100.198 +
100.199 + Notification notif;
100.200 + for (int i=1; i<=nb.intValue(); i++) {
100.201 + notif = new Notification(myType, this, i);
100.202 +
100.203 + notif.setUserData(new Object());
100.204 + sendNotification(notif);
100.205 + }
100.206 + }
100.207 +
100.208 + /**
100.209 + * Send Notification objects.
100.210 + *
100.211 + * @param nb The number of notifications to send
100.212 + */
100.213 + public void sendNotifications(Integer nb) {
100.214 + Notification notif;
100.215 + for (int i=1; i<=nb.intValue(); i++) {
100.216 + notif = new Notification(myType, this, i);
100.217 +
100.218 + sendNotification(notif);
100.219 + }
100.220 + }
100.221 +
100.222 + private final String myType = "notification.my_notification";
100.223 + }
100.224 +
100.225 + public interface NotificationEmitterMBean {
100.226 + public void sendNotifications(Integer nb);
100.227 +
100.228 + public void sendNotserializableNotifs(Integer nb);
100.229 + }
100.230 +}
101.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
101.2 +++ b/test/javax/management/eventService/PublishTest.java Thu Aug 21 09:55:18 2008 -0700
101.3 @@ -0,0 +1,184 @@
101.4 +/*
101.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
101.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
101.7 + *
101.8 + * This code is free software; you can redistribute it and/or modify it
101.9 + * under the terms of the GNU General Public License version 2 only, as
101.10 + * published by the Free Software Foundation.
101.11 + *
101.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
101.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
101.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
101.15 + * version 2 for more details (a copy is included in the LICENSE file that
101.16 + * accompanied this code).
101.17 + *
101.18 + * You should have received a copy of the GNU General Public License version
101.19 + * 2 along with this work; if not, write to the Free Software Foundation,
101.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
101.21 + *
101.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
101.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
101.24 + * have any questions.
101.25 + */
101.26 +
101.27 +import javax.management.MBeanServer;
101.28 +import javax.management.MBeanServerConnection;
101.29 +import javax.management.MBeanServerFactory;
101.30 +import javax.management.Notification;
101.31 +import javax.management.NotificationListener;
101.32 +import javax.management.ObjectName;
101.33 +import javax.management.event.*;
101.34 +import javax.management.remote.JMXConnector;
101.35 +import javax.management.remote.JMXConnectorFactory;
101.36 +import javax.management.remote.JMXConnectorServer;
101.37 +import javax.management.remote.JMXConnectorServerFactory;
101.38 +import javax.management.remote.JMXServiceURL;
101.39 +
101.40 +/**
101.41 + *
101.42 + */
101.43 +public class PublishTest {
101.44 + private static MBeanServer mbeanServer;
101.45 + private static EventManager eventManager;
101.46 + private static ObjectName emitter;
101.47 + private static JMXServiceURL url;
101.48 + private static JMXConnectorServer server;
101.49 + private static JMXConnector conn;
101.50 + private static MBeanServerConnection client;
101.51 +
101.52 + /**
101.53 + * @param args the command line arguments
101.54 + */
101.55 + public static void main(String[] args) throws Exception {
101.56 + System.out.println(">>> PublishTest-main basic tests ...");
101.57 + mbeanServer = MBeanServerFactory.createMBeanServer();
101.58 +
101.59 + // for 1.5
101.60 + if (System.getProperty("java.version").startsWith("1.5") &&
101.61 + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
101.62 + System.out.print("Working on "+System.getProperty("java.version")+
101.63 + " register "+EventClientDelegateMBean.OBJECT_NAME);
101.64 +
101.65 + mbeanServer.registerMBean(EventClientDelegate.
101.66 + getEventClientDelegate(mbeanServer),
101.67 + EventClientDelegateMBean.OBJECT_NAME);
101.68 + }
101.69 +
101.70 + eventManager = EventManager.getEventManager(mbeanServer);
101.71 +
101.72 + emitter = new ObjectName("Default:name=NotificationEmitter");
101.73 +
101.74 + url = new JMXServiceURL("rmi", null, 0) ;
101.75 + server =
101.76 + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer);
101.77 + server.start();
101.78 +
101.79 + url = server.getAddress();
101.80 + conn = JMXConnectorFactory.connect(url, null);
101.81 + client = conn.getMBeanServerConnection();
101.82 +
101.83 + boolean succeed;
101.84 +
101.85 + System.out.println(">>> PublishTest-main: using the fetching EventRelay...");
101.86 + succeed = test(new EventClient(client));
101.87 +
101.88 + System.out.println(">>> PublishTest-main: using the pushing EventRelay...");
101.89 + succeed &= test(new EventClient(client,
101.90 + new RMIPushEventRelay(EventClientDelegate.getProxy(client)),
101.91 + null,
101.92 + EventClient.DEFAULT_LEASE_TIMEOUT));
101.93 +
101.94 + conn.close();
101.95 + server.stop();
101.96 +
101.97 + if (succeed) {
101.98 + System.out.println(">>> PublishTest-main: PASSE!");
101.99 + } else {
101.100 + System.out.println("\n>>> PublishTest-main: FAILED!");
101.101 + System.exit(1);
101.102 + }
101.103 + }
101.104 +
101.105 + public static boolean test(EventClient efClient) throws Exception {
101.106 + // add listener from the client side
101.107 + Listener listener = new Listener();
101.108 + efClient.subscribe(emitter, listener, null, null);
101.109 +
101.110 + ObjectName other = new ObjectName("Default:name=other");
101.111 + // publish notifs
101.112 + for (int i=0; i<sendNB; i++) {
101.113 + Notification notif = new Notification(myType, emitter, count++);
101.114 + Notification notif2 = new Notification(myType, other, 0);
101.115 + //System.out.println(">>> EventManagerService-NotificationEmitter-sendNotifications: "+i);
101.116 +
101.117 + eventManager.publish(emitter, notif);
101.118 + eventManager.publish(other, notif2); // should not received
101.119 + }
101.120 +
101.121 + // waiting
101.122 + long toWait = 6000;
101.123 + long stopTime = System.currentTimeMillis() + toWait;
101.124 +
101.125 + synchronized(listener) {
101.126 + while(listener.received < sendNB && toWait > 0) {
101.127 + listener.wait(toWait);
101.128 + toWait = stopTime - System.currentTimeMillis();
101.129 + }
101.130 + }
101.131 +
101.132 + // clean
101.133 + efClient.unsubscribe(emitter, listener);
101.134 + efClient.close();
101.135 +
101.136 + if (listener.received != sendNB) {
101.137 + System.out.println(">>> PublishTest-test: FAILED! Expected to receive "+sendNB+", but got "+listener.received);
101.138 +
101.139 + return false;
101.140 + } else if (listener.seqErr > 0) {
101.141 + System.out.println(">>> PublishTest-test: FAILED! The receiving sequence is not correct.");
101.142 +
101.143 + return false;
101.144 + } else {
101.145 + System.out.println(">>> PublishTest-test: got all expected "+listener.received);
101.146 + return true;
101.147 + }
101.148 + }
101.149 +
101.150 + private static class Listener implements NotificationListener {
101.151 + public int received = 0;
101.152 + public int seqErr = 0;
101.153 +
101.154 + private long lastSeq = -1;
101.155 +
101.156 + public void handleNotification(Notification notif, Object handback) {
101.157 + if (!myType.equals(notif.getType())) {
101.158 + System.out.println(">>> PublishTest-Listener: got unexpected notif: "+notif);
101.159 + System.exit(1);
101.160 + } else if (!emitter.equals(notif.getSource())) {
101.161 + System.out.println(">>> PublishTest-Listener: unknown ObjectName: "+notif.getSource());
101.162 + System.exit(1);
101.163 + }
101.164 +
101.165 + if (lastSeq == -1) {
101.166 + lastSeq = notif.getSequenceNumber();
101.167 + } else if (notif.getSequenceNumber() - lastSeq++ != 1) {
101.168 + seqErr++;
101.169 + }
101.170 +
101.171 + System.out.println(">>> PublishTest-Listener: got notif "+notif.getSequenceNumber());
101.172 +
101.173 + synchronized(this) {
101.174 + if (++received >= sendNB) {
101.175 + this.notify();
101.176 + }
101.177 + }
101.178 +
101.179 + System.out.println(">>> PublishTest-Listener: received = "+received);
101.180 + }
101.181 + }
101.182 +
101.183 + private static int sendNB = 20;
101.184 + private static long count = 0;
101.185 +
101.186 + private static final String myType = "notification.my_notification";
101.187 +}
102.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
102.2 +++ b/test/javax/management/eventService/ReconnectableConnectorTest.java Thu Aug 21 09:55:18 2008 -0700
102.3 @@ -0,0 +1,488 @@
102.4 +/*
102.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
102.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
102.7 + *
102.8 + * This code is free software; you can redistribute it and/or modify it
102.9 + * under the terms of the GNU General Public License version 2 only, as
102.10 + * published by the Free Software Foundation.
102.11 + *
102.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
102.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
102.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
102.15 + * version 2 for more details (a copy is included in the LICENSE file that
102.16 + * accompanied this code).
102.17 + *
102.18 + * You should have received a copy of the GNU General Public License version
102.19 + * 2 along with this work; if not, write to the Free Software Foundation,
102.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
102.21 + *
102.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
102.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
102.24 + * have any questions.
102.25 + */
102.26 +
102.27 +/*
102.28 + * @test ReconnectableJMXConnector
102.29 + * @bug 5108776
102.30 + * @summary Check that the Event Service can be used to build a
102.31 + * ReconnectableJMXConnector.
102.32 + * @author Eamonn McManus
102.33 + */
102.34 +
102.35 +import java.io.IOException;
102.36 +import java.lang.reflect.InvocationHandler;
102.37 +import java.lang.reflect.InvocationTargetException;
102.38 +import java.lang.reflect.Method;
102.39 +import java.lang.reflect.Proxy;
102.40 +import java.util.Date;
102.41 +import java.util.Map;
102.42 +import java.util.NoSuchElementException;
102.43 +import java.util.concurrent.ArrayBlockingQueue;
102.44 +import java.util.concurrent.BlockingQueue;
102.45 +import java.util.concurrent.TimeUnit;
102.46 +import java.util.concurrent.atomic.AtomicLong;
102.47 +import java.util.concurrent.atomic.AtomicReference;
102.48 +import java.util.concurrent.locks.Condition;
102.49 +import java.util.concurrent.locks.Lock;
102.50 +import java.util.concurrent.locks.ReentrantLock;
102.51 +import javax.management.ListenerNotFoundException;
102.52 +import javax.management.MBeanServer;
102.53 +import javax.management.MBeanServerConnection;
102.54 +import javax.management.MBeanServerFactory;
102.55 +import javax.management.Notification;
102.56 +import javax.management.NotificationBroadcasterSupport;
102.57 +import javax.management.NotificationFilter;
102.58 +import javax.management.NotificationListener;
102.59 +import javax.management.ObjectName;
102.60 +import javax.management.event.EventClient;
102.61 +import javax.management.remote.JMXConnectionNotification;
102.62 +import javax.management.remote.JMXConnector;
102.63 +import javax.management.remote.JMXConnectorFactory;
102.64 +import javax.management.remote.JMXConnectorServer;
102.65 +import javax.management.remote.JMXConnectorServerFactory;
102.66 +import javax.management.remote.JMXServiceURL;
102.67 +import javax.security.auth.Subject;
102.68 +
102.69 +/*
102.70 + * This test checks that it is possible to use the Event Service to create
102.71 + * a "reconnectable connector".
102.72 + *
102.73 + * In the JMX Remote API, we deliberately specified that a connector client
102.74 + * (JMXConnector) that encounters a network failure is then permanently broken.
102.75 + * The idea being that adding recovery logic to the basic connector client
102.76 + * would make it much more complicated and less reliable, and the logic would
102.77 + * in any case never correspond to what a given situation needs. Some of
102.78 + * the tough questions are: Should the connector try to mask the failure by
102.79 + * blocking operations until the failure is resolved? How long should the
102.80 + * connector try to reestablish the connection before giving up? Rather than
102.81 + * try to solve this problem in the connector, we suggested that people who
102.82 + * wanted to recover from network failures could implement the JMXConnector
102.83 + * interface themselves so that it forwards to a wrapped JMXConnector that can
102.84 + * be replaced in case of network failure.
102.85 + *
102.86 + * This works fine except that the connector client has state,
102.87 + * in the form of listeners added by the user through the
102.88 + * MBeanServerConnection.addNotificationListener method. It's possible
102.89 + * for the wrapper to keep track of these listeners as well as forwarding
102.90 + * them to the wrapped JMXConnector, so that it can reapply them to
102.91 + * a replacement JMXConnector after failure recover. But it's quite
102.92 + * tricky, particularly because of the two- and four-argument versions of
102.93 + * removeNotificationListener.
102.94 + *
102.95 + * The Event Service can take care of this for you through the EventClient
102.96 + * class. Listeners added through that class are implemented in a way that
102.97 + * doesn't require the connector client to maintain any state, so they should
102.98 + * continue to work transparently after replacing the wrapped JMXConnector.
102.99 + * This test is a proof of concept that shows it works. Quite a number of
102.100 + * details would need to be changed to build a reliable reconnectable
102.101 + * connector.
102.102 + *
102.103 + * The test simulates network failure by rewrapping the wrapped JMXConnector's
102.104 + * MBeanServerConnection (MBSC) in a "breakable" MBSC which we can cause
102.105 + * to stop working. We do this in two phases. The first phase suspends
102.106 + * any MBSC calls just at the point where they would return to the caller.
102.107 + * The goal here is to block an EventClientDelegateMBean.fetchNotifications
102.108 + * operation when it has received notifications but not yet delivered them
102.109 + * to the EventClient. This is the most delicate point where a breakage
102.110 + * can occur, because the EventClientDelegate must not drop those notifs
102.111 + * from its buffer until another fetchNotifs call arrives with a later
102.112 + * sequence number (which is an implicit ack of the previous set of
102.113 + * notifs). Once the fetchNotifs call is suspended, we "kill" the MBSC,
102.114 + * causing it to throw IOException from this and any other calls. That
102.115 + * triggers the reconnect logic, which will make a new MBSC and issue
102.116 + * the same fetchNotifs call to it.
102.117 + *
102.118 + * The test could be improved by synchronizing explicitly between the
102.119 + * breakable MBSC and the mainline, so we only proceed to kill the MBSC
102.120 + * when we are sure that the fetchNotifs call is blocked. As it is,
102.121 + * we have a small delay which both ensures that no notifs are delivered
102.122 + * while the connection is suspended, and if the machine is fast enough
102.123 + * allows the fetchNotifs call to reach the blocking point.
102.124 + */
102.125 +public class ReconnectableConnectorTest {
102.126 + private static class ReconnectableJMXConnector implements JMXConnector {
102.127 + private final JMXServiceURL url;
102.128 + private AtomicReference<JMXConnector> wrappedJMXC =
102.129 + new AtomicReference<JMXConnector>();
102.130 + private AtomicReference<MBeanServerConnection> wrappedMBSC =
102.131 + new AtomicReference<MBeanServerConnection>();
102.132 + private final NotificationBroadcasterSupport broadcaster =
102.133 + new NotificationBroadcasterSupport();
102.134 + private final Lock connectLock = new ReentrantLock();
102.135 +
102.136 + ReconnectableJMXConnector(JMXServiceURL url) {
102.137 + this.url = url;
102.138 + }
102.139 +
102.140 + private class ReconnectIH implements InvocationHandler {
102.141 + public Object invoke(Object proxy, Method method, Object[] args)
102.142 + throws Throwable {
102.143 + try {
102.144 + return method.invoke(wrappedMBSC.get(), args);
102.145 + } catch (InvocationTargetException e) {
102.146 + if (e.getCause() instanceof IOException) {
102.147 + connect();
102.148 + try {
102.149 + return method.invoke(wrappedMBSC.get(),args);
102.150 + } catch (InvocationTargetException ee) {
102.151 + throw ee.getCause();
102.152 + }
102.153 + }
102.154 + throw e.getCause();
102.155 + }
102.156 + }
102.157 + }
102.158 +
102.159 + private class FailureListener implements NotificationListener {
102.160 + public void handleNotification(Notification n, Object h) {
102.161 + String type = n.getType();
102.162 + if (type.equals(JMXConnectionNotification.FAILED)) {
102.163 + try {
102.164 + connect();
102.165 + } catch (IOException e) {
102.166 + broadcaster.sendNotification(n);
102.167 + }
102.168 + } else if (type.equals(JMXConnectionNotification.NOTIFS_LOST))
102.169 + broadcaster.sendNotification(n);
102.170 + }
102.171 + }
102.172 +
102.173 + public void connect() throws IOException {
102.174 + connectLock.lock();
102.175 + try {
102.176 + connectWithLock();
102.177 + } finally {
102.178 + connectLock.unlock();
102.179 + }
102.180 + }
102.181 +
102.182 + private void connectWithLock() throws IOException {
102.183 + MBeanServerConnection mbsc = wrappedMBSC.get();
102.184 + if (mbsc != null) {
102.185 + try {
102.186 + mbsc.getDefaultDomain();
102.187 + return; // the connection works
102.188 + } catch (IOException e) {
102.189 + // OK: the connection doesn't work, so make a new one
102.190 + }
102.191 + }
102.192 + // This is where we would need to add the fancy logic that
102.193 + // allows the connection to keep failing for a while
102.194 + // before giving up.
102.195 + JMXConnector jmxc = JMXConnectorFactory.connect(url);
102.196 + jmxc.addConnectionNotificationListener(
102.197 + new FailureListener(), null, null);
102.198 + wrappedJMXC.set(jmxc);
102.199 + if (false)
102.200 + wrappedMBSC.set(jmxc.getMBeanServerConnection());
102.201 + else {
102.202 + mbsc = jmxc.getMBeanServerConnection();
102.203 + InvocationHandler ih = new BreakableIH(mbsc);
102.204 + mbsc = (MBeanServerConnection) Proxy.newProxyInstance(
102.205 + MBeanServerConnection.class.getClassLoader(),
102.206 + new Class<?>[] {MBeanServerConnection.class},
102.207 + ih);
102.208 + wrappedMBSC.set(mbsc);
102.209 + }
102.210 + }
102.211 +
102.212 + private BreakableIH breakableIH() {
102.213 + MBeanServerConnection mbsc = wrappedMBSC.get();
102.214 + return (BreakableIH) Proxy.getInvocationHandler(mbsc);
102.215 + }
102.216 +
102.217 + void suspend() {
102.218 + BreakableIH ih = breakableIH();
102.219 + ih.suspend();
102.220 + }
102.221 +
102.222 + void kill() throws IOException {
102.223 + BreakableIH ih = breakableIH();
102.224 + wrappedJMXC.get().close();
102.225 + ih.kill();
102.226 + }
102.227 +
102.228 + public void connect(Map<String, ?> env) throws IOException {
102.229 + throw new UnsupportedOperationException("Not supported yet.");
102.230 + }
102.231 +
102.232 + private final AtomicReference<MBeanServerConnection> mbscRef =
102.233 + new AtomicReference<MBeanServerConnection>();
102.234 +
102.235 + public MBeanServerConnection getMBeanServerConnection()
102.236 + throws IOException {
102.237 + connect();
102.238 + // Synchro here is not strictly correct: two threads could make
102.239 + // an MBSC at the same time. OK for a test but beware for real
102.240 + // code.
102.241 + MBeanServerConnection mbsc = mbscRef.get();
102.242 + if (mbsc != null)
102.243 + return mbsc;
102.244 + mbsc = (MBeanServerConnection) Proxy.newProxyInstance(
102.245 + MBeanServerConnection.class.getClassLoader(),
102.246 + new Class<?>[] {MBeanServerConnection.class},
102.247 + new ReconnectIH());
102.248 + mbsc = EventClient.getEventClientConnection(mbsc);
102.249 + mbscRef.set(mbsc);
102.250 + return mbsc;
102.251 + }
102.252 +
102.253 + public MBeanServerConnection getMBeanServerConnection(
102.254 + Subject delegationSubject) throws IOException {
102.255 + throw new UnsupportedOperationException("Not supported yet.");
102.256 + }
102.257 +
102.258 + public void close() throws IOException {
102.259 + wrappedJMXC.get().close();
102.260 + }
102.261 +
102.262 + public void addConnectionNotificationListener(
102.263 + NotificationListener l, NotificationFilter f, Object h) {
102.264 + broadcaster.addNotificationListener(l, f, h);
102.265 + }
102.266 +
102.267 + public void removeConnectionNotificationListener(NotificationListener l)
102.268 + throws ListenerNotFoundException {
102.269 + broadcaster.removeNotificationListener(l);
102.270 + }
102.271 +
102.272 + public void removeConnectionNotificationListener(
102.273 + NotificationListener l, NotificationFilter f, Object h)
102.274 + throws ListenerNotFoundException {
102.275 + broadcaster.removeNotificationListener(l, f, h);
102.276 + }
102.277 +
102.278 + public String getConnectionId() throws IOException {
102.279 + return wrappedJMXC.get().getConnectionId();
102.280 + }
102.281 + }
102.282 +
102.283 + // InvocationHandler that allows us to perform a two-phase "break" of
102.284 + // an object. The first phase suspends the object, so that calls to
102.285 + // it are blocked just before they return. The second phase unblocks
102.286 + // suspended threads and causes them to throw IOException.
102.287 + private static class BreakableIH implements InvocationHandler {
102.288 + private final Object wrapped;
102.289 + private final Holder<String> state = new Holder<String>("running");
102.290 +
102.291 + BreakableIH(Object wrapped) {
102.292 + this.wrapped = wrapped;
102.293 + }
102.294 +
102.295 + void suspend() {
102.296 + state.set("suspended");
102.297 + }
102.298 +
102.299 + void kill() {
102.300 + state.set("killed");
102.301 + }
102.302 +
102.303 + public Object invoke(Object proxy, Method method, Object[] args)
102.304 + throws Throwable {
102.305 + Object result;
102.306 + try {
102.307 + result = method.invoke(wrapped, args);
102.308 + } catch (InvocationTargetException e) {
102.309 + throw e.getCause();
102.310 + }
102.311 + String s = state.get();
102.312 + if (s.equals("suspended"))
102.313 + state.waitUntilEqual("killed", 3, TimeUnit.SECONDS);
102.314 + else if (s.equals("killed"))
102.315 + throw new IOException("Broken");
102.316 + return result;
102.317 + }
102.318 + }
102.319 +
102.320 + private static class Holder<T> {
102.321 + private T held;
102.322 + private Lock lock = new ReentrantLock();
102.323 + private Condition changed = lock.newCondition();
102.324 +
102.325 + Holder(T value) {
102.326 + lock.lock();
102.327 + this.held = value;
102.328 + lock.unlock();
102.329 + }
102.330 +
102.331 + void waitUntilEqual(T value, long timeout, TimeUnit units)
102.332 + throws InterruptedException {
102.333 + long millis = units.toMillis(timeout);
102.334 + long stop = System.currentTimeMillis() + millis;
102.335 + Date stopDate = new Date(stop);
102.336 + lock.lock();
102.337 + try {
102.338 + while (!value.equals(held)) {
102.339 + boolean ok = changed.awaitUntil(stopDate);
102.340 + if (!ok)
102.341 + throw new InterruptedException("Timed out");
102.342 + }
102.343 + } finally {
102.344 + lock.unlock();
102.345 + }
102.346 + }
102.347 +
102.348 + void set(T value) {
102.349 + lock.lock();
102.350 + try {
102.351 + held = value;
102.352 + changed.signalAll();
102.353 + } finally {
102.354 + lock.unlock();
102.355 + }
102.356 + }
102.357 +
102.358 + T get() {
102.359 + lock.lock();
102.360 + try {
102.361 + return held;
102.362 + } finally {
102.363 + lock.unlock();
102.364 + }
102.365 + }
102.366 + }
102.367 +
102.368 + private static class StoreListener implements NotificationListener {
102.369 + final BlockingQueue<Notification> queue =
102.370 + new ArrayBlockingQueue<Notification>(100);
102.371 +
102.372 + public void handleNotification(Notification n, Object h) {
102.373 + queue.add(n);
102.374 + }
102.375 +
102.376 + Notification nextNotification(long time, TimeUnit units)
102.377 + throws InterruptedException {
102.378 + Notification n = queue.poll(time, units);
102.379 + if (n == null)
102.380 + throw new NoSuchElementException("Notification wait timed out");
102.381 + return n;
102.382 + }
102.383 +
102.384 + int notifCount() {
102.385 + return queue.size();
102.386 + }
102.387 + }
102.388 +
102.389 + public static interface SenderMBean {}
102.390 + public static class Sender
102.391 + extends NotificationBroadcasterSupport implements SenderMBean {
102.392 + private AtomicLong seqNo = new AtomicLong(0);
102.393 +
102.394 + void send() {
102.395 + Notification n =
102.396 + new Notification("type", this, seqNo.getAndIncrement());
102.397 + sendNotification(n);
102.398 + }
102.399 + }
102.400 +
102.401 + public static void main(String[] args) throws Exception {
102.402 + MBeanServer mbs = MBeanServerFactory.newMBeanServer();
102.403 + Sender sender = new Sender();
102.404 + ObjectName name = new ObjectName("a:b=c");
102.405 + mbs.registerMBean(sender, name);
102.406 +
102.407 + System.out.println("Creating connector server");
102.408 + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///");
102.409 + JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(
102.410 + url, null, mbs);
102.411 + cs.start();
102.412 +
102.413 + StoreListener csListener = new StoreListener();
102.414 + cs.addNotificationListener(csListener, null, null);
102.415 +
102.416 + System.out.println("Creating reconnectable client");
102.417 + JMXServiceURL addr = cs.getAddress();
102.418 + ReconnectableJMXConnector cc = new ReconnectableJMXConnector(addr);
102.419 + MBeanServerConnection mbsc = cc.getMBeanServerConnection();
102.420 +
102.421 + System.out.println("Checking server has sent new-client notif");
102.422 + Notification csn = csListener.nextNotification(1, TimeUnit.SECONDS);
102.423 + assertEquals("CS notif type",
102.424 + JMXConnectionNotification.OPENED, csn.getType());
102.425 +
102.426 + StoreListener listener = new StoreListener();
102.427 + mbsc.addNotificationListener(name, listener, null, null);
102.428 +
102.429 + System.out.println("Sending 10 notifs and checking they are received");
102.430 + for (int i = 0; i < 10; i++)
102.431 + sender.send();
102.432 + checkNotifs(listener, 0, 10);
102.433 +
102.434 + System.out.println("Suspending the fetchNotifs operation");
102.435 + cc.suspend();
102.436 + System.out.println("Sending a notif while fetchNotifs is suspended");
102.437 + sender.send();
102.438 + System.out.println("Brief wait before checking no notif is received");
102.439 + Thread.sleep(2);
102.440 + // dumpThreads();
102.441 + assertEquals("notif queue while connector suspended",
102.442 + 0, listener.notifCount());
102.443 + assertEquals("connector server notif queue while connector suspended",
102.444 + 0, csListener.notifCount());
102.445 +
102.446 + System.out.println("Breaking the connection so fetchNotifs will fail over");
102.447 + cc.kill();
102.448 +
102.449 + System.out.println("Checking that client has reconnected");
102.450 + csn = csListener.nextNotification(1, TimeUnit.SECONDS);
102.451 + assertEquals("First CS notif type after kill",
102.452 + JMXConnectionNotification.CLOSED, csn.getType());
102.453 + csn = csListener.nextNotification(1, TimeUnit.SECONDS);
102.454 + assertEquals("Second CS notif type after kill",
102.455 + JMXConnectionNotification.OPENED, csn.getType());
102.456 +
102.457 + System.out.println("Checking that suspended notif has been received");
102.458 + checkNotifs(listener, 10, 11);
102.459 + }
102.460 +
102.461 + private static void checkNotifs(
102.462 + StoreListener sl, long start, long stop)
102.463 + throws Exception {
102.464 + for (long i = start; i < stop; i++) {
102.465 + Notification n = sl.nextNotification(1, TimeUnit.SECONDS);
102.466 + assertEquals("received sequence number", i, n.getSequenceNumber());
102.467 + }
102.468 + }
102.469 +
102.470 + private static void assertEquals(String what, Object expect, Object actual)
102.471 + throws Exception {
102.472 + if (!expect.equals(actual)) {
102.473 + fail(what + " should be " + expect + " but is " + actual);
102.474 + }
102.475 + }
102.476 +
102.477 + private static void fail(String why) throws Exception {
102.478 + throw new Exception("TEST FAILED: " + why);
102.479 + }
102.480 +
102.481 + private static void dumpThreads() {
102.482 + System.out.println("Thread stack dump");
102.483 + Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
102.484 + for (Map.Entry<Thread, StackTraceElement[]> entry : traces.entrySet()) {
102.485 + Thread t = entry.getKey();
102.486 + System.out.println("===Thread " + t.getName() + "===");
102.487 + for (StackTraceElement ste : entry.getValue())
102.488 + System.out.println(" " + ste);
102.489 + }
102.490 + }
102.491 +}
103.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
103.2 +++ b/test/javax/management/eventService/SharingThreadTest.java Thu Aug 21 09:55:18 2008 -0700
103.3 @@ -0,0 +1,365 @@
103.4 +/*/*
103.5 + * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
103.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
103.7 + *
103.8 + * This code is free software; you can redistribute it and/or modify it
103.9 + * under the terms of the GNU General Public License version 2 only, as
103.10 + * published by the Free Software Foundation.
103.11 + *
103.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
103.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
103.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
103.15 + * version 2 for more details (a copy is included in the LICENSE file that
103.16 + * accompanied this code).
103.17 + *
103.18 + * You should have received a copy of the GNU General Public License version
103.19 + * 2 along with this work; if not, write to the Free Software Foundation,
103.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
103.21 + *
103.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
103.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
103.24 + * have any questions.
103.25 + */
103.26 +
103.27 +/*
103.28 + * @test SharingThreadTest.java 1.3 08/01/22
103.29 + * @bug 5108776
103.30 + * @summary Basic test for EventClient to see internal thread management.
103.31 + * @author Shanliang JIANG
103.32 + * @run clean SharingThreadTest
103.33 + * @run build SharingThreadTest
103.34 + * @run main SharingThreadTest
103.35 + */
103.36 +
103.37 +import java.io.IOException;
103.38 +import java.util.concurrent.ArrayBlockingQueue;
103.39 +import java.util.concurrent.Executor;
103.40 +import java.util.concurrent.ThreadPoolExecutor;
103.41 +import java.util.concurrent.TimeUnit;
103.42 +import javax.management.MBeanServer;
103.43 +import javax.management.MBeanServerFactory;
103.44 +import javax.management.Notification;
103.45 +import javax.management.NotificationBroadcasterSupport;
103.46 +import javax.management.NotificationFilter;
103.47 +import javax.management.NotificationListener;
103.48 +import javax.management.ObjectName;
103.49 +import javax.management.event.EventClient;
103.50 +import javax.management.event.EventClientDelegate;
103.51 +import javax.management.event.EventClientDelegateMBean;
103.52 +import javax.management.event.FetchingEventRelay;
103.53 +import javax.management.event.RMIPushEventRelay;
103.54 +import javax.management.remote.JMXConnector;
103.55 +import javax.management.remote.JMXConnectorFactory;
103.56 +import javax.management.remote.JMXConnectorServer;
103.57 +import javax.management.remote.JMXConnectorServerFactory;
103.58 +import javax.management.remote.JMXServiceURL;
103.59 +
103.60 +
103.61 +public class SharingThreadTest {
103.62 +
103.63 + private static MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer();
103.64 + private static ObjectName emitter;
103.65 + private static NotificationEmitter emitterImpl;
103.66 + private static JMXServiceURL url;
103.67 + private static JMXConnectorServer server;
103.68 +
103.69 +
103.70 + private static int toSend = 10;
103.71 + private static final long bigWaiting = 6000;
103.72 + private static int counter = 0;
103.73 + private static int jobs = 10;
103.74 + private static int endedJobs = 0;
103.75 +
103.76 + private static volatile String failure;
103.77 +
103.78 + private static Executor sharedExecutor = new ThreadPoolExecutor(0, 1, 1000,
103.79 + TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(jobs));
103.80 + //Executors.newFixedThreadPool(1);
103.81 +
103.82 + public static void main(String[] args) throws Exception {
103.83 + System.out.println(">>> Test on sharing threads for multiple EventClient.");
103.84 +
103.85 + // for 1.5
103.86 + if (System.getProperty("java.version").startsWith("1.5") &&
103.87 + !mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
103.88 + System.out.print("Working on "+System.getProperty("java.version")+
103.89 + " register "+EventClientDelegateMBean.OBJECT_NAME);
103.90 +
103.91 + mbeanServer.registerMBean(EventClientDelegate.
103.92 + getEventClientDelegate(mbeanServer),
103.93 + EventClientDelegateMBean.OBJECT_NAME);
103.94 +
103.95 + sharedExecutor = new ThreadPoolExecutor(1, 1, 1000,
103.96 + TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(jobs));
103.97 + }
103.98 +
103.99 + emitter = new ObjectName("Default:name=NotificationEmitter");
103.100 + emitterImpl = new NotificationEmitter();
103.101 + mbeanServer.registerMBean(emitterImpl, emitter);
103.102 +
103.103 + String[] types = new String[]{"PushEventRelay", "FetchingEventRelay"};
103.104 + String[] protos = new String[]{"rmi", "iiop", "jmxmp"};
103.105 + for (String prot : protos) {
103.106 + url = new JMXServiceURL(prot, null, 0);
103.107 +
103.108 + try {
103.109 + server =
103.110 + JMXConnectorServerFactory.newJMXConnectorServer(url,
103.111 + null, mbeanServer);
103.112 + server.start();
103.113 + } catch (Exception e) {
103.114 + System.out.println(">>> Skip "+prot+", not support.");
103.115 + continue;
103.116 + }
103.117 +
103.118 + url = server.getAddress();
103.119 +
103.120 + // noise
103.121 + Thread noise = new Thread(new Runnable() {
103.122 + public void run() {
103.123 + while (true) {
103.124 + emitterImpl.sendNotif(1, null);
103.125 + try {
103.126 + Thread.sleep(10);
103.127 + } catch (Exception e) {
103.128 + // OK
103.129 + }
103.130 + }
103.131 + }
103.132 + });
103.133 + noise.setDaemon(true);
103.134 + noise.start();
103.135 +
103.136 + try {
103.137 + for (String type: types) {
103.138 + System.out.println("\n\n>>> Testing "+type+" on "+url+" ...");
103.139 + JMXConnector conn = newConn();
103.140 + try {
103.141 + testType(type, conn);
103.142 + } finally {
103.143 + conn.close();
103.144 + System.out.println(">>> Testing "+type+" on "+url+" ... done");
103.145 + }
103.146 + }
103.147 + } finally {
103.148 + server.stop();
103.149 + }
103.150 + }
103.151 + }
103.152 +
103.153 + private static void testType(String type, JMXConnector conn) throws Exception {
103.154 + Thread[] threads = new Thread[jobs];
103.155 + for (int i=0; i<jobs; i++) {
103.156 + threads[i] = new Thread(new Job(type, conn));
103.157 + threads[i].setDaemon(true);
103.158 + threads[i].start();
103.159 + }
103.160 +
103.161 + // to wait
103.162 + long toWait = bigWaiting*jobs;
103.163 + long stopTime = System.currentTimeMillis() + toWait;
103.164 +
103.165 + synchronized(SharingThreadTest.class) {
103.166 + while (endedJobs < jobs && toWait > 0 && failure == null) {
103.167 + SharingThreadTest.class.wait(toWait);
103.168 + toWait = stopTime - System.currentTimeMillis();
103.169 + }
103.170 + }
103.171 +
103.172 + if (endedJobs != jobs && failure == null) {
103.173 + throw new RuntimeException("Need to set bigger waiting timeout?");
103.174 + }
103.175 +
103.176 + endedJobs = 0;
103.177 + }
103.178 +
103.179 + public static class Job implements Runnable {
103.180 + public Job(String type, JMXConnector conn) {
103.181 + this.type = type;
103.182 + this.conn = conn;
103.183 + }
103.184 + public void run() {
103.185 + try {
103.186 + test(type, conn);
103.187 +
103.188 + synchronized(SharingThreadTest.class) {
103.189 + endedJobs++;
103.190 + if (endedJobs>=jobs) {
103.191 + SharingThreadTest.class.notify();
103.192 + }
103.193 + }
103.194 + } catch (RuntimeException re) {
103.195 + re.printStackTrace(System.out);
103.196 + throw re;
103.197 + } catch (Exception e) {
103.198 + throw new RuntimeException(e);
103.199 + }
103.200 + }
103.201 +
103.202 + private final String type;
103.203 + private final JMXConnector conn;
103.204 + }
103.205 +
103.206 + private static void test(String type, JMXConnector conn) throws Exception {
103.207 + String id = getId();
103.208 +
103.209 + Listener listener = new Listener(id);
103.210 + Filter filter = new Filter(id);
103.211 +
103.212 + //newConn();
103.213 + EventClient ec = newEventClient(type, conn);
103.214 +
103.215 + System.out.println(">>> ("+id+") To receive notifications "+toSend);
103.216 + ec.addNotificationListener(emitter,
103.217 + listener, filter, null);
103.218 +
103.219 + emitterImpl.sendNotif(toSend, id);
103.220 + listener.waitNotifs(bigWaiting, toSend);
103.221 + if (listener.received != toSend) {
103.222 + throw new RuntimeException(">>> ("+id+") Expected to receive: "
103.223 + +toSend+", but got: "+listener.received);
103.224 + }
103.225 +
103.226 + ec.close();
103.227 + }
103.228 +
103.229 +//--------------------------
103.230 +// private classes
103.231 +//--------------------------
103.232 +
103.233 + private static class Listener implements NotificationListener {
103.234 + public Listener(String id) {
103.235 + this.id = id;
103.236 + }
103.237 + public void handleNotification(Notification notif, Object handback) {
103.238 + if (!id.equals(notif.getUserData())) {
103.239 + System.out.println("("+id+") Filter error, my id is: "+id+
103.240 + ", but got "+notif.getUserData());
103.241 + System.exit(1);
103.242 + }
103.243 + System.out.println("("+id+") received "+notif.getSequenceNumber());
103.244 + synchronized (this) {
103.245 + received++;
103.246 +
103.247 + if (sequenceNB < 0) {
103.248 + sequenceNB = notif.getSequenceNumber();
103.249 + } else if(++sequenceNB != notif.getSequenceNumber()) {
103.250 + fail("(" + id + ") Wrong sequence number, expected: "
103.251 + +sequenceNB+", but got: "+notif.getSequenceNumber());
103.252 + }
103.253 + if (received >= toSend || failure != null) {
103.254 + this.notify();
103.255 + }
103.256 + }
103.257 + }
103.258 +
103.259 + public void waitNotifs(long timeout, int nb) throws Exception {
103.260 + long toWait = timeout;
103.261 + long stopTime = System.currentTimeMillis() + timeout;
103.262 + synchronized(this) {
103.263 + while (received < nb && toWait > 0 && failure == null) {
103.264 + this.wait(toWait);
103.265 + toWait = stopTime - System.currentTimeMillis();
103.266 + }
103.267 + }
103.268 + }
103.269 +
103.270 + private String id;
103.271 + private int received = 0;
103.272 +
103.273 + private long sequenceNB = -1;
103.274 + }
103.275 +
103.276 + private static class Filter implements NotificationFilter {
103.277 + public Filter(String id) {
103.278 + this.id = id;
103.279 + }
103.280 +
103.281 + public boolean isNotificationEnabled(Notification n) {
103.282 + return id.equals(n.getUserData());
103.283 + }
103.284 + private String id;
103.285 + }
103.286 +
103.287 + public static class NotificationEmitter extends NotificationBroadcasterSupport
103.288 + implements NotificationEmitterMBean {
103.289 +
103.290 + /**
103.291 + * Send Notification objects.
103.292 + *
103.293 + * @param nb The number of notifications to send
103.294 + */
103.295 + public void sendNotif(int nb, String userData) {
103.296 + new Thread(new SendJob(nb, userData)).start();
103.297 + }
103.298 +
103.299 + private class SendJob implements Runnable {
103.300 + public SendJob(int nb, String userData) {
103.301 + this.nb = nb;
103.302 + this.userData = userData;
103.303 + }
103.304 +
103.305 + public void run() {
103.306 + if (userData != null) {
103.307 + System.out.println(">>> ("+userData+") sending "+nb);
103.308 + }
103.309 + long sequenceNumber = 0;
103.310 + for (int i = 0; i<nb; i++) {
103.311 + Notification notif = new Notification(myType, emitter,
103.312 + sequenceNumber++);
103.313 + notif.setUserData(userData);
103.314 + sendNotification(notif);
103.315 + Thread.yield();
103.316 + try {
103.317 + Thread.sleep(1);
103.318 + } catch (Exception e) {}
103.319 + }
103.320 + if (userData != null) {
103.321 + System.out.println(">>> ("+userData+") sending done");
103.322 + }
103.323 + }
103.324 + private int nb;
103.325 + private String userData;
103.326 + }
103.327 + private final String myType = "notification.my_notification";
103.328 + }
103.329 +
103.330 + public interface NotificationEmitterMBean {
103.331 + public void sendNotif(int nb, String userData);
103.332 + }
103.333 +
103.334 + private static JMXConnector newConn() throws IOException {
103.335 + return JMXConnectorFactory.connect(url);
103.336 + }
103.337 +
103.338 + private static EventClient newEventClient(String type, JMXConnector conn)
103.339 + throws Exception {
103.340 + EventClientDelegateMBean proxy =
103.341 + EventClientDelegate.getProxy(conn.getMBeanServerConnection());
103.342 + if (type.equals("PushEventRelay")) {
103.343 + return new EventClient(proxy,
103.344 + new RMIPushEventRelay(proxy), sharedExecutor, null, 600);
103.345 + } else if (type.equals("FetchingEventRelay")) {
103.346 + return new EventClient(proxy,
103.347 + new FetchingEventRelay(proxy,
103.348 + FetchingEventRelay.DEFAULT_BUFFER_SIZE,
103.349 + 10,
103.350 + FetchingEventRelay.DEFAULT_MAX_NOTIFICATIONS,
103.351 + sharedExecutor),
103.352 + null, null, 600);
103.353 + } else {
103.354 + throw new RuntimeException("Wrong event client type: "+type);
103.355 + }
103.356 + }
103.357 +
103.358 + private static String getId() {
103.359 + synchronized(SharingThreadTest.class) {
103.360 + return String.valueOf(counter++);
103.361 + }
103.362 + }
103.363 +
103.364 + private static void fail(String msg) {
103.365 + System.out.println("FAIL: " + msg);
103.366 + failure = msg;
103.367 + }
103.368 +}
104.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
104.2 +++ b/test/javax/management/eventService/SubscribeTest.java Thu Aug 21 09:55:18 2008 -0700
104.3 @@ -0,0 +1,127 @@
104.4 +/*
104.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
104.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
104.7 + *
104.8 + * This code is free software; you can redistribute it and/or modify it
104.9 + * under the terms of the GNU General Public License version 2 only, as
104.10 + * published by the Free Software Foundation.
104.11 + *
104.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
104.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
104.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
104.15 + * version 2 for more details (a copy is included in the LICENSE file that
104.16 + * accompanied this code).
104.17 + *
104.18 + * You should have received a copy of the GNU General Public License version
104.19 + * 2 along with this work; if not, write to the Free Software Foundation,
104.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
104.21 + *
104.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
104.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
104.24 + * have any questions.
104.25 + */
104.26 +
104.27 +/*
104.28 + * @test
104.29 + * @bug 5108776
104.30 + * @summary Test that EventSubscriber.subscribe works
104.31 + * @author Eamonn McManus
104.32 + */
104.33 +
104.34 +import java.lang.management.ManagementFactory;
104.35 +import javax.management.MBeanServer;
104.36 +import javax.management.Notification;
104.37 +import javax.management.NotificationBroadcasterSupport;
104.38 +import javax.management.NotificationFilter;
104.39 +import javax.management.NotificationListener;
104.40 +import javax.management.ObjectName;
104.41 +import javax.management.event.EventSubscriber;
104.42 +
104.43 +public class SubscribeTest {
104.44 + private static class CountListener implements NotificationListener {
104.45 + volatile int count;
104.46 +
104.47 + public void handleNotification(Notification n, Object h) {
104.48 + count++;
104.49 + }
104.50 + }
104.51 +
104.52 + private static class SwitchFilter implements NotificationFilter {
104.53 + volatile boolean enabled;
104.54 +
104.55 + public boolean isNotificationEnabled(Notification n) {
104.56 + return enabled;
104.57 + }
104.58 + }
104.59 +
104.60 + public static interface SenderMBean {}
104.61 +
104.62 + public static class Sender extends NotificationBroadcasterSupport
104.63 + implements SenderMBean {
104.64 + void send() {
104.65 + Notification n = new Notification("type", this, 1L);
104.66 + sendNotification(n);
104.67 + }
104.68 + }
104.69 +
104.70 + public static void main(String[] args) throws Exception {
104.71 + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
104.72 + ObjectName name1 = new ObjectName("d:type=Sender,id=1");
104.73 + ObjectName name2 = new ObjectName("d:type=Sender,id=2");
104.74 + ObjectName pattern = new ObjectName("d:type=Sender,*");
104.75 + Sender sender1 = new Sender();
104.76 + Sender sender2 = new Sender();
104.77 + mbs.registerMBean(sender1, name1);
104.78 + mbs.registerMBean(sender2, name2);
104.79 +
104.80 + EventSubscriber sub = EventSubscriber.getEventSubscriber(mbs);
104.81 +
104.82 + System.out.println("Single subscribe covering both MBeans");
104.83 + CountListener listen1 = new CountListener();
104.84 + sub.subscribe(pattern, listen1, null, null);
104.85 + sender1.send();
104.86 + assertEquals("Notifs after sender1 send", 1, listen1.count);
104.87 + sender2.send();
104.88 + assertEquals("Notifs after sender2 send", 2, listen1.count);
104.89 +
104.90 + System.out.println("Unsubscribe");
104.91 + sub.unsubscribe(pattern, listen1);
104.92 + sender1.send();
104.93 + assertEquals("Notifs after sender1 send", 2, listen1.count);
104.94 +
104.95 + System.out.println("Subscribe twice to same MBean with same listener " +
104.96 + "but different filters");
104.97 + SwitchFilter filter1 = new SwitchFilter();
104.98 + sub.subscribe(name1, listen1, null, null);
104.99 + sub.subscribe(name1, listen1, filter1, null);
104.100 + listen1.count = 0;
104.101 + sender1.send();
104.102 + // switch is off, so only one notif expected
104.103 + assertEquals("Notifs after sender1 send", 1, listen1.count);
104.104 + filter1.enabled = true;
104.105 + sender1.send();
104.106 + // switch is on, so two more notifs expected
104.107 + assertEquals("Notifs after sender1 send", 3, listen1.count);
104.108 +
104.109 + System.out.println("Remove those subscriptions");
104.110 + sub.unsubscribe(name1, listen1);
104.111 + sender1.send();
104.112 + assertEquals("Notifs after sender1 send", 3, listen1.count);
104.113 + }
104.114 +
104.115 + private static void assertEquals(String what, Object expected, Object actual)
104.116 + throws Exception {
104.117 + if (!equal(expected, actual)) {
104.118 + String msg = "Expected " + expected + "; got " + actual;
104.119 + throw new Exception("TEST FAILED: " + what + ": " + msg);
104.120 + }
104.121 + }
104.122 +
104.123 + private static boolean equal(Object x, Object y) {
104.124 + if (x == y)
104.125 + return true;
104.126 + if (x == null)
104.127 + return false;
104.128 + return (x.equals(y));
104.129 + }
104.130 +}
105.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
105.2 +++ b/test/javax/management/eventService/UsingEventService.java Thu Aug 21 09:55:18 2008 -0700
105.3 @@ -0,0 +1,84 @@
105.4 +/*
105.5 + * @test UsingEventService.java 1.10 08/01/22
105.6 + * @bug 5108776
105.7 + * @summary Basic test for EventManager.
105.8 + * @author Shanliang JIANG
105.9 + * @run clean UsingEventService
105.10 + * @run build UsingEventService
105.11 + * @run main UsingEventService
105.12 + */
105.13 +
105.14 +import java.util.HashMap;
105.15 +import java.util.Map;
105.16 +import javax.management.MBeanServer;
105.17 +import javax.management.MBeanServerConnection;
105.18 +import javax.management.MBeanServerFactory;
105.19 +import javax.management.Notification;
105.20 +import javax.management.NotificationListener;
105.21 +import javax.management.ObjectName;
105.22 +import javax.management.event.EventConsumer;
105.23 +import javax.management.remote.JMXConnector;
105.24 +import javax.management.remote.JMXConnectorFactory;
105.25 +import javax.management.remote.JMXConnectorServer;
105.26 +import javax.management.remote.JMXConnectorServerFactory;
105.27 +import javax.management.remote.JMXServiceURL;
105.28 +
105.29 +public class UsingEventService {
105.30 + private static JMXServiceURL url;
105.31 + private static JMXConnectorServer server;
105.32 + private static JMXConnector conn;
105.33 + private static MBeanServerConnection client;
105.34 +
105.35 + public static void main(String[] args) throws Exception {
105.36 + if (System.getProperty("java.version").startsWith("1.5")) {
105.37 + System.out.println(">>> UsingEventService-main not available for JDK1.5, bye");
105.38 + return;
105.39 + }
105.40 +
105.41 + ObjectName oname = new ObjectName("test:t=t");
105.42 + Notification n = new Notification("", oname, 0);
105.43 +
105.44 + System.out.println(">>> UsingEventService-main basic tests ...");
105.45 + MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer();
105.46 +
105.47 + url = new JMXServiceURL("rmi", null, 0) ;
105.48 + JMXConnectorServer server =
105.49 + JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer);
105.50 + server.start();
105.51 + url = server.getAddress();
105.52 +
105.53 + System.out.println(">>> UsingEventService-main test to not use the event service...");
105.54 + conn = JMXConnectorFactory.connect(url, null);
105.55 + client = conn.getMBeanServerConnection();
105.56 +
105.57 + System.out.println(">>> UsingEventService-main test to use the event service...");
105.58 + Map env = new HashMap(1);
105.59 + env.put("jmx.remote.use.event.service", "true");
105.60 + conn = JMXConnectorFactory.connect(url, env);
105.61 + client = conn.getMBeanServerConnection();
105.62 +
105.63 + ((EventConsumer)client).subscribe(oname, listener, null, null);
105.64 +
105.65 + System.out.println(">>> UsingEventService-main using event service as expected!");
105.66 +
105.67 + System.out.println(">>> UsingEventService-main test to use" +
105.68 + " the event service with system property...");
105.69 +
105.70 + System.setProperty("jmx.remote.use.event.service", "true");
105.71 + conn = JMXConnectorFactory.connect(url, null);
105.72 + client = conn.getMBeanServerConnection();
105.73 +
105.74 + ((EventConsumer)client).subscribe(oname, listener, null, null);
105.75 +
105.76 + System.out.println("" +
105.77 + ">>> UsingEventService-main using event service as expected!");
105.78 +
105.79 + System.out.println(">>> Happy bye bye!");
105.80 + }
105.81 +
105.82 + private final static NotificationListener listener = new NotificationListener() {
105.83 + public void handleNotification(Notification n, Object hk) {
105.84 + //
105.85 + }
105.86 + };
105.87 +}
106.1 --- a/test/javax/management/mxbean/GenericArrayTypeTest.java Tue Aug 19 07:50:03 2008 -0700
106.2 +++ b/test/javax/management/mxbean/GenericArrayTypeTest.java Thu Aug 21 09:55:18 2008 -0700
106.3 @@ -32,17 +32,19 @@
106.4 */
106.5
106.6 import java.lang.management.ManagementFactory;
106.7 -import java.lang.management.MonitorInfo;
106.8 import java.util.ArrayList;
106.9 import java.util.HashMap;
106.10 import java.util.HashSet;
106.11 import java.util.List;
106.12 import java.util.Map;
106.13 import java.util.Set;
106.14 +import javax.management.Attribute;
106.15 import javax.management.JMX;
106.16 import javax.management.MBeanServer;
106.17 import javax.management.MBeanServerConnection;
106.18 import javax.management.ObjectName;
106.19 +import javax.management.StandardMBean;
106.20 +import javax.management.openmbean.CompositeData;
106.21 import javax.management.remote.JMXConnector;
106.22 import javax.management.remote.JMXConnectorFactory;
106.23 import javax.management.remote.JMXConnectorServer;
106.24 @@ -50,6 +52,58 @@
106.25 import javax.management.remote.JMXServiceURL;
106.26
106.27 public class GenericArrayTypeTest {
106.28 + // A version of java.lang.management.MonitorInfo so we can run this test
106.29 + // on JDK 5, where that class didn't exist.
106.30 + public static class MonitorInfo {
106.31 + private final String className;
106.32 + private final int identityHashCode;
106.33 + private final int lockedStackDepth;
106.34 + private final StackTraceElement lockedStackFrame;
106.35 +
106.36 + public MonitorInfo(
106.37 + String className, int identityHashCode,
106.38 + int lockedStackDepth, StackTraceElement lockedStackFrame) {
106.39 + this.className = className;
106.40 + this.identityHashCode = identityHashCode;
106.41 + this.lockedStackDepth = lockedStackDepth;
106.42 + this.lockedStackFrame = lockedStackFrame;
106.43 + }
106.44 +
106.45 + public static MonitorInfo from(CompositeData cd) {
106.46 + try {
106.47 + CompositeData stecd = (CompositeData) cd.get("lockedStackFrame");
106.48 + StackTraceElement ste = new StackTraceElement(
106.49 + (String) stecd.get("className"),
106.50 + (String) stecd.get("methodName"),
106.51 + (String) stecd.get("fileName"),
106.52 + (Integer) stecd.get("lineNumber"));
106.53 + return new MonitorInfo(
106.54 + (String) cd.get("className"),
106.55 + (Integer) cd.get("identityHashCode"),
106.56 + (Integer) cd.get("lockedStackDepth"),
106.57 + ste);
106.58 + } catch (Exception e) {
106.59 + throw new RuntimeException(e);
106.60 + }
106.61 + }
106.62 +
106.63 + public String getClassName() {
106.64 + return className;
106.65 + }
106.66 +
106.67 + public int getIdentityHashCode() {
106.68 + return identityHashCode;
106.69 + }
106.70 +
106.71 + public int getLockedStackDepth() {
106.72 + return lockedStackDepth;
106.73 + }
106.74 +
106.75 + public StackTraceElement getLockedStackFrame() {
106.76 + return lockedStackFrame;
106.77 + }
106.78 + }
106.79 +
106.80
106.81 public interface TestMXBean {
106.82
107.1 --- a/test/javax/management/mxbean/LeakTest.java Tue Aug 19 07:50:03 2008 -0700
107.2 +++ b/test/javax/management/mxbean/LeakTest.java Thu Aug 21 09:55:18 2008 -0700
107.3 @@ -25,7 +25,7 @@
107.4 * @bug 6482247
107.5 * @summary Test that creating MXBeans does not introduce memory leaks.
107.6 * @author Eamonn McManus
107.7 - * @run build LeakTest
107.8 + * @run build LeakTest RandomMXBeanTest
107.9 * @run main LeakTest
107.10 */
107.11
108.1 --- a/test/javax/management/mxbean/MBeanOperationInfoTest.java Tue Aug 19 07:50:03 2008 -0700
108.2 +++ b/test/javax/management/mxbean/MBeanOperationInfoTest.java Thu Aug 21 09:55:18 2008 -0700
108.3 @@ -86,7 +86,8 @@
108.4 if (error > 0) {
108.5 System.out.println("\nTEST FAILED");
108.6 throw new Exception("TEST FAILED: " + error + " wrong return types");
108.7 - } else if (tested != returnTypes.length) {
108.8 + } else if (tested != returnTypes.length &&
108.9 + !System.getProperty("java.specification.version").equals("1.5")) {
108.10 System.out.println("\nTEST FAILED");
108.11 throw new Exception("TEST FAILED: " + tested + " cases tested, " +
108.12 returnTypes.length + " expected");
109.1 --- a/test/javax/management/mxbean/MXBeanTest.java Tue Aug 19 07:50:03 2008 -0700
109.2 +++ b/test/javax/management/mxbean/MXBeanTest.java Thu Aug 21 09:55:18 2008 -0700
109.3 @@ -149,7 +149,7 @@
109.4 if (mbai.getName().equals("Ints")
109.5 && mbai.isReadable() && !mbai.isWritable()
109.6 && mbai.getDescriptor().getFieldValue("openType")
109.7 - .equals(new ArrayType(SimpleType.INTEGER, true))
109.8 + .equals(new ArrayType<int[]>(SimpleType.INTEGER, true))
109.9 && attrs[0].getType().equals("[I"))
109.10 success("MBeanAttributeInfo");
109.11 else
110.1 --- a/test/javax/management/mxbean/ThreadMXBeanTest.java Tue Aug 19 07:50:03 2008 -0700
110.2 +++ b/test/javax/management/mxbean/ThreadMXBeanTest.java Thu Aug 21 09:55:18 2008 -0700
110.3 @@ -46,7 +46,8 @@
110.4 long[] ids1 = proxy.getAllThreadIds();
110.5
110.6 // Add some random ids to the list so we'll get back null ThreadInfo
110.7 - long[] ids2 = Arrays.copyOf(ids1, ids1.length + 10);
110.8 + long[] ids2 = new long[ids1.length + 10];
110.9 + System.arraycopy(ids1, 0, ids2, 0, ids1.length);
110.10 Random r = new Random();
110.11 for (int i = ids1.length; i < ids2.length; i++)
110.12 ids2[i] = Math.abs(r.nextLong());
111.1 --- a/test/javax/management/mxbean/TigerMXBean.java Tue Aug 19 07:50:03 2008 -0700
111.2 +++ b/test/javax/management/mxbean/TigerMXBean.java Thu Aug 21 09:55:18 2008 -0700
111.3 @@ -83,20 +83,20 @@
111.4 Tuiseal opEnum(Tuiseal x, Tuiseal y);
111.5
111.6 List<String> StringList = Arrays.asList(new String[] {"a", "b", "x"});
111.7 - ArrayType StringListType =
111.8 + ArrayType<?> StringListType =
111.9 MerlinMXBean.ArrayTypeMaker.make(1, SimpleType.STRING);
111.10 List<String> getStringList();
111.11 void setStringList(List<String> x);
111.12 List<String> opStringList(List<String> x, List<String> y);
111.13
111.14 - Set<String> StringSet = new HashSet(StringList);
111.15 - ArrayType StringSetType = StringListType;
111.16 + Set<String> StringSet = new HashSet<String>(StringList);
111.17 + ArrayType<?> StringSetType = StringListType;
111.18 Set<String> getStringSet();
111.19 void setStringSet(Set<String> x);
111.20 Set<String> opStringSet(Set<String> x, Set<String> y);
111.21
111.22 - SortedSet<String> SortedStringSet = new TreeSet(StringList);
111.23 - ArrayType SortedStringSetType = StringListType;
111.24 + SortedSet<String> SortedStringSet = new TreeSet<String>(StringList);
111.25 + ArrayType<?> SortedStringSetType = StringListType;
111.26 SortedSet<String> getSortedStringSet();
111.27 void setSortedStringSet(SortedSet<String> x);
111.28 SortedSet<String> opSortedStringSet(SortedSet<String> x,
111.29 @@ -119,7 +119,7 @@
111.30 Map<String,List<String>> y);
111.31
111.32 SortedMap<String,String> XSortedMap =
111.33 - new TreeMap(Collections.singletonMap("foo", "bar"));
111.34 + new TreeMap<String,String>(Collections.singletonMap("foo", "bar"));
111.35 String XSortedMapTypeName =
111.36 "java.util.SortedMap<java.lang.String, java.lang.String>";
111.37 CompositeType XSortedMapRowType = MerlinMXBean.CompositeTypeMaker.make(
111.38 @@ -137,8 +137,8 @@
111.39
111.40 // For bug 6319960, try constructing Set and Map with non-Comparable
111.41
111.42 - Set<Point> PointSet = new HashSet(Collections.singleton(Point));
111.43 - ArrayType PointSetType =
111.44 + Set<Point> PointSet = new HashSet<Point>(Collections.singleton(Point));
111.45 + ArrayType<?> PointSetType =
111.46 MerlinMXBean.ArrayTypeMaker.make(1, PointType);
111.47 Set<Point> getPointSet();
111.48 void setPointSet(Set<Point> x);
112.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
112.2 +++ b/test/javax/management/openmbean/CompositeDataStringTest.java Thu Aug 21 09:55:18 2008 -0700
112.3 @@ -0,0 +1,89 @@
112.4 +/*
112.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
112.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
112.7 + *
112.8 + * This code is free software; you can redistribute it and/or modify it
112.9 + * under the terms of the GNU General Public License version 2 only, as
112.10 + * published by the Free Software Foundation.
112.11 + *
112.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
112.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
112.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
112.15 + * version 2 for more details (a copy is included in the LICENSE file that
112.16 + * accompanied this code).
112.17 + *
112.18 + * You should have received a copy of the GNU General Public License version
112.19 + * 2 along with this work; if not, write to the Free Software Foundation,
112.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
112.21 + *
112.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
112.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
112.24 + * have any questions.
112.25 + */
112.26 +
112.27 +import javax.management.openmbean.CompositeType;
112.28 +import javax.management.openmbean.OpenType;
112.29 +import javax.management.openmbean.SimpleType;
112.30 +
112.31 +/*
112.32 + * @test
112.33 + * @bug 6610174
112.34 + * @summary Test that CompositeDataSupport.toString() represents arrays correctly
112.35 + * @author Eamonn McManus
112.36 + */
112.37 +import javax.management.openmbean.ArrayType;
112.38 +import javax.management.openmbean.CompositeData;
112.39 +import javax.management.openmbean.CompositeDataSupport;
112.40 +
112.41 +public class CompositeDataStringTest {
112.42 + public static void main(String[] args) throws Exception {
112.43 + CompositeType basicCT = new CompositeType(
112.44 + "basicCT", "basic CompositeType",
112.45 + new String[] {"name", "value"},
112.46 + new String[] {"name", "value"},
112.47 + new OpenType<?>[] {SimpleType.STRING, SimpleType.INTEGER});
112.48 + CompositeType ct = new CompositeType(
112.49 + "noddy", "descr",
112.50 + new String[] {"strings", "ints", "cds"},
112.51 + new String[] {"string array", "int array", "composite data array"},
112.52 + new OpenType<?>[] {
112.53 + ArrayType.getArrayType(SimpleType.STRING),
112.54 + ArrayType.getPrimitiveArrayType(int[].class),
112.55 + ArrayType.getArrayType(basicCT)
112.56 + });
112.57 + CompositeData basicCD1 = new CompositeDataSupport(
112.58 + basicCT, new String[] {"name", "value"}, new Object[] {"ceathar", 4});
112.59 + CompositeData basicCD2 = new CompositeDataSupport(
112.60 + basicCT, new String[] {"name", "value"}, new Object[] {"naoi", 9});
112.61 + CompositeData cd = new CompositeDataSupport(
112.62 + ct,
112.63 + new String[] {"strings", "ints", "cds"},
112.64 + new Object[] {
112.65 + new String[] {"fred", "jim", "sheila"},
112.66 + new int[] {2, 3, 5, 7},
112.67 + new CompositeData[] {basicCD1, basicCD2}
112.68 + });
112.69 + String s = cd.toString();
112.70 + System.out.println("CompositeDataSupport.toString(): " + s);
112.71 + String[] expected = {
112.72 + "fred, jim, sheila",
112.73 + "2, 3, 5, 7",
112.74 + "ceathar",
112.75 + "naoi",
112.76 + };
112.77 + boolean ok = true;
112.78 + for (String expect : expected) {
112.79 + if (s.contains(expect))
112.80 + System.out.println("OK: string contains <" + expect + ">");
112.81 + else {
112.82 + ok = false;
112.83 + System.out.println("NOT OK: string does not contain <" +
112.84 + expect + ">");
112.85 + }
112.86 + }
112.87 + if (ok)
112.88 + System.out.println("TEST PASSED");
112.89 + else
112.90 + throw new Exception("TEST FAILED: string did not contain expected substrings");
112.91 + }
112.92 +}
113.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
113.2 +++ b/test/javax/management/openmbean/TabularDataOrderTest.java Thu Aug 21 09:55:18 2008 -0700
113.3 @@ -0,0 +1,190 @@
113.4 +/*
113.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
113.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
113.7 + *
113.8 + * This code is free software; you can redistribute it and/or modify it
113.9 + * under the terms of the GNU General Public License version 2 only, as
113.10 + * published by the Free Software Foundation.
113.11 + *
113.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
113.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
113.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
113.15 + * version 2 for more details (a copy is included in the LICENSE file that
113.16 + * accompanied this code).
113.17 + *
113.18 + * You should have received a copy of the GNU General Public License version
113.19 + * 2 along with this work; if not, write to the Free Software Foundation,
113.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
113.21 + *
113.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
113.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
113.24 + * have any questions.
113.25 + */
113.26 +
113.27 +/*
113.28 + * @test
113.29 + * @bug 6334663
113.30 + * @summary Test that TabularDataSupport preserves the order elements were added
113.31 + * @author Eamonn McManus
113.32 + */
113.33 +
113.34 +import java.io.ByteArrayInputStream;
113.35 +import java.io.ByteArrayOutputStream;
113.36 +import java.io.ObjectInputStream;
113.37 +import java.io.ObjectOutputStream;
113.38 +import java.lang.reflect.Field;
113.39 +import java.lang.reflect.Modifier;
113.40 +import java.util.ArrayList;
113.41 +import java.util.HashMap;
113.42 +import java.util.LinkedHashMap;
113.43 +import java.util.List;
113.44 +import java.util.Map;
113.45 +import javax.management.JMX;
113.46 +import javax.management.MBeanServer;
113.47 +import javax.management.MBeanServerFactory;
113.48 +import javax.management.ObjectName;
113.49 +import javax.management.openmbean.CompositeData;
113.50 +import javax.management.openmbean.CompositeDataSupport;
113.51 +import javax.management.openmbean.CompositeType;
113.52 +import javax.management.openmbean.OpenDataException;
113.53 +import javax.management.openmbean.OpenType;
113.54 +import javax.management.openmbean.SimpleType;
113.55 +import javax.management.openmbean.TabularData;
113.56 +import javax.management.openmbean.TabularDataSupport;
113.57 +import javax.management.openmbean.TabularType;
113.58 +
113.59 +public class TabularDataOrderTest {
113.60 + private static String failure;
113.61 +
113.62 + private static final String COMPAT_PROP_NAME = "jmx.tabular.data.hash.map";
113.63 +
113.64 + private static final String[] intNames = {
113.65 + "unus", "duo", "tres", "quatuor", "quinque", "sex", "septem",
113.66 + "octo", "novem", "decim",
113.67 + };
113.68 + private static final Map<String, Integer> stringToValue =
113.69 + new LinkedHashMap<String, Integer>();
113.70 + static {
113.71 + for (int i = 0; i < intNames.length; i++)
113.72 + stringToValue.put(intNames[i], i + 1);
113.73 + }
113.74 +
113.75 + public static interface TestMXBean {
113.76 + public Map<String, Integer> getMap();
113.77 + }
113.78 +
113.79 + public static class TestImpl implements TestMXBean {
113.80 + public Map<String, Integer> getMap() {
113.81 + return stringToValue;
113.82 + }
113.83 + }
113.84 +
113.85 + private static final CompositeType ct;
113.86 + private static final TabularType tt;
113.87 + static {
113.88 + try {
113.89 + ct = new CompositeType(
113.90 + "a.b.c", "name and int",
113.91 + new String[] {"name", "int"},
113.92 + new String[] {"name of integer", "value of integer"},
113.93 + new OpenType<?>[] {SimpleType.STRING, SimpleType.INTEGER});
113.94 + tt = new TabularType(
113.95 + "d.e.f", "name and int indexed by name", ct,
113.96 + new String[] {"name"});
113.97 + } catch (OpenDataException e) {
113.98 + throw new AssertionError(e);
113.99 + }
113.100 + }
113.101 +
113.102 + private static TabularData makeTable() throws OpenDataException {
113.103 + TabularData td = new TabularDataSupport(tt);
113.104 + for (Map.Entry<String, Integer> entry : stringToValue.entrySet()) {
113.105 + CompositeData cd = new CompositeDataSupport(
113.106 + ct,
113.107 + new String[] {"name", "int"},
113.108 + new Object[] {entry.getKey(), entry.getValue()});
113.109 + td.put(cd);
113.110 + }
113.111 + return td;
113.112 + }
113.113 +
113.114 + public static void main(String[] args) throws Exception {
113.115 + System.out.println("Testing standard behaviour");
113.116 + TabularData td = makeTable();
113.117 + System.out.println(td);
113.118 +
113.119 + // Test that a default TabularData has the order keys were added in
113.120 + int last = 0;
113.121 + boolean ordered = true;
113.122 + for (Object x : td.values()) {
113.123 + CompositeData cd = (CompositeData) x;
113.124 + String name = (String) cd.get("name");
113.125 + int value = (Integer) cd.get("int");
113.126 + System.out.println(name + " = " + value);
113.127 + if (last + 1 != value)
113.128 + ordered = false;
113.129 + last = value;
113.130 + }
113.131 + if (!ordered)
113.132 + fail("Order not preserved");
113.133 +
113.134 + // Now test the undocumented property that causes HashMap to be used
113.135 + // instead of LinkedHashMap, in case serializing to a 1.3 client.
113.136 + // We serialize and deserialize in case the implementation handles
113.137 + // this at serialization time. Then we look at object fields; that's
113.138 + // not guaranteed to work but at worst it will fail spuriously and
113.139 + // we'll have to update the test.
113.140 + System.out.println("Testing compatible behaviour");
113.141 + System.setProperty(COMPAT_PROP_NAME, "true");
113.142 + td = makeTable();
113.143 + System.out.println(td);
113.144 + ByteArrayOutputStream bout = new ByteArrayOutputStream();
113.145 + ObjectOutputStream oout = new ObjectOutputStream(bout);
113.146 + oout.writeObject(td);
113.147 + oout.close();
113.148 + byte[] bytes = bout.toByteArray();
113.149 + ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
113.150 + ObjectInputStream oin = new ObjectInputStream(bin);
113.151 + td = (TabularData) oin.readObject();
113.152 + boolean found = false;
113.153 + for (Field f : td.getClass().getDeclaredFields()) {
113.154 + if (Modifier.isStatic(f.getModifiers()))
113.155 + continue;
113.156 + f.setAccessible(true);
113.157 + Object x = f.get(td);
113.158 + if (x != null && x.getClass() == HashMap.class) {
113.159 + found = true;
113.160 + System.out.println(
113.161 + x.getClass().getName() + " TabularDataSupport." +
113.162 + f.getName() + " = " + x);
113.163 + break;
113.164 + }
113.165 + }
113.166 + if (!found) {
113.167 + fail("TabularDataSupport does not contain HashMap though " +
113.168 + COMPAT_PROP_NAME + "=true");
113.169 + }
113.170 + System.clearProperty(COMPAT_PROP_NAME);
113.171 +
113.172 + System.out.println("Testing MXBean behaviour");
113.173 + MBeanServer mbs = MBeanServerFactory.newMBeanServer();
113.174 + ObjectName name = new ObjectName("a:b=c");
113.175 + mbs.registerMBean(new TestImpl(), name);
113.176 + TestMXBean proxy = JMX.newMXBeanProxy(mbs, name, TestMXBean.class);
113.177 + Map<String, Integer> map = proxy.getMap();
113.178 + List<String> origNames = new ArrayList<String>(stringToValue.keySet());
113.179 + List<String> proxyNames = new ArrayList<String>(map.keySet());
113.180 + if (!origNames.equals(proxyNames))
113.181 + fail("Order mangled after passage through MXBean: " + proxyNames);
113.182 +
113.183 + if (failure == null)
113.184 + System.out.println("TEST PASSED");
113.185 + else
113.186 + throw new Exception("TEST FAILED: " + failure);
113.187 + }
113.188 +
113.189 + private static void fail(String why) {
113.190 + System.out.println("FAILED: " + why);
113.191 + failure = why;
113.192 + }
113.193 +}
114.1 --- a/test/javax/management/query/QueryNotifFilterTest.java Tue Aug 19 07:50:03 2008 -0700
114.2 +++ b/test/javax/management/query/QueryNotifFilterTest.java Thu Aug 21 09:55:18 2008 -0700
114.3 @@ -98,7 +98,7 @@
114.4 this.queryString = queryString;
114.5 }
114.6 abstract boolean apply(MBeanServer mbs, ObjectName name) throws Exception;
114.7 - @Override
114.8 + //@Override - doesn't override in JDK5
114.9 public boolean apply(ObjectName name) {
114.10 try {
114.11 return apply(getMBeanServer(), name);
115.1 --- a/test/javax/management/remote/mandatory/connection/CloseServerTest.java Tue Aug 19 07:50:03 2008 -0700
115.2 +++ b/test/javax/management/remote/mandatory/connection/CloseServerTest.java Thu Aug 21 09:55:18 2008 -0700
115.3 @@ -31,11 +31,16 @@
115.4 * @run main CloseServerTest
115.5 */
115.6
115.7 +import com.sun.jmx.remote.util.EnvHelp;
115.8 import java.net.MalformedURLException;
115.9 -import java.io.IOException;
115.10
115.11 +import java.util.Arrays;
115.12 +import java.util.Collections;
115.13 +import java.util.List;
115.14 +import java.util.Map;
115.15 import javax.management.*;
115.16 import javax.management.remote.*;
115.17 +import javax.management.remote.rmi.RMIConnectorServer;
115.18
115.19 public class CloseServerTest {
115.20 private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
115.21 @@ -131,40 +136,54 @@
115.22
115.23 server.stop();
115.24
115.25 - // with a client listener, but close the server first
115.26 - System.out.println(">>> Open, start a server, create a client, add a listener, close the server then the client.");
115.27 - server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
115.28 - server.start();
115.29 + List<Map<String, String>> envs = Arrays.asList(
115.30 + Collections.singletonMap(
115.31 + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false"),
115.32 + Collections.singletonMap(
115.33 + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "true"));
115.34
115.35 - addr = server.getAddress();
115.36 - client = JMXConnectorFactory.newJMXConnector(addr, null);
115.37 - client.connect(null);
115.38 + for (Map<String, String> env : envs) {
115.39 + System.out.println(
115.40 + ">>>>>>>> " + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE +
115.41 + " = " + env.get(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE));
115.42
115.43 - mserver = client.getMBeanServerConnection();
115.44 - mserver.addNotificationListener(delegateName, dummyListener, null, null);
115.45 + // with a client listener, but close the server first
115.46 + System.out.println(">>> Open, start a server, create a client, " +
115.47 + "add a listener, close the server then the client.");
115.48 + server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
115.49 + server.start();
115.50
115.51 - server.stop();
115.52 + addr = server.getAddress();
115.53 + client = JMXConnectorFactory.newJMXConnector(addr, null);
115.54 + client.connect(null);
115.55
115.56 - try {
115.57 + mserver = client.getMBeanServerConnection();
115.58 + mserver.addNotificationListener(delegateName, dummyListener, null, null);
115.59 +
115.60 + server.stop();
115.61 +
115.62 + try {
115.63 + client.close();
115.64 + } catch (Exception e) {
115.65 + // ok, it is because the server has been closed.
115.66 + }
115.67 +
115.68 + // with a client listener, but close the client first
115.69 + System.out.println(">>> Open, start a server, create a client, " +
115.70 + "add a listener, close the client then the server.");
115.71 + server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
115.72 + server.start();
115.73 +
115.74 + addr = server.getAddress();
115.75 + client = JMXConnectorFactory.newJMXConnector(addr, null);
115.76 + client.connect(null);
115.77 +
115.78 + mserver = client.getMBeanServerConnection();
115.79 + mserver.addNotificationListener(delegateName, dummyListener, null, null);
115.80 +
115.81 client.close();
115.82 - } catch (Exception e) {
115.83 - // ok, it is because the server has been closed.
115.84 + server.stop();
115.85 }
115.86 -
115.87 - // with a client listener, but close the client first
115.88 - System.out.println(">>> Open, start a server, create a client, add a listener, close the client then the server.");
115.89 - server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
115.90 - server.start();
115.91 -
115.92 - addr = server.getAddress();
115.93 - client = JMXConnectorFactory.newJMXConnector(addr, null);
115.94 - client.connect(null);
115.95 -
115.96 - mserver = client.getMBeanServerConnection();
115.97 - mserver.addNotificationListener(delegateName, dummyListener, null, null);
115.98 -
115.99 - client.close();
115.100 - server.stop();
115.101 } catch (MalformedURLException e) {
115.102 System.out.println(">>> Skipping unsupported URL " + u);
115.103 return true;
116.1 --- a/test/javax/management/remote/mandatory/connection/DeadLockTest.java Tue Aug 19 07:50:03 2008 -0700
116.2 +++ b/test/javax/management/remote/mandatory/connection/DeadLockTest.java Thu Aug 21 09:55:18 2008 -0700
116.3 @@ -37,6 +37,7 @@
116.4
116.5 import javax.management.*;
116.6 import javax.management.remote.*;
116.7 +import javax.management.remote.rmi.RMIConnectorServer;
116.8
116.9 public class DeadLockTest {
116.10 private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
116.11 @@ -72,6 +73,9 @@
116.12 // disable the client ping
116.13 env.put("jmx.remote.x.client.connection.check.period", "0");
116.14
116.15 + // ensure we are not internally using the Event Service on the server
116.16 + env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
116.17 +
116.18 try {
116.19 u = new JMXServiceURL(proto, null, 0);
116.20 server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
117.1 --- a/test/javax/management/remote/mandatory/connection/IdleTimeoutTest.java Tue Aug 19 07:50:03 2008 -0700
117.2 +++ b/test/javax/management/remote/mandatory/connection/IdleTimeoutTest.java Thu Aug 21 09:55:18 2008 -0700
117.3 @@ -50,6 +50,8 @@
117.4 import javax.management.remote.JMXConnectorServerFactory;
117.5 import javax.management.remote.JMXServiceURL;
117.6 import com.sun.jmx.remote.util.EnvHelp;
117.7 +import java.util.Collections;
117.8 +import javax.management.remote.rmi.RMIConnectorServer;
117.9
117.10 public class IdleTimeoutTest {
117.11 public static void main(String[] args) throws Exception {
117.12 @@ -88,8 +90,13 @@
117.13
117.14 private static long getIdleTimeout(MBeanServer mbs, JMXServiceURL url)
117.15 throws Exception {
117.16 + // If the connector server is using the Event Service, then connections
117.17 + // never time out. This is by design.
117.18 + Map<String, String> env =
117.19 + Collections.singletonMap(
117.20 + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
117.21 JMXConnectorServer server =
117.22 - JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
117.23 + JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
117.24 server.start();
117.25 try {
117.26 url = server.getAddress();
117.27 @@ -164,6 +171,7 @@
117.28
117.29 Map idleMap = new HashMap();
117.30 idleMap.put(EnvHelp.SERVER_CONNECTION_TIMEOUT, new Long(timeout));
117.31 + idleMap.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
117.32 JMXConnectorServer server =
117.33 JMXConnectorServerFactory.newJMXConnectorServer(url,idleMap,mbs);
117.34
118.1 --- a/test/javax/management/remote/mandatory/connection/RMIExitTest.java Tue Aug 19 07:50:03 2008 -0700
118.2 +++ b/test/javax/management/remote/mandatory/connection/RMIExitTest.java Thu Aug 21 09:55:18 2008 -0700
118.3 @@ -35,6 +35,8 @@
118.4 import java.net.MalformedURLException;
118.5 import java.io.IOException;
118.6
118.7 +import java.util.Collections;
118.8 +import java.util.Map;
118.9 import javax.management.MBeanServerFactory;
118.10 import javax.management.MBeanServer;
118.11 import javax.management.ObjectName;
118.12 @@ -47,12 +49,14 @@
118.13 import javax.management.remote.JMXConnector;
118.14 import javax.management.remote.JMXConnectorServerFactory;
118.15 import javax.management.remote.JMXConnectorFactory;
118.16 +import javax.management.remote.rmi.RMIConnectorServer;
118.17
118.18 /**
118.19 * VM shutdown hook. Test that the hook is called less than 5 secs
118.20 * after expected exit.
118.21 */
118.22 class TimeChecker extends Thread {
118.23 + @Override
118.24 public void run() {
118.25 System.out.println("shutdown hook called");
118.26 long elapsedTime =
118.27 @@ -81,12 +85,15 @@
118.28 public static void main(String[] args) {
118.29 System.out.println("Start test");
118.30 Runtime.getRuntime().addShutdownHook(new TimeChecker());
118.31 - test();
118.32 + test(false);
118.33 + test(true);
118.34 exitStartTime = System.currentTimeMillis();
118.35 System.out.println("End test");
118.36 }
118.37
118.38 - private static void test() {
118.39 + private static void test(boolean eventService) {
118.40 + System.out.println(
118.41 + "---testing with" + (eventService ? "" : "out") + " Event Service");
118.42 try {
118.43 JMXServiceURL u = new JMXServiceURL("rmi", null, 0);
118.44 JMXConnectorServer server;
118.45 @@ -105,8 +112,11 @@
118.46 }
118.47 };
118.48
118.49 + Map<String, String> env = Collections.singletonMap(
118.50 + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
118.51 + Boolean.toString(eventService));
118.52 server = JMXConnectorServerFactory.newJMXConnectorServer(u,
118.53 - null,
118.54 + env,
118.55 mbs);
118.56 server.start();
118.57
119.1 --- a/test/javax/management/remote/mandatory/connection/ReconnectTest.java Tue Aug 19 07:50:03 2008 -0700
119.2 +++ b/test/javax/management/remote/mandatory/connection/ReconnectTest.java Thu Aug 21 09:55:18 2008 -0700
119.3 @@ -33,10 +33,10 @@
119.4
119.5 import java.util.*;
119.6 import java.net.MalformedURLException;
119.7 -import java.io.IOException;
119.8
119.9 import javax.management.*;
119.10 import javax.management.remote.*;
119.11 +import javax.management.remote.rmi.RMIConnectorServer;
119.12
119.13 public class ReconnectTest {
119.14 private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
119.15 @@ -48,6 +48,7 @@
119.16 String timeout = "1000";
119.17 env.put("jmx.remote.x.server.connection.timeout", timeout);
119.18 env.put("jmx.remote.x.client.connection.check.period", timeout);
119.19 + env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
119.20 }
119.21
119.22 public static void main(String[] args) throws Exception {
120.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
120.2 +++ b/test/javax/management/remote/mandatory/connectorServer/ForwarderChainTest.java Thu Aug 21 09:55:18 2008 -0700
120.3 @@ -0,0 +1,274 @@
120.4 +/*
120.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
120.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
120.7 + *
120.8 + * This code is free software; you can redistribute it and/or modify it
120.9 + * under the terms of the GNU General Public License version 2 only, as
120.10 + * published by the Free Software Foundation.
120.11 + *
120.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
120.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
120.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
120.15 + * version 2 for more details (a copy is included in the LICENSE file that
120.16 + * accompanied this code).
120.17 + *
120.18 + * You should have received a copy of the GNU General Public License version
120.19 + * 2 along with this work; if not, write to the Free Software Foundation,
120.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
120.21 + *
120.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
120.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
120.24 + * have any questions.
120.25 + */
120.26 +
120.27 +import java.util.NoSuchElementException;
120.28 +import java.util.Random;
120.29 +import javax.management.MBeanServer;
120.30 +import javax.management.MBeanServerConnection;
120.31 +import javax.management.MBeanServerFactory;
120.32 +import javax.management.remote.IdentityMBeanServerForwarder;
120.33 +import javax.management.remote.JMXConnector;
120.34 +import javax.management.remote.JMXConnectorFactory;
120.35 +import javax.management.remote.JMXConnectorServer;
120.36 +import javax.management.remote.JMXServiceURL;
120.37 +import javax.management.remote.MBeanServerForwarder;
120.38 +
120.39 +/*
120.40 + * @test
120.41 + * @bug 6218920
120.42 + * @summary Tests manipulation of MBeanServerForwarder chains.
120.43 + * @author Eamonn McManus
120.44 + */
120.45 +import javax.management.remote.rmi.RMIConnectorServer;
120.46 +
120.47 +public class ForwarderChainTest {
120.48 + private static final TestMBeanServerForwarder[] forwarders =
120.49 + new TestMBeanServerForwarder[10];
120.50 + static {
120.51 + for (int i = 0; i < forwarders.length; i++)
120.52 + forwarders[i] = new TestMBeanServerForwarder(i);
120.53 + }
120.54 +
120.55 + private static class TestMBeanServerForwarder
120.56 + extends IdentityMBeanServerForwarder {
120.57 + private final int index;
120.58 + volatile int defaultDomainCount;
120.59 +
120.60 + TestMBeanServerForwarder(int index) {
120.61 + this.index = index;
120.62 + }
120.63 +
120.64 + @Override
120.65 + public String getDefaultDomain() {
120.66 + defaultDomainCount++;
120.67 + return super.getDefaultDomain();
120.68 + }
120.69 +
120.70 + @Override
120.71 + public String toString() {
120.72 + return "forwarders[" + index + "]";
120.73 + }
120.74 + }
120.75 +
120.76 + private static String failure;
120.77 +
120.78 + public static void main(String[] args) throws Exception {
120.79 +
120.80 + System.out.println("===Test with newly created, unattached server===");
120.81 +
120.82 + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///");
120.83 + JMXConnectorServer cs = new RMIConnectorServer(url, null);
120.84 + test(cs, null);
120.85 +
120.86 + System.out.println("===Test with server attached to MBS===");
120.87 + MBeanServer mbs = MBeanServerFactory.newMBeanServer();
120.88 + cs = new RMIConnectorServer(url, null, mbs);
120.89 + test(cs, mbs);
120.90 +
120.91 + System.out.println("===Remove any leftover forwarders===");
120.92 + while (cs.getSystemMBeanServer() instanceof MBeanServerForwarder) {
120.93 + MBeanServerForwarder mbsf =
120.94 + (MBeanServerForwarder) cs.getSystemMBeanServer();
120.95 + cs.removeMBeanServerForwarder(mbsf);
120.96 + }
120.97 + expectChain(cs, "U", mbs);
120.98 +
120.99 + System.out.println("===Ensure forwarders are called===");
120.100 + cs.setMBeanServerForwarder(forwarders[0]);
120.101 + cs.setSystemMBeanServerForwarder(forwarders[1]);
120.102 + expectChain(cs, "1U0", mbs);
120.103 + cs.start();
120.104 + if (forwarders[0].defaultDomainCount != 0 ||
120.105 + forwarders[1].defaultDomainCount != 0) {
120.106 + fail("defaultDomainCount not zero");
120.107 + }
120.108 + JMXServiceURL addr = cs.getAddress();
120.109 + JMXConnector cc = JMXConnectorFactory.connect(addr);
120.110 + MBeanServerConnection mbsc = cc.getMBeanServerConnection();
120.111 + mbsc.getDefaultDomain();
120.112 + cc.close();
120.113 + cs.stop();
120.114 + for (boolean system : new boolean[] {false, true}) {
120.115 + TestMBeanServerForwarder mbsf = system ? forwarders[1] : forwarders[0];
120.116 + if (mbsf.defaultDomainCount != 1) {
120.117 + fail((system ? "System" : "User") + " forwarder called " +
120.118 + mbsf.defaultDomainCount + " times");
120.119 + }
120.120 + }
120.121 +
120.122 + if (failure == null)
120.123 + System.out.println("TEST PASSED");
120.124 + else
120.125 + throw new Exception("TEST FAILED: " + failure);
120.126 + }
120.127 +
120.128 + private static void test(JMXConnectorServer cs, MBeanServer end) {
120.129 + // A newly-created connector server might have system forwarders,
120.130 + // so get rid of those.
120.131 + while (cs.getSystemMBeanServer() != cs.getMBeanServer())
120.132 + cs.removeMBeanServerForwarder((MBeanServerForwarder) cs.getSystemMBeanServer());
120.133 +
120.134 + expectChain(cs, "U", end);
120.135 +
120.136 + System.out.println("Add a user forwarder");
120.137 + cs.setMBeanServerForwarder(forwarders[0]);
120.138 + expectChain(cs, "U0", end);
120.139 +
120.140 + System.out.println("Add another user forwarder");
120.141 + cs.setMBeanServerForwarder(forwarders[1]);
120.142 + expectChain(cs, "U10", end);
120.143 +
120.144 + System.out.println("Add a system forwarder");
120.145 + cs.setSystemMBeanServerForwarder(forwarders[2]);
120.146 + expectChain(cs, "2U10", end);
120.147 +
120.148 + System.out.println("Add another user forwarder");
120.149 + cs.setMBeanServerForwarder(forwarders[3]);
120.150 + expectChain(cs, "2U310", end);
120.151 +
120.152 + System.out.println("Add another system forwarder");
120.153 + cs.setSystemMBeanServerForwarder(forwarders[4]);
120.154 + expectChain(cs, "42U310", end);
120.155 +
120.156 + System.out.println("Remove the first user forwarder");
120.157 + cs.removeMBeanServerForwarder(forwarders[3]);
120.158 + expectChain(cs, "42U10", end);
120.159 +
120.160 + System.out.println("Remove the last user forwarder");
120.161 + cs.removeMBeanServerForwarder(forwarders[0]);
120.162 + expectChain(cs, "42U1", end);
120.163 +
120.164 + System.out.println("Remove the first system forwarder");
120.165 + cs.removeMBeanServerForwarder(forwarders[4]);
120.166 + expectChain(cs, "2U1", end);
120.167 +
120.168 + System.out.println("Remove the last system forwarder");
120.169 + cs.removeMBeanServerForwarder(forwarders[2]);
120.170 + expectChain(cs, "U1", end);
120.171 +
120.172 + System.out.println("Remove the last forwarder");
120.173 + cs.removeMBeanServerForwarder(forwarders[1]);
120.174 + expectChain(cs, "U", end);
120.175 +
120.176 + System.out.println("---Doing random manipulations---");
120.177 + // In this loop we pick one of the forwarders at random each time.
120.178 + // If it is already in the chain, then we remove it. If it is not
120.179 + // in the chain, then we do one of three things: try to remove it
120.180 + // (expecting an exception); add it to the user chain; or add it
120.181 + // to the system chain.
120.182 + // A subtle point is that if there is no MBeanServer then
120.183 + // cs.setMBeanServerForwarder(mbsf) does not change mbsf.getMBeanServer().
120.184 + // Since we're recycling a random forwarder[i], we explicitly
120.185 + // call mbsf.setMBeanServer(null) in this case.
120.186 + String chain = "U";
120.187 + Random r = new Random();
120.188 + for (int i = 0; i < 50; i++) {
120.189 + int fwdi = r.nextInt(10);
120.190 + MBeanServerForwarder mbsf = forwarders[fwdi];
120.191 + char c = (char) ('0' + fwdi);
120.192 + int ci = chain.indexOf(c);
120.193 + if (ci >= 0) {
120.194 + System.out.println("Remove " + c);
120.195 + cs.removeMBeanServerForwarder(mbsf);
120.196 + chain = chain.substring(0, ci) + chain.substring(ci + 1);
120.197 + } else {
120.198 + switch (r.nextInt(3)) {
120.199 + case 0: { // try to remove it
120.200 + try {
120.201 + System.out.println("Try to remove absent " + c);
120.202 + cs.removeMBeanServerForwarder(mbsf);
120.203 + fail("Remove succeeded but should not have");
120.204 + return;
120.205 + } catch (NoSuchElementException e) {
120.206 + }
120.207 + break;
120.208 + }
120.209 + case 1: { // add it to the user chain
120.210 + System.out.println("Add " + c + " to user chain");
120.211 + if (cs.getMBeanServer() == null)
120.212 + mbsf.setMBeanServer(null);
120.213 + cs.setMBeanServerForwarder(mbsf);
120.214 + int postu = chain.indexOf('U') + 1;
120.215 + chain = chain.substring(0, postu) + c +
120.216 + chain.substring(postu);
120.217 + break;
120.218 + }
120.219 + case 2: { // add it to the system chain
120.220 + System.out.println("Add " + c + " to system chain");
120.221 + if (cs.getSystemMBeanServer() == null)
120.222 + mbsf.setMBeanServer(null);
120.223 + cs.setSystemMBeanServerForwarder(mbsf);
120.224 + chain = c + chain;
120.225 + break;
120.226 + }
120.227 + }
120.228 + }
120.229 + expectChain(cs, chain, end);
120.230 + }
120.231 + }
120.232 +
120.233 + /*
120.234 + * Check that the forwarder chain has the expected contents. The forwarders
120.235 + * are encoded as a string. For example, "12U34" means that the system
120.236 + * chain contains forwarders[1] followed by forwarders[2], and the user
120.237 + * chain contains forwarders[3] followed by forwarders[4]. Since the
120.238 + * user chain is attached to the end of the system chain, another way to
120.239 + * look at this is that the U marks the transition from one to the other.
120.240 + *
120.241 + * After traversing the chains, we should be pointing at "end".
120.242 + */
120.243 + private static void expectChain(
120.244 + JMXConnectorServer cs, String chain, MBeanServer end) {
120.245 + System.out.println("...expected chain: " + chain);
120.246 + MBeanServer curr = cs.getSystemMBeanServer();
120.247 + int i = 0;
120.248 + while (i < chain.length()) {
120.249 + char c = chain.charAt(i);
120.250 + if (c == 'U') {
120.251 + if (cs.getMBeanServer() != curr) {
120.252 + fail("User chain should have started here: " + curr);
120.253 + return;
120.254 + }
120.255 + } else {
120.256 + int fwdi = c - '0';
120.257 + MBeanServerForwarder forwarder = forwarders[fwdi];
120.258 + if (curr != forwarder) {
120.259 + fail("Expected forwarder " + c + " here: " + curr);
120.260 + return;
120.261 + }
120.262 + curr = ((MBeanServerForwarder) curr).getMBeanServer();
120.263 + }
120.264 + i++;
120.265 + }
120.266 + if (curr != end) {
120.267 + fail("End of chain is " + curr + ", should be " + end);
120.268 + return;
120.269 + }
120.270 + System.out.println("...OK");
120.271 + }
120.272 +
120.273 + private static void fail(String msg) {
120.274 + System.out.println("FAILED: " + msg);
120.275 + failure = msg;
120.276 + }
120.277 +}
121.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
121.2 +++ b/test/javax/management/remote/mandatory/connectorServer/StandardForwardersTest.java Thu Aug 21 09:55:18 2008 -0700
121.3 @@ -0,0 +1,177 @@
121.4 +/*
121.5 + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
121.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
121.7 + *
121.8 + * This code is free software; you can redistribute it and/or modify it
121.9 + * under the terms of the GNU General Public License version 2 only, as
121.10 + * published by the Free Software Foundation.
121.11 + *
121.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
121.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
121.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
121.15 + * version 2 for more details (a copy is included in the LICENSE file that
121.16 + * accompanied this code).
121.17 + *
121.18 + * You should have received a copy of the GNU General Public License version
121.19 + * 2 along with this work; if not, write to the Free Software Foundation,
121.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
121.21 + *
121.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
121.23 + * CA 95054 USA or visit www.sun.com if you need additional information or
121.24 + * have any questions.
121.25 + */
121.26 +
121.27 +import java.lang.management.ManagementFactory;
121.28 +import java.util.ArrayList;
121.29 +import java.util.HashMap;
121.30 +import java.util.List;
121.31 +import java.util.Map;
121.32 +import javax.management.MBeanServer;
121.33 +import javax.management.event.EventClientDelegate;
121.34 +import javax.management.remote.JMXConnectorServer;
121.35 +
121.36 +/*
121.37 + * @test
121.38 + * @bug 6663757
121.39 + * @summary Tests standard MBeanServerForwarders introduced by connector server
121.40 + * options.
121.41 + * @author Eamonn McManus
121.42 + */
121.43 +import javax.management.remote.JMXConnectorServerFactory;
121.44 +import javax.management.remote.JMXServiceURL;
121.45 +import javax.management.remote.MBeanServerForwarder;
121.46 +
121.47 +public class StandardForwardersTest {
121.48 + private static String failure;
121.49 +
121.50 + private static class Forwarder {
121.51 + private final String attribute;
121.52 + private final boolean defaultEnabled;
121.53 + private final Class<?> expectedClass;
121.54 +
121.55 + public Forwarder(String attribute, boolean defaultEnabled,
121.56 + Class<?> expectedClass) {
121.57 + this.attribute = attribute;
121.58 + this.defaultEnabled = defaultEnabled;
121.59 + this.expectedClass = expectedClass;
121.60 + }
121.61 + }
121.62 +
121.63 + private static enum Status {DISABLED, ENABLED, DEFAULT}
121.64 +
121.65 + public static void main(String[] args) throws Exception {
121.66 + MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
121.67 +
121.68 + MBeanServerForwarder ecdFwd =
121.69 + EventClientDelegate.newForwarder();
121.70 + Forwarder ecd = new Forwarder(
121.71 + JMXConnectorServer.EVENT_CLIENT_DELEGATE_FORWARDER, true,
121.72 + ecdFwd.getClass());
121.73 +
121.74 + Forwarder[] forwarders = {ecd};
121.75 +
121.76 + // Now go through every combination of forwarders. Each forwarder
121.77 + // may be explicitly enabled, explicitly disabled, or left to its
121.78 + // default value.
121.79 + int nStatus = Status.values().length;
121.80 + int limit = (int) Math.pow(nStatus, forwarders.length);
121.81 + for (int i = 0; i < limit; i++) {
121.82 + Status[] status = new Status[forwarders.length];
121.83 + int ii = i;
121.84 + for (int j = 0; j < status.length; j++) {
121.85 + status[j] = Status.values()[ii % nStatus];
121.86 + ii /= nStatus;
121.87 + }
121.88 + Map<String, String> env = new HashMap<String, String>();
121.89 + String test = "";
121.90 + for (int j = 0; j < status.length; j++) {
121.91 + if (!test.equals(""))
121.92 + test += "; ";
121.93 + test += forwarders[j].attribute;
121.94 + switch (status[j]) {
121.95 + case DISABLED:
121.96 + test += "=false";
121.97 + env.put(forwarders[j].attribute, "false");
121.98 + break;
121.99 + case ENABLED:
121.100 + test += "=true";
121.101 + env.put(forwarders[j].attribute, "true");
121.102 + break;
121.103 + case DEFAULT:
121.104 + test += "=default(" + forwarders[j].defaultEnabled + ")";
121.105 + break;
121.106 + }
121.107 + }
121.108 + boolean consistent = isConsistent(env);
121.109 + test += "; (" + (consistent ? "" : "in") + "consistent)";
121.110 + System.out.println(test);
121.111 + JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///");
121.112 + try {
121.113 + JMXConnectorServer cs =
121.114 + JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
121.115 + if (!consistent) {
121.116 + fail("Inconsistent attributes should have been rejected " +
121.117 + "but were not");
121.118 + }
121.119 + checkForwarders(cs, forwarders, status);
121.120 + } catch (IllegalArgumentException e) {
121.121 + if (consistent) {
121.122 + fail("Consistent attributes provoked IllegalArgumentException");
121.123 + e.printStackTrace(System.out);
121.124 + }
121.125 + }
121.126 + }
121.127 +
121.128 + if (failure == null)
121.129 + System.out.println("TEST PASSED");
121.130 + else
121.131 + throw new Exception(failure);
121.132 + }
121.133 +
121.134 + // Check that the classes of the forwarders in the system chain correspond
121.135 + // to what we expect given the options we have passed. This check is a bit
121.136 + // superficial in the sense that a forwarder might be for example a
121.137 + // SingleMBeanForwarderHandler but that doesn't prove that it is the
121.138 + // right Single MBean. Nevertheless the test should expose any severe
121.139 + // wrongness.
121.140 + //
121.141 + // The check here makes some assumptions that could become untrue in the
121.142 + // future. First, it assumes that the forwarders that are added have
121.143 + // exactly the classes that are in the Forwarder[] array. So for example
121.144 + // the forwarder for CONTEXT_FORWARDER must be of the same class as an
121.145 + // explicit call to ClientContext.newContextForwarder. The spec doesn't
121.146 + // require that - it only requires that the forwarder have the same
121.147 + // behaviour. The second assumption is that the connector server doesn't
121.148 + // add any forwarders of its own into the system chain, and again the spec
121.149 + // doesn't disallow that.
121.150 + private static void checkForwarders(
121.151 + JMXConnectorServer cs, Forwarder[] forwarders, Status[] status) {
121.152 + List<Class<?>> expectedClasses = new ArrayList<Class<?>>();
121.153 + for (int i = 0; i < forwarders.length; i++) {
121.154 + if (status[i] == Status.ENABLED ||
121.155 + (status[i] == Status.DEFAULT && forwarders[i].defaultEnabled))
121.156 + expectedClasses.add(forwarders[i].expectedClass);
121.157 + }
121.158 + MBeanServer stop = cs.getMBeanServer();
121.159 + List<Class<?>> foundClasses = new ArrayList<Class<?>>();
121.160 + for (MBeanServer mbs = cs.getSystemMBeanServer(); mbs != stop;
121.161 + mbs = ((MBeanServerForwarder) mbs).getMBeanServer())
121.162 + foundClasses.add(mbs.getClass());
121.163 + if (!expectedClasses.equals(foundClasses)) {
121.164 + fail("Incorrect forwarder chain: expected " + expectedClasses +
121.165 + "; found " + foundClasses);
121.166 + }
121.167 + }
121.168 +
121.169 + // env is consistent if either (a) localizer is not enabled or (b)
121.170 + // localizer is enabled and context is enabled.
121.171 + // Neither of those is present in this codebase so env is always consistent.
121.172 + private static boolean isConsistent(Map<String, String> env) {
121.173 + return true;
121.174 + }
121.175 +
121.176 + private static void fail(String why) {
121.177 + System.out.println("FAILED: " + why);
121.178 + failure = why;
121.179 + }
121.180 +}
122.1 --- a/test/javax/management/remote/mandatory/loading/MissingClassTest.java Tue Aug 19 07:50:03 2008 -0700
122.2 +++ b/test/javax/management/remote/mandatory/loading/MissingClassTest.java Thu Aug 21 09:55:18 2008 -0700
122.3 @@ -44,13 +44,33 @@
122.4 We also test objects that are of known class but not serializable.
122.5 The test cases are similar.
122.6 */
122.7 -import java.io.*;
122.8 -import java.net.*;
122.9 -import java.util.*;
122.10 -import javax.management.*;
122.11 -import javax.management.loading.*;
122.12 -import javax.management.remote.*;
122.13 +
122.14 +import java.io.ByteArrayOutputStream;
122.15 +import java.io.IOException;
122.16 +import java.io.NotSerializableException;
122.17 +import java.io.ObjectOutputStream;
122.18 +import java.net.MalformedURLException;
122.19 +import java.util.HashMap;
122.20 +import java.util.Map;
122.21 +import java.util.Random;
122.22 +import java.util.Set;
122.23 +import javax.management.Attribute;
122.24 +import javax.management.MBeanServer;
122.25 +import javax.management.MBeanServerConnection;
122.26 +import javax.management.MBeanServerFactory;
122.27 +import javax.management.Notification;
122.28 +import javax.management.NotificationBroadcasterSupport;
122.29 +import javax.management.NotificationFilter;
122.30 +import javax.management.NotificationListener;
122.31 +import javax.management.ObjectName;
122.32 +import javax.management.remote.JMXConnectionNotification;
122.33 +import javax.management.remote.JMXConnector;
122.34 +import javax.management.remote.JMXConnectorFactory;
122.35 +import javax.management.remote.JMXConnectorServer;
122.36 +import javax.management.remote.JMXConnectorServerFactory;
122.37 +import javax.management.remote.JMXServiceURL;
122.38 import javax.management.remote.rmi.RMIConnectorServer;
122.39 +import org.omg.CORBA.MARSHAL;
122.40
122.41 public class MissingClassTest {
122.42 private static final int NNOTIFS = 50;
122.43 @@ -84,7 +104,6 @@
122.44 serverLoader.loadClass("$ClientUnknown$").newInstance();
122.45
122.46 final String[] protos = {"rmi", /*"iiop",*/ "jmxmp"};
122.47 - // iiop commented out until bug 4935098 is fixed
122.48 boolean ok = true;
122.49 for (int i = 0; i < protos.length; i++) {
122.50 try {
122.51 @@ -105,7 +124,16 @@
122.52 }
122.53
122.54 private static boolean test(String proto) throws Exception {
122.55 - System.out.println("Testing for proto " + proto);
122.56 + boolean ok = true;
122.57 + for (boolean eventService : new boolean[] {false, true})
122.58 + ok &= test(proto, eventService);
122.59 + return ok;
122.60 + }
122.61 +
122.62 + private static boolean test(String proto, boolean eventService)
122.63 + throws Exception {
122.64 + System.out.println("Testing for proto " + proto + " with" +
122.65 + (eventService ? "" : "out") + " Event Service");
122.66
122.67 boolean ok = true;
122.68
122.69 @@ -117,6 +145,8 @@
122.70 Map serverMap = new HashMap();
122.71 serverMap.put(JMXConnectorServerFactory.DEFAULT_CLASS_LOADER,
122.72 serverLoader);
122.73 + serverMap.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
122.74 + Boolean.toString(eventService));
122.75
122.76 // make sure no auto-close at server side
122.77 serverMap.put("jmx.remote.x.server.connection.timeout", "888888888");
122.78 @@ -155,6 +185,8 @@
122.79 ok = false;
122.80 } catch (IOException e) {
122.81 Throwable cause = e.getCause();
122.82 + if (cause instanceof MARSHAL) // see CR 4935098
122.83 + cause = cause.getCause();
122.84 if (cause instanceof ClassNotFoundException) {
122.85 System.out.println("Success: got an IOException wrapping " +
122.86 "a ClassNotFoundException");
122.87 @@ -177,6 +209,8 @@
122.88 ok = false;
122.89 } catch (IOException e) {
122.90 Throwable wrapped = e.getCause();
122.91 + if (wrapped instanceof MARSHAL) // see CR 4935098
122.92 + wrapped = wrapped.getCause();
122.93 if (wrapped instanceof ClassNotFoundException) {
122.94 System.out.println("Success: got an IOException wrapping " +
122.95 "a ClassNotFoundException: " +
122.96 @@ -228,6 +262,8 @@
122.97 ok = false;
122.98 } catch (IOException e) {
122.99 Throwable cause = e.getCause();
122.100 + if (cause instanceof MARSHAL) // see CR 4935098
122.101 + cause = cause.getCause();
122.102 if (cause instanceof ClassNotFoundException) {
122.103 System.out.println("Success: got an IOException " +
122.104 "wrapping a ClassNotFoundException");
122.105 @@ -461,12 +497,13 @@
122.106 while ((remain = deadline - System.currentTimeMillis()) >= 0) {
122.107 synchronized (result) {
122.108 if (result.failed
122.109 - || (result.knownCount == NNOTIFS
122.110 - && result.lostCount == NNOTIFS*2))
122.111 + || (result.knownCount >= NNOTIFS
122.112 + && result.lostCount >= NNOTIFS*2))
122.113 break;
122.114 result.wait(remain);
122.115 }
122.116 }
122.117 + Thread.sleep(2); // allow any spurious extra notifs to arrive
122.118 if (result.failed) {
122.119 System.out.println("TEST FAILS: Notification strangeness");
122.120 return false;
122.121 @@ -476,6 +513,11 @@
122.122 "got NOTIFS_LOST for unknown and " +
122.123 "unserializable ones");
122.124 return true;
122.125 + } else if (result.knownCount >= NNOTIFS
122.126 + || result.lostCount >= NNOTIFS*2) {
122.127 + System.out.println("TEST FAILS: Received too many notifs: " +
122.128 + "known=" + result.knownCount + "; lost=" + result.lostCount);
122.129 + return false;
122.130 } else {
122.131 System.out.println("TEST FAILS: Timed out without receiving " +
122.132 "all notifs: known=" + result.knownCount +
123.1 --- a/test/javax/management/remote/mandatory/notif/AddRemoveTest.java Tue Aug 19 07:50:03 2008 -0700
123.2 +++ b/test/javax/management/remote/mandatory/notif/AddRemoveTest.java Thu Aug 21 09:55:18 2008 -0700
123.3 @@ -33,10 +33,12 @@
123.4 */
123.5
123.6 import java.net.MalformedURLException;
123.7 -import java.io.IOException;
123.8
123.9 +import java.util.Collections;
123.10 +import java.util.Map;
123.11 import javax.management.*;
123.12 import javax.management.remote.*;
123.13 +import javax.management.remote.rmi.RMIConnectorServer;
123.14
123.15 public class AddRemoveTest {
123.16 private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
123.17 @@ -69,9 +71,16 @@
123.18 }
123.19 }
123.20
123.21 - private static boolean test(String proto)
123.22 + private static boolean test(String proto) throws Exception {
123.23 + boolean ok = test(proto, false);
123.24 + ok &= test(proto, true);
123.25 + return ok;
123.26 + }
123.27 +
123.28 + private static boolean test(String proto, boolean eventService)
123.29 throws Exception {
123.30 - System.out.println(">>> Test for protocol " + proto);
123.31 + System.out.println(">>> Test for protocol " + proto + " with" +
123.32 + (eventService ? "" : "out") + " event service");
123.33 JMXServiceURL u = new JMXServiceURL(proto, null, 0);
123.34 JMXConnectorServer server;
123.35 JMXServiceURL addr;
123.36 @@ -89,7 +98,10 @@
123.37
123.38 try {
123.39 // with a client listener, but close the server first
123.40 - server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
123.41 + Map<String, String> env = Collections.singletonMap(
123.42 + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
123.43 + Boolean.toString(eventService));
123.44 + server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
123.45 server.start();
123.46
123.47 addr = server.getAddress();
124.1 --- a/test/javax/management/remote/mandatory/notif/DiffHBTest.java Tue Aug 19 07:50:03 2008 -0700
124.2 +++ b/test/javax/management/remote/mandatory/notif/DiffHBTest.java Thu Aug 21 09:55:18 2008 -0700
124.3 @@ -31,11 +31,12 @@
124.4 * @run main DiffHBTest
124.5 */
124.6
124.7 -import java.net.MalformedURLException;
124.8 -import java.io.IOException;
124.9
124.10 +import java.util.Collections;
124.11 +import java.util.Map;
124.12 import javax.management.*;
124.13 import javax.management.remote.*;
124.14 +import javax.management.remote.rmi.RMIConnectorServer;
124.15
124.16 /**
124.17 * This test registeres an unique listener with two different handbacks,
124.18 @@ -48,11 +49,6 @@
124.19 private static ObjectName delegateName;
124.20 private static ObjectName timerName;
124.21
124.22 - public static int received = 0;
124.23 - public static final int[] receivedLock = new int[0];
124.24 - public static Notification receivedNotif = null;
124.25 -
124.26 - public static Object receivedHB = null;
124.27 public static final String[] hbs = new String[] {"0", "1"};
124.28
124.29 public static void main(String[] args) throws Exception {
124.30 @@ -61,162 +57,174 @@
124.31 delegateName = new ObjectName("JMImplementation:type=MBeanServerDelegate");
124.32 timerName = new ObjectName("MBean:name=Timer");
124.33
124.34 - boolean ok = true;
124.35 + String errors = "";
124.36 +
124.37 for (int i = 0; i < protocols.length; i++) {
124.38 - try {
124.39 - if (!test(protocols[i])) {
124.40 - System.out.println(">>> Test failed for " + protocols[i]);
124.41 - ok = false;
124.42 + final String s = test(protocols[i]);
124.43 + if (s != null) {
124.44 + if ("".equals(errors)) {
124.45 + errors = "Failed to " + protocols[i] + ": "+s;
124.46 } else {
124.47 - System.out.println(">>> Test successed for " + protocols[i]);
124.48 + errors = "\tFailed to " + protocols[i] + ": "+s;
124.49 }
124.50 - } catch (Exception e) {
124.51 - System.out.println(">>> Test failed for " + protocols[i]);
124.52 - e.printStackTrace(System.out);
124.53 - ok = false;
124.54 }
124.55 }
124.56
124.57 - if (ok) {
124.58 - System.out.println(">>> Test passed");
124.59 + if ("".equals(errors)) {
124.60 + System.out.println(">>> Passed!");
124.61 } else {
124.62 - System.out.println(">>> TEST FAILED");
124.63 - System.exit(1);
124.64 + System.out.println(">>> Failed!");
124.65 +
124.66 + throw new RuntimeException(errors);
124.67 }
124.68 }
124.69
124.70 - private static boolean test(String proto) throws Exception {
124.71 - System.out.println(">>> Test for protocol " + proto);
124.72 + private static String test(String proto) throws Exception {
124.73 + String ret = null;
124.74 + for (boolean eventService : new boolean[] {false, true}) {
124.75 + String s = test(proto, eventService);
124.76 + if (s != null) {
124.77 + if (ret == null)
124.78 + ret = s;
124.79 + else
124.80 + ret = ret + "; " + s;
124.81 + }
124.82 + }
124.83 + return ret;
124.84 + }
124.85 +
124.86 + private static String test(String proto, boolean eventService)
124.87 + throws Exception {
124.88 + System.out.println(">>> Test for protocol " + proto + " with" +
124.89 + (eventService ? "" : "out") + " event service");
124.90 JMXServiceURL u = new JMXServiceURL(proto, null, 0);
124.91 JMXConnectorServer server;
124.92 - JMXServiceURL addr;
124.93 JMXConnector client;
124.94 - MBeanServerConnection mserver;
124.95
124.96 - final NotificationListener dummyListener = new NotificationListener() {
124.97 - public void handleNotification(Notification n, Object o) {
124.98 - synchronized(receivedLock) {
124.99 - if (n == null) {
124.100 - System.out.println(">>> Got a null notification.");
124.101 - System.exit(1);
124.102 - }
124.103 + try {
124.104 + Map<String, String> env = Collections.singletonMap(
124.105 + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
124.106 + Boolean.toString(eventService));
124.107 + server =
124.108 + JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
124.109 + server.start();
124.110 + JMXServiceURL addr = server.getAddress();
124.111 + client = JMXConnectorFactory.connect(addr, null);
124.112 + } catch (Exception e) {
124.113 + // not support
124.114 + System.out.println(">>> not support: " + proto);
124.115 + return null;
124.116 + }
124.117
124.118 - // check number
124.119 - if (received > 2) {
124.120 - System.out.println(">>> Expect to receive 2 notifs, but get "+received);
124.121 - System.exit(1);
124.122 - }
124.123 + MBeanServerConnection mserver = client.getMBeanServerConnection();
124.124
124.125 - if (received == 0) { // first time
124.126 - receivedNotif = n;
124.127 - receivedHB = o;
124.128 + System.out.print(">>>\t");
124.129 + for (int i=0; i<5; i++) {
124.130 + System.out.print(i + "\t");
124.131 + final MyListener dummyListener = new MyListener();
124.132 + mserver.addNotificationListener(
124.133 + delegateName, dummyListener, null, hbs[0]);
124.134 + mserver.addNotificationListener(
124.135 + delegateName, dummyListener, null, hbs[1]);
124.136
124.137 - if (!hbs[0].equals(o) && !hbs[1].equals(o)) {
124.138 - System.out.println(">>> Unkown handback: "+o);
124.139 - System.exit(1);
124.140 - }
124.141 - } else { // second time
124.142 - if (!receivedNotif.equals(n)) {
124.143 - System.out.println(">>> Not get same notif twice.");
124.144 - System.exit(1);
124.145 - } else if (!hbs[0].equals(o) && !hbs[1].equals(o)) { // validate handback
124.146 - System.out.println(">>> Unkown handback: "+o);
124.147 - System.exit(1);
124.148 - } else if (receivedHB.equals(o)) {
124.149 - System.out.println(">>> Got same handback twice: "+o);
124.150 - System.exit(1);
124.151 - }
124.152 - }
124.153 + mserver.createMBean("javax.management.timer.Timer", timerName);
124.154
124.155 - ++received;
124.156 + long remainingTime = waitingTime;
124.157 + final long startTime = System.currentTimeMillis();
124.158
124.159 - if (received == 2) {
124.160 - receivedLock.notify();
124.161 - }
124.162 + try {
124.163 + synchronized(dummyListener) {
124.164 + while (!dummyListener.done && remainingTime > 0) {
124.165 + dummyListener.wait(remainingTime);
124.166 + remainingTime = waitingTime -
124.167 + (System.currentTimeMillis() - startTime);
124.168 + }
124.169 +
124.170 + if (dummyListener.errorInfo != null) {
124.171 + return dummyListener.errorInfo;
124.172 }
124.173 }
124.174 - };
124.175 + } finally {
124.176 + //System.out.println("Unregister: "+i);
124.177 + mserver.unregisterMBean(timerName);
124.178 + mserver.removeNotificationListener(delegateName, dummyListener);
124.179 + }
124.180 + }
124.181
124.182 - try {
124.183 - server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
124.184 - server.start();
124.185 + System.out.println("");
124.186 + client.close();
124.187 + server.stop();
124.188
124.189 - addr = server.getAddress();
124.190 - client = JMXConnectorFactory.newJMXConnector(addr, null);
124.191 - client.connect(null);
124.192 + return null;
124.193 + }
124.194
124.195 - mserver = client.getMBeanServerConnection();
124.196 + private static class MyListener implements NotificationListener {
124.197 + public boolean done = false;
124.198 + public String errorInfo = null;
124.199
124.200 - mserver.addNotificationListener(delegateName, dummyListener, null, hbs[0]);
124.201 - mserver.addNotificationListener(delegateName, dummyListener, null, hbs[1]);
124.202 + private int received = 0;
124.203 + private MBeanServerNotification receivedNotif = null;
124.204 + private Object receivedHB = null;
124.205 + public void handleNotification(Notification n, Object o) {
124.206 + if (!(n instanceof MBeanServerNotification)) {
124.207 + failed("Received an unexpected notification: "+n);
124.208 + return;
124.209 + }
124.210
124.211 - for (int i=0; i<20; i++) {
124.212 - synchronized(receivedLock) {
124.213 - received = 0;
124.214 + if (!hbs[0].equals(o) && !hbs[1].equals(o)) {
124.215 + failed("Unkown handback: "+o);
124.216 + return;
124.217 + }
124.218 +
124.219 + // what we need
124.220 + final MBeanServerNotification msn = (MBeanServerNotification)n;
124.221 + if (!(MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(
124.222 + msn.getType())) ||
124.223 + !msn.getMBeanName().equals(timerName)) {
124.224 + return;
124.225 + }
124.226 +
124.227 + synchronized(this) {
124.228 + received++;
124.229 +
124.230 + if (received == 1) { // first time
124.231 + receivedNotif = msn;
124.232 + receivedHB = o;
124.233 +
124.234 + return;
124.235 }
124.236
124.237 - mserver.createMBean("javax.management.timer.Timer", timerName);
124.238 + if (received > 2) {
124.239 + failed("Expect to receive 2 notifs, but get "+received);
124.240
124.241 - synchronized(receivedLock) {
124.242 - if (received != 2) {
124.243 - long remainingTime = waitingTime;
124.244 - final long startTime = System.currentTimeMillis();
124.245 -
124.246 - while (received != 2 && remainingTime > 0) {
124.247 - receivedLock.wait(remainingTime);
124.248 - remainingTime = waitingTime -
124.249 - (System.currentTimeMillis() - startTime);
124.250 - }
124.251 - }
124.252 -
124.253 - if (received != 2) {
124.254 - System.out.println(">>> Expected 2 notifis, but received "+received);
124.255 -
124.256 - return false;
124.257 - }
124.258 + return;
124.259 }
124.260
124.261 -
124.262 - synchronized(receivedLock) {
124.263 - received = 0;
124.264 + // second time
124.265 + if (receivedHB.equals(o)) {
124.266 + failed("Got same handback twice: "+o);
124.267 + } else if(!hbs[0].equals(o) && !hbs[1].equals(o)) {
124.268 + failed("Unknown handback: "+o);
124.269 + } else if (receivedNotif.getSequenceNumber() !=
124.270 + msn.getSequenceNumber()) {
124.271 + failed("expected to receive:\n"
124.272 + +receivedNotif
124.273 + +"\n but got\n"+msn);
124.274 }
124.275
124.276 - mserver.unregisterMBean(timerName);
124.277 -
124.278 - synchronized(receivedLock) {
124.279 - if (received != 2) {
124.280 -
124.281 - long remainingTime = waitingTime;
124.282 - final long startTime = System.currentTimeMillis();
124.283 -
124.284 - while (received != 2 && remainingTime >0) {
124.285 - receivedLock.wait(remainingTime);
124.286 - remainingTime = waitingTime -
124.287 - (System.currentTimeMillis() - startTime);
124.288 - }
124.289 - }
124.290 -
124.291 - if (received != 2) {
124.292 - System.out.println(">>> Expected 2 notifis, but received "+received);
124.293 -
124.294 - return false;
124.295 - }
124.296 - }
124.297 + // passed
124.298 + done = true;
124.299 + this.notify();
124.300 }
124.301 -
124.302 - mserver.removeNotificationListener(delegateName, dummyListener);
124.303 -
124.304 - client.close();
124.305 -
124.306 - server.stop();
124.307 -
124.308 - } catch (MalformedURLException e) {
124.309 - System.out.println(">>> Skipping unsupported URL " + u);
124.310 - return true;
124.311 }
124.312
124.313 - return true;
124.314 + private void failed(String errorInfo) {
124.315 + this.errorInfo = errorInfo;
124.316 + done = true;
124.317 +
124.318 + this.notify();
124.319 + }
124.320 }
124.321
124.322 - private final static long waitingTime = 10000;
124.323 + private final static long waitingTime = 2000;
124.324 }
125.1 --- a/test/javax/management/remote/mandatory/notif/EmptyDomainNotificationTest.java Tue Aug 19 07:50:03 2008 -0700
125.2 +++ b/test/javax/management/remote/mandatory/notif/EmptyDomainNotificationTest.java Thu Aug 21 09:55:18 2008 -0700
125.3 @@ -29,11 +29,12 @@
125.4 * @author Shanliang JIANG
125.5 * @run clean EmptyDomainNotificationTest
125.6 * @run build EmptyDomainNotificationTest
125.7 - * @run main EmptyDomainNotificationTest
125.8 + * @run main EmptyDomainNotificationTest classic
125.9 + * @run main EmptyDomainNotificationTest event
125.10 */
125.11
125.12 -import java.util.ArrayList;
125.13 -import java.util.List;
125.14 +import java.util.Collections;
125.15 +import java.util.Map;
125.16 import javax.management.MBeanServer;
125.17 import javax.management.MBeanServerConnection;
125.18 import javax.management.MBeanServerFactory;
125.19 @@ -46,6 +47,7 @@
125.20 import javax.management.remote.JMXConnectorServer;
125.21 import javax.management.remote.JMXConnectorServerFactory;
125.22 import javax.management.remote.JMXServiceURL;
125.23 +import javax.management.remote.rmi.RMIConnectorServer;
125.24
125.25 public class EmptyDomainNotificationTest {
125.26
125.27 @@ -80,11 +82,25 @@
125.28
125.29 public static void main(String[] args) throws Exception {
125.30
125.31 + String type = args[0];
125.32 + boolean eventService;
125.33 + if (type.equals("classic"))
125.34 + eventService = false;
125.35 + else if (type.equals("event"))
125.36 + eventService = true;
125.37 + else
125.38 + throw new IllegalArgumentException(type);
125.39 +
125.40 final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
125.41
125.42 final JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
125.43
125.44 - JMXConnectorServer server = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
125.45 + Map<String, String> env = Collections.singletonMap(
125.46 + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
125.47 + Boolean.toString(eventService));
125.48 +
125.49 + JMXConnectorServer server =
125.50 + JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
125.51 server.start();
125.52
125.53 JMXConnector client = JMXConnectorFactory.connect(server.getAddress(), null);
126.1 --- a/test/javax/management/remote/mandatory/notif/ListenerScaleTest.java Tue Aug 19 07:50:03 2008 -0700
126.2 +++ b/test/javax/management/remote/mandatory/notif/ListenerScaleTest.java Thu Aug 21 09:55:18 2008 -0700
126.3 @@ -51,18 +51,29 @@
126.4 * been compiled by the second.
126.5 */
126.6
126.7 -import java.lang.management.ManagementFactory;
126.8 -import javax.management.*;
126.9 -import javax.management.remote.*;
126.10 -import java.util.concurrent.*;
126.11 +import java.util.Collections;
126.12 +import java.util.Map;
126.13 +import java.util.concurrent.Semaphore;
126.14 +import javax.management.MBeanServer;
126.15 +import javax.management.MBeanServerConnection;
126.16 +import javax.management.MBeanServerFactory;
126.17 +import javax.management.MalformedObjectNameException;
126.18 +import javax.management.Notification;
126.19 +import javax.management.NotificationBroadcasterSupport;
126.20 +import javax.management.NotificationListener;
126.21 +import javax.management.ObjectName;
126.22 +import javax.management.remote.JMXConnector;
126.23 +import javax.management.remote.JMXConnectorFactory;
126.24 +import javax.management.remote.JMXConnectorServer;
126.25 +import javax.management.remote.JMXConnectorServerFactory;
126.26 +import javax.management.remote.JMXServiceURL;
126.27 +import javax.management.remote.rmi.RMIConnectorServer;
126.28
126.29 public class ListenerScaleTest {
126.30 private static final int WARMUP_WITH_ONE_MBEAN = 1000;
126.31 private static final int NOTIFS_TO_TIME = 100;
126.32 private static final int EXTRA_MBEANS = 20000;
126.33
126.34 - private static final MBeanServer mbs =
126.35 - ManagementFactory.getPlatformMBeanServer();
126.36 private static final ObjectName testObjectName;
126.37 static {
126.38 try {
126.39 @@ -87,7 +98,7 @@
126.40 }
126.41 };
126.42
126.43 - private static final long timeNotif() {
126.44 + private static final long timeNotif(MBeanServer mbs) {
126.45 try {
126.46 startTime = System.nanoTime();
126.47 nnotifs = 0;
126.48 @@ -117,12 +128,20 @@
126.49 };
126.50
126.51 public static void main(String[] args) throws Exception {
126.52 - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
126.53 + test(false);
126.54 + test(true);
126.55 + }
126.56 +
126.57 + private static void test(boolean eventService) throws Exception {
126.58 + MBeanServer mbs = MBeanServerFactory.newMBeanServer();
126.59 Sender sender = new Sender();
126.60 mbs.registerMBean(sender, testObjectName);
126.61 JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
126.62 + Map<String, String> env = Collections.singletonMap(
126.63 + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
126.64 + Boolean.toString(eventService));
126.65 JMXConnectorServer cs =
126.66 - JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
126.67 + JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
126.68 cs.start();
126.69 JMXServiceURL addr = cs.getAddress();
126.70 JMXConnector cc = JMXConnectorFactory.connect(addr);
126.71 @@ -140,7 +159,7 @@
126.72 mbsc.addNotificationListener(testObjectName, timingListener, null, null);
126.73 long singleMBeanTime = 0;
126.74 for (int i = 0; i < WARMUP_WITH_ONE_MBEAN; i++)
126.75 - singleMBeanTime = timeNotif();
126.76 + singleMBeanTime = timeNotif(mbs);
126.77 if (singleMBeanTime == 0)
126.78 singleMBeanTime = 1;
126.79 System.out.println("Time with a single MBean: " + singleMBeanTime + "ns");
126.80 @@ -165,7 +184,7 @@
126.81 }
126.82 System.out.println();
126.83 System.out.println("Timing a notification send now");
126.84 - long manyMBeansTime = timeNotif();
126.85 + long manyMBeansTime = timeNotif(mbs);
126.86 System.out.println("Time with many MBeans: " + manyMBeansTime + "ns");
126.87 double ratio = (double) manyMBeansTime / singleMBeanTime;
126.88 if (ratio > 100.0)
127.1 --- a/test/javax/management/remote/mandatory/notif/NotifBufferSizePropertyNameTest.java Tue Aug 19 07:50:03 2008 -0700
127.2 +++ b/test/javax/management/remote/mandatory/notif/NotifBufferSizePropertyNameTest.java Thu Aug 21 09:55:18 2008 -0700
127.3 @@ -31,11 +31,11 @@
127.4 * @run main NotifBufferSizePropertyNameTest
127.5 */
127.6
127.7 -import java.io.IOException;
127.8 import java.util.*;
127.9
127.10 import javax.management.*;
127.11 import javax.management.remote.*;
127.12 +import javax.management.remote.rmi.RMIConnectorServer;
127.13
127.14 /**
127.15 * This class tests also the size of a server notification buffer.
127.16 @@ -88,6 +88,9 @@
127.17 private static void test(Map env) throws Exception {
127.18 final MBeanServer mbs = MBeanServerFactory.newMBeanServer();
127.19
127.20 + env = new HashMap((env == null) ? Collections.emptyMap() : env);
127.21 + env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
127.22 +
127.23 mbs.registerMBean(new NotificationEmitter(), oname);
127.24 JMXConnectorServer server = JMXConnectorServerFactory.newJMXConnectorServer(
127.25 url,
128.1 --- a/test/javax/management/remote/mandatory/notif/NotifReconnectDeadlockTest.java Tue Aug 19 07:50:03 2008 -0700
128.2 +++ b/test/javax/management/remote/mandatory/notif/NotifReconnectDeadlockTest.java Thu Aug 21 09:55:18 2008 -0700
128.3 @@ -22,7 +22,7 @@
128.4 */
128.5
128.6 /*
128.7 - * @test NotifReconnectDeadlockTest
128.8 + * @test
128.9 * @bug 6199899
128.10 * @summary Tests reconnection done by a fetching notif thread.
128.11 * @author Shanliang JIANG
128.12 @@ -31,11 +31,21 @@
128.13 * @run main NotifReconnectDeadlockTest
128.14 */
128.15
128.16 -import java.io.IOException;
128.17 -import java.util.*;
128.18 -
128.19 -import javax.management.*;
128.20 -import javax.management.remote.*;
128.21 +import java.util.HashMap;
128.22 +import java.util.Map;
128.23 +import javax.management.MBeanServer;
128.24 +import javax.management.MBeanServerFactory;
128.25 +import javax.management.Notification;
128.26 +import javax.management.NotificationBroadcasterSupport;
128.27 +import javax.management.NotificationListener;
128.28 +import javax.management.ObjectName;
128.29 +import javax.management.remote.JMXConnectionNotification;
128.30 +import javax.management.remote.JMXConnector;
128.31 +import javax.management.remote.JMXConnectorFactory;
128.32 +import javax.management.remote.JMXConnectorServer;
128.33 +import javax.management.remote.JMXConnectorServerFactory;
128.34 +import javax.management.remote.JMXServiceURL;
128.35 +import javax.management.remote.rmi.RMIConnectorServer;
128.36
128.37 /**
128.38 * "This test checks for a bug whereby reconnection did not work if (a) it was
128.39 @@ -64,6 +74,7 @@
128.40 Map env = new HashMap(2);
128.41 env.put("jmx.remote.x.server.connection.timeout", new Long(serverTimeout));
128.42 env.put("jmx.remote.x.client.connection.check.period", new Long(Long.MAX_VALUE));
128.43 + env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
128.44
128.45 final MBeanServer mbs = MBeanServerFactory.newMBeanServer();
128.46
129.1 --- a/test/javax/management/remote/mandatory/notif/NotificationAccessControllerTest.java Tue Aug 19 07:50:03 2008 -0700
129.2 +++ b/test/javax/management/remote/mandatory/notif/NotificationAccessControllerTest.java Thu Aug 21 09:55:18 2008 -0700
129.3 @@ -156,7 +156,8 @@
129.4 List<Notification> received,
129.5 List<ObjectName> expected) {
129.6 if (received.size() != size) {
129.7 - echo("Error: expecting " + size + " notifications");
129.8 + echo("Error: expecting " + size + " notifications, got " +
129.9 + received.size());
129.10 return 1;
129.11 } else {
129.12 for (Notification n : received) {
130.1 --- a/test/javax/management/remote/mandatory/notif/NotificationBufferCreationTest.java Tue Aug 19 07:50:03 2008 -0700
130.2 +++ b/test/javax/management/remote/mandatory/notif/NotificationBufferCreationTest.java Thu Aug 21 09:55:18 2008 -0700
130.3 @@ -32,6 +32,8 @@
130.4 */
130.5 import java.net.MalformedURLException;
130.6
130.7 +import java.util.Collections;
130.8 +import java.util.Map;
130.9 import javax.management.MBeanServerFactory;
130.10 import javax.management.MBeanServer;
130.11 import javax.management.MBeanServerConnection;
130.12 @@ -44,6 +46,7 @@
130.13 import javax.management.remote.JMXConnectorServer;
130.14 import javax.management.remote.JMXConnectorServerFactory;
130.15 import javax.management.remote.JMXServiceURL;
130.16 +import javax.management.remote.rmi.RMIConnectorServer;
130.17
130.18 public class NotificationBufferCreationTest {
130.19 private static final MBeanServer mbs =
130.20 @@ -86,6 +89,8 @@
130.21 JMXServiceURL u = null;
130.22 try {
130.23 u = new JMXServiceURL(protocol, null, 0);
130.24 + Map<String, String> env = Collections.singletonMap(
130.25 + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
130.26 server =
130.27 JMXConnectorServerFactory.newJMXConnectorServer(u,
130.28 null,
131.1 --- a/test/javax/management/remote/mandatory/notif/NotificationBufferDeadlockTest.java Tue Aug 19 07:50:03 2008 -0700
131.2 +++ b/test/javax/management/remote/mandatory/notif/NotificationBufferDeadlockTest.java Thu Aug 21 09:55:18 2008 -0700
131.3 @@ -35,7 +35,9 @@
131.4 import java.lang.reflect.Method;
131.5 import java.lang.reflect.Proxy;
131.6 import java.net.MalformedURLException;
131.7 +import java.util.Collections;
131.8 import java.util.List;
131.9 +import java.util.Map;
131.10 import java.util.Set;
131.11 import java.util.Vector;
131.12 import javax.management.*;
131.13 @@ -88,6 +90,7 @@
131.14 * If the logic for adding the notification buffer's listener is incorrect
131.15 * we could remove zero or two notifications from an MBean.
131.16 */
131.17 +import javax.management.remote.rmi.RMIConnectorServer;
131.18 public class NotificationBufferDeadlockTest {
131.19 public static void main(String[] args) throws Exception {
131.20 System.out.println("Check no deadlock if notif sent while initial " +
131.21 @@ -109,7 +112,13 @@
131.22 }
131.23
131.24 private static void test(String proto) throws Exception {
131.25 - System.out.println("Testing protocol " + proto);
131.26 + test(proto, false);
131.27 + test(proto, true);
131.28 + }
131.29 +
131.30 + private static void test(String proto, boolean eventService) throws Exception {
131.31 + System.out.println("Testing protocol " + proto + " with" +
131.32 + (eventService ? "" : "out") + " event service");
131.33 MBeanServer mbs = MBeanServerFactory.newMBeanServer();
131.34 ObjectName testName = newName();
131.35 DeadlockTest test = new DeadlockTest();
131.36 @@ -117,8 +126,11 @@
131.37 JMXServiceURL url = new JMXServiceURL("service:jmx:" + proto + ":///");
131.38 JMXConnectorServer cs;
131.39 try {
131.40 + Map<String, String> env = Collections.singletonMap(
131.41 + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
131.42 + Boolean.toString(eventService));
131.43 cs =
131.44 - JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
131.45 + JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
131.46 } catch (MalformedURLException e) {
131.47 System.out.println("...protocol not supported, ignoring");
131.48 return;
132.1 --- a/test/javax/management/remote/mandatory/notif/NotificationEmissionTest.java Tue Aug 19 07:50:03 2008 -0700
132.2 +++ b/test/javax/management/remote/mandatory/notif/NotificationEmissionTest.java Thu Aug 21 09:55:18 2008 -0700
132.3 @@ -29,11 +29,16 @@
132.4 * @author Luis-Miguel Alventosa
132.5 * @run clean NotificationEmissionTest
132.6 * @run build NotificationEmissionTest
132.7 - * @run main NotificationEmissionTest 1
132.8 - * @run main NotificationEmissionTest 2
132.9 - * @run main NotificationEmissionTest 3
132.10 - * @run main NotificationEmissionTest 4
132.11 - * @run main NotificationEmissionTest 5
132.12 + * @run main NotificationEmissionTest 1 Classic
132.13 + * @run main NotificationEmissionTest 2 Classic
132.14 + * @run main NotificationEmissionTest 3 Classic
132.15 + * @run main NotificationEmissionTest 4 Classic
132.16 + * @run main NotificationEmissionTest 5 Classic
132.17 + * @run main NotificationEmissionTest 1 EventService
132.18 + * @run main NotificationEmissionTest 2 EventService
132.19 + * @run main NotificationEmissionTest 3 EventService
132.20 + * @run main NotificationEmissionTest 4 EventService
132.21 + * @run main NotificationEmissionTest 5 EventService
132.22 */
132.23
132.24 import java.io.File;
132.25 @@ -56,9 +61,15 @@
132.26 import javax.management.remote.JMXConnectorServerFactory;
132.27 import javax.management.remote.JMXPrincipal;
132.28 import javax.management.remote.JMXServiceURL;
132.29 +import javax.management.remote.rmi.RMIConnectorServer;
132.30 import javax.security.auth.Subject;
132.31
132.32 public class NotificationEmissionTest {
132.33 + private final boolean eventService;
132.34 +
132.35 + public NotificationEmissionTest(boolean eventService) {
132.36 + this.eventService = eventService;
132.37 + }
132.38
132.39 public class CustomJMXAuthenticator implements JMXAuthenticator {
132.40 public Subject authenticate(Object credentials) {
132.41 @@ -102,7 +113,8 @@
132.42 List<Notification> received,
132.43 List<ObjectName> expected) {
132.44 if (received.size() != size) {
132.45 - echo("Error: expecting " + size + " notifications");
132.46 + echo("Error: expecting " + size + " notifications, got " +
132.47 + received.size());
132.48 return 1;
132.49 } else {
132.50 for (Notification n : received) {
132.51 @@ -216,8 +228,13 @@
132.52 //
132.53 final Map<String,Object> env = new HashMap<String,Object>();
132.54 env.put("jmx.remote.authenticator", new CustomJMXAuthenticator());
132.55 - if (prop)
132.56 + env.put(RMIConnectorServer.EVENT_CLIENT_DELEGATE_FORWARDER,
132.57 + Boolean.toString(eventService));
132.58 + if (prop) {
132.59 + echo("Setting jmx.remote.x.check.notification.emission to " +
132.60 + propValue);
132.61 env.put("jmx.remote.x.check.notification.emission", propValue);
132.62 + }
132.63
132.64 // Create the JMXServiceURL
132.65 //
132.66 @@ -282,9 +299,24 @@
132.67 new Object[] {2, nb3},
132.68 new String[] {"int", "javax.management.ObjectName"});
132.69
132.70 + // If the check is effective and we're using policy.negative,
132.71 + // then we should see the two notifs sent by nb2 (of which one
132.72 + // has a getSource() that is nb3), but not the notif sent by nb1.
132.73 + // Otherwise we should see all three notifs. If we're using the
132.74 + // Event Service with a Security Manager then the logic to
132.75 + // reapply the addNL permission test for every notification is
132.76 + // always enabled, regardless of the value of
132.77 + // jmx.remote.x.check.notification.emission. Otherwise, the
132.78 + // test is only applied if that property is explicitly true.
132.79 + int expectedNotifs =
132.80 + ((prop || eventService) && sm && !policyPositive) ? 2 : 3;
132.81 +
132.82 // Wait for notifications to be emitted
132.83 //
132.84 - Thread.sleep(2000);
132.85 + long deadline = System.currentTimeMillis() + 2000;
132.86 + while (li.notifs.size() < expectedNotifs &&
132.87 + System.currentTimeMillis() < deadline)
132.88 + Thread.sleep(1);
132.89
132.90 // Remove notification listener
132.91 //
132.92 @@ -297,16 +329,10 @@
132.93 sources.add(nb2);
132.94 sources.add(nb3);
132.95
132.96 - if (prop && sm && !policyPositive) {
132.97 - // List must contain two notifs from sources nb2 and nb3
132.98 - //
132.99 - result = checkNotifs(2, li.notifs, sources);
132.100 - } else {
132.101 - // List must contain three notifs from sources nb1, nb2 and nb3
132.102 - //
132.103 - result = checkNotifs(3, li.notifs, sources);
132.104 - }
132.105 + result = checkNotifs(expectedNotifs, li.notifs, sources);
132.106 if (result > 0) {
132.107 + echo("...SecurityManager=" + sm + "; policy=" + policyPositive +
132.108 + "; eventService=" + eventService);
132.109 return result;
132.110 }
132.111 } finally {
132.112 @@ -336,9 +362,18 @@
132.113 public static void main(String[] args) throws Exception {
132.114
132.115 echo("\n--- Check the emission of notifications " +
132.116 - "when a Security Manager is installed ---");
132.117 + "when a Security Manager is installed [" +
132.118 + args[1] + "] ---");
132.119
132.120 - NotificationEmissionTest net = new NotificationEmissionTest();
132.121 + boolean eventService;
132.122 + if (args[1].equals("Classic"))
132.123 + eventService = false;
132.124 + else if (args[1].equals("EventService"))
132.125 + eventService = true;
132.126 + else
132.127 + throw new IllegalArgumentException(args[1]);
132.128 +
132.129 + NotificationEmissionTest net = new NotificationEmissionTest(eventService);
132.130
132.131 int error = 0;
132.132
133.1 --- a/test/javax/management/remote/mandatory/notif/RMINotifTest.java Tue Aug 19 07:50:03 2008 -0700
133.2 +++ b/test/javax/management/remote/mandatory/notif/RMINotifTest.java Thu Aug 21 09:55:18 2008 -0700
133.3 @@ -22,36 +22,53 @@
133.4 */
133.5
133.6 /*
133.7 - * @test RMINotifTest.java
133.8 + * @test
133.9 * @bug 7654321
133.10 - * @summary Tests to receive notifications for opened and closed connect
133.11 -ions
133.12 + * @summary Tests to receive notifications for opened and closed connections
133.13 * @author sjiang
133.14 * @run clean RMINotifTest
133.15 * @run build RMINotifTest
133.16 - * @run main RMINotifTest
133.17 + * @run main RMINotifTest classic
133.18 + * @run main RMINotifTest event
133.19 */
133.20
133.21 // java imports
133.22 //
133.23 -import java.io.IOException;
133.24 -import java.net.UnknownHostException;
133.25
133.26 -import java.rmi.*;
133.27 -import java.rmi.registry.*;
133.28 +import java.rmi.RemoteException;
133.29 +import java.rmi.registry.LocateRegistry;
133.30 +import java.rmi.registry.Registry;
133.31 +import java.util.Collections;
133.32 +import java.util.Map;
133.33 import java.util.Random;
133.34 -
133.35 -// JMX imports
133.36 -//
133.37 -import javax.management.* ;
133.38 -
133.39 -import javax.management.remote.*;
133.40 -import javax.management.remote.rmi.*;
133.41 +import javax.management.MBeanNotificationInfo;
133.42 +import javax.management.MBeanServer;
133.43 +import javax.management.MBeanServerConnection;
133.44 +import javax.management.MBeanServerFactory;
133.45 +import javax.management.Notification;
133.46 +import javax.management.NotificationBroadcasterSupport;
133.47 +import javax.management.NotificationFilterSupport;
133.48 +import javax.management.NotificationListener;
133.49 +import javax.management.ObjectInstance;
133.50 +import javax.management.ObjectName;
133.51 +import javax.management.remote.JMXConnector;
133.52 +import javax.management.remote.JMXConnectorFactory;
133.53 +import javax.management.remote.JMXConnectorServer;
133.54 +import javax.management.remote.JMXConnectorServerFactory;
133.55 import javax.management.remote.JMXServiceURL;
133.56 +import javax.management.remote.rmi.RMIConnectorServer;
133.57
133.58 public class RMINotifTest {
133.59
133.60 public static void main(String[] args) {
133.61 + String eventService;
133.62 + if (args[0].equals("classic"))
133.63 + eventService = "false";
133.64 + else if (args[0].equals("event"))
133.65 + eventService = "true";
133.66 + else
133.67 + throw new IllegalArgumentException(args[0]);
133.68 +
133.69 try {
133.70 // create a rmi registry
133.71 Registry reg = null;
133.72 @@ -88,9 +105,10 @@
133.73 "/jndi/rmi://:" + port + "/server" + port);
133.74 System.out.println("RMIConnectorServer address " + url);
133.75
133.76 + Map<String, String> env = Collections.singletonMap(
133.77 + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, eventService);
133.78 JMXConnectorServer sServer =
133.79 - JMXConnectorServerFactory.newJMXConnectorServer(url, null,
133.80 - null);
133.81 + JMXConnectorServerFactory.newJMXConnectorServer(url, env, null);
133.82
133.83 ObjectInstance ss = server.registerMBean(sServer, new ObjectName("Default:name=RmiConnectorServer"));
133.84
134.1 --- a/test/javax/management/remote/mandatory/notif/UnexpectedNotifTest.java Tue Aug 19 07:50:03 2008 -0700
134.2 +++ b/test/javax/management/remote/mandatory/notif/UnexpectedNotifTest.java Thu Aug 21 09:55:18 2008 -0700
134.3 @@ -32,68 +32,88 @@
134.4 * @run main UnexpectedNotifTest
134.5 */
134.6
134.7 -// java imports
134.8 +import java.util.ArrayList;
134.9 +import java.util.Collections;
134.10 +import java.util.List;
134.11 +import java.util.Map;
134.12 +import javax.management.MBeanNotificationInfo;
134.13 +import javax.management.MBeanServer;
134.14 +import javax.management.MBeanServerConnection;
134.15 +import javax.management.MBeanServerFactory;
134.16 +import javax.management.Notification;
134.17 +import javax.management.NotificationBroadcasterSupport;
134.18 +import javax.management.NotificationListener;
134.19 +import javax.management.ObjectName;
134.20 +import javax.management.remote.JMXConnector;
134.21 +import javax.management.remote.JMXConnectorFactory;
134.22 +import javax.management.remote.JMXConnectorServer;
134.23 +import javax.management.remote.JMXConnectorServerFactory;
134.24 +import javax.management.remote.JMXServiceURL;
134.25 //
134.26 -import java.io.IOException;
134.27 -
134.28 -// JMX imports
134.29 -//
134.30 -import javax.management.*;
134.31 -import javax.management.remote.*;
134.32 +import javax.management.remote.rmi.RMIConnectorServer;
134.33
134.34 public class UnexpectedNotifTest {
134.35
134.36 public static void main(String[] args) throws Exception {
134.37 - String[] protos = null;
134.38 + List<String> protos = new ArrayList<String>();
134.39 + protos.add("rmi");
134.40 try {
134.41 Class.forName("javax.management.remote.jmxmp.JMXMPConnectorServer");
134.42 - protos = new String[2];
134.43 - protos[0] = "rmi";
134.44 - protos[1] = "jmxmp";
134.45 + protos.add("jmxmp");
134.46 } catch (ClassNotFoundException e) {
134.47 - protos = new String[1];
134.48 - protos[0] = "rmi";
134.49 + // OK: JMXMP not present so don't test it.
134.50 }
134.51 - for (int i = 0; i < protos.length; i++) {
134.52 - System.out.println("Unexpected notifications test for protocol " +
134.53 - protos[i]);
134.54 - MBeanServer mbs = null;
134.55 - try {
134.56 - // Create a MBeanServer
134.57 - //
134.58 - mbs = MBeanServerFactory.createMBeanServer();
134.59 + for (String proto : protos) {
134.60 + test(proto, false);
134.61 + test(proto, true);
134.62 + }
134.63 + }
134.64
134.65 - // Create a NotificationEmitter MBean
134.66 - //
134.67 - mbean = new ObjectName ("Default:name=NotificationEmitter");
134.68 - mbs.registerMBean(new NotificationEmitter(), mbean);
134.69 + private static void test(String proto, boolean eventService)
134.70 + throws Exception {
134.71 + System.out.println("Unexpected notifications test for protocol " +
134.72 + proto + " with" +
134.73 + (eventService ? "" : "out") + " event service");
134.74 + MBeanServer mbs = null;
134.75 + try {
134.76 + // Create a MBeanServer
134.77 + //
134.78 + mbs = MBeanServerFactory.createMBeanServer();
134.79
134.80 - // Create a connector server
134.81 - //
134.82 - url = new JMXServiceURL("service:jmx:" + protos[i] + "://");
134.83 - server = JMXConnectorServerFactory.newJMXConnectorServer(url,
134.84 - null,
134.85 - mbs);
134.86 + // Create a NotificationEmitter MBean
134.87 + //
134.88 + mbean = new ObjectName ("Default:name=NotificationEmitter");
134.89 + mbs.registerMBean(new NotificationEmitter(), mbean);
134.90
134.91 - mbs.registerMBean(
134.92 - server,
134.93 - new ObjectName("Default:name=ConnectorServer"));
134.94 + // Create a connector server
134.95 + //
134.96 + url = new JMXServiceURL("service:jmx:" + proto + "://");
134.97 + Map<String, String> env = Collections.singletonMap(
134.98 + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
134.99 + Boolean.toString(eventService));
134.100
134.101 - server.start();
134.102 + server = JMXConnectorServerFactory.newJMXConnectorServer(url,
134.103 + env,
134.104 + mbs);
134.105
134.106 - url = server.getAddress();
134.107 + mbs.registerMBean(
134.108 + server,
134.109 + new ObjectName("Default:name=ConnectorServer"));
134.110
134.111 - for (int j = 0; j < 2; j++) {
134.112 - test();
134.113 - }
134.114 - } finally {
134.115 - // Stop server
134.116 - //
134.117 - server.stop();
134.118 - // Release the MBeanServer
134.119 - //
134.120 - MBeanServerFactory.releaseMBeanServer(mbs);
134.121 + server.start();
134.122 +
134.123 + url = server.getAddress();
134.124 +
134.125 + for (int j = 0; j < 2; j++) {
134.126 + test();
134.127 }
134.128 + } finally {
134.129 + // Stop server
134.130 + //
134.131 + server.stop();
134.132 + // Release the MBeanServer
134.133 + //
134.134 + MBeanServerFactory.releaseMBeanServer(mbs);
134.135 }
134.136 }
134.137