Merge jdk7-b34
authorxdono
Thu, 21 Aug 2008 09:55:18 -0700
changeset 491434055a0716e
parent 490 db4bd081eff2
parent 487 e7d93d1d2bf0
child 492 bf580c41f68f
child 499 9f53e194687c
child 533 a23b7f4f8b3c
child 564 9d5e52760d53
Merge
     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 + *     &#64;Override
  15.137 + *     protected {@code Set<ObjectName>} {@link #getNames getNames}() {
  15.138 + *         return Collections.singleton(objectName);
  15.139 + *     }
  15.140 + *
  15.141 + *     &#64;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 + *     &#64;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 + *     &#64;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 + *     &#64;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 + *     &#64;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>&lt;default buffer
   56.56 +     * size&gt;</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