5108776: Add reliable event handling to the JMX API
6218920
: API bug - impossible to delete last MBeanServerForwarder on a connector
Reviewed-by: emcmanus
This commit is contained in:
parent
22260fb95d
commit
cf105cf085
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package com.sun.jmx.event;
|
||||
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class DaemonThreadFactory implements ThreadFactory {
|
||||
public DaemonThreadFactory(String nameTemplate) {
|
||||
this(nameTemplate, null);
|
||||
}
|
||||
|
||||
// nameTemplate should be a format with %d in it, which will be replaced
|
||||
// by a sequence number of threads created by this factory.
|
||||
public DaemonThreadFactory(String nameTemplate, ThreadGroup threadGroup) {
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("DaemonThreadFactory",
|
||||
"Construct a new daemon factory: "+nameTemplate);
|
||||
}
|
||||
|
||||
if (threadGroup == null) {
|
||||
SecurityManager s = System.getSecurityManager();
|
||||
threadGroup = (s != null) ? s.getThreadGroup() :
|
||||
Thread.currentThread().getThreadGroup();
|
||||
}
|
||||
|
||||
this.nameTemplate = nameTemplate;
|
||||
this.threadGroup = threadGroup;
|
||||
}
|
||||
|
||||
public Thread newThread(Runnable r) {
|
||||
final String name =
|
||||
String.format(nameTemplate, threadNumber.getAndIncrement());
|
||||
Thread t = new Thread(threadGroup, r, name, 0);
|
||||
t.setDaemon(true);
|
||||
if (t.getPriority() != Thread.NORM_PRIORITY)
|
||||
t.setPriority(Thread.NORM_PRIORITY);
|
||||
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("newThread",
|
||||
"Create a new daemon thread with the name "+t.getName());
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
private final String nameTemplate;
|
||||
private final ThreadGroup threadGroup;
|
||||
private final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("com.sun.jmx.event", "DaemonThreadFactory");
|
||||
}
|
252
jdk/src/share/classes/com/sun/jmx/event/EventBuffer.java
Normal file
252
jdk/src/share/classes/com/sun/jmx/event/EventBuffer.java
Normal file
@ -0,0 +1,252 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package com.sun.jmx.event;
|
||||
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.management.remote.NotificationResult;
|
||||
import javax.management.remote.TargetedNotification;
|
||||
|
||||
public class EventBuffer {
|
||||
|
||||
public EventBuffer() {
|
||||
this(Integer.MAX_VALUE, null);
|
||||
}
|
||||
|
||||
public EventBuffer(int capacity) {
|
||||
this(capacity, new ArrayList<TargetedNotification>());
|
||||
}
|
||||
|
||||
public EventBuffer(int capacity, final List<TargetedNotification> list) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("EventBuffer", "New buffer with the capacity: "
|
||||
+capacity);
|
||||
}
|
||||
if (capacity < 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"The capacity must be bigger than 0");
|
||||
}
|
||||
|
||||
if (list == null) {
|
||||
throw new NullPointerException("Null list.");
|
||||
}
|
||||
|
||||
this.capacity = capacity;
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
public void add(TargetedNotification tn) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("add", "Add one notif.");
|
||||
}
|
||||
|
||||
synchronized(lock) {
|
||||
if (list.size() == capacity) { // have to throw one
|
||||
passed++;
|
||||
list.remove(0);
|
||||
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("add", "Over, remove the oldest one.");
|
||||
}
|
||||
}
|
||||
|
||||
list.add(tn);
|
||||
lock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
public void add(TargetedNotification[] tns) {
|
||||
if (tns == null || tns.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("add", "Add notifs: "+tns.length);
|
||||
}
|
||||
|
||||
synchronized(lock) {
|
||||
final int d = list.size() - capacity + tns.length;
|
||||
if (d > 0) { // have to throw
|
||||
passed += d;
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("add",
|
||||
"Over, remove the oldest: "+d);
|
||||
}
|
||||
if (tns.length <= capacity){
|
||||
list.subList(0, d).clear();
|
||||
} else {
|
||||
list.clear();
|
||||
TargetedNotification[] tmp =
|
||||
new TargetedNotification[capacity];
|
||||
System.arraycopy(tns, tns.length-capacity, tmp, 0, capacity);
|
||||
tns = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
Collections.addAll(list,tns);
|
||||
lock.notify();
|
||||
}
|
||||
}
|
||||
|
||||
public NotificationResult fetchNotifications(long startSequenceNumber,
|
||||
long timeout,
|
||||
int maxNotifications) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("fetchNotifications",
|
||||
"Being called: "
|
||||
+startSequenceNumber+" "
|
||||
+timeout+" "+maxNotifications);
|
||||
}
|
||||
if (startSequenceNumber < 0 ||
|
||||
timeout < 0 ||
|
||||
maxNotifications < 0) {
|
||||
throw new IllegalArgumentException("Negative value.");
|
||||
}
|
||||
|
||||
TargetedNotification[] tns = new TargetedNotification[0];
|
||||
long earliest = startSequenceNumber < passed ?
|
||||
passed : startSequenceNumber;
|
||||
long next = earliest;
|
||||
|
||||
final long startTime = System.currentTimeMillis();
|
||||
long toWait = timeout;
|
||||
synchronized(lock) {
|
||||
int toSkip = (int)(startSequenceNumber - passed);
|
||||
|
||||
// skip those before startSequenceNumber.
|
||||
while (!closed && toSkip > 0) {
|
||||
toWait = timeout - (System.currentTimeMillis() - startTime);
|
||||
if (list.size() == 0) {
|
||||
if (toWait <= 0) {
|
||||
// the notification of startSequenceNumber
|
||||
// does not arrive yet.
|
||||
return new NotificationResult(startSequenceNumber,
|
||||
startSequenceNumber,
|
||||
new TargetedNotification[0]);
|
||||
}
|
||||
|
||||
waiting(toWait);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (toSkip <= list.size()) {
|
||||
list.subList(0, toSkip).clear();
|
||||
passed += toSkip;
|
||||
|
||||
break;
|
||||
} else {
|
||||
passed += list.size();
|
||||
toSkip -= list.size();
|
||||
|
||||
list.clear();
|
||||
}
|
||||
}
|
||||
|
||||
earliest = passed;
|
||||
|
||||
if (list.size() == 0) {
|
||||
toWait = timeout - (System.currentTimeMillis() - startTime);
|
||||
|
||||
waiting(toWait);
|
||||
}
|
||||
|
||||
if (list.size() == 0) {
|
||||
tns = new TargetedNotification[0];
|
||||
} else if (list.size() <= maxNotifications) {
|
||||
tns = list.toArray(new TargetedNotification[0]);
|
||||
} else {
|
||||
tns = new TargetedNotification[maxNotifications];
|
||||
for (int i=0; i<maxNotifications; i++) {
|
||||
tns[i] = list.get(i);
|
||||
}
|
||||
}
|
||||
|
||||
next = earliest + tns.length;
|
||||
}
|
||||
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("fetchNotifications",
|
||||
"Return: "+earliest+" "+next+" "+tns.length);
|
||||
}
|
||||
|
||||
return new NotificationResult(earliest, next, tns);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
public void addLost(long nb) {
|
||||
synchronized(lock) {
|
||||
passed += nb;
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("clear", "done");
|
||||
}
|
||||
|
||||
synchronized(lock) {
|
||||
list.clear();
|
||||
closed = true;
|
||||
lock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------
|
||||
// private classes
|
||||
// -------------------------------------------
|
||||
private void waiting(long timeout) {
|
||||
final long startTime = System.currentTimeMillis();
|
||||
long toWait = timeout;
|
||||
synchronized(lock) {
|
||||
while (!closed && list.size() == 0 && toWait > 0) {
|
||||
try {
|
||||
lock.wait(toWait);
|
||||
|
||||
toWait = timeout - (System.currentTimeMillis() - startTime);
|
||||
} catch (InterruptedException ire) {
|
||||
logger.trace("waiting", ire);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final int capacity;
|
||||
private final List<TargetedNotification> list;
|
||||
private boolean closed;
|
||||
|
||||
private long passed = 0;
|
||||
private final int[] lock = new int[0];
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.event", "EventBuffer");
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package com.sun.jmx.event;
|
||||
|
||||
import javax.management.event.*;
|
||||
|
||||
/**
|
||||
* Implemented by objects which are using an {@link EventClient} to
|
||||
* subscribe for Notifications.
|
||||
*
|
||||
*/
|
||||
public interface EventClientFactory {
|
||||
/**
|
||||
* Returns the {@code EventClient} that the object implementing this
|
||||
* interface uses to subscribe for Notifications. This method returns
|
||||
* {@code null} if no {@code EventClient} can be used - e.g. because
|
||||
* the underlying server does not have any {@link EventDelegate}.
|
||||
*
|
||||
* @return an {@code EventClient} or {@code null}.
|
||||
**/
|
||||
public EventClient getEventClient();
|
||||
|
||||
}
|
81
jdk/src/share/classes/com/sun/jmx/event/EventConnection.java
Normal file
81
jdk/src/share/classes/com/sun/jmx/event/EventConnection.java
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package com.sun.jmx.event;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.event.EventClient;
|
||||
import javax.management.event.EventClientDelegate;
|
||||
import javax.management.event.EventConsumer;
|
||||
import javax.management.event.NotificationManager;
|
||||
|
||||
/**
|
||||
* Override the methods related to the notification to use the
|
||||
* Event service.
|
||||
*/
|
||||
public interface EventConnection extends MBeanServerConnection, EventConsumer {
|
||||
public EventClient getEventClient();
|
||||
|
||||
public static class Factory {
|
||||
public static EventConnection make(
|
||||
final MBeanServerConnection mbsc,
|
||||
final EventClient eventClient)
|
||||
throws IOException {
|
||||
if (!mbsc.isRegistered(EventClientDelegate.OBJECT_NAME)) {
|
||||
throw new IOException(
|
||||
"The server does not support the event service.");
|
||||
}
|
||||
InvocationHandler ih = new InvocationHandler() {
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Throwable {
|
||||
Class<?> intf = method.getDeclaringClass();
|
||||
try {
|
||||
if (intf.isInstance(eventClient))
|
||||
return method.invoke(eventClient, args);
|
||||
else
|
||||
return method.invoke(mbsc, args);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
};
|
||||
// It is important to declare NotificationManager.class first
|
||||
// in the array below, so that the relevant addNL and removeNL
|
||||
// methods will show up with method.getDeclaringClass() as
|
||||
// being from that interface and not MBeanServerConnection.
|
||||
return (EventConnection) Proxy.newProxyInstance(
|
||||
NotificationManager.class.getClassLoader(),
|
||||
new Class<?>[] {
|
||||
NotificationManager.class, EventConnection.class,
|
||||
},
|
||||
ih);
|
||||
}
|
||||
}
|
||||
}
|
65
jdk/src/share/classes/com/sun/jmx/event/EventParams.java
Normal file
65
jdk/src/share/classes/com/sun/jmx/event/EventParams.java
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package com.sun.jmx.event;
|
||||
|
||||
import com.sun.jmx.mbeanserver.GetPropertyAction;
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.security.AccessController;
|
||||
import javax.management.event.EventClient;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author sjiang
|
||||
*/
|
||||
public class EventParams {
|
||||
public static final String DEFAULT_LEASE_TIMEOUT =
|
||||
"com.sun.event.lease.time";
|
||||
|
||||
|
||||
@SuppressWarnings("cast") // cast for jdk 1.5
|
||||
public static long getLeaseTimeout() {
|
||||
long timeout = EventClient.DEFAULT_LEASE_TIMEOUT;
|
||||
try {
|
||||
final GetPropertyAction act =
|
||||
new GetPropertyAction(DEFAULT_LEASE_TIMEOUT);
|
||||
final String s = (String)AccessController.doPrivileged(act);
|
||||
if (s != null) {
|
||||
timeout = Long.parseLong(s);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
logger.fine("getLeaseTimeout", "exception getting property", e);
|
||||
}
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/** Creates a new instance of EventParams */
|
||||
private EventParams() {
|
||||
}
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.event", "EventParams");
|
||||
}
|
146
jdk/src/share/classes/com/sun/jmx/event/LeaseManager.java
Normal file
146
jdk/src/share/classes/com/sun/jmx/event/LeaseManager.java
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package com.sun.jmx.event;
|
||||
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* <p>Manage a renewable lease. The lease can be renewed indefinitely
|
||||
* but if the lease runs to its current expiry date without being renewed
|
||||
* then the expiry callback is invoked. If the lease has already expired
|
||||
* when renewal is attempted then the lease method returns zero.</p>
|
||||
* @author sjiang
|
||||
* @author emcmanus
|
||||
*/
|
||||
// The synchronization logic of this class is tricky to deal correctly with the
|
||||
// case where the lease expires at the same time as the |lease| or |stop| method
|
||||
// is called. If the lease is active then the field |scheduled| represents
|
||||
// the expiry task; otherwise |scheduled| is null. Renewing or stopping the
|
||||
// lease involves canceling this task and setting |scheduled| either to a new
|
||||
// task (to renew) or to null (to stop).
|
||||
//
|
||||
// Suppose the expiry task runs at the same time as the |lease| method is called.
|
||||
// If the task enters its synchronized block before the method starts, then
|
||||
// it will set |scheduled| to null and the method will return 0. If the method
|
||||
// starts before the task enters its synchronized block, then the method will
|
||||
// cancel the task which will see that when it later enters the block.
|
||||
// Similar reasoning applies to the |stop| method. It is not expected that
|
||||
// different threads will call |lease| or |stop| simultaneously, although the
|
||||
// logic should be correct then too.
|
||||
public class LeaseManager {
|
||||
public LeaseManager(Runnable callback) {
|
||||
this(callback, EventParams.getLeaseTimeout());
|
||||
}
|
||||
|
||||
public LeaseManager(Runnable callback, long timeout) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("LeaseManager", "new manager with lease: "+timeout);
|
||||
}
|
||||
if (callback == null) {
|
||||
throw new NullPointerException("Null callback.");
|
||||
}
|
||||
if (timeout <= 0)
|
||||
throw new IllegalArgumentException("Timeout must be positive: " + timeout);
|
||||
|
||||
this.callback = callback;
|
||||
schedule(timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Renew the lease for the given time. The new time can be shorter
|
||||
* than the previous one, in which case the lease will expire earlier
|
||||
* than it would have.</p>
|
||||
*
|
||||
* <p>Calling this method after the lease has expired will return zero
|
||||
* immediately and have no other effect.</p>
|
||||
*
|
||||
* @param timeout the new lifetime. If zero, the lease
|
||||
* will expire immediately.
|
||||
*/
|
||||
public synchronized long lease(long timeout) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("lease", "new lease to: "+timeout);
|
||||
}
|
||||
|
||||
if (timeout < 0)
|
||||
throw new IllegalArgumentException("Negative lease: " + timeout);
|
||||
|
||||
if (scheduled == null)
|
||||
return 0L;
|
||||
|
||||
scheduled.cancel(false);
|
||||
|
||||
if (logger.traceOn())
|
||||
logger.trace("lease", "start lease: "+timeout);
|
||||
schedule(timeout);
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
private class Expire implements Runnable {
|
||||
ScheduledFuture<?> task;
|
||||
|
||||
public void run() {
|
||||
synchronized (LeaseManager.this) {
|
||||
if (task.isCancelled())
|
||||
return;
|
||||
scheduled = null;
|
||||
}
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void schedule(long timeout) {
|
||||
Expire expire = new Expire();
|
||||
scheduled = executor.schedule(expire, timeout, TimeUnit.MILLISECONDS);
|
||||
expire.task = scheduled;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Cancel the lease without calling the expiry callback.</p>
|
||||
*/
|
||||
public synchronized void stop() {
|
||||
logger.trace("stop", "canceling lease");
|
||||
scheduled.cancel(false);
|
||||
scheduled = null;
|
||||
}
|
||||
|
||||
private final Runnable callback;
|
||||
private ScheduledFuture scheduled; // If null, the lease has expired.
|
||||
|
||||
private final ScheduledExecutorService executor
|
||||
= Executors.newScheduledThreadPool(1,
|
||||
new DaemonThreadFactory("LeaseManager"));
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.event", "LeaseManager");
|
||||
|
||||
}
|
143
jdk/src/share/classes/com/sun/jmx/event/LeaseRenewer.java
Normal file
143
jdk/src/share/classes/com/sun/jmx/event/LeaseRenewer.java
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package com.sun.jmx.event;
|
||||
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author sjiang
|
||||
*/
|
||||
public class LeaseRenewer {
|
||||
public LeaseRenewer(ScheduledExecutorService scheduler, Callable<Long> doRenew) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("LeaseRenewer", "New LeaseRenewer.");
|
||||
}
|
||||
|
||||
if (doRenew == null) {
|
||||
throw new NullPointerException("Null job to call server.");
|
||||
}
|
||||
|
||||
this.doRenew = doRenew;
|
||||
nextRenewTime = System.currentTimeMillis();
|
||||
|
||||
this.scheduler = scheduler;
|
||||
future = this.scheduler.schedule(myRenew, 0, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("close", "Close the lease.");
|
||||
}
|
||||
|
||||
synchronized(lock) {
|
||||
if (closed) {
|
||||
return;
|
||||
} else {
|
||||
closed = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
future.cancel(false); // not interrupt if running
|
||||
} catch (Exception e) {
|
||||
// OK
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("close", "Failed to cancel the leasing job.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean closed() {
|
||||
synchronized(lock) {
|
||||
return closed;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------
|
||||
// private
|
||||
// ------------------------------
|
||||
private final Runnable myRenew = new Runnable() {
|
||||
public void run() {
|
||||
synchronized(lock) {
|
||||
if (closed()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
long next = nextRenewTime - System.currentTimeMillis();
|
||||
if (next < MIN_MILLIS) {
|
||||
try {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("myRenew-run", "");
|
||||
}
|
||||
next = doRenew.call().longValue();
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.fine("myRenew-run", "Failed to renew lease", e);
|
||||
close();
|
||||
}
|
||||
|
||||
if (next > 0 && next < Long.MAX_VALUE) {
|
||||
next = next/2;
|
||||
next = (next < MIN_MILLIS) ? MIN_MILLIS : next;
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
nextRenewTime = System.currentTimeMillis() + next;
|
||||
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("myRenew-run", "Next leasing: "+next);
|
||||
}
|
||||
|
||||
synchronized(lock) {
|
||||
if (!closed) {
|
||||
future = scheduler.schedule(this, next, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Callable<Long> doRenew;
|
||||
private ScheduledFuture future;
|
||||
private boolean closed = false;
|
||||
private long nextRenewTime;
|
||||
|
||||
private final int[] lock = new int[0];
|
||||
|
||||
private final ScheduledExecutorService scheduler;
|
||||
|
||||
private static final long MIN_MILLIS = 50;
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.event", "LeaseRenewer");
|
||||
}
|
97
jdk/src/share/classes/com/sun/jmx/event/ReceiverBuffer.java
Normal file
97
jdk/src/share/classes/com/sun/jmx/event/ReceiverBuffer.java
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package com.sun.jmx.event;
|
||||
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.management.remote.NotificationResult;
|
||||
import javax.management.remote.TargetedNotification;
|
||||
|
||||
|
||||
public class ReceiverBuffer {
|
||||
public void addNotifs(NotificationResult nr) {
|
||||
if (nr == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TargetedNotification[] tns = nr.getTargetedNotifications();
|
||||
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("addNotifs", "" + tns.length);
|
||||
}
|
||||
|
||||
long impliedStart = nr.getEarliestSequenceNumber();
|
||||
final long missed = impliedStart - start;
|
||||
start = nr.getNextSequenceNumber();
|
||||
|
||||
if (missed > 0) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("addNotifs",
|
||||
"lost: "+missed);
|
||||
}
|
||||
|
||||
lost += missed;
|
||||
}
|
||||
|
||||
Collections.addAll(notifList, nr.getTargetedNotifications());
|
||||
}
|
||||
|
||||
public TargetedNotification[] removeNotifs() {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("removeNotifs", String.valueOf(notifList.size()));
|
||||
}
|
||||
|
||||
if (notifList.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TargetedNotification[] ret = notifList.toArray(
|
||||
new TargetedNotification[]{});
|
||||
notifList.clear();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return notifList.size();
|
||||
}
|
||||
|
||||
public int removeLost() {
|
||||
int ret = lost;
|
||||
lost = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private List<TargetedNotification> notifList
|
||||
= new ArrayList<TargetedNotification>();
|
||||
private long start = 0;
|
||||
private int lost = 0;
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.event", "ReceiverBuffer");
|
||||
}
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package com.sun.jmx.event;
|
||||
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
/**
|
||||
* <p>A task that is repeatedly run by an Executor. The task will be
|
||||
* repeated as long as the {@link #isSuspended()} method returns true. Once
|
||||
* that method returns false, the task is no longer executed until someone
|
||||
* calls {@link #resume()}.</p>
|
||||
* @author sjiang
|
||||
*/
|
||||
public abstract class RepeatedSingletonJob implements Runnable {
|
||||
public RepeatedSingletonJob(Executor executor) {
|
||||
if (executor == null) {
|
||||
throw new NullPointerException("Null executor!");
|
||||
}
|
||||
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public boolean isWorking() {
|
||||
return working;
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
|
||||
synchronized(this) {
|
||||
if (!working) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("resume", "");
|
||||
}
|
||||
working = true;
|
||||
execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void task();
|
||||
public abstract boolean isSuspended();
|
||||
|
||||
public void run() {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("run", "execute the task");
|
||||
}
|
||||
try {
|
||||
task();
|
||||
} catch (Exception e) {
|
||||
// A correct task() implementation should not throw exceptions.
|
||||
// It may cause isSuspended() to start returning true, though.
|
||||
logger.trace("run", "failed to execute the task", e);
|
||||
}
|
||||
|
||||
synchronized(this) {
|
||||
if (!isSuspended()) {
|
||||
execute();
|
||||
} else {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("run", "suspend the task");
|
||||
}
|
||||
working = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void execute() {
|
||||
try {
|
||||
executor.execute(this);
|
||||
} catch (RejectedExecutionException e) {
|
||||
logger.warning(
|
||||
"setEventReceiver", "Executor threw exception", e);
|
||||
throw new RejectedExecutionException(
|
||||
"Executor.execute threw exception -" +
|
||||
"should not be possible", e);
|
||||
// User-supplied Executor should not be configured in a way that
|
||||
// might cause this exception, for example if it is shared between
|
||||
// several client objects and doesn't have capacity for one job
|
||||
// from each one. CR 6732037 will add text to the spec explaining
|
||||
// the problem. The rethrown exception will propagate either out
|
||||
// of resume() to user code, or out of run() to the Executor
|
||||
// (which will probably ignore it).
|
||||
}
|
||||
}
|
||||
|
||||
private boolean working = false;
|
||||
private final Executor executor;
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.event", "RepeatedSingletonJob");
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,414 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package com.sun.jmx.interceptor;
|
||||
|
||||
import com.sun.jmx.mbeanserver.Util;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import javax.management.Attribute;
|
||||
import javax.management.AttributeList;
|
||||
import javax.management.AttributeNotFoundException;
|
||||
import javax.management.DynamicMBean;
|
||||
import javax.management.InstanceAlreadyExistsException;
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.IntrospectionException;
|
||||
import javax.management.InvalidAttributeValueException;
|
||||
import javax.management.ListenerNotFoundException;
|
||||
import javax.management.MBeanException;
|
||||
import javax.management.MBeanInfo;
|
||||
import javax.management.MBeanRegistrationException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.NotCompliantMBeanException;
|
||||
import javax.management.NotificationEmitter;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectInstance;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.QueryExp;
|
||||
import javax.management.ReflectionException;
|
||||
import javax.management.remote.IdentityMBeanServerForwarder;
|
||||
|
||||
public class SingleMBeanForwarder extends IdentityMBeanServerForwarder {
|
||||
|
||||
private final ObjectName mbeanName;
|
||||
private DynamicMBean mbean;
|
||||
|
||||
private MBeanServer mbeanMBS = new MBeanServerSupport() {
|
||||
|
||||
@Override
|
||||
public DynamicMBean getDynamicMBeanFor(ObjectName name)
|
||||
throws InstanceNotFoundException {
|
||||
if (mbeanName.equals(name)) {
|
||||
return mbean;
|
||||
} else {
|
||||
throw new InstanceNotFoundException(name.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<ObjectName> getNames() {
|
||||
return Collections.singleton(mbeanName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationEmitter getNotificationEmitterFor(
|
||||
ObjectName name) {
|
||||
if (mbean instanceof NotificationEmitter)
|
||||
return (NotificationEmitter) mbean;
|
||||
return null;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public SingleMBeanForwarder(ObjectName mbeanName, DynamicMBean mbean) {
|
||||
this.mbeanName = mbeanName;
|
||||
setSingleMBean(mbean);
|
||||
}
|
||||
|
||||
protected void setSingleMBean(DynamicMBean mbean) {
|
||||
this.mbean = mbean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotificationListener(ObjectName name, ObjectName listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException {
|
||||
if (mbeanName.equals(name))
|
||||
mbeanMBS.addNotificationListener(name, listener, filter, handback);
|
||||
else
|
||||
super.addNotificationListener(name, listener, filter, handback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotificationListener(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException {
|
||||
if (mbeanName.equals(name))
|
||||
mbeanMBS.addNotificationListener(name, listener, filter, handback);
|
||||
else
|
||||
super.addNotificationListener(name, listener, filter, handback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectInstance createMBean(String className, ObjectName name,
|
||||
ObjectName loaderName, Object[] params,
|
||||
String[] signature)
|
||||
throws ReflectionException,
|
||||
InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException,
|
||||
MBeanException,
|
||||
NotCompliantMBeanException,
|
||||
InstanceNotFoundException {
|
||||
if (mbeanName.equals(name))
|
||||
throw new InstanceAlreadyExistsException(mbeanName.toString());
|
||||
else
|
||||
return super.createMBean(className, name, loaderName, params, signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectInstance createMBean(String className, ObjectName name,
|
||||
Object[] params, String[] signature)
|
||||
throws ReflectionException, InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException, MBeanException,
|
||||
NotCompliantMBeanException {
|
||||
if (mbeanName.equals(name))
|
||||
throw new InstanceAlreadyExistsException(mbeanName.toString());
|
||||
return super.createMBean(className, name, params, signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectInstance createMBean(String className, ObjectName name,
|
||||
ObjectName loaderName)
|
||||
throws ReflectionException,
|
||||
InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException,
|
||||
MBeanException,
|
||||
NotCompliantMBeanException,
|
||||
InstanceNotFoundException {
|
||||
if (mbeanName.equals(name))
|
||||
throw new InstanceAlreadyExistsException(mbeanName.toString());
|
||||
return super.createMBean(className, name, loaderName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectInstance createMBean(String className, ObjectName name)
|
||||
throws ReflectionException,
|
||||
InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException,
|
||||
MBeanException,
|
||||
NotCompliantMBeanException {
|
||||
if (mbeanName.equals(name))
|
||||
throw new InstanceAlreadyExistsException(mbeanName.toString());
|
||||
return super.createMBean(className, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getAttribute(ObjectName name, String attribute)
|
||||
throws MBeanException,
|
||||
AttributeNotFoundException,
|
||||
InstanceNotFoundException,
|
||||
ReflectionException {
|
||||
if (mbeanName.equals(name))
|
||||
return mbeanMBS.getAttribute(name, attribute);
|
||||
else
|
||||
return super.getAttribute(name, attribute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeList getAttributes(ObjectName name, String[] attributes)
|
||||
throws InstanceNotFoundException, ReflectionException {
|
||||
if (mbeanName.equals(name))
|
||||
return mbeanMBS.getAttributes(name, attributes);
|
||||
else
|
||||
return super.getAttributes(name, attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader(ObjectName loaderName)
|
||||
throws InstanceNotFoundException {
|
||||
if (mbeanName.equals(loaderName))
|
||||
return mbeanMBS.getClassLoader(loaderName);
|
||||
else
|
||||
return super.getClassLoader(loaderName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoaderFor(ObjectName name)
|
||||
throws InstanceNotFoundException {
|
||||
if (mbeanName.equals(name))
|
||||
return mbeanMBS.getClassLoaderFor(name);
|
||||
else
|
||||
return super.getClassLoaderFor(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDomains() {
|
||||
TreeSet<String> domainSet =
|
||||
new TreeSet<String>(Arrays.asList(super.getDomains()));
|
||||
domainSet.add(mbeanName.getDomain());
|
||||
return domainSet.toArray(new String[domainSet.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getMBeanCount() {
|
||||
Integer count = super.getMBeanCount();
|
||||
if (!super.isRegistered(mbeanName))
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MBeanInfo getMBeanInfo(ObjectName name)
|
||||
throws InstanceNotFoundException,
|
||||
IntrospectionException,
|
||||
ReflectionException {
|
||||
if (mbeanName.equals(name))
|
||||
return mbeanMBS.getMBeanInfo(name);
|
||||
else
|
||||
return super.getMBeanInfo(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectInstance getObjectInstance(ObjectName name)
|
||||
throws InstanceNotFoundException {
|
||||
if (mbeanName.equals(name))
|
||||
return mbeanMBS.getObjectInstance(name);
|
||||
else
|
||||
return super.getObjectInstance(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(ObjectName name, String operationName, Object[] params,
|
||||
String[] signature)
|
||||
throws InstanceNotFoundException,
|
||||
MBeanException,
|
||||
ReflectionException {
|
||||
if (mbeanName.equals(name))
|
||||
return mbeanMBS.invoke(name, operationName, params, signature);
|
||||
else
|
||||
return super.invoke(name, operationName, params, signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInstanceOf(ObjectName name, String className)
|
||||
throws InstanceNotFoundException {
|
||||
if (mbeanName.equals(name))
|
||||
return mbeanMBS.isInstanceOf(name, className);
|
||||
else
|
||||
return super.isInstanceOf(name, className);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRegistered(ObjectName name) {
|
||||
if (mbeanName.equals(name))
|
||||
return true;
|
||||
else
|
||||
return super.isRegistered(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a ugly hack. Although jmx.context//*:* matches jmx.context//:*
|
||||
* queryNames(jmx.context//*:*,null) must not return jmx.context//:*
|
||||
* @param pattern the pattern to match against. must not be null.
|
||||
* @return true if mbeanName can be included, false if it must not.
|
||||
*/
|
||||
private boolean applies(ObjectName pattern) {
|
||||
// we know pattern is not null.
|
||||
if (!pattern.apply(mbeanName))
|
||||
return false;
|
||||
|
||||
// final String dompat = pattern.getDomain();
|
||||
// if (!dompat.contains(JMXNamespaces.NAMESPACE_SEPARATOR))
|
||||
// return true; // We already checked that patterns apply.
|
||||
//
|
||||
// if (mbeanName.getDomain().endsWith(JMXNamespaces.NAMESPACE_SEPARATOR)) {
|
||||
// // only matches if pattern ends with //
|
||||
// return dompat.endsWith(JMXNamespaces.NAMESPACE_SEPARATOR);
|
||||
// }
|
||||
|
||||
// should not come here, unless mbeanName contains a // in the
|
||||
// middle of its domain, which would be weird.
|
||||
// let query on mbeanMBS proceed and take care of that.
|
||||
//
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
|
||||
Set<ObjectInstance> names = super.queryMBeans(name, query);
|
||||
if (name == null || applies(name) ) {
|
||||
// Don't assume mbs.queryNames returns a writable set.
|
||||
names = Util.cloneSet(names);
|
||||
names.addAll(mbeanMBS.queryMBeans(name, query));
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
|
||||
Set<ObjectName> names = super.queryNames(name, query);
|
||||
if (name == null || applies(name)) {
|
||||
// Don't assume mbs.queryNames returns a writable set.
|
||||
names = Util.cloneSet(names);
|
||||
names.addAll(mbeanMBS.queryNames(name, query));
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ObjectInstance registerMBean(Object object, ObjectName name)
|
||||
throws InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException,
|
||||
NotCompliantMBeanException {
|
||||
if (mbeanName.equals(name))
|
||||
throw new InstanceAlreadyExistsException(mbeanName.toString());
|
||||
else
|
||||
return super.registerMBean(object, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNotificationListener(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException,
|
||||
ListenerNotFoundException {
|
||||
if (mbeanName.equals(name))
|
||||
mbeanMBS.removeNotificationListener(name, listener, filter, handback);
|
||||
else
|
||||
super.removeNotificationListener(name, listener, filter, handback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNotificationListener(ObjectName name,
|
||||
NotificationListener listener)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException {
|
||||
if (mbeanName.equals(name))
|
||||
mbeanMBS.removeNotificationListener(name, listener);
|
||||
else
|
||||
super.removeNotificationListener(name, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNotificationListener(ObjectName name, ObjectName listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException,
|
||||
ListenerNotFoundException {
|
||||
if (mbeanName.equals(name))
|
||||
mbeanMBS.removeNotificationListener(name, listener, filter, handback);
|
||||
else
|
||||
super.removeNotificationListener(name, listener, filter, handback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNotificationListener(ObjectName name, ObjectName listener)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException {
|
||||
if (mbeanName.equals(name))
|
||||
mbeanMBS.removeNotificationListener(name, listener);
|
||||
else
|
||||
super.removeNotificationListener(name, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(ObjectName name, Attribute attribute)
|
||||
throws InstanceNotFoundException,
|
||||
AttributeNotFoundException,
|
||||
InvalidAttributeValueException,
|
||||
MBeanException,
|
||||
ReflectionException {
|
||||
if (mbeanName.equals(name))
|
||||
mbeanMBS.setAttribute(name, attribute);
|
||||
else
|
||||
super.setAttribute(name, attribute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeList setAttributes(ObjectName name,
|
||||
AttributeList attributes)
|
||||
throws InstanceNotFoundException, ReflectionException {
|
||||
if (mbeanName.equals(name))
|
||||
return mbeanMBS.setAttributes(name, attributes);
|
||||
else
|
||||
return super.setAttributes(name, attributes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterMBean(ObjectName name)
|
||||
throws InstanceNotFoundException,
|
||||
MBeanRegistrationException {
|
||||
if (mbeanName.equals(name))
|
||||
mbeanMBS.unregisterMBean(name);
|
||||
else
|
||||
super.unregisterMBean(name);
|
||||
}
|
||||
}
|
@ -29,5 +29,8 @@ have any questions.
|
||||
</head>
|
||||
<body bgcolor="white">
|
||||
Provides specific classes to <B>Sun JMX Reference Implementation</B>.
|
||||
<p><b>
|
||||
This API is a Sun internal API and is subject to changes without notice.
|
||||
</b></p>
|
||||
</BODY>
|
||||
</HTML>
|
||||
|
@ -172,7 +172,7 @@ public class MBeanInjector {
|
||||
* reference.
|
||||
*
|
||||
* So we accept a Field if it has a @Resource annotation and either
|
||||
* (a) its type is ObjectName or a subclass and its @Resource type is
|
||||
* (a) its type is exactly ObjectName and its @Resource type is
|
||||
* compatible with ObjectName (e.g. it is Object); or
|
||||
* (b) its type is compatible with ObjectName and its @Resource type
|
||||
* is exactly ObjectName. Fields that meet these criteria will not
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
package com.sun.jmx.mbeanserver;
|
||||
|
||||
import static com.sun.jmx.mbeanserver.Util.*;
|
||||
|
||||
import javax.management.Attribute;
|
||||
import javax.management.AttributeList;
|
||||
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package com.sun.jmx.mbeanserver;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
/**
|
||||
* <p>A factory for ThreadPoolExecutor objects that allows the same object to
|
||||
* be shared by all users of the factory that are in the same ThreadGroup.</p>
|
||||
*/
|
||||
// We return a ThreadPoolExecutor rather than the more general ExecutorService
|
||||
// because we need to be able to call allowCoreThreadTimeout so that threads in
|
||||
// the pool will eventually be destroyed when the pool is no longer in use.
|
||||
// Otherwise these threads would keep the ThreadGroup alive forever.
|
||||
public class PerThreadGroupPool<T extends ThreadPoolExecutor> {
|
||||
private final WeakIdentityHashMap<ThreadGroup, WeakReference<T>> map =
|
||||
WeakIdentityHashMap.make();
|
||||
|
||||
public static interface Create<T extends ThreadPoolExecutor> {
|
||||
public T createThreadPool(ThreadGroup group);
|
||||
}
|
||||
|
||||
private PerThreadGroupPool() {}
|
||||
|
||||
public static <T extends ThreadPoolExecutor> PerThreadGroupPool<T> make() {
|
||||
return new PerThreadGroupPool<T>();
|
||||
}
|
||||
|
||||
public synchronized T getThreadPoolExecutor(Create<T> create) {
|
||||
// Find out if there's already an existing executor for the calling
|
||||
// thread and reuse it. Otherwise, create a new one and store it in
|
||||
// the executors map. If there is a SecurityManager, the group of
|
||||
// System.getSecurityManager() is used, else the group of the calling
|
||||
// thread.
|
||||
SecurityManager s = System.getSecurityManager();
|
||||
ThreadGroup group = (s != null) ? s.getThreadGroup() :
|
||||
Thread.currentThread().getThreadGroup();
|
||||
WeakReference<T> wr = map.get(group);
|
||||
T executor = (wr == null) ? null : wr.get();
|
||||
if (executor == null) {
|
||||
executor = create.createThreadPool(group);
|
||||
executor.allowCoreThreadTimeOut(true);
|
||||
map.put(group, new WeakReference<T>(executor));
|
||||
}
|
||||
return executor;
|
||||
}
|
||||
}
|
@ -38,10 +38,13 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.WeakHashMap;
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.loading.ClassLoaderRepository;
|
||||
|
||||
public class Util {
|
||||
static <K, V> Map<K, V> newMap() {
|
||||
@ -142,4 +145,97 @@ public class Util {
|
||||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a set of ObjectName according to a given pattern.
|
||||
*
|
||||
* @param pattern the pattern that the returned names must match.
|
||||
* @param all the set of names to filter.
|
||||
* @return a set of ObjectName from which non matching names
|
||||
* have been removed.
|
||||
*/
|
||||
public static Set<ObjectName> filterMatchingNames(ObjectName pattern,
|
||||
Set<ObjectName> all) {
|
||||
// If no pattern, just return all names
|
||||
if (pattern == null
|
||||
|| all.isEmpty()
|
||||
|| ObjectName.WILDCARD.equals(pattern))
|
||||
return all;
|
||||
|
||||
// If there's a pattern, do the matching.
|
||||
final Set<ObjectName> res = equivalentEmptySet(all);
|
||||
for (ObjectName n : all) if (pattern.apply(n)) res.add(n);
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstract ClassLoaderRepository that contains a single class loader.
|
||||
**/
|
||||
private final static class SingleClassLoaderRepository
|
||||
implements ClassLoaderRepository {
|
||||
private final ClassLoader singleLoader;
|
||||
|
||||
SingleClassLoaderRepository(ClassLoader loader) {
|
||||
this.singleLoader = loader;
|
||||
}
|
||||
|
||||
ClassLoader getSingleClassLoader() {
|
||||
return singleLoader;
|
||||
}
|
||||
|
||||
private Class<?> loadClass(String className, ClassLoader loader)
|
||||
throws ClassNotFoundException {
|
||||
return Class.forName(className, false, loader);
|
||||
}
|
||||
|
||||
public Class<?> loadClass(String className)
|
||||
throws ClassNotFoundException {
|
||||
return loadClass(className, getSingleClassLoader());
|
||||
}
|
||||
|
||||
public Class<?> loadClassWithout(ClassLoader exclude,
|
||||
String className) throws ClassNotFoundException {
|
||||
final ClassLoader loader = getSingleClassLoader();
|
||||
if (exclude != null && exclude.equals(loader))
|
||||
throw new ClassNotFoundException(className);
|
||||
return loadClass(className, loader);
|
||||
}
|
||||
|
||||
public Class<?> loadClassBefore(ClassLoader stop, String className)
|
||||
throws ClassNotFoundException {
|
||||
return loadClassWithout(stop, className);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ClassLoaderRepository that contains a single class loader.
|
||||
* @param loader the class loader contained in the returned repository.
|
||||
* @return a ClassLoaderRepository that contains the single loader.
|
||||
*/
|
||||
public static ClassLoaderRepository getSingleClassLoaderRepository(
|
||||
final ClassLoader loader) {
|
||||
return new SingleClassLoaderRepository(loader);
|
||||
}
|
||||
|
||||
public static <T> Set<T> cloneSet(Set<T> set) {
|
||||
if (set instanceof SortedSet) {
|
||||
@SuppressWarnings("unchecked")
|
||||
SortedSet<T> sset = (SortedSet<T>) set;
|
||||
set = new TreeSet<T>(sset.comparator());
|
||||
set.addAll(sset);
|
||||
} else
|
||||
set = new HashSet<T>(set);
|
||||
return set;
|
||||
}
|
||||
|
||||
public static <T> Set<T> equivalentEmptySet(Set<T> set) {
|
||||
if (set instanceof SortedSet) {
|
||||
@SuppressWarnings("unchecked")
|
||||
SortedSet<T> sset = (SortedSet<T>) set;
|
||||
set = new TreeSet<T>(sset.comparator());
|
||||
} else if (set != null) {
|
||||
set = new HashSet<T>(set.size());
|
||||
} else
|
||||
set = new HashSet<T>();
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
@ -576,6 +576,7 @@ public abstract class ClientNotifForwarder {
|
||||
int notFoundCount = 0;
|
||||
|
||||
NotificationResult result = null;
|
||||
long firstEarliest = -1;
|
||||
|
||||
while (result == null && !shouldStop()) {
|
||||
NotificationResult nr;
|
||||
@ -598,6 +599,8 @@ public abstract class ClientNotifForwarder {
|
||||
return null;
|
||||
|
||||
startSequenceNumber = nr.getNextSequenceNumber();
|
||||
if (firstEarliest < 0)
|
||||
firstEarliest = nr.getEarliestSequenceNumber();
|
||||
|
||||
try {
|
||||
// 1 notif to skip possible missing class
|
||||
@ -628,6 +631,17 @@ public abstract class ClientNotifForwarder {
|
||||
(notFoundCount == 1 ? "" : "s") +
|
||||
" because classes were missing locally";
|
||||
lostNotifs(msg, notFoundCount);
|
||||
// Even if result.getEarliestSequenceNumber() is now greater than
|
||||
// it was initially, meaning some notifs have been dropped
|
||||
// from the buffer, we don't want the caller to see that
|
||||
// because it is then likely to renotify about the lost notifs.
|
||||
// So we put back the first value of earliestSequenceNumber
|
||||
// that we saw.
|
||||
if (result != null) {
|
||||
result = new NotificationResult(
|
||||
firstEarliest, result.getNextSequenceNumber(),
|
||||
result.getTargetedNotifications());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -33,10 +33,8 @@ import org.omg.CORBA.Any;
|
||||
import org.omg.CORBA.Context;
|
||||
import org.omg.CORBA.NO_IMPLEMENT;
|
||||
import org.omg.CORBA.ORB;
|
||||
import org.omg.CORBA.Principal;
|
||||
import org.omg.CORBA.TypeCode;
|
||||
import org.omg.CORBA.portable.BoxedValueHelper;
|
||||
import org.omg.CORBA_2_3.portable.InputStream;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ProxyInputStream extends org.omg.CORBA_2_3.portable.InputStream {
|
||||
@ -160,54 +158,71 @@ public class ProxyInputStream extends org.omg.CORBA_2_3.portable.InputStream {
|
||||
return in.read_any();
|
||||
}
|
||||
|
||||
public Principal read_Principal() {
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public org.omg.CORBA.Principal read_Principal() {
|
||||
return in.read_Principal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return in.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal read_fixed() {
|
||||
return in.read_fixed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context read_Context() {
|
||||
return in.read_Context();
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.omg.CORBA.Object read_Object(java.lang.Class clz) {
|
||||
return in.read_Object(clz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ORB orb() {
|
||||
return in.orb();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable read_value() {
|
||||
return narrow().read_value();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable read_value(Class clz) {
|
||||
return narrow().read_value(clz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable read_value(BoxedValueHelper factory) {
|
||||
return narrow().read_value(factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable read_value(String rep_id) {
|
||||
return narrow().read_value(rep_id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable read_value(Serializable value) {
|
||||
return narrow().read_value(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read_abstract_interface() {
|
||||
return narrow().read_abstract_interface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read_abstract_interface(Class clz) {
|
||||
return narrow().read_abstract_interface(clz);
|
||||
}
|
||||
|
@ -31,8 +31,6 @@ import java.io.ObjectOutput;
|
||||
import java.lang.reflect.Method;
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.server.Operation;
|
||||
import java.rmi.server.RemoteCall;
|
||||
import java.rmi.server.RemoteObject;
|
||||
import java.rmi.server.RemoteRef;
|
||||
|
||||
@ -54,7 +52,11 @@ public class ProxyRef implements RemoteRef {
|
||||
ref.writeExternal(out);
|
||||
}
|
||||
|
||||
public void invoke(RemoteCall call) throws Exception {
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public void invoke(java.rmi.server.RemoteCall call) throws Exception {
|
||||
ref.invoke(call);
|
||||
}
|
||||
|
||||
@ -63,7 +65,11 @@ public class ProxyRef implements RemoteRef {
|
||||
return ref.invoke(obj, method, params, opnum);
|
||||
}
|
||||
|
||||
public void done(RemoteCall call) throws RemoteException {
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public void done(java.rmi.server.RemoteCall call) throws RemoteException {
|
||||
ref.done(call);
|
||||
}
|
||||
|
||||
@ -71,7 +77,12 @@ public class ProxyRef implements RemoteRef {
|
||||
return ref.getRefClass(out);
|
||||
}
|
||||
|
||||
public RemoteCall newCall(RemoteObject obj, Operation[] op, int opnum,
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated
|
||||
public java.rmi.server.RemoteCall newCall(RemoteObject obj,
|
||||
java.rmi.server.Operation[] op, int opnum,
|
||||
long hash) throws RemoteException {
|
||||
return ref.newCall(obj, op, opnum, hash);
|
||||
}
|
||||
|
@ -25,16 +25,16 @@
|
||||
|
||||
package com.sun.jmx.remote.internal;
|
||||
|
||||
import com.sun.jmx.mbeanserver.Util;
|
||||
import com.sun.jmx.remote.security.NotificationAccessController;
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import com.sun.jmx.remote.util.EnvHelp;
|
||||
import java.io.IOException;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -67,9 +67,9 @@ public class ServerNotifForwarder {
|
||||
connectionTimeout = EnvHelp.getServerConnectionTimeout(env);
|
||||
checkNotificationEmission = EnvHelp.computeBooleanFromString(
|
||||
env,
|
||||
"jmx.remote.x.check.notification.emission");
|
||||
notificationAccessController = (NotificationAccessController)
|
||||
env.get("com.sun.jmx.remote.notification.access.controller");
|
||||
"jmx.remote.x.check.notification.emission",false);
|
||||
notificationAccessController =
|
||||
EnvHelp.getNotificationAccessController(env);
|
||||
}
|
||||
|
||||
public Integer addNotificationListener(final ObjectName name,
|
||||
@ -88,9 +88,7 @@ public class ServerNotifForwarder {
|
||||
checkMBeanPermission(name, "addNotificationListener");
|
||||
if (notificationAccessController != null) {
|
||||
notificationAccessController.addNotificationListener(
|
||||
connectionId,
|
||||
name,
|
||||
Subject.getSubject(AccessController.getContext()));
|
||||
connectionId, name, getSubject());
|
||||
}
|
||||
try {
|
||||
boolean instanceOf =
|
||||
@ -160,9 +158,7 @@ public class ServerNotifForwarder {
|
||||
checkMBeanPermission(name, "removeNotificationListener");
|
||||
if (notificationAccessController != null) {
|
||||
notificationAccessController.removeNotificationListener(
|
||||
connectionId,
|
||||
name,
|
||||
Subject.getSubject(AccessController.getContext()));
|
||||
connectionId, name, getSubject());
|
||||
}
|
||||
|
||||
Exception re = null;
|
||||
@ -312,6 +308,10 @@ public class ServerNotifForwarder {
|
||||
// PRIVATE METHODS
|
||||
//----------------
|
||||
|
||||
private Subject getSubject() {
|
||||
return Subject.getSubject(AccessController.getContext());
|
||||
}
|
||||
|
||||
private void checkState() throws IOException {
|
||||
synchronized(terminationLock) {
|
||||
if (terminated) {
|
||||
@ -332,7 +332,13 @@ public class ServerNotifForwarder {
|
||||
*/
|
||||
private void checkMBeanPermission(final ObjectName name,
|
||||
final String actions)
|
||||
throws InstanceNotFoundException, SecurityException {
|
||||
throws InstanceNotFoundException, SecurityException {
|
||||
checkMBeanPermission(mbeanServer, name, actions);
|
||||
}
|
||||
|
||||
public static void checkMBeanPermission(
|
||||
final MBeanServer mbs, final ObjectName name, final String actions)
|
||||
throws InstanceNotFoundException, SecurityException {
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
AccessControlContext acc = AccessController.getContext();
|
||||
@ -342,7 +348,7 @@ public class ServerNotifForwarder {
|
||||
new PrivilegedExceptionAction<ObjectInstance>() {
|
||||
public ObjectInstance run()
|
||||
throws InstanceNotFoundException {
|
||||
return mbeanServer.getObjectInstance(name);
|
||||
return mbs.getObjectInstance(name);
|
||||
}
|
||||
});
|
||||
} catch (PrivilegedActionException e) {
|
||||
@ -364,14 +370,12 @@ public class ServerNotifForwarder {
|
||||
TargetedNotification tn) {
|
||||
try {
|
||||
if (checkNotificationEmission) {
|
||||
checkMBeanPermission(name, "addNotificationListener");
|
||||
checkMBeanPermission(
|
||||
name, "addNotificationListener");
|
||||
}
|
||||
if (notificationAccessController != null) {
|
||||
notificationAccessController.fetchNotification(
|
||||
connectionId,
|
||||
name,
|
||||
tn.getNotification(),
|
||||
Subject.getSubject(AccessController.getContext()));
|
||||
connectionId, name, tn.getNotification(), getSubject());
|
||||
}
|
||||
return true;
|
||||
} catch (SecurityException e) {
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package com.sun.jmx.remote.security;
|
||||
|
||||
import com.sun.jmx.mbeanserver.GetPropertyAction;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@ -47,8 +48,6 @@ import com.sun.jmx.remote.util.ClassLogger;
|
||||
import com.sun.jmx.remote.util.EnvHelp;
|
||||
import sun.management.jmxremote.ConnectorBootstrap;
|
||||
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
/**
|
||||
* This {@link LoginModule} performs file-based authentication.
|
||||
*
|
||||
@ -479,7 +478,7 @@ public class FileLoginModule implements LoginModule {
|
||||
if (userSuppliedPasswordFile || hasJavaHomePermission) {
|
||||
throw e;
|
||||
} else {
|
||||
FilePermission fp =
|
||||
final FilePermission fp =
|
||||
new FilePermission(passwordFileDisplayName, "read");
|
||||
AccessControlException ace = new AccessControlException(
|
||||
"access denied " + fp.toString());
|
||||
@ -488,10 +487,13 @@ public class FileLoginModule implements LoginModule {
|
||||
}
|
||||
}
|
||||
try {
|
||||
BufferedInputStream bis = new BufferedInputStream(fis);
|
||||
userCredentials = new Properties();
|
||||
userCredentials.load(bis);
|
||||
bis.close();
|
||||
final BufferedInputStream bis = new BufferedInputStream(fis);
|
||||
try {
|
||||
userCredentials = new Properties();
|
||||
userCredentials.load(bis);
|
||||
} finally {
|
||||
bis.close();
|
||||
}
|
||||
} finally {
|
||||
fis.close();
|
||||
}
|
||||
|
@ -40,9 +40,6 @@ import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.MBeanServer;
|
||||
@ -50,6 +47,9 @@ import javax.management.InstanceNotFoundException;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import com.sun.jmx.mbeanserver.GetPropertyAction;
|
||||
import com.sun.jmx.remote.security.NotificationAccessController;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
|
||||
public class EnvHelp {
|
||||
|
||||
@ -346,7 +346,24 @@ public class EnvHelp {
|
||||
*/
|
||||
public static long getFetchTimeout(Map env) {
|
||||
return getIntegerAttribute(env, FETCH_TIMEOUT, 60000L, 0,
|
||||
Long.MAX_VALUE);
|
||||
Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Name of the attribute that specifies an object that will check
|
||||
* accesses to add/removeNotificationListener and also attempts to
|
||||
* receive notifications. The value associated with this attribute
|
||||
* should be a <code>NotificationAccessController</code> object.
|
||||
* The default value is null.</p>
|
||||
* This field is not public because of its com.sun dependency.
|
||||
*/
|
||||
public static final String NOTIF_ACCESS_CONTROLLER =
|
||||
"com.sun.jmx.remote.notification.access.controller";
|
||||
|
||||
public static NotificationAccessController getNotificationAccessController(
|
||||
Map env) {
|
||||
return (env == null) ? null :
|
||||
(NotificationAccessController) env.get(NOTIF_ACCESS_CONTROLLER);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -470,24 +487,24 @@ public class EnvHelp {
|
||||
}
|
||||
|
||||
/**
|
||||
The value of this attribute, if present, is a string specifying
|
||||
what other attributes should not appear in
|
||||
JMXConnectorServer.getAttributes(). It is a space-separated
|
||||
list of attribute patterns, where each pattern is either an
|
||||
attribute name, or an attribute prefix followed by a "*"
|
||||
character. The "*" has no special significance anywhere except
|
||||
at the end of a pattern. By default, this list is added to the
|
||||
list defined by {@link #DEFAULT_HIDDEN_ATTRIBUTES} (which
|
||||
uses the same format). If the value of this attribute begins
|
||||
with an "=", then the remainder of the string defines the
|
||||
complete list of attribute patterns.
|
||||
* The value of this attribute, if present, is a string specifying
|
||||
* what other attributes should not appear in
|
||||
* JMXConnectorServer.getAttributes(). It is a space-separated
|
||||
* list of attribute patterns, where each pattern is either an
|
||||
* attribute name, or an attribute prefix followed by a "*"
|
||||
* character. The "*" has no special significance anywhere except
|
||||
* at the end of a pattern. By default, this list is added to the
|
||||
* list defined by {@link #DEFAULT_HIDDEN_ATTRIBUTES} (which
|
||||
* uses the same format). If the value of this attribute begins
|
||||
* with an "=", then the remainder of the string defines the
|
||||
* complete list of attribute patterns.
|
||||
*/
|
||||
public static final String HIDDEN_ATTRIBUTES =
|
||||
"jmx.remote.x.hidden.attributes";
|
||||
|
||||
/**
|
||||
Default list of attributes not to show.
|
||||
@see #HIDDEN_ATTRIBUTES
|
||||
* Default list of attributes not to show.
|
||||
* @see #HIDDEN_ATTRIBUTES
|
||||
*/
|
||||
/* This list is copied directly from the spec, plus
|
||||
java.naming.security.*. Most of the attributes here would have
|
||||
@ -651,6 +668,8 @@ public class EnvHelp {
|
||||
* @param env the environment map.
|
||||
* @param prop the name of the property in the environment map whose
|
||||
* returned string value must be converted into a boolean value.
|
||||
* @param systemProperty if true, consult a system property of the
|
||||
* same name if there is no entry in the environment map.
|
||||
*
|
||||
* @return
|
||||
* <ul>
|
||||
@ -671,16 +690,73 @@ public class EnvHelp {
|
||||
* @throws ClassCastException if {@code env.get(prop)} cannot be cast
|
||||
* to {@code String}.
|
||||
*/
|
||||
public static boolean computeBooleanFromString(Map env, String prop)
|
||||
throws IllegalArgumentException, ClassCastException {
|
||||
public static boolean computeBooleanFromString(
|
||||
Map env, String prop, boolean systemProperty) {
|
||||
|
||||
if (env == null)
|
||||
throw new IllegalArgumentException("env map cannot be null");
|
||||
|
||||
// returns a default value of 'false' if no property is found...
|
||||
return computeBooleanFromString(env,prop,systemProperty,false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes a boolean value from a string value retrieved from a
|
||||
* property in the given map.
|
||||
*
|
||||
* @param env the environment map.
|
||||
* @param prop the name of the property in the environment map whose
|
||||
* returned string value must be converted into a boolean value.
|
||||
* @param systemProperty if true, consult a system property of the
|
||||
* same name if there is no entry in the environment map.
|
||||
* @param defaultValue a default value to return in case no property
|
||||
* was defined.
|
||||
*
|
||||
* @return
|
||||
* <ul>
|
||||
* <li>{@code defaultValue} if {@code env.get(prop)} is {@code null}
|
||||
* and {@code systemProperty} is {@code false}</li>
|
||||
* <li>{@code defaultValue} if {@code env.get(prop)} is {@code null}
|
||||
* and {@code systemProperty} is {@code true} and
|
||||
* {@code System.getProperty(prop)} is {@code null}</li>
|
||||
* <li>{@code false} if {@code env.get(prop)} is {@code null}
|
||||
* and {@code systemProperty} is {@code true} and
|
||||
* {@code System.getProperty(prop).equalsIgnoreCase("false")}
|
||||
* is {@code true}</li>
|
||||
* <li>{@code true} if {@code env.get(prop)} is {@code null}
|
||||
* and {@code systemProperty} is {@code true} and
|
||||
* {@code System.getProperty(prop).equalsIgnoreCase("true")}
|
||||
* is {@code true}</li>
|
||||
* <li>{@code false} if
|
||||
* {@code ((String)env.get(prop)).equalsIgnoreCase("false")}
|
||||
* is {@code true}</li>
|
||||
* <li>{@code true} if
|
||||
* {@code ((String)env.get(prop)).equalsIgnoreCase("true")}
|
||||
* is {@code true}</li>
|
||||
* </ul>
|
||||
*
|
||||
* @throws IllegalArgumentException if {@code env} is {@code null} or
|
||||
* {@code env.get(prop)} is not {@code null} and
|
||||
* {@code ((String)env.get(prop)).equalsIgnoreCase("false")} and
|
||||
* {@code ((String)env.get(prop)).equalsIgnoreCase("true")} are
|
||||
* {@code false}.
|
||||
* @throws ClassCastException if {@code env.get(prop)} cannot be cast
|
||||
* to {@code String}.
|
||||
*/
|
||||
public static boolean computeBooleanFromString(
|
||||
Map env, String prop, boolean systemProperty, boolean defaultValue) {
|
||||
|
||||
if (env == null)
|
||||
throw new IllegalArgumentException("env map cannot be null");
|
||||
|
||||
String stringBoolean = (String) env.get(prop);
|
||||
if (stringBoolean == null && systemProperty) {
|
||||
stringBoolean =
|
||||
AccessController.doPrivileged(new GetPropertyAction(prop));
|
||||
}
|
||||
|
||||
if (stringBoolean == null)
|
||||
return false;
|
||||
return defaultValue;
|
||||
else if (stringBoolean.equalsIgnoreCase("true"))
|
||||
return true;
|
||||
else if (stringBoolean.equalsIgnoreCase("false"))
|
||||
@ -703,6 +779,65 @@ public class EnvHelp {
|
||||
return new Hashtable<K, V>(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the parameter JMXConnector.USE_EVENT_SERVICE is set to a
|
||||
* String equals "true" by ignoring case in the map or in the System.
|
||||
*/
|
||||
public static boolean eventServiceEnabled(Map env) {
|
||||
return computeBooleanFromString(env, JMXConnector.USE_EVENT_SERVICE, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the parameter JMXConnectorServer.DELEGATE_TO_EVENT_SERVICE
|
||||
* is set to a String equals "true" (ignores case).
|
||||
* If the property DELEGATE_TO_EVENT_SERVICE is not set, returns
|
||||
* a default value of "true".
|
||||
*/
|
||||
public static boolean delegateToEventService(Map env) {
|
||||
return computeBooleanFromString(env,
|
||||
JMXConnectorServer.DELEGATE_TO_EVENT_SERVICE, true, true);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * <p>Name of the attribute that specifies an EventRelay object to use.
|
||||
// */
|
||||
// public static final String EVENT_RELAY =
|
||||
// "jmx.remote.x.event.relay";
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * Returns an EventRelay object. The default one is FetchingEventRelay.
|
||||
// * If {@code EVENT_RELAY} is specified in {@code env} as a key,
|
||||
// * its value will be returned as an EventRelay object, if the value is
|
||||
// * not of type {@code EventRelay}, the default {@code FetchingEventRelay}
|
||||
// * will be returned.
|
||||
// * If {@code EVENT_RELAY} is not specified but {@code ENABLE_EVENT_RELAY}
|
||||
// * is specified as a key and its value is <code true>, the default {@code FetchingEventRelay}
|
||||
// * will be returned.
|
||||
// */
|
||||
// public static EventRelay getEventRelay(Map env) {
|
||||
// Map info = env == null ?
|
||||
// Collections.EMPTY_MAP : env;
|
||||
//
|
||||
// Object o = env.get(EVENT_RELAY);
|
||||
// if (o instanceof EventRelay) {
|
||||
// return (EventRelay)o;
|
||||
// } else if (o != null) {
|
||||
// logger.warning("getEventRelay",
|
||||
// "The user specified object is not an EventRelay object, " +
|
||||
// "using the default class FetchingEventRelay.");
|
||||
//
|
||||
// return new FetchingEventRelay();
|
||||
// }
|
||||
//
|
||||
// if (enableEventRelay(env)) {
|
||||
// return new FetchingEventRelay();
|
||||
// }
|
||||
//
|
||||
// return null;
|
||||
// }
|
||||
|
||||
|
||||
private static final class SinkOutputStream extends OutputStream {
|
||||
public void write(byte[] b, int off, int len) {}
|
||||
public void write(int b) {}
|
||||
|
@ -0,0 +1,471 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package com.sun.jmx.remote.util;
|
||||
|
||||
import com.sun.jmx.event.EventClientFactory;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.EventClient;
|
||||
import javax.management.event.EventClientDelegate;
|
||||
|
||||
/**
|
||||
* Class EventClientConnection - a {@link Proxy} that wraps an
|
||||
* {@link MBeanServerConnection} and an {@link EventClient}.
|
||||
* All methods are routed to the underlying {@code MBeanServerConnection},
|
||||
* except add/remove notification listeners which are routed to the
|
||||
* {@code EventClient}.
|
||||
* The caller only sees an {@code MBeanServerConnection} which uses an
|
||||
* {@code EventClient} behind the scenes.
|
||||
*
|
||||
* @author Sun Microsystems, Inc.
|
||||
*/
|
||||
public class EventClientConnection implements InvocationHandler,
|
||||
EventClientFactory {
|
||||
|
||||
/**
|
||||
* A logger for this class.
|
||||
**/
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(EventClientConnection.class.getName());
|
||||
|
||||
private static final String NAMESPACE_SEPARATOR = "//";
|
||||
private static final int NAMESPACE_SEPARATOR_LENGTH =
|
||||
NAMESPACE_SEPARATOR.length();
|
||||
|
||||
/**
|
||||
* Creates a new {@code EventClientConnection}.
|
||||
* @param connection The underlying MBeanServerConnection.
|
||||
*/
|
||||
public EventClientConnection(MBeanServerConnection connection) {
|
||||
this(connection,null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@code EventClientConnection}.
|
||||
* @param connection The underlying MBeanServerConnection.
|
||||
* @param eventClientFactory a factory object that will be invoked
|
||||
* to create an {@link EventClient} when needed.
|
||||
* The {@code EventClient} is created lazily, when it is needed
|
||||
* for the first time. If null, a default factory will be used
|
||||
* (see {@link #createEventClient}).
|
||||
*/
|
||||
public EventClientConnection(MBeanServerConnection connection,
|
||||
Callable<EventClient> eventClientFactory) {
|
||||
|
||||
if (connection == null) {
|
||||
throw new IllegalArgumentException("Null connection");
|
||||
}
|
||||
this.connection = connection;
|
||||
if (eventClientFactory == null) {
|
||||
eventClientFactory = new Callable<EventClient>() {
|
||||
public final EventClient call() throws Exception {
|
||||
return createEventClient(EventClientConnection.this.connection);
|
||||
}
|
||||
};
|
||||
}
|
||||
this.eventClientFactory = eventClientFactory;
|
||||
this.lock = new ReentrantLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>The MBean server connection through which the methods of
|
||||
* a proxy using this handler are forwarded.</p>
|
||||
*
|
||||
* @return the MBean server connection.
|
||||
*
|
||||
* @since 1.6
|
||||
*/
|
||||
public MBeanServerConnection getMBeanServerConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new EventClientConnection proxy instance.
|
||||
*
|
||||
* @param <T> The underlying {@code MBeanServerConnection} - which should
|
||||
* not be using the Event Service itself.
|
||||
* @param interfaceClass {@code MBeanServerConnection.class}, or a subclass.
|
||||
* @param eventClientFactory a factory used to create the EventClient.
|
||||
* If null, a default factory is used (see {@link
|
||||
* #createEventClient}).
|
||||
* @return the new proxy instance, which will route add/remove notification
|
||||
* listener calls through an {@code EventClient}.
|
||||
*
|
||||
*/
|
||||
private static <T extends MBeanServerConnection> T
|
||||
newProxyInstance(T connection,
|
||||
Class<T> interfaceClass, Callable<EventClient> eventClientFactory) {
|
||||
final InvocationHandler handler =
|
||||
new EventClientConnection(connection,eventClientFactory);
|
||||
final Class[] interfaces =
|
||||
new Class[] {interfaceClass, EventClientFactory.class};
|
||||
|
||||
Object proxy =
|
||||
Proxy.newProxyInstance(interfaceClass.getClassLoader(),
|
||||
interfaces,
|
||||
handler);
|
||||
return interfaceClass.cast(proxy);
|
||||
}
|
||||
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Throwable {
|
||||
final String methodName = method.getName();
|
||||
|
||||
// add/remove notification listener are routed to the EventClient
|
||||
if (methodName.equals("addNotificationListener")
|
||||
|| methodName.equals("removeNotificationListener")) {
|
||||
final Class[] sig = method.getParameterTypes();
|
||||
if (sig.length>1 &&
|
||||
NotificationListener.class.isAssignableFrom(sig[1])) {
|
||||
return invokeBroadcasterMethod(proxy,method,args);
|
||||
}
|
||||
}
|
||||
|
||||
// subscribe/unsubscribe are also routed to the EventClient.
|
||||
final Class clazz = method.getDeclaringClass();
|
||||
if (clazz.equals(EventClientFactory.class)) {
|
||||
return invokeEventClientSubscriberMethod(proxy,method,args);
|
||||
}
|
||||
|
||||
// local or not: equals, toString, hashCode
|
||||
if (shouldDoLocally(proxy, method))
|
||||
return doLocally(proxy, method, args);
|
||||
|
||||
return call(connection,method,args);
|
||||
}
|
||||
|
||||
// The purpose of this method is to unwrap InvocationTargetException,
|
||||
// in order to avoid throwing UndeclaredThrowableException for
|
||||
// declared exceptions.
|
||||
//
|
||||
// When calling method.invoke(), any exception thrown by the invoked
|
||||
// method will be wrapped in InvocationTargetException. If we don't
|
||||
// unwrap this exception, the proxy will always throw
|
||||
// UndeclaredThrowableException, even for runtime exceptions.
|
||||
//
|
||||
private Object call(final Object obj, final Method m,
|
||||
final Object[] args)
|
||||
throws Throwable {
|
||||
try {
|
||||
return m.invoke(obj,args);
|
||||
} catch (InvocationTargetException x) {
|
||||
final Throwable xx = x.getTargetException();
|
||||
if (xx == null) throw x;
|
||||
else throw xx;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Route add/remove notification listener to the event client.
|
||||
**/
|
||||
private Object invokeBroadcasterMethod(Object proxy, Method method,
|
||||
Object[] args) throws Exception {
|
||||
final String methodName = method.getName();
|
||||
final int nargs = (args == null) ? 0 : args.length;
|
||||
|
||||
if (nargs < 1) {
|
||||
final String msg =
|
||||
"Bad arg count: " + nargs;
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
final ObjectName mbean = (ObjectName) args[0];
|
||||
final EventClient client = getEventClient();
|
||||
|
||||
// Fails if client is null AND the MBean we try to listen to is
|
||||
// in a subnamespace. We fail here because we know this will not
|
||||
// work.
|
||||
//
|
||||
// Note that if the wrapped MBeanServerConnection points to a an
|
||||
// earlier agent (JDK 1.6 or earlier), then the EventClient will
|
||||
// be null (we can't use the event service with earlier JDKs).
|
||||
//
|
||||
// In principle a null client indicates that the remote VM is of
|
||||
// an earlier version, in which case it shouldn't contain any namespace.
|
||||
//
|
||||
// So having a null client AND an MBean contained in a namespace is
|
||||
// clearly an error case.
|
||||
//
|
||||
if (client == null) {
|
||||
final String domain = mbean.getDomain();
|
||||
final int index = domain.indexOf(NAMESPACE_SEPARATOR);
|
||||
if (index > -1 && index <
|
||||
(domain.length()-NAMESPACE_SEPARATOR_LENGTH)) {
|
||||
throw new UnsupportedOperationException(method.getName()+
|
||||
" on namespace "+domain.substring(0,index+
|
||||
NAMESPACE_SEPARATOR_LENGTH));
|
||||
}
|
||||
}
|
||||
|
||||
if (methodName.equals("addNotificationListener")) {
|
||||
/* The various throws of IllegalArgumentException here
|
||||
should not happen, since we know what the methods in
|
||||
NotificationBroadcaster and NotificationEmitter
|
||||
are. */
|
||||
if (nargs != 4) {
|
||||
final String msg =
|
||||
"Bad arg count to addNotificationListener: " + nargs;
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
/* Other inconsistencies will produce ClassCastException
|
||||
below. */
|
||||
|
||||
final NotificationListener listener = (NotificationListener) args[1];
|
||||
final NotificationFilter filter = (NotificationFilter) args[2];
|
||||
final Object handback = args[3];
|
||||
|
||||
if (client != null) {
|
||||
// general case
|
||||
client.addNotificationListener(mbean,listener,filter,handback);
|
||||
} else {
|
||||
// deprecated case. Only works for mbean in local namespace.
|
||||
connection.addNotificationListener(mbean,listener,filter,
|
||||
handback);
|
||||
}
|
||||
return null;
|
||||
|
||||
} else if (methodName.equals("removeNotificationListener")) {
|
||||
|
||||
/* NullPointerException if method with no args, but that
|
||||
shouldn't happen because removeNL does have args. */
|
||||
NotificationListener listener = (NotificationListener) args[1];
|
||||
|
||||
switch (nargs) {
|
||||
case 2:
|
||||
if (client != null) {
|
||||
// general case
|
||||
client.removeNotificationListener(mbean,listener);
|
||||
} else {
|
||||
// deprecated case. Only works for mbean in local namespace.
|
||||
connection.removeNotificationListener(mbean, listener);
|
||||
}
|
||||
return null;
|
||||
|
||||
case 4:
|
||||
NotificationFilter filter = (NotificationFilter) args[2];
|
||||
Object handback = args[3];
|
||||
if (client != null) {
|
||||
client.removeNotificationListener(mbean,
|
||||
listener,
|
||||
filter,
|
||||
handback);
|
||||
} else {
|
||||
connection.removeNotificationListener(mbean,
|
||||
listener,
|
||||
filter,
|
||||
handback);
|
||||
}
|
||||
return null;
|
||||
|
||||
default:
|
||||
final String msg =
|
||||
"Bad arg count to removeNotificationListener: " + nargs;
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException("Bad method name: " +
|
||||
methodName);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldDoLocally(Object proxy, Method method) {
|
||||
final String methodName = method.getName();
|
||||
if ((methodName.equals("hashCode") || methodName.equals("toString"))
|
||||
&& method.getParameterTypes().length == 0
|
||||
&& isLocal(proxy, method))
|
||||
return true;
|
||||
if (methodName.equals("equals")
|
||||
&& Arrays.equals(method.getParameterTypes(),
|
||||
new Class[] {Object.class})
|
||||
&& isLocal(proxy, method))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private Object doLocally(Object proxy, Method method, Object[] args) {
|
||||
final String methodName = method.getName();
|
||||
|
||||
if (methodName.equals("equals")) {
|
||||
|
||||
if (this == args[0]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(args[0] instanceof Proxy)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final InvocationHandler ihandler =
|
||||
Proxy.getInvocationHandler(args[0]);
|
||||
|
||||
if (ihandler == null ||
|
||||
!(ihandler instanceof EventClientConnection)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final EventClientConnection handler =
|
||||
(EventClientConnection)ihandler;
|
||||
|
||||
return connection.equals(handler.connection) &&
|
||||
proxy.getClass().equals(args[0].getClass());
|
||||
} else if (methodName.equals("hashCode")) {
|
||||
return connection.hashCode();
|
||||
}
|
||||
|
||||
throw new RuntimeException("Unexpected method name: " + methodName);
|
||||
}
|
||||
|
||||
private static boolean isLocal(Object proxy, Method method) {
|
||||
final Class<?>[] interfaces = proxy.getClass().getInterfaces();
|
||||
if(interfaces == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
final String methodName = method.getName();
|
||||
final Class<?>[] params = method.getParameterTypes();
|
||||
for (Class<?> intf : interfaces) {
|
||||
try {
|
||||
intf.getMethod(methodName, params);
|
||||
return false; // found method in one of our interfaces
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
// OK.
|
||||
}
|
||||
}
|
||||
|
||||
return true; // did not find in any interface
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the EventClient used by this object. Can be null if the
|
||||
* remote VM is of an earlier JDK version which doesn't have the
|
||||
* event service.<br>
|
||||
* This method will invoke the event client factory the first time
|
||||
* it is called.
|
||||
**/
|
||||
public final EventClient getEventClient() {
|
||||
if (initialized) return client;
|
||||
try {
|
||||
if (!lock.tryLock(TRYLOCK_TIMEOUT,TimeUnit.SECONDS))
|
||||
throw new IllegalStateException("can't acquire lock");
|
||||
try {
|
||||
client = eventClientFactory.call();
|
||||
initialized = true;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} catch (RuntimeException x) {
|
||||
throw x;
|
||||
} catch (Exception x) {
|
||||
throw new IllegalStateException("Can't create EventClient: "+x,x);
|
||||
}
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an event client for the wrapped {@code MBeanServerConnection}.
|
||||
* This is the method invoked by the default event client factory.
|
||||
* @param connection the wrapped {@code MBeanServerConnection}.
|
||||
**/
|
||||
protected EventClient createEventClient(MBeanServerConnection connection)
|
||||
throws Exception {
|
||||
final ObjectName name =
|
||||
EventClientDelegate.OBJECT_NAME;
|
||||
if (connection.isRegistered(name)) {
|
||||
return new EventClient(connection);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link MBeanServerConnection} that goes through an
|
||||
* {@link EventClient} to receive/subscribe to notifications.
|
||||
* @param connection the underlying {@link MBeanServerConnection}.
|
||||
* The given <code>connection</code> shouldn't be already
|
||||
* using an {@code EventClient}.
|
||||
* @param eventClientFactory a factory object that will be invoked
|
||||
* to create an {@link EventClient} when needed.
|
||||
* The {@code EventClient} is created lazily, when it is needed
|
||||
* for the first time. If null, a default factory will be used
|
||||
* (see {@link #createEventClient}).
|
||||
* @return the
|
||||
**/
|
||||
public static MBeanServerConnection getEventConnectionFor(
|
||||
MBeanServerConnection connection,
|
||||
Callable<EventClient> eventClientFactory) {
|
||||
// if c already uses an EventClient no need to create a new one.
|
||||
//
|
||||
if (connection instanceof EventClientFactory
|
||||
&& eventClientFactory != null)
|
||||
throw new IllegalArgumentException("connection already uses EventClient");
|
||||
|
||||
if (connection instanceof EventClientFactory)
|
||||
return connection;
|
||||
|
||||
// create a new proxy using an event client.
|
||||
//
|
||||
if (LOG.isLoggable(Level.FINE))
|
||||
LOG.fine("Creating EventClient for: "+connection);
|
||||
return newProxyInstance(connection,
|
||||
MBeanServerConnection.class,
|
||||
eventClientFactory);
|
||||
}
|
||||
|
||||
private Object invokeEventClientSubscriberMethod(Object proxy,
|
||||
Method method, Object[] args) throws Throwable {
|
||||
return call(this,method,args);
|
||||
}
|
||||
|
||||
// Maximum lock timeout in seconds. Obviously arbitrary.
|
||||
//
|
||||
private final static short TRYLOCK_TIMEOUT = 3;
|
||||
|
||||
private final MBeanServerConnection connection;
|
||||
private final Callable<EventClient> eventClientFactory;
|
||||
private final Lock lock;
|
||||
private volatile EventClient client = null;
|
||||
private volatile boolean initialized = false;
|
||||
|
||||
}
|
@ -45,15 +45,9 @@ public class ThreadService implements TaskServer {
|
||||
minThreads = threadNumber;
|
||||
threadList = new ExecutorThread[threadNumber];
|
||||
|
||||
// for (int i=0; i<threadNumber; i++) {
|
||||
// threadList[i] = new ExecutorThread();
|
||||
// threadList[i].start();
|
||||
// }
|
||||
|
||||
priority = Thread.currentThread().getPriority();
|
||||
cloader = Thread.currentThread().getContextClassLoader();
|
||||
|
||||
//System.out.println("---jsl: ThreadService: running threads = "+threadNumber);
|
||||
}
|
||||
|
||||
// public methods
|
||||
@ -89,7 +83,6 @@ public class ThreadService implements TaskServer {
|
||||
|
||||
synchronized(jobList) {
|
||||
jobList.add(jobList.size(), task);
|
||||
//System.out.println("jsl-ThreadService: added job "+addedJobs++);
|
||||
|
||||
jobList.notify();
|
||||
}
|
||||
@ -196,8 +189,6 @@ public class ThreadService implements TaskServer {
|
||||
try {
|
||||
idle--;
|
||||
job.run();
|
||||
//System.out.println("jsl-ThreadService: done job "+doneJobs++);
|
||||
|
||||
} catch (Exception e) {
|
||||
// TODO
|
||||
e.printStackTrace();
|
||||
@ -228,7 +219,6 @@ public class ThreadService implements TaskServer {
|
||||
ExecutorThread et = new ExecutorThread();
|
||||
et.start();
|
||||
threadList[currThreds++] = et;
|
||||
//System.out.println("jsl-ThreadService: create new thread: "+currThreds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -128,13 +128,13 @@ public class ImmutableDescriptor implements Descriptor {
|
||||
* @throws InvalidObjectException if the read object has invalid fields.
|
||||
*/
|
||||
private Object readResolve() throws InvalidObjectException {
|
||||
if (names.length == 0 && getClass() == ImmutableDescriptor.class)
|
||||
return EMPTY_DESCRIPTOR;
|
||||
|
||||
boolean bad = false;
|
||||
if (names == null || values == null || names.length != values.length)
|
||||
bad = true;
|
||||
if (!bad) {
|
||||
if (names.length == 0 && getClass() == ImmutableDescriptor.class)
|
||||
return EMPTY_DESCRIPTOR;
|
||||
final Comparator<String> compare = String.CASE_INSENSITIVE_ORDER;
|
||||
String lastName = ""; // also catches illegal null name
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
|
@ -420,7 +420,13 @@ public interface MBeanServer extends MBeanServerConnection {
|
||||
// doc comment inherited from MBeanServerConnection
|
||||
public String[] getDomains();
|
||||
|
||||
// doc comment inherited from MBeanServerConnection
|
||||
// doc comment inherited from MBeanServerConnection, plus:
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* If the source of the notification
|
||||
* is a reference to an MBean object, the MBean server will replace it
|
||||
* by that MBean's ObjectName. Otherwise the source is unchanged.
|
||||
*/
|
||||
public void addNotificationListener(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
|
@ -29,6 +29,7 @@ package javax.management;
|
||||
// java import
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
import javax.management.event.NotificationManager;
|
||||
|
||||
|
||||
/**
|
||||
@ -39,7 +40,7 @@ import java.util.Set;
|
||||
*
|
||||
* @since 1.5
|
||||
*/
|
||||
public interface MBeanServerConnection {
|
||||
public interface MBeanServerConnection extends NotificationManager {
|
||||
/**
|
||||
* <p>Instantiates and registers an MBean in the MBean server. The
|
||||
* MBean server will use its {@link
|
||||
@ -676,32 +677,7 @@ public interface MBeanServerConnection {
|
||||
public String[] getDomains()
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* <p>Adds a listener to a registered MBean.</p>
|
||||
*
|
||||
* <P> A notification emitted by an MBean will be forwarded by the
|
||||
* MBeanServer to the listener. If the source of the notification
|
||||
* is a reference to an MBean object, the MBean server will replace it
|
||||
* by that MBean's ObjectName. Otherwise the source is unchanged.
|
||||
*
|
||||
* @param name The name of the MBean on which the listener should
|
||||
* be added.
|
||||
* @param listener The listener object which will handle the
|
||||
* notifications emitted by the registered MBean.
|
||||
* @param filter The filter object. If filter is null, no
|
||||
* filtering will be performed before handling notifications.
|
||||
* @param handback The context to be sent to the listener when a
|
||||
* notification is emitted.
|
||||
*
|
||||
* @exception InstanceNotFoundException The MBean name provided
|
||||
* does not match any of the registered MBeans.
|
||||
* @exception IOException A communication problem occurred when
|
||||
* talking to the MBean server.
|
||||
*
|
||||
* @see #removeNotificationListener(ObjectName, NotificationListener)
|
||||
* @see #removeNotificationListener(ObjectName, NotificationListener,
|
||||
* NotificationFilter, Object)
|
||||
*/
|
||||
// doc inherited from NotificationManager
|
||||
public void addNotificationListener(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
@ -818,65 +794,13 @@ public interface MBeanServerConnection {
|
||||
throws InstanceNotFoundException, ListenerNotFoundException,
|
||||
IOException;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Removes a listener from a registered MBean.</p>
|
||||
*
|
||||
* <P> If the listener is registered more than once, perhaps with
|
||||
* different filters or callbacks, this method will remove all
|
||||
* those registrations.
|
||||
*
|
||||
* @param name The name of the MBean on which the listener should
|
||||
* be removed.
|
||||
* @param listener The listener to be removed.
|
||||
*
|
||||
* @exception InstanceNotFoundException The MBean name provided
|
||||
* does not match any of the registered MBeans.
|
||||
* @exception ListenerNotFoundException The listener is not
|
||||
* registered in the MBean.
|
||||
* @exception IOException A communication problem occurred when
|
||||
* talking to the MBean server.
|
||||
*
|
||||
* @see #addNotificationListener(ObjectName, NotificationListener,
|
||||
* NotificationFilter, Object)
|
||||
*/
|
||||
// doc inherited from NotificationManager
|
||||
public void removeNotificationListener(ObjectName name,
|
||||
NotificationListener listener)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException,
|
||||
IOException;
|
||||
|
||||
/**
|
||||
* <p>Removes a listener from a registered MBean.</p>
|
||||
*
|
||||
* <p>The MBean must have a listener that exactly matches the
|
||||
* given <code>listener</code>, <code>filter</code>, and
|
||||
* <code>handback</code> parameters. If there is more than one
|
||||
* such listener, only one is removed.</p>
|
||||
*
|
||||
* <p>The <code>filter</code> and <code>handback</code> parameters
|
||||
* may be null if and only if they are null in a listener to be
|
||||
* removed.</p>
|
||||
*
|
||||
* @param name The name of the MBean on which the listener should
|
||||
* be removed.
|
||||
* @param listener The listener to be removed.
|
||||
* @param filter The filter that was specified when the listener
|
||||
* was added.
|
||||
* @param handback The handback that was specified when the
|
||||
* listener was added.
|
||||
*
|
||||
* @exception InstanceNotFoundException The MBean name provided
|
||||
* does not match any of the registered MBeans.
|
||||
* @exception ListenerNotFoundException The listener is not
|
||||
* registered in the MBean, or it is not registered with the given
|
||||
* filter and handback.
|
||||
* @exception IOException A communication problem occurred when
|
||||
* talking to the MBean server.
|
||||
*
|
||||
* @see #addNotificationListener(ObjectName, NotificationListener,
|
||||
* NotificationFilter, Object)
|
||||
*
|
||||
*/
|
||||
// doc inherited from NotificationManager
|
||||
public void removeNotificationListener(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
|
@ -33,7 +33,6 @@ import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
// remaining imports are for Javadoc
|
||||
import java.beans.ConstructorProperties;
|
||||
import java.io.InvalidObjectException;
|
||||
import java.lang.management.MemoryUsage;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
@ -865,7 +864,8 @@ public interface ModuleMXBean {
|
||||
<em>J</em>.</p></li>
|
||||
|
||||
<li><p>Otherwise, if <em>J</em> has at least one public
|
||||
constructor with a {@link ConstructorProperties} annotation, then one
|
||||
constructor with a {@link java.beans.ConstructorProperties
|
||||
ConstructorProperties} annotation, then one
|
||||
of those constructors (not necessarily always the same one)
|
||||
will be called to reconstruct an instance of <em>J</em>.
|
||||
Every such annotation must list as many strings as the
|
||||
|
@ -312,7 +312,7 @@ class QueryParser {
|
||||
if (e > 0)
|
||||
ss = s.substring(0, e);
|
||||
ss = ss.replace("0", "").replace(".", "");
|
||||
if (!ss.isEmpty())
|
||||
if (!ss.equals(""))
|
||||
throw new NumberFormatException("Underflow: " + s);
|
||||
}
|
||||
return d;
|
||||
|
@ -85,6 +85,7 @@ public class StringValueExp implements ValueExp {
|
||||
/* There is no need for this method, because if a query is being
|
||||
evaluated a StringValueExp can only appear inside a QueryExp,
|
||||
and that QueryExp will itself have done setMBeanServer. */
|
||||
@Deprecated
|
||||
public void setMBeanServer(MBeanServer s) { }
|
||||
|
||||
/**
|
||||
|
1068
jdk/src/share/classes/javax/management/event/EventClient.java
Normal file
1068
jdk/src/share/classes/javax/management/event/EventClient.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,766 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*
|
||||
* @since JMX 2.0
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.ListenerNotFoundException;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.remote.NotificationResult;
|
||||
import com.sun.jmx.event.EventBuffer;
|
||||
import com.sun.jmx.event.LeaseManager;
|
||||
import com.sun.jmx.interceptor.SingleMBeanForwarder;
|
||||
import com.sun.jmx.mbeanserver.Util;
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import javax.management.DynamicMBean;
|
||||
import javax.management.MBeanException;
|
||||
import javax.management.MBeanPermission;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MBeanServerDelegate;
|
||||
import javax.management.MBeanServerInvocationHandler;
|
||||
import javax.management.MBeanServerNotification;
|
||||
import javax.management.ObjectInstance;
|
||||
import javax.management.StandardMBean;
|
||||
import javax.management.remote.MBeanServerForwarder;
|
||||
|
||||
/**
|
||||
* This is the default implementation of the MBean
|
||||
* {@link EventClientDelegateMBean}.
|
||||
*/
|
||||
public class EventClientDelegate implements EventClientDelegateMBean {
|
||||
|
||||
private EventClientDelegate(MBeanServer server) {
|
||||
if (server == null) {
|
||||
throw new NullPointerException("Null MBeanServer.");
|
||||
}
|
||||
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("EventClientDelegate", "new one");
|
||||
}
|
||||
mbeanServer = server;
|
||||
eventSubscriber = EventSubscriber.getEventSubscriber(mbeanServer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@code EventClientDelegate} instance for the given
|
||||
* {@code MBeanServer}. Calling this method more than once with the same
|
||||
* {@code server} argument may return the same object or a different object
|
||||
* each time. See {@link EventClientDelegateMBean} for an example use of
|
||||
* this method.
|
||||
*
|
||||
* @param server An MBean server instance to work with.
|
||||
* @return An {@code EventClientDelegate} instance.
|
||||
* @throws NullPointerException If {@code server} is null.
|
||||
*/
|
||||
public static EventClientDelegate getEventClientDelegate(MBeanServer server) {
|
||||
EventClientDelegate delegate = null;
|
||||
synchronized(delegateMap) {
|
||||
final WeakReference wrf = delegateMap.get(server);
|
||||
delegate = (wrf == null) ? null : (EventClientDelegate)wrf.get();
|
||||
|
||||
if (delegate == null) {
|
||||
delegate = new EventClientDelegate(server);
|
||||
try {
|
||||
// TODO: this may not work with federated MBean, because
|
||||
// the delegate will *not* emit notifications for those MBeans.
|
||||
delegate.mbeanServer.addNotificationListener(
|
||||
MBeanServerDelegate.DELEGATE_NAME,
|
||||
delegate.cleanListener, null, null);
|
||||
} catch (InstanceNotFoundException e) {
|
||||
logger.fine(
|
||||
"getEventClientDelegate",
|
||||
"Could not add MBeanServerDelegate listener", e);
|
||||
}
|
||||
delegateMap.put(server,
|
||||
new WeakReference<EventClientDelegate>(delegate));
|
||||
}
|
||||
}
|
||||
|
||||
return delegate;
|
||||
}
|
||||
|
||||
// Logic for the MBeanServerForwarder that simulates the existence of the
|
||||
// EventClientDelegate MBean. Things are complicated by the fact that
|
||||
// there may not be anything in the chain after this forwarder when it is
|
||||
// created - the connection to a real MBeanServer might only come later.
|
||||
// Recall that there are two ways of creating a JMXConnectorServer -
|
||||
// either you specify its MBeanServer when you create it, or you specify
|
||||
// no MBeanServer and register it in an MBeanServer later. In the latter
|
||||
// case, the forwarder chain points nowhere until this registration
|
||||
// happens. Since EventClientDelegate wants to add a listener to the
|
||||
// MBeanServerDelegate, we can't create an EventClientDelegate until
|
||||
// there is an MBeanServer. So the forwarder initially has
|
||||
// a dummy ECD where every method throws an exception, and
|
||||
// the real ECD is created as soon as doing so does not produce an
|
||||
// exception.
|
||||
// TODO: rewrite so that the switch from the dummy to the real ECD happens
|
||||
// just before we would otherwise have thrown UnsupportedOperationException.
|
||||
// This is more correct, because it's not guaranteed that we will see the
|
||||
// moment where the real MBeanServer is attached, if it happens by virtue
|
||||
// of a setMBeanServer on some other forwarder later in the chain.
|
||||
|
||||
private static class Forwarder extends SingleMBeanForwarder {
|
||||
|
||||
private static class UnsupportedInvocationHandler
|
||||
implements InvocationHandler {
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Throwable {
|
||||
throw new UnsupportedOperationException(
|
||||
"EventClientDelegate unavailable: no MBeanServer, or " +
|
||||
"MBeanServer inaccessible");
|
||||
}
|
||||
}
|
||||
|
||||
private static DynamicMBean makeUnsupportedECD() {
|
||||
EventClientDelegateMBean unsupported = (EventClientDelegateMBean)
|
||||
Proxy.newProxyInstance(
|
||||
EventClientDelegateMBean.class.getClassLoader(),
|
||||
new Class<?>[] {EventClientDelegateMBean.class},
|
||||
new UnsupportedInvocationHandler());
|
||||
return new StandardMBean(
|
||||
unsupported, EventClientDelegateMBean.class, false);
|
||||
}
|
||||
|
||||
private volatile boolean madeECD;
|
||||
|
||||
Forwarder() {
|
||||
super(OBJECT_NAME, makeUnsupportedECD());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setMBeanServer(final MBeanServer mbs) {
|
||||
super.setMBeanServer(mbs);
|
||||
|
||||
if (!madeECD) {
|
||||
try {
|
||||
EventClientDelegate ecd =
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedAction<EventClientDelegate>() {
|
||||
public EventClientDelegate run() {
|
||||
return getEventClientDelegate(Forwarder.this);
|
||||
}
|
||||
});
|
||||
DynamicMBean mbean = new StandardMBean(
|
||||
ecd, EventClientDelegateMBean.class, false);
|
||||
setSingleMBean(mbean);
|
||||
madeECD = true;
|
||||
} catch (Exception e) {
|
||||
// OK: assume no MBeanServer
|
||||
logger.fine("setMBeanServer", "isRegistered", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Create a new {@link MBeanServerForwarder} that simulates the existence
|
||||
* of an {@code EventClientDelegateMBean} with the {@linkplain
|
||||
* #OBJECT_NAME default name}. This forwarder intercepts MBean requests
|
||||
* that are targeted for that MBean and handles them itself. All other
|
||||
* requests are forwarded to the next element in the forwarder chain.</p>
|
||||
*
|
||||
* @return a new {@code MBeanServerForwarder} that simulates the existence
|
||||
* of an {@code EventClientDelegateMBean}.
|
||||
*/
|
||||
public static MBeanServerForwarder newForwarder() {
|
||||
return new Forwarder();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a proxy of the default {@code EventClientDelegateMBean}.
|
||||
*
|
||||
* @param conn An {@link MBeanServerConnection} to work with.
|
||||
*/
|
||||
@SuppressWarnings("cast") // cast for jdk 1.5
|
||||
public static EventClientDelegateMBean getProxy(MBeanServerConnection conn) {
|
||||
return (EventClientDelegateMBean)MBeanServerInvocationHandler.
|
||||
newProxyInstance(conn,
|
||||
OBJECT_NAME,
|
||||
EventClientDelegateMBean.class,
|
||||
false);
|
||||
}
|
||||
|
||||
public String addClient(String className, Object[] params, String[] sig)
|
||||
throws MBeanException {
|
||||
return addClient(className, null, params, sig, true);
|
||||
}
|
||||
|
||||
public String addClient(String className,
|
||||
ObjectName classLoader,
|
||||
Object[] params,
|
||||
String[] sig) throws MBeanException {
|
||||
return addClient(className, classLoader, params, sig, false);
|
||||
}
|
||||
|
||||
private String addClient(String className,
|
||||
ObjectName classLoader,
|
||||
Object[] params,
|
||||
String[] sig,
|
||||
boolean classLoaderRepository) throws MBeanException {
|
||||
try {
|
||||
return addClientX(
|
||||
className, classLoader, params, sig, classLoaderRepository);
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
throw new MBeanException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String addClientX(String className,
|
||||
ObjectName classLoader,
|
||||
Object[] params,
|
||||
String[] sig,
|
||||
boolean classLoaderRepository) throws Exception {
|
||||
if (className == null) {
|
||||
throw new IllegalArgumentException("Null class name.");
|
||||
}
|
||||
|
||||
final Object o;
|
||||
|
||||
// The special treatment of standard EventForwarders is so that no
|
||||
// special permissions are necessary to use them. Otherwise you
|
||||
// couldn't use EventClient if you didn't have permission to call
|
||||
// MBeanServer.instantiate. We do require that permission for
|
||||
// non-standard forwarders, because otherwise you could instantiate
|
||||
// any class with possibly adverse consequences. We also avoid using
|
||||
// MBeanInstantiator because it looks up constructors by loading each
|
||||
// class in the sig array, which means a remote user could cause any
|
||||
// class to be loaded. That's probably not hugely risky but still.
|
||||
if (className.startsWith("javax.management.event.")) {
|
||||
Class<?> c = Class.forName(
|
||||
className, false, this.getClass().getClassLoader());
|
||||
Constructor<?> foundCons = null;
|
||||
if (sig == null)
|
||||
sig = new String[0];
|
||||
for (Constructor cons : c.getConstructors()) {
|
||||
Class<?>[] types = cons.getParameterTypes();
|
||||
String[] consSig = new String[types.length];
|
||||
for (int i = 0; i < types.length; i++)
|
||||
consSig[i] = types[i].getName();
|
||||
if (Arrays.equals(sig, consSig)) {
|
||||
foundCons = cons;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundCons == null) {
|
||||
throw new NoSuchMethodException(
|
||||
"Constructor for " + className + " with argument types " +
|
||||
Arrays.toString(sig));
|
||||
}
|
||||
o = foundCons.newInstance(params);
|
||||
} else if (classLoaderRepository) {
|
||||
o = mbeanServer.instantiate(className, params, sig);
|
||||
} else {
|
||||
o = mbeanServer.instantiate(className, classLoader, params, sig);
|
||||
}
|
||||
|
||||
if (!(o instanceof EventForwarder)) {
|
||||
throw new IllegalArgumentException(
|
||||
className+" is not an EventForwarder class.");
|
||||
}
|
||||
|
||||
final EventForwarder forwarder = (EventForwarder)o;
|
||||
final String clientId = UUID.randomUUID().toString();
|
||||
ClientInfo clientInfo = new ClientInfo(clientId, forwarder);
|
||||
|
||||
clientInfoMap.put(clientId, clientInfo);
|
||||
|
||||
forwarder.setClientId(clientId);
|
||||
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("addClient", clientId);
|
||||
}
|
||||
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public Integer[] getListenerIds(String clientId)
|
||||
throws IOException, EventClientNotFoundException {
|
||||
ClientInfo clientInfo = getClientInfo(clientId);
|
||||
|
||||
if (clientInfo == null) {
|
||||
throw new EventClientNotFoundException("The client is not found.");
|
||||
}
|
||||
|
||||
Map<Integer, AddedListener> listenerInfoMap = clientInfo.listenerInfoMap;
|
||||
synchronized (listenerInfoMap) {
|
||||
Set<Integer> ids = listenerInfoMap.keySet();
|
||||
return ids.toArray(new Integer[ids.size()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The execution of this method includes a call to
|
||||
* {@link MBeanServer#addNotificationListener(ObjectName,
|
||||
* NotificationListener, NotificationFilter, Object)}.</p>
|
||||
*/
|
||||
public Integer addListener(String clientId,
|
||||
final ObjectName name,
|
||||
NotificationFilter filter)
|
||||
throws EventClientNotFoundException, InstanceNotFoundException {
|
||||
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("addListener", "");
|
||||
}
|
||||
|
||||
return getClientInfo(clientId).addListenerInfo(name, filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The execution of this method can include call to
|
||||
* {@link MBeanServer#removeNotificationListener(ObjectName,
|
||||
* NotificationListener, NotificationFilter, Object)}.</p>
|
||||
*/
|
||||
public void removeListenerOrSubscriber(String clientId, Integer listenerId)
|
||||
throws InstanceNotFoundException,
|
||||
ListenerNotFoundException,
|
||||
EventClientNotFoundException,
|
||||
IOException {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("removeListener", ""+listenerId);
|
||||
}
|
||||
getClientInfo(clientId).removeListenerInfo(listenerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* <p>The execution of this method includes a call to
|
||||
* {@link MBeanServer#addNotificationListener(ObjectName,
|
||||
* NotificationListener, NotificationFilter, Object)} for
|
||||
* every MBean matching {@code name}. If {@code name} is
|
||||
* an {@code ObjectName} pattern, then the execution of this
|
||||
* method will include a call to {@link MBeanServer#queryNames}.</p>
|
||||
*/
|
||||
public Integer addSubscriber(String clientId, ObjectName name,
|
||||
NotificationFilter filter)
|
||||
throws EventClientNotFoundException, IOException {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("addSubscriber", "");
|
||||
}
|
||||
return getClientInfo(clientId).subscribeListenerInfo(name, filter);
|
||||
}
|
||||
|
||||
public NotificationResult fetchNotifications(String clientId,
|
||||
long startSequenceNumber,
|
||||
int maxNotifs,
|
||||
long timeout)
|
||||
throws EventClientNotFoundException {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("fetchNotifications", "for "+clientId);
|
||||
}
|
||||
return getClientInfo(clientId).fetchNotifications(startSequenceNumber,
|
||||
maxNotifs,
|
||||
timeout);
|
||||
}
|
||||
|
||||
public void removeClient(String clientId)
|
||||
throws EventClientNotFoundException {
|
||||
if (clientId == null)
|
||||
throw new EventClientNotFoundException("Null clientId");
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("removeClient", clientId);
|
||||
}
|
||||
ClientInfo ci = null;
|
||||
ci = clientInfoMap.remove(clientId);
|
||||
|
||||
if (ci == null) {
|
||||
throw new EventClientNotFoundException("clientId is "+clientId);
|
||||
} else {
|
||||
ci.clean();
|
||||
}
|
||||
}
|
||||
|
||||
public long lease(String clientId, long timeout)
|
||||
throws IOException, EventClientNotFoundException {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("lease", "for "+clientId);
|
||||
}
|
||||
return getClientInfo(clientId).lease(timeout);
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
// private classes
|
||||
// ------------------------------------
|
||||
private class ClientInfo {
|
||||
String clientId;
|
||||
EventBuffer buffer;
|
||||
NotificationListener clientListener;
|
||||
Map<Integer, AddedListener> listenerInfoMap =
|
||||
new HashMap<Integer, AddedListener>();
|
||||
|
||||
ClientInfo(String clientId, EventForwarder forwarder) {
|
||||
this.clientId = clientId;
|
||||
this.forwarder = forwarder;
|
||||
clientListener =
|
||||
new ForwardingClientListener(listenerInfoMap, forwarder);
|
||||
}
|
||||
|
||||
Integer addOrSubscribeListenerInfo(
|
||||
ObjectName name, NotificationFilter filter, boolean subscribe)
|
||||
throws InstanceNotFoundException, IOException {
|
||||
|
||||
final Integer listenerId = nextListenerId();
|
||||
AddedListener listenerInfo = new AddedListener(
|
||||
listenerId, filter, name, subscribe);
|
||||
if (subscribe) {
|
||||
eventSubscriber.subscribe(name,
|
||||
clientListener,
|
||||
filter,
|
||||
listenerInfo);
|
||||
} else {
|
||||
mbeanServer.addNotificationListener(name,
|
||||
clientListener,
|
||||
filter,
|
||||
listenerInfo);
|
||||
}
|
||||
|
||||
synchronized(listenerInfoMap) {
|
||||
listenerInfoMap.put(listenerId, listenerInfo);
|
||||
}
|
||||
|
||||
return listenerId;
|
||||
}
|
||||
|
||||
Integer addListenerInfo(ObjectName name,
|
||||
NotificationFilter filter) throws InstanceNotFoundException {
|
||||
try {
|
||||
return addOrSubscribeListenerInfo(name, filter, false);
|
||||
} catch (IOException e) { // can't happen
|
||||
logger.warning(
|
||||
"EventClientDelegate.addListenerInfo",
|
||||
"unexpected exception", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
Integer subscribeListenerInfo(ObjectName name,
|
||||
NotificationFilter filter) throws IOException {
|
||||
try {
|
||||
return addOrSubscribeListenerInfo(name, filter, true);
|
||||
} catch (InstanceNotFoundException e) { // can't happen
|
||||
logger.warning(
|
||||
"EventClientDelegate.subscribeListenerInfo",
|
||||
"unexpected exception", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final AtomicInteger nextListenerId = new AtomicInteger();
|
||||
|
||||
private Integer nextListenerId() {
|
||||
return nextListenerId.getAndIncrement();
|
||||
}
|
||||
|
||||
NotificationResult fetchNotifications(long startSequenceNumber,
|
||||
int maxNotifs,
|
||||
long timeout) {
|
||||
|
||||
if (!(forwarder instanceof FetchingEventForwarder)) {
|
||||
throw new IllegalArgumentException(
|
||||
"This client is using Event Postal Service!");
|
||||
}
|
||||
|
||||
return ((FetchingEventForwarder)forwarder).
|
||||
fetchNotifications(startSequenceNumber,
|
||||
maxNotifs, timeout);
|
||||
}
|
||||
|
||||
void removeListenerInfo(Integer listenerId)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException, IOException {
|
||||
AddedListener listenerInfo;
|
||||
synchronized(listenerInfoMap) {
|
||||
listenerInfo = listenerInfoMap.remove(listenerId);
|
||||
}
|
||||
|
||||
if (listenerInfo == null) {
|
||||
throw new ListenerNotFoundException("The listener is not found.");
|
||||
}
|
||||
|
||||
if (listenerInfo.subscription) {
|
||||
eventSubscriber.unsubscribe(listenerInfo.name,
|
||||
clientListener);
|
||||
} else {
|
||||
mbeanServer.removeNotificationListener(listenerInfo.name,
|
||||
clientListener,
|
||||
listenerInfo.filter,
|
||||
listenerInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void clean(ObjectName name) {
|
||||
synchronized(listenerInfoMap) {
|
||||
for (Map.Entry<Integer, AddedListener> entry :
|
||||
listenerInfoMap.entrySet()) {
|
||||
AddedListener li = entry.getValue();
|
||||
if (name.equals(li.name)) {
|
||||
listenerInfoMap.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clean() {
|
||||
synchronized(listenerInfoMap) {
|
||||
for (AddedListener li : listenerInfoMap.values()) {
|
||||
try {
|
||||
mbeanServer.removeNotificationListener(li.name,
|
||||
clientListener);
|
||||
} catch (Exception e) {
|
||||
logger.trace("ClientInfo.clean", "removeNL", e);
|
||||
}
|
||||
}
|
||||
listenerInfoMap.clear();
|
||||
}
|
||||
|
||||
try {
|
||||
forwarder.close();
|
||||
} catch (Exception e) {
|
||||
logger.trace(
|
||||
"ClientInfo.clean", "forwarder.close", e);
|
||||
}
|
||||
|
||||
if (leaseManager != null) {
|
||||
leaseManager.stop();
|
||||
}
|
||||
}
|
||||
|
||||
long lease(long timeout) {
|
||||
return leaseManager.lease(timeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (o instanceof ClientInfo &&
|
||||
clientId.equals(((ClientInfo)o).clientId)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return clientId.hashCode();
|
||||
}
|
||||
|
||||
private EventForwarder forwarder = null;
|
||||
|
||||
private final Runnable leaseExpiryCallback = new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
removeClient(clientId);
|
||||
} catch (Exception e) {
|
||||
logger.trace(
|
||||
"ClientInfo.leaseExpiryCallback", "removeClient", e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private LeaseManager leaseManager = new LeaseManager(leaseExpiryCallback);
|
||||
}
|
||||
|
||||
private class ForwardingClientListener implements NotificationListener {
|
||||
public ForwardingClientListener(Map<Integer, AddedListener> listenerInfoMap,
|
||||
EventForwarder forwarder) {
|
||||
this.listenerInfoMap = listenerInfoMap;
|
||||
this.forwarder = forwarder;
|
||||
}
|
||||
|
||||
public void handleNotification(Notification n, Object o) {
|
||||
if (n == null || (!(o instanceof AddedListener))) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("ForwardingClientListener-handleNotification",
|
||||
"received a unknown notif");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
AddedListener li = (AddedListener) o;
|
||||
|
||||
if (checkListenerPermission(li.name,li.acc)) {
|
||||
try {
|
||||
forwarder.forward(n, li.listenerId);
|
||||
} catch (Exception e) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace(
|
||||
"ForwardingClientListener-handleNotification",
|
||||
"forwarding failed.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<Integer, AddedListener> listenerInfoMap;
|
||||
private final EventForwarder forwarder;
|
||||
}
|
||||
|
||||
private class AddedListener {
|
||||
final int listenerId;
|
||||
final NotificationFilter filter;
|
||||
final ObjectName name;
|
||||
final boolean subscription;
|
||||
final AccessControlContext acc;
|
||||
|
||||
public AddedListener(
|
||||
int listenerId,
|
||||
NotificationFilter filter,
|
||||
ObjectName name,
|
||||
boolean subscription) {
|
||||
this.listenerId = listenerId;
|
||||
this.filter = filter;
|
||||
this.name = name;
|
||||
this.subscription = subscription;
|
||||
acc = AccessController.getContext();
|
||||
}
|
||||
}
|
||||
|
||||
private class CleanListener implements NotificationListener {
|
||||
public void handleNotification(Notification notification,
|
||||
Object handback) {
|
||||
if (notification instanceof MBeanServerNotification) {
|
||||
if (MBeanServerNotification.UNREGISTRATION_NOTIFICATION.equals(
|
||||
notification.getType())) {
|
||||
final ObjectName name =
|
||||
((MBeanServerNotification)notification).getMBeanName();
|
||||
|
||||
final Collection <ClientInfo> list =
|
||||
Collections.unmodifiableCollection(clientInfoMap.values());
|
||||
|
||||
for (ClientInfo ci : list) {
|
||||
ci.clean(name);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
// private method
|
||||
// -------------------------------------------------
|
||||
private ClientInfo getClientInfo(String clientId)
|
||||
throws EventClientNotFoundException {
|
||||
ClientInfo clientInfo = null;
|
||||
clientInfo = clientInfoMap.get(clientId);
|
||||
|
||||
if (clientInfo == null) {
|
||||
throw new EventClientNotFoundException("The client is not found.");
|
||||
}
|
||||
|
||||
return clientInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explicitly check the MBeanPermission for
|
||||
* the current access control context.
|
||||
*/
|
||||
private boolean checkListenerPermission(final ObjectName name,
|
||||
final AccessControlContext acc) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("checkListenerPermission", "");
|
||||
}
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
try {
|
||||
ObjectInstance oi = (ObjectInstance) AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<Object>() {
|
||||
public Object run()
|
||||
throws InstanceNotFoundException {
|
||||
return mbeanServer.getObjectInstance(name);
|
||||
}
|
||||
});
|
||||
|
||||
String classname = oi.getClassName();
|
||||
MBeanPermission perm = new MBeanPermission(
|
||||
classname,
|
||||
null,
|
||||
name,
|
||||
"addNotificationListener");
|
||||
sm.checkPermission(perm, acc);
|
||||
} catch (Exception e) {
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("checkListenerPermission", "refused.", e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
// private variables
|
||||
// ------------------------------------
|
||||
private final MBeanServer mbeanServer;
|
||||
private volatile String mbeanServerName = null;
|
||||
private Map<String, ClientInfo> clientInfoMap =
|
||||
new ConcurrentHashMap<String, ClientInfo>();
|
||||
|
||||
private final CleanListener cleanListener = new CleanListener();
|
||||
private final EventSubscriber eventSubscriber;
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.event", "EventClientDelegate");
|
||||
|
||||
private static final
|
||||
Map<MBeanServer, WeakReference<EventClientDelegate>> delegateMap =
|
||||
new WeakHashMap<MBeanServer, WeakReference<EventClientDelegate>>();
|
||||
}
|
@ -0,0 +1,318 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import com.sun.jmx.mbeanserver.Util;
|
||||
import java.io.IOException;
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.ListenerNotFoundException;
|
||||
import javax.management.MBeanException;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.remote.NotificationResult;
|
||||
|
||||
/**
|
||||
* <p>This interface specifies necessary methods on the MBean server
|
||||
* side for a JMX remote client to manage its notification listeners as
|
||||
* if they are local.
|
||||
* Users do not usually work directly with this MBean; instead, the {@link
|
||||
* EventClient} class is designed to be used directly by the user.</p>
|
||||
*
|
||||
* <p>A default implementation of this interface can be added to an MBean
|
||||
* Server in one of several ways.</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li><p>The most usual is to insert an {@link
|
||||
* javax.management.remote.MBeanServerForwarder MBeanServerForwarder} between
|
||||
* the {@linkplain javax.management.remote.JMXConnectorServer Connector Server}
|
||||
* and the MBean Server, that will intercept accesses to the Event Client
|
||||
* Delegate MBean and treat them as the real MBean would. This forwarder is
|
||||
* inserted by default with the standard RMI Connector Server, and can also
|
||||
* be created explicitly using {@link EventClientDelegate#newForwarder()}.
|
||||
*
|
||||
* <li><p>A variant on the above is to replace the MBean Server that is
|
||||
* used locally with a forwarder as described above. Since
|
||||
* {@code MBeanServerForwarder} extends {@code MBeanServer}, you can use
|
||||
* a forwarder anywhere you would have used the original MBean Server. The
|
||||
* code to do this replacement typically looks something like this:</p>
|
||||
*
|
||||
* <pre>
|
||||
* MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); // or whatever
|
||||
* MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
|
||||
* mbsf.setMBeanServer(mbs);
|
||||
* mbs = mbsf;
|
||||
* // now use mbs just as you did before, but it will have an EventClientDelegate
|
||||
* </pre>
|
||||
*
|
||||
* <li><p>The final way is to create an instance of {@link EventClientDelegate}
|
||||
* and register it in the MBean Server under the standard {@linkplain
|
||||
* #OBJECT_NAME name}:</p>
|
||||
*
|
||||
* <pre>
|
||||
* MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); // or whatever
|
||||
* EventClientDelegate ecd = EventClientDelegate.getEventClientDelegate(mbs);
|
||||
* mbs.registerMBean(ecd, EventClientDelegateMBean.OBJECT_NAME);
|
||||
* <pre>
|
||||
* </ul>
|
||||
*
|
||||
* @since JMX 2.0
|
||||
*/
|
||||
public interface EventClientDelegateMBean {
|
||||
/**
|
||||
* The string representation of {@link #OBJECT_NAME}.
|
||||
*/
|
||||
// This shouldn't really be necessary but an apparent javadoc bug
|
||||
// meant that the {@value} tags didn't work if this was a
|
||||
// field in EventClientDelegate, even a public field.
|
||||
public static final String OBJECT_NAME_STRING =
|
||||
"javax.management.event:type=EventClientDelegate";
|
||||
|
||||
/**
|
||||
* The standard <code>ObjectName</code> used to register the default
|
||||
* <code>EventClientDelegateMBean</code>. The name is
|
||||
* <code>{@value #OBJECT_NAME_STRING}</code>.
|
||||
*/
|
||||
public final static ObjectName OBJECT_NAME =
|
||||
Util.newObjectName(OBJECT_NAME_STRING);
|
||||
|
||||
/**
|
||||
* A unique listener identifier specified for an EventClient.
|
||||
* Any notification associated with this id is intended for
|
||||
* the EventClient which receives the notification, rather than
|
||||
* a listener added using that EventClient.
|
||||
*/
|
||||
public static final int EVENT_CLIENT_LISTENER_ID = -100;
|
||||
|
||||
/**
|
||||
* Adds a new client to the <code>EventClientDelegateMBean</code> with
|
||||
* a user-specified
|
||||
* {@link EventForwarder} to forward notifications to the client. The
|
||||
* <code>EventForwarder</code> is created by calling
|
||||
* {@link javax.management.MBeanServer#instantiate(String, Object[],
|
||||
* String[])}.
|
||||
*
|
||||
* @param className The class name used to create an
|
||||
* {@code EventForwarder}.
|
||||
* @param params An array containing the parameters of the constructor to
|
||||
* be invoked.
|
||||
* @param sig An array containing the signature of the constructor to be
|
||||
* invoked
|
||||
* @return A client identifier.
|
||||
* @exception IOException Reserved for a remote call to throw on the client
|
||||
* side.
|
||||
* @exception MBeanException An exception thrown when creating the user
|
||||
* specified <code>EventForwarder</code>.
|
||||
*/
|
||||
public String addClient(String className, Object[] params, String[] sig)
|
||||
throws IOException, MBeanException;
|
||||
|
||||
/**
|
||||
* Adds a new client to the <code>EventClientDelegateMBean</code> with
|
||||
* a user-specified
|
||||
* {@link EventForwarder} to forward notifications to the client. The
|
||||
* <code>EventForwarder</code> is created by calling
|
||||
* {@link javax.management.MBeanServer#instantiate(String, ObjectName,
|
||||
* Object[], String[])}. A user-specified class loader is used to create
|
||||
* this <code>EventForwarder</code>.
|
||||
*
|
||||
* @param className The class name used to create an
|
||||
* {@code EventForwarder}.
|
||||
* @param classLoader An ObjectName registered as a
|
||||
* <code>ClassLoader</code> MBean.
|
||||
* @param params An array containing the parameters of the constructor to
|
||||
* be invoked.
|
||||
* @param sig An array containing the signature of the constructor to be
|
||||
* invoked
|
||||
* @return A client identifier.
|
||||
* @exception IOException Reserved for a remote call to throw on the client
|
||||
* side.
|
||||
* @exception MBeanException An exception thrown when creating the user
|
||||
* specified <code>EventForwarder</code>.
|
||||
*/
|
||||
public String addClient(String className,
|
||||
ObjectName classLoader,
|
||||
Object[] params,
|
||||
String[] sig) throws IOException, MBeanException;
|
||||
|
||||
/**
|
||||
* Removes an added client. Calling this method will remove all listeners
|
||||
* added with the client.
|
||||
*
|
||||
* @exception EventClientNotFoundException If the {@code clientId} is
|
||||
* not found.
|
||||
* @exception IOException Reserved for a remote call to throw on the client
|
||||
* side.
|
||||
*/
|
||||
public void removeClient(String clientID)
|
||||
throws EventClientNotFoundException, IOException;
|
||||
|
||||
/**
|
||||
* Returns the identifiers of listeners added or subscribed to with the
|
||||
* specified client identifier.
|
||||
* <P> If no listener is currently registered with the client, an empty
|
||||
* array is returned.
|
||||
* @param clientID The client identifier with which the listeners are
|
||||
* added or subscribed to.
|
||||
* @return An array of listener identifiers.
|
||||
* @exception EventClientNotFoundException If the {@code clientId} is
|
||||
* not found.
|
||||
* @exception IOException Reserved for a remote call to throw on the client
|
||||
* side.
|
||||
*/
|
||||
public Integer[] getListenerIds(String clientID)
|
||||
throws EventClientNotFoundException, IOException;
|
||||
|
||||
/**
|
||||
* Adds a listener to receive notifications from an MBean and returns
|
||||
* a non-negative integer as the identifier of the listener.
|
||||
* <P>This method is called by an {@link EventClient} to implement the
|
||||
* method {@link EventClient#addNotificationListener(ObjectName,
|
||||
* NotificationListener, NotificationFilter, Object)}.
|
||||
*
|
||||
* @param name The name of the MBean onto which the listener should be added.
|
||||
* @param filter The filter object. If {@code filter} is null,
|
||||
* no filtering will be performed before handling notifications.
|
||||
* @param clientId The client identifier with which the listener is added.
|
||||
* @return A listener identifier.
|
||||
* @throws EventClientNotFoundException Thrown if the {@code clientId} is
|
||||
* not found.
|
||||
* @throws InstanceNotFoundException Thrown if the MBean is not found.
|
||||
* @throws IOException Reserved for a remote call to throw on the client
|
||||
* side.
|
||||
*/
|
||||
public Integer addListener(String clientId,
|
||||
ObjectName name,
|
||||
NotificationFilter filter)
|
||||
throws InstanceNotFoundException, EventClientNotFoundException,
|
||||
IOException;
|
||||
|
||||
|
||||
/**
|
||||
* <p>Subscribes a listener to receive notifications from an MBean or a
|
||||
* set of MBeans represented by an {@code ObjectName} pattern. (It is
|
||||
* not an error if no MBeans match the pattern at the time this method is
|
||||
* called.)</p>
|
||||
*
|
||||
* <p>Returns a non-negative integer as the identifier of the listener.</p>
|
||||
*
|
||||
* <p>This method is called by an {@link EventClient} to execute its
|
||||
* method {@link EventClient#subscribe(ObjectName, NotificationListener,
|
||||
* NotificationFilter, Object)}.</p>
|
||||
*
|
||||
* @param clientId The remote client's identifier.
|
||||
* @param name The name of an MBean or an {@code ObjectName} pattern
|
||||
* representing a set of MBeans to which the listener should listen.
|
||||
* @param filter The filter object. If {@code filter} is null, no
|
||||
* filtering will be performed before notifications are handled.
|
||||
*
|
||||
* @return A listener identifier.
|
||||
*
|
||||
* @throws IllegalArgumentException If the {@code name} or
|
||||
* {@code listener} is null.
|
||||
* @throws EventClientNotFoundException If the client ID is not found.
|
||||
* @throws IOException Reserved for a remote client to throw if
|
||||
* an I/O error occurs.
|
||||
*
|
||||
* @see EventConsumer#subscribe(ObjectName, NotificationListener,
|
||||
* NotificationFilter,Object)
|
||||
* @see #removeListenerOrSubscriber(String, Integer)
|
||||
*/
|
||||
public Integer addSubscriber(String clientId, ObjectName name,
|
||||
NotificationFilter filter)
|
||||
throws EventClientNotFoundException, IOException;
|
||||
|
||||
/**
|
||||
* Removes a listener, to stop receiving notifications.
|
||||
* <P> This method is called by an {@link EventClient} to execute its
|
||||
* methods {@link EventClient#removeNotificationListener(ObjectName,
|
||||
* NotificationListener, NotificationFilter, Object)},
|
||||
* {@link EventClient#removeNotificationListener(ObjectName,
|
||||
* NotificationListener)}, and {@link EventClient#unsubscribe}.
|
||||
*
|
||||
* @param clientId The client identifier with which the listener was added.
|
||||
* @param listenerId The listener identifier to be removed. This must be
|
||||
* an identifier returned by a previous {@link #addListener addListener}
|
||||
* or {@link #addSubscriber addSubscriber} call.
|
||||
*
|
||||
* @throws InstanceNotFoundException if the MBean on which the listener
|
||||
* was added no longer exists.
|
||||
* @throws ListenerNotFoundException if there is no listener with the
|
||||
* given {@code listenerId}.
|
||||
* @throws EventClientNotFoundException if the {@code clientId} is
|
||||
* not found.
|
||||
* @throws IOException Reserved for a remote call to throw on the client
|
||||
* side.
|
||||
*/
|
||||
public void removeListenerOrSubscriber(String clientId, Integer listenerId)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException,
|
||||
EventClientNotFoundException, IOException;
|
||||
|
||||
/**
|
||||
* Called by a client to fetch notifications that are to be sent to its
|
||||
* listeners.
|
||||
*
|
||||
* @param clientId The client's identifier.
|
||||
* @param startSequenceNumber The first sequence number to
|
||||
* consider.
|
||||
* @param timeout The maximum waiting time.
|
||||
* @param maxNotifs The maximum number of notifications to return.
|
||||
*
|
||||
* @throws EventClientNotFoundException Thrown if the {@code clientId} is
|
||||
* not found.
|
||||
* @throws IllegalArgumentException if the client was {@linkplain
|
||||
* #addClient(String, Object[], String[]) added} with an {@link
|
||||
* EventForwarder} that is not a {@link FetchingEventForwarder}.
|
||||
* @throws IOException Reserved for a remote call to throw on the client
|
||||
* side.
|
||||
*/
|
||||
public NotificationResult fetchNotifications(String clientId,
|
||||
long startSequenceNumber,
|
||||
int maxNotifs,
|
||||
long timeout)
|
||||
throws EventClientNotFoundException, IOException;
|
||||
|
||||
/**
|
||||
* An {@code EventClient} calls this method to keep its {@code clientId}
|
||||
* alive in this MBean. The client will be removed if the lease times out.
|
||||
*
|
||||
* @param clientId The client's identifier.
|
||||
* @param timeout The time in milliseconds by which the lease is to be
|
||||
* extended. The value zero has no special meaning, so it will cause the
|
||||
* lease to time out immediately.
|
||||
*
|
||||
* @return The new lifetime of the lease in milliseconds. This may be
|
||||
* different from the requested time.
|
||||
*
|
||||
* @throws EventClientNotFoundException if the {@code clientId} is
|
||||
* not found.
|
||||
* @throws IOException reserved for a remote call to throw on the client
|
||||
* side.
|
||||
* @throws IllegalArgumentException if {@code clientId} is null or
|
||||
* {@code timeout} is negative.
|
||||
*/
|
||||
public long lease(String clientId, long timeout)
|
||||
throws IOException, EventClientNotFoundException;
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import javax.management.JMException;
|
||||
|
||||
/**
|
||||
* Thrown if an event client identifier is unknown.
|
||||
*/
|
||||
public class EventClientNotFoundException extends JMException {
|
||||
|
||||
/* Serial version */
|
||||
private static final long serialVersionUID = -3910667345840643089L;
|
||||
|
||||
/**
|
||||
*Constructs a {@code ClientNotFoundException} without a detail message.
|
||||
*/
|
||||
public EventClientNotFoundException() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code ClientNotFoundException} with the specified detail message.
|
||||
* @param message The message.
|
||||
*/
|
||||
public EventClientNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code ClientNotFoundException} with the specified detail message
|
||||
* and cause.
|
||||
*
|
||||
* @param message The message.
|
||||
* @param cause The cause (which is saved for later retrieval by the
|
||||
* {@code Throwable.getCause()} method). A null value is permitted, and indicates
|
||||
* that the cause is non-existent or unknown.
|
||||
*/
|
||||
public EventClientNotFoundException(String message, Throwable cause) {
|
||||
super(message);
|
||||
|
||||
initCause(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new exception with the specified cause.
|
||||
* @param cause The cause (which is saved for later retrieval by the
|
||||
* {@code Throwable.getCause()} method). A null value is permitted, and indicates
|
||||
* that the cause is non-existent or unknown.
|
||||
*/
|
||||
public EventClientNotFoundException(Throwable cause) {
|
||||
super();
|
||||
|
||||
initCause(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.management.ListenerNotFoundException;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
/**
|
||||
* This interface specifies methods to subscribe a listener to receive events
|
||||
* from an MBean or a set of MBeans. The MBeans can already be registered in
|
||||
* an MBean server, or they can be pending registration, or they can be MBeans
|
||||
* that will never be registered, or they can be MBeans that will be registered
|
||||
* then unregistered.
|
||||
* @since JMX 2.0
|
||||
*/
|
||||
public interface EventConsumer {
|
||||
/**
|
||||
* <p>Subscribes a listener to receive events from an MBean or a set
|
||||
* of MBeans represented by an {@code ObjectName} pattern.</p>
|
||||
*
|
||||
* <P> An event emitted by an MBean is forwarded to every listener that was
|
||||
* subscribed with the name of that MBean, or with a pattern that matches
|
||||
* that name.</p>
|
||||
*
|
||||
* @param name The name of an MBean or an {@code ObjectName} pattern
|
||||
* representing a set of MBeans to which the listener should listen.
|
||||
* @param listener The listener object that will handle the
|
||||
* notifications emitted by the MBeans.
|
||||
* @param filter The filter object. If {@code filter} is null, no
|
||||
* filtering will be performed before notification handling.
|
||||
* @param handback The context to be sent to the listener when a
|
||||
* notification is emitted.
|
||||
*
|
||||
* @throws IllegalArgumentException If the {@code name} or
|
||||
* {@code listener} is null.
|
||||
* @throws IOException for a remote client, thrown if
|
||||
* an I/O error occurs.
|
||||
* @see #unsubscribe(ObjectName, NotificationListener)
|
||||
*/
|
||||
public void subscribe(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* <p>Unsubscribes a listener which is listening to an MBean or a set of
|
||||
* MBeans represented by an {@code ObjectName} pattern.</p>
|
||||
*
|
||||
* <p>The listener to be removed must have been added by the {@link
|
||||
* #subscribe subscribe} method with the given {@code name}. If the {@code
|
||||
* name} is a pattern, then the {@code subscribe} must have used the same
|
||||
* pattern. If the same listener has been subscribed more than once to the
|
||||
* {@code name}, perhaps with different filters or handbacks, then all such
|
||||
* listeners are removed.</p>
|
||||
*
|
||||
* @param name The name of the MBean or an {@code ObjectName} pattern
|
||||
* representing a set of MBeans to which the listener was subscribed.
|
||||
* @param listener A listener that was previously subscribed to the
|
||||
* MBean(s).
|
||||
*
|
||||
* @throws ListenerNotFoundException The given {@code listener} was not
|
||||
* subscribed to the given {@code name}.
|
||||
* @throws IOException for a remote client, thrown if
|
||||
* an I/O error occurs.
|
||||
*
|
||||
* @see #subscribe
|
||||
*/
|
||||
public void unsubscribe(ObjectName name,
|
||||
NotificationListener listener)
|
||||
throws ListenerNotFoundException, IOException;
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.management.Notification;
|
||||
|
||||
/**
|
||||
* This interface can be used to specify a custom forwarding mechanism for
|
||||
* {@code EventClientDelegateMBean} to forward events to the client.
|
||||
*
|
||||
* @see <a href="package-summary.html#transports">Custom notification
|
||||
* transports</a>
|
||||
*/
|
||||
public interface EventForwarder {
|
||||
/**
|
||||
* Forwards a notification.
|
||||
* @param n The notification to be forwarded to a remote listener.
|
||||
* @param listenerId The identifier of the listener to receive the notification.
|
||||
* @throws IOException If it is closed or an I/O error occurs.
|
||||
*/
|
||||
public void forward(Notification n, Integer listenerId)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Informs the {@code EventForwarder} to shut down.
|
||||
* <p> After this method is called, any call to the method
|
||||
* {@link #forward forward(Notification, Integer)} may get an {@code IOException}.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
public void close() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets an event client identifier created by {@link EventClientDelegateMBean}.
|
||||
* <P> This method will be called just after this {@code EventForwarder}
|
||||
* is constructed and before calling the {@code forward} method to forward any
|
||||
* notifications.
|
||||
*/
|
||||
public void setClientId(String clientId) throws IOException;
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import javax.management.remote.NotificationResult;
|
||||
|
||||
/**
|
||||
* An object implementing this interface is passed by an {@link EventClient}
|
||||
* to its {@link EventRelay}, to allow the {@code EventRelay} to communicate
|
||||
* received notifications to the {@code EventClient}.
|
||||
*
|
||||
* @see <a href="package-summary.html#transports">Custom notification
|
||||
* transports</a>
|
||||
*/
|
||||
public interface EventReceiver {
|
||||
|
||||
/**
|
||||
* This method is implemented by {@code EventClient} as a callback to
|
||||
* receive notifications from {@code EventRelay}.
|
||||
* <P>The notifications are included in an object specified by the class
|
||||
* {@link NotificationResult}. In
|
||||
* addition to a set of notifications, the class object also contains two values:
|
||||
* {@code earliestSequenceNumber} and {@code nextSequenceNumber}.
|
||||
* These two values determine whether any notifications have been lost.
|
||||
* The {@code nextSequenceNumber} value of the last time is compared
|
||||
* to the received value {@code earliestSequenceNumber}. If the
|
||||
* received {@code earliesSequenceNumber} is greater, than the difference
|
||||
* signifies the number of lost notifications. A sender should
|
||||
* ensure the sequence of notifications sent, meaning that the value
|
||||
* {@code earliestSequenceNumber} of the next return should be always equal to
|
||||
* or greater than the value {@code nextSequenceNumber} of the last return.
|
||||
*
|
||||
* @param nr the received notifications and sequence numbers.
|
||||
*/
|
||||
public void receive(NotificationResult nr);
|
||||
|
||||
/**
|
||||
* Allows the {@link EventRelay} to report when it receives an unexpected
|
||||
* exception, which may be fatal and which may make it stop receiving
|
||||
* notifications.
|
||||
*
|
||||
* @param t The unexpected exception received while {@link EventRelay} was running.
|
||||
*/
|
||||
public void failed(Throwable t);
|
||||
|
||||
/**
|
||||
* Allows the {@link EventRelay} to report when it receives an unexpected
|
||||
* exception that is not fatal. For example, a notification received is not
|
||||
* serializable or its class is not found.
|
||||
*
|
||||
* @param e The unexpected exception received while notifications are being received.
|
||||
*/
|
||||
public void nonFatal(Exception e);
|
||||
}
|
80
jdk/src/share/classes/javax/management/event/EventRelay.java
Normal file
80
jdk/src/share/classes/javax/management/event/EventRelay.java
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executors; // for javadoc
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
|
||||
/**
|
||||
* This interface is used to specify a way to receive
|
||||
* notifications from a remote MBean server and then to forward the notifications
|
||||
* to an {@link EventClient}.
|
||||
*
|
||||
* @see <a href="package-summary.html#transports">Custom notification
|
||||
* transports</a>
|
||||
*/
|
||||
public interface EventRelay {
|
||||
/**
|
||||
* Returns an identifier that is used by this {@code EventRelay} to identify
|
||||
* the client when communicating with the {@link EventClientDelegateMBean}.
|
||||
* <P> This identifier is obtained by calling
|
||||
* {@link EventClientDelegateMBean#addClient(String, Object[], String[])
|
||||
* EventClientDelegateMBean.addClient}.
|
||||
* <P> It is the {@code EventRelay} that calls {@code EventClientDelegateMBean} to obtain
|
||||
* the client identifier because it is the {@code EventRelay} that decides
|
||||
* how to get notifications from the {@code EventClientDelegateMBean},
|
||||
* by creating the appropriate {@link EventForwarder}.
|
||||
*
|
||||
* @return A client identifier.
|
||||
* @throws IOException If an I/O error occurs when communicating with
|
||||
* the {@code EventClientDelegateMBean}.
|
||||
*/
|
||||
public String getClientId() throws IOException;
|
||||
|
||||
/**
|
||||
* This method is called by {@link EventClient} to register a callback
|
||||
* to receive notifications from an {@link EventClientDelegateMBean} object.
|
||||
* A {@code null} value is allowed, which means that the {@code EventClient} suspends
|
||||
* reception of notifications, so that the {@code EventRelay} can decide to stop receiving
|
||||
* notifications from its {@code EventForwarder}.
|
||||
*
|
||||
* @param eventReceiver An {@link EventClient} callback to receive
|
||||
* events.
|
||||
*/
|
||||
public void setEventReceiver(EventReceiver eventReceiver);
|
||||
|
||||
/**
|
||||
* Stops receiving and forwarding notifications and performs any necessary
|
||||
* cleanup. After calling this method, the {@link EventClient} will never
|
||||
* call any other methods of this object.
|
||||
*
|
||||
* @throws IOException If an I/O exception appears.
|
||||
*
|
||||
* @see EventClient#close
|
||||
*/
|
||||
public void stop() throws IOException;
|
||||
}
|
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Method;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.ListenerNotFoundException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MBeanServerDelegate;
|
||||
import javax.management.MBeanServerNotification;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcaster;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.Query;
|
||||
import javax.management.QueryEval;
|
||||
import javax.management.QueryExp;
|
||||
|
||||
/**
|
||||
* <p>An object that can be used to subscribe for notifications from all MBeans
|
||||
* in an MBeanServer that match a pattern. For example, to listen for
|
||||
* notifications from all MBeans in the MBeanServer {@code mbs} that match
|
||||
* {@code com.example:type=Controller,name=*} you could write:</p>
|
||||
*
|
||||
* <pre>
|
||||
* EventSubscriber subscriber = EventSubscriber.getEventSubscriber(mbs);
|
||||
* ObjectName pattern = new ObjectName("com.example:type=Controller,name=*");
|
||||
* NotificationListener myListener = ...;
|
||||
* NotificationFilter myFilter = null; // or whatever
|
||||
* Object handback = null; // or whatever
|
||||
* subscriber.subscribe(pattern, myListener, myFilter, myHandback);
|
||||
* </pre>
|
||||
*/
|
||||
public class EventSubscriber implements EventConsumer {
|
||||
/**
|
||||
* Returns an {@code EventSubscriber} object to subscribe for notifications
|
||||
* from the given {@code MBeanServer}. Calling this method more
|
||||
* than once with the same parameter may or may not return the same object.
|
||||
*
|
||||
* @param mbs the {@code MBeanServer} containing MBeans to be subscribed to.
|
||||
* @return An {@code EventSubscriber} object.
|
||||
*
|
||||
* @throws NullPointerException if mbs is null.
|
||||
*/
|
||||
public static EventSubscriber getEventSubscriber(MBeanServer mbs) {
|
||||
if (mbs == null)
|
||||
throw new NullPointerException("Null MBeanServer");
|
||||
|
||||
EventSubscriber eventSubscriber = null;
|
||||
synchronized (subscriberMap) {
|
||||
final WeakReference<EventSubscriber> wrf = subscriberMap.get(mbs);
|
||||
eventSubscriber = (wrf == null) ? null : wrf.get();
|
||||
|
||||
if (eventSubscriber == null) {
|
||||
eventSubscriber = new EventSubscriber(mbs);
|
||||
|
||||
subscriberMap.put(mbs,
|
||||
new WeakReference<EventSubscriber>(eventSubscriber));
|
||||
}
|
||||
}
|
||||
|
||||
return eventSubscriber;
|
||||
}
|
||||
|
||||
private EventSubscriber(final MBeanServer mbs) {
|
||||
logger.trace("EventSubscriber", "create a new one");
|
||||
this.mbeanServer = mbs;
|
||||
|
||||
Exception x = null;
|
||||
try {
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<Void>() {
|
||||
public Void run() throws Exception {
|
||||
mbs.addNotificationListener(
|
||||
MBeanServerDelegate.DELEGATE_NAME,
|
||||
myMBeanServerListener, null, null);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
} catch (PrivilegedActionException ex) {
|
||||
x = ex.getException();
|
||||
}
|
||||
|
||||
// handle possible exceptions.
|
||||
//
|
||||
// Fail unless x is null or x is instance of InstanceNotFoundException
|
||||
// The logic here is that if the MBeanServerDelegate is not present,
|
||||
// we will assume that the connection will not emit any
|
||||
// MBeanServerNotifications.
|
||||
//
|
||||
if (x != null && !(x instanceof InstanceNotFoundException)) {
|
||||
if (x instanceof RuntimeException)
|
||||
throw (RuntimeException) x;
|
||||
throw new RuntimeException(
|
||||
"Can't add listener to MBean server delegate: " + x, x);
|
||||
}
|
||||
}
|
||||
|
||||
public void subscribe(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws IOException {
|
||||
|
||||
if (logger.traceOn())
|
||||
logger.trace("subscribe", "" + name);
|
||||
|
||||
if (name == null)
|
||||
throw new IllegalArgumentException("Null MBean name");
|
||||
|
||||
if (listener == null)
|
||||
throw new IllegalArgumentException("Null listener");
|
||||
|
||||
final ListenerInfo li = new ListenerInfo(listener, filter, handback);
|
||||
List<ListenerInfo> list;
|
||||
|
||||
Map<ObjectName, List<ListenerInfo>> map;
|
||||
Set<ObjectName> names;
|
||||
if (name.isPattern()) {
|
||||
map = patternSubscriptionMap;
|
||||
names = mbeanServer.queryNames(name, notificationBroadcasterExp);
|
||||
} else {
|
||||
map = exactSubscriptionMap;
|
||||
names = Collections.singleton(name);
|
||||
}
|
||||
|
||||
synchronized (map) {
|
||||
list = map.get(name);
|
||||
if (list == null) {
|
||||
list = new ArrayList<ListenerInfo>();
|
||||
map.put(name, list);
|
||||
}
|
||||
list.add(li);
|
||||
}
|
||||
|
||||
for (ObjectName mbeanName : names) {
|
||||
try {
|
||||
mbeanServer.addNotificationListener(mbeanName,
|
||||
listener,
|
||||
filter,
|
||||
handback);
|
||||
} catch (Exception e) {
|
||||
logger.fine("subscribe", "addNotificationListener", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unsubscribe(ObjectName name,
|
||||
NotificationListener listener)
|
||||
throws ListenerNotFoundException, IOException {
|
||||
|
||||
if (logger.traceOn())
|
||||
logger.trace("unsubscribe", "" + name);
|
||||
|
||||
if (name == null)
|
||||
throw new IllegalArgumentException("Null MBean name");
|
||||
|
||||
if (listener == null)
|
||||
throw new ListenerNotFoundException();
|
||||
|
||||
Map<ObjectName, List<ListenerInfo>> map;
|
||||
Set<ObjectName> names;
|
||||
|
||||
if (name.isPattern()) {
|
||||
map = patternSubscriptionMap;
|
||||
names = mbeanServer.queryNames(name, notificationBroadcasterExp);
|
||||
} else {
|
||||
map = exactSubscriptionMap;
|
||||
names = Collections.singleton(name);
|
||||
}
|
||||
|
||||
final ListenerInfo li = new ListenerInfo(listener, null, null);
|
||||
List<ListenerInfo> list;
|
||||
synchronized (map) {
|
||||
list = map.get(name);
|
||||
if (list == null || !list.remove(li))
|
||||
throw new ListenerNotFoundException();
|
||||
|
||||
if (list.isEmpty())
|
||||
map.remove(name);
|
||||
}
|
||||
|
||||
for (ObjectName mbeanName : names) {
|
||||
try {
|
||||
mbeanServer.removeNotificationListener(mbeanName, li.listener);
|
||||
} catch (Exception e) {
|
||||
logger.fine("unsubscribe", "removeNotificationListener", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
// private stuff
|
||||
// ---------------------------------
|
||||
// used to receive MBeanServerNotification
|
||||
private NotificationListener myMBeanServerListener =
|
||||
new NotificationListener() {
|
||||
public void handleNotification(Notification n, Object hb) {
|
||||
if (!(n instanceof MBeanServerNotification) ||
|
||||
!MBeanServerNotification.
|
||||
REGISTRATION_NOTIFICATION.equals(n.getType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ObjectName name =
|
||||
((MBeanServerNotification)n).getMBeanName();
|
||||
try {
|
||||
if (!mbeanServer.isInstanceOf(name,
|
||||
NotificationBroadcaster.class.getName())) {
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// The only documented exception is InstanceNotFoundException,
|
||||
// which could conceivably happen if the MBean is unregistered
|
||||
// immediately after being registered.
|
||||
logger.fine("myMBeanServerListener.handleNotification",
|
||||
"isInstanceOf", e);
|
||||
return;
|
||||
}
|
||||
|
||||
final List<ListenerInfo> listeners = new ArrayList<ListenerInfo>();
|
||||
|
||||
// If there are subscribers for the exact name that has just arrived
|
||||
// then add their listeners to the list.
|
||||
synchronized (exactSubscriptionMap) {
|
||||
List<ListenerInfo> exactListeners = exactSubscriptionMap.get(name);
|
||||
if (exactListeners != null)
|
||||
listeners.addAll(exactListeners);
|
||||
}
|
||||
|
||||
// For every subscription pattern that matches the new name,
|
||||
// add all the listeners for that pattern to "listeners".
|
||||
synchronized (patternSubscriptionMap) {
|
||||
for (ObjectName on : patternSubscriptionMap.keySet()) {
|
||||
if (on.apply(name)) {
|
||||
listeners.addAll(patternSubscriptionMap.get(on));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add all the listeners just found to the new MBean.
|
||||
for (ListenerInfo li : listeners) {
|
||||
try {
|
||||
mbeanServer.addNotificationListener(
|
||||
name,
|
||||
li.listener,
|
||||
li.filter,
|
||||
li.handback);
|
||||
} catch (Exception e) {
|
||||
logger.fine("myMBeanServerListener.handleNotification",
|
||||
"addNotificationListener", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static class ListenerInfo {
|
||||
public final NotificationListener listener;
|
||||
public final NotificationFilter filter;
|
||||
public final Object handback;
|
||||
|
||||
public ListenerInfo(NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
Object handback) {
|
||||
|
||||
if (listener == null)
|
||||
throw new IllegalArgumentException("Null listener");
|
||||
|
||||
this.listener = listener;
|
||||
this.filter = filter;
|
||||
this.handback = handback;
|
||||
}
|
||||
|
||||
/* Two ListenerInfo instances are equal if they have the same
|
||||
* NotificationListener. This means that we can use List.remove
|
||||
* to implement the two-argument removeNotificationListener.
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this)
|
||||
return true;
|
||||
|
||||
if (!(o instanceof ListenerInfo))
|
||||
return false;
|
||||
|
||||
return listener.equals(((ListenerInfo)o).listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return listener.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
// private methods
|
||||
// ---------------------------------
|
||||
// ---------------------------------
|
||||
// private variables
|
||||
// ---------------------------------
|
||||
private final MBeanServer mbeanServer;
|
||||
|
||||
private final Map<ObjectName, List<ListenerInfo>> exactSubscriptionMap =
|
||||
new HashMap<ObjectName, List<ListenerInfo>>();
|
||||
private final Map<ObjectName, List<ListenerInfo>> patternSubscriptionMap =
|
||||
new HashMap<ObjectName, List<ListenerInfo>>();
|
||||
|
||||
|
||||
|
||||
// trace issues
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.event", "EventSubscriber");
|
||||
|
||||
// Compatibility code, so we can run on Tiger:
|
||||
private static final QueryExp notificationBroadcasterExp;
|
||||
static {
|
||||
QueryExp broadcasterExp;
|
||||
try {
|
||||
final Method m = Query.class.getMethod("isInstanceOf",
|
||||
new Class[] {String.class});
|
||||
broadcasterExp = (QueryExp)m.invoke(Query.class,
|
||||
new Object[] {NotificationBroadcaster.class.getName()});
|
||||
} catch (Exception e) {
|
||||
broadcasterExp = new BroadcasterQueryExp();
|
||||
}
|
||||
notificationBroadcasterExp = broadcasterExp;
|
||||
}
|
||||
private static class BroadcasterQueryExp extends QueryEval implements QueryExp {
|
||||
private static final long serialVersionUID = 1234L;
|
||||
public boolean apply(ObjectName name) {
|
||||
try {
|
||||
return getMBeanServer().isInstanceOf(
|
||||
name, NotificationBroadcaster.class.getName());
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final
|
||||
Map<MBeanServerConnection, WeakReference<EventSubscriber>> subscriberMap =
|
||||
new WeakHashMap<MBeanServerConnection, WeakReference<EventSubscriber>>();
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import com.sun.jmx.event.EventBuffer;
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import javax.management.Notification;
|
||||
import javax.management.remote.NotificationResult;
|
||||
import javax.management.remote.TargetedNotification;
|
||||
|
||||
/**
|
||||
* This class is used by {@link FetchingEventRelay}. When
|
||||
* {@link FetchingEventRelay} calls {@link
|
||||
* EventClientDelegateMBean#addClient(String, Object[], String[])} to get a new
|
||||
* client identifier, it uses
|
||||
* this class name as the first argument to ask {@code EventClientDelegateMBean}
|
||||
* to create an object of this class.
|
||||
* Then {@code EventClientDelegateMBean} forwards client notifications
|
||||
* to this object.
|
||||
* When {@link FetchingEventRelay} calls
|
||||
* {@link EventClientDelegateMBean#fetchNotifications(String, long, int, long)}
|
||||
* to fetch notifications, the {@code EventClientDelegateMBean} will forward
|
||||
* the call to this object.
|
||||
*/
|
||||
public class FetchingEventForwarder implements EventForwarder {
|
||||
|
||||
/**
|
||||
* Construct a new {@code FetchingEventForwarder} with the given
|
||||
* buffer size.
|
||||
* @param bufferSize the size of the buffer that will store notifications
|
||||
* until they have been fetched and acknowledged by the client.
|
||||
*/
|
||||
public FetchingEventForwarder(int bufferSize) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("Constructor", "buffer size is "+bufferSize);
|
||||
}
|
||||
|
||||
buffer = new EventBuffer(bufferSize);
|
||||
this.bufferSize = bufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by an {@link EventClientDelegateMBean} to forward a user call
|
||||
* {@link EventClientDelegateMBean#fetchNotifications(String, long, int, long)}.
|
||||
* A call of this method is considered to acknowledge reception of all
|
||||
* notifications whose sequence numbers are less the
|
||||
* {@code startSequenceNumber}, so all these notifications can be deleted
|
||||
* from this object.
|
||||
*
|
||||
* @param startSequenceNumber The first sequence number to
|
||||
* consider.
|
||||
* @param timeout The maximum waiting time in milliseconds.
|
||||
* If no notifications have arrived after this period of time, the call
|
||||
* will return with an empty list of notifications.
|
||||
* @param maxNotifs The maximum number of notifications to return.
|
||||
*/
|
||||
public NotificationResult fetchNotifications(long startSequenceNumber,
|
||||
int maxNotifs, long timeout) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("fetchNotifications",
|
||||
startSequenceNumber+" "+
|
||||
maxNotifs+" "+
|
||||
timeout);
|
||||
}
|
||||
|
||||
return buffer.fetchNotifications(startSequenceNumber,
|
||||
timeout,
|
||||
maxNotifs);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* In this implementation, the notification is stored in the local buffer
|
||||
* waiting for {@link #fetchNotifications fetchNotifications} to pick
|
||||
* it up.
|
||||
*/
|
||||
public void forward(Notification n, Integer listenerId) throws IOException {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("forward", n+" "+listenerId);
|
||||
}
|
||||
|
||||
buffer.add(new TargetedNotification(n, listenerId));
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("close", "");
|
||||
}
|
||||
|
||||
buffer.close();
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) throws IOException {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("setClientId", clientId);
|
||||
}
|
||||
this.clientId = clientId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a user specific list to save notifications in server side
|
||||
* before forwarding to an FetchingEventRelay in client side.
|
||||
* <P> This method should be called before any notification is
|
||||
* forwarded to this forwader.
|
||||
*
|
||||
* @param list a user specific list to save notifications
|
||||
*/
|
||||
protected void setList(List<TargetedNotification> list) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("setList", "");
|
||||
}
|
||||
|
||||
if (clientId == null) {
|
||||
buffer = new EventBuffer(bufferSize, list);
|
||||
} else {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
||||
private EventBuffer buffer;
|
||||
private int bufferSize;
|
||||
private String clientId;
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.event", "FetchingEventForwarder");
|
||||
}
|
@ -0,0 +1,389 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import com.sun.jmx.event.DaemonThreadFactory;
|
||||
import com.sun.jmx.event.RepeatedSingletonJob;
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.io.IOException;
|
||||
import java.io.NotSerializableException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.management.MBeanException;
|
||||
import javax.management.remote.NotificationResult;
|
||||
|
||||
/**
|
||||
* This class is an implementation of the {@link EventRelay} interface. It calls
|
||||
* {@link EventClientDelegateMBean#fetchNotifications
|
||||
* fetchNotifications(String, long, int, long)} to get
|
||||
* notifications and then forwards them to an {@link EventReceiver} object.
|
||||
*
|
||||
* @since JMX 2.0
|
||||
*/
|
||||
public class FetchingEventRelay implements EventRelay {
|
||||
/**
|
||||
* The default buffer size: {@value #DEFAULT_BUFFER_SIZE}.
|
||||
*/
|
||||
public final static int DEFAULT_BUFFER_SIZE = 1000;
|
||||
|
||||
/**
|
||||
* The default waiting timeout: {@value #DEFAULT_WAITING_TIMEOUT}
|
||||
* in millseconds when fetching notifications from
|
||||
* an {@code EventClientDelegateMBean}.
|
||||
*/
|
||||
public final static long DEFAULT_WAITING_TIMEOUT = 60000;
|
||||
|
||||
/**
|
||||
* The default maximum notifications to fetch every time:
|
||||
* {@value #DEFAULT_MAX_NOTIFICATIONS}.
|
||||
*/
|
||||
public final static int DEFAULT_MAX_NOTIFICATIONS = DEFAULT_BUFFER_SIZE;
|
||||
|
||||
/**
|
||||
* Constructs a default {@code FetchingEventRelay} object by using the default
|
||||
* configuration: {@code DEFAULT_BUFFER_SIZE}, {@code DEFAULT_WAITING_TIMEOUT}
|
||||
* {@code DEFAULT_MAX_NOTIFICATIONS}. A single thread is created
|
||||
* to do fetching.
|
||||
*
|
||||
* @param delegate The {@code EventClientDelegateMBean} to work with.
|
||||
* @throws IOException If failed to work with the {@code delegate}.
|
||||
* @throws MBeanException if unable to add a client to the remote
|
||||
* {@code EventClientDelegateMBean} (see {@link
|
||||
* EventClientDelegateMBean#addClient(String, Object[], String[])
|
||||
* EventClientDelegateMBean.addClient}).
|
||||
* @throws IllegalArgumentException If {@code delegate} is {@code null}.
|
||||
*/
|
||||
public FetchingEventRelay(EventClientDelegateMBean delegate)
|
||||
throws IOException, MBeanException {
|
||||
this(delegate, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code FetchingEventRelay} object by using the default
|
||||
* configuration: {@code DEFAULT_BUFFER_SIZE}, {@code DEFAULT_WAITING_TIMEOUT}
|
||||
* {@code DEFAULT_MAX_NOTIFICATIONS}, with a user-specific executor to do
|
||||
* the fetching.
|
||||
*
|
||||
* @param delegate The {@code EventClientDelegateMBean} to work with.
|
||||
* @param executor Used to do the fetching. A new thread is created if
|
||||
* {@code null}.
|
||||
* @throws IOException If failed to work with the {@code delegate}.
|
||||
* @throws MBeanException if unable to add a client to the remote
|
||||
* {@code EventClientDelegateMBean} (see {@link
|
||||
* EventClientDelegateMBean#addClient(String, Object[], String[])
|
||||
* EventClientDelegateMBean.addClient}).
|
||||
* @throws IllegalArgumentException If {@code delegate} is {@code null}.
|
||||
*/
|
||||
public FetchingEventRelay(EventClientDelegateMBean delegate,
|
||||
Executor executor) throws IOException, MBeanException {
|
||||
this(delegate,
|
||||
DEFAULT_BUFFER_SIZE,
|
||||
DEFAULT_WAITING_TIMEOUT,
|
||||
DEFAULT_MAX_NOTIFICATIONS,
|
||||
executor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code FetchingEventRelay} object with user-specific
|
||||
* configuration and executor to fetch notifications via the
|
||||
* {@link EventClientDelegateMBean}.
|
||||
*
|
||||
* @param delegate The {@code EventClientDelegateMBean} to work with.
|
||||
* @param bufferSize The buffer size for saving notifications in
|
||||
* {@link EventClientDelegateMBean} before they are fetched.
|
||||
* @param timeout The waiting time in millseconds when fetching
|
||||
* notifications from an {@code EventClientDelegateMBean}.
|
||||
* @param maxNotifs The maximum notifications to fetch every time.
|
||||
* @param executor Used to do the fetching. A new thread is created if
|
||||
* {@code null}.
|
||||
* @throws IOException if failed to communicate with the {@code delegate}.
|
||||
* @throws MBeanException if unable to add a client to the remote
|
||||
* {@code EventClientDelegateMBean} (see {@link
|
||||
* EventClientDelegateMBean#addClient(String, Object[], String[])
|
||||
* EventClientDelegateMBean.addClient}).
|
||||
* @throws IllegalArgumentException If {@code delegate} is {@code null}.
|
||||
*/
|
||||
public FetchingEventRelay(EventClientDelegateMBean delegate,
|
||||
int bufferSize,
|
||||
long timeout,
|
||||
int maxNotifs,
|
||||
Executor executor) throws IOException, MBeanException {
|
||||
this(delegate,
|
||||
bufferSize,
|
||||
timeout,
|
||||
maxNotifs,
|
||||
executor,
|
||||
FetchingEventForwarder.class.getName(),
|
||||
new Object[] {bufferSize},
|
||||
new String[] {int.class.getName()});
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code FetchingEventRelay} object with user-specific
|
||||
* configuration and executor to fetch notifications via the
|
||||
* {@link EventClientDelegateMBean}.
|
||||
*
|
||||
* @param delegate The {@code EventClientDelegateMBean} to work with.
|
||||
* @param bufferSize The buffer size for saving notifications in
|
||||
* {@link EventClientDelegateMBean} before they are fetched.
|
||||
* @param timeout The waiting time in millseconds when fetching
|
||||
* notifications from an {@code EventClientDelegateMBean}.
|
||||
* @param maxNotifs The maximum notifications to fetch every time.
|
||||
* @param executor Used to do the fetching.
|
||||
* @param forwarderName the class name of a user specific EventForwarder
|
||||
* to create in server to forward notifications to this object. The class
|
||||
* should be a subclass of the class {@link FetchingEventForwarder}.
|
||||
* @param params the parameters passed to create {@code forwarderName}
|
||||
* @param sig the signature of the {@code params}
|
||||
* @throws IOException if failed to communicate with the {@code delegate}.
|
||||
* @throws MBeanException if unable to add a client to the remote
|
||||
* {@code EventClientDelegateMBean} (see {@link
|
||||
* EventClientDelegateMBean#addClient(String, Object[], String[])
|
||||
* EventClientDelegateMBean.addClient}).
|
||||
* @throws IllegalArgumentException if {@code bufferSize} or
|
||||
* {@code maxNotifs} is less than {@code 1}
|
||||
* @throws NullPointerException if {@code delegate} is {@code null}.
|
||||
*/
|
||||
public FetchingEventRelay(EventClientDelegateMBean delegate,
|
||||
int bufferSize,
|
||||
long timeout,
|
||||
int maxNotifs,
|
||||
Executor executor,
|
||||
String forwarderName,
|
||||
Object[] params,
|
||||
String[] sig) throws IOException, MBeanException {
|
||||
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("FetchingEventRelay", "delegateMBean "+
|
||||
bufferSize+" "+
|
||||
timeout+" "+
|
||||
maxNotifs+" "+
|
||||
executor+" "+
|
||||
forwarderName+" ");
|
||||
}
|
||||
|
||||
if(delegate == null) {
|
||||
throw new NullPointerException("Null EventClientDelegateMBean!");
|
||||
}
|
||||
|
||||
|
||||
if (bufferSize<=1) {
|
||||
throw new IllegalArgumentException(
|
||||
"The bufferSize cannot be less than 1, no meaning.");
|
||||
}
|
||||
|
||||
if (maxNotifs<=1) {
|
||||
throw new IllegalArgumentException(
|
||||
"The maxNotifs cannot be less than 1, no meaning.");
|
||||
}
|
||||
|
||||
clientId = delegate.addClient(
|
||||
forwarderName,
|
||||
params,
|
||||
sig);
|
||||
|
||||
this.delegate = delegate;
|
||||
this.timeout = timeout;
|
||||
this.maxNotifs = maxNotifs;
|
||||
|
||||
if (executor == null) {
|
||||
executor = Executors.newSingleThreadScheduledExecutor(
|
||||
daemonThreadFactory);
|
||||
}
|
||||
this.executor = executor;
|
||||
if (executor instanceof ScheduledExecutorService)
|
||||
leaseScheduler = (ScheduledExecutorService) executor;
|
||||
else {
|
||||
leaseScheduler = Executors.newSingleThreadScheduledExecutor(
|
||||
daemonThreadFactory);
|
||||
}
|
||||
|
||||
startSequenceNumber = 0;
|
||||
fetchingJob = new MyJob();
|
||||
}
|
||||
|
||||
public void setEventReceiver(EventReceiver eventReceiver) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("setEventReceiver", ""+eventReceiver);
|
||||
}
|
||||
|
||||
EventReceiver old = this.eventReceiver;
|
||||
synchronized(fetchingJob) {
|
||||
this.eventReceiver = eventReceiver;
|
||||
if (old == null && eventReceiver != null)
|
||||
fetchingJob.resume();
|
||||
}
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("stop", "");
|
||||
}
|
||||
synchronized(fetchingJob) {
|
||||
if (stopped) {
|
||||
return;
|
||||
}
|
||||
|
||||
stopped = true;
|
||||
clientId = null;
|
||||
}
|
||||
}
|
||||
|
||||
private class MyJob extends RepeatedSingletonJob {
|
||||
public MyJob() {
|
||||
super(executor);
|
||||
}
|
||||
|
||||
public boolean isSuspended() {
|
||||
boolean b;
|
||||
synchronized(FetchingEventRelay.this) {
|
||||
b = stopped ||
|
||||
(eventReceiver == null) ||
|
||||
(clientId == null);
|
||||
}
|
||||
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("-MyJob-isSuspended", ""+b);
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
public void task() {
|
||||
logger.trace("MyJob-task", "");
|
||||
long fetchTimeout = timeout;
|
||||
NotificationResult nr = null;
|
||||
Throwable failedExcep = null;
|
||||
try {
|
||||
nr = delegate.fetchNotifications(
|
||||
clientId,
|
||||
startSequenceNumber,
|
||||
maxNotifs,
|
||||
fetchTimeout);
|
||||
} catch (Exception e) {
|
||||
if (isSerialOrClassNotFound(e)) {
|
||||
try {
|
||||
nr = fetchOne();
|
||||
} catch (Exception ee) {
|
||||
failedExcep = e;
|
||||
}
|
||||
} else {
|
||||
failedExcep = e;
|
||||
}
|
||||
}
|
||||
|
||||
if (failedExcep != null &&
|
||||
!isSuspended()) {
|
||||
logger.fine("MyJob-task",
|
||||
"Failed to fetch notification, stopping...", failedExcep);
|
||||
try {
|
||||
eventReceiver.failed(failedExcep);
|
||||
} catch (Exception e) {
|
||||
logger.trace(
|
||||
"MyJob-task", "exception from eventReceiver.failed", e);
|
||||
}
|
||||
|
||||
stop();
|
||||
} else if (nr != null) {
|
||||
try {
|
||||
eventReceiver.receive(nr);
|
||||
} catch (RuntimeException e) {
|
||||
logger.trace(
|
||||
"MyJob-task",
|
||||
"exception delivering notifs to EventClient", e);
|
||||
} finally {
|
||||
startSequenceNumber = nr.getNextSequenceNumber();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private NotificationResult fetchOne() throws Exception {
|
||||
logger.trace("fetchOne", "");
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
// 1 notif to skip possible missing class
|
||||
return delegate.fetchNotifications(
|
||||
clientId,
|
||||
startSequenceNumber,
|
||||
1,
|
||||
timeout);
|
||||
} catch (Exception e) {
|
||||
if (isSerialOrClassNotFound(e)) { // skip and continue
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("fetchOne", "Ignore", e);
|
||||
}
|
||||
eventReceiver.nonFatal(e);
|
||||
startSequenceNumber++;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isSerialOrClassNotFound(Exception e) {
|
||||
Throwable cause = e.getCause();
|
||||
|
||||
while (cause != null &&
|
||||
!(cause instanceof ClassNotFoundException) &&
|
||||
!(cause instanceof NotSerializableException)) {
|
||||
cause = cause.getCause();
|
||||
}
|
||||
|
||||
return (cause instanceof ClassNotFoundException ||
|
||||
cause instanceof NotSerializableException);
|
||||
}
|
||||
|
||||
private long startSequenceNumber = 0;
|
||||
private EventReceiver eventReceiver = null;
|
||||
private final EventClientDelegateMBean delegate;
|
||||
private String clientId;
|
||||
private boolean stopped = false;
|
||||
private volatile ScheduledFuture<?> leaseRenewalFuture;
|
||||
|
||||
private final Executor executor;
|
||||
private final ScheduledExecutorService leaseScheduler;
|
||||
private final MyJob fetchingJob;
|
||||
|
||||
private final long timeout;
|
||||
private final int maxNotifs;
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.event",
|
||||
"FetchingEventRelay");
|
||||
private static final ThreadFactory daemonThreadFactory =
|
||||
new DaemonThreadFactory("FetchingEventRelay-executor");
|
||||
}
|
169
jdk/src/share/classes/javax/management/event/ListenerInfo.java
Normal file
169
jdk/src/share/classes/javax/management/event/ListenerInfo.java
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
/**
|
||||
* This class specifies all the information required to register a user listener into
|
||||
* a remote MBean server. This class is not serializable because a user listener
|
||||
* is not serialized in order to be sent to the remote server.
|
||||
*
|
||||
* @since JMX 2.0
|
||||
*/
|
||||
public class ListenerInfo {
|
||||
|
||||
/**
|
||||
* Constructs a {@code ListenerInfo} object.
|
||||
*
|
||||
* @param name The name of the MBean to which the listener should
|
||||
* be added.
|
||||
* @param listener The listener object which will handle the
|
||||
* notifications emitted by the MBean.
|
||||
* @param filter The filter object. If the filter is null, no
|
||||
* filtering will be performed before notifications are handled.
|
||||
* @param handback The context to be sent to the listener when a
|
||||
* notification is emitted.
|
||||
* @param isSubscription If true, the listener is subscribed via
|
||||
* an {@code EventManager}. Otherwise it is added to a registered MBean.
|
||||
*/
|
||||
public ListenerInfo(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
Object handback,
|
||||
boolean isSubscription) {
|
||||
this.name = name;
|
||||
this.listener = listener;
|
||||
this.filter = filter;
|
||||
this.handback = handback;
|
||||
this.isSubscription = isSubscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an MBean or an MBean pattern that the listener listens to.
|
||||
*
|
||||
* @return An MBean or an MBean pattern.
|
||||
*/
|
||||
public ObjectName getObjectName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the listener.
|
||||
*
|
||||
* @return The listener.
|
||||
*/
|
||||
public NotificationListener getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the listener filter.
|
||||
*
|
||||
* @return The filter.
|
||||
*/
|
||||
public NotificationFilter getFilter() {
|
||||
return filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the listener handback.
|
||||
*
|
||||
* @return The handback.
|
||||
*/
|
||||
public Object getHandback() {
|
||||
return handback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this is a subscription listener.
|
||||
*
|
||||
* @return True if this is a subscription listener.
|
||||
*
|
||||
* @see EventClient#addListeners
|
||||
*/
|
||||
public boolean isSubscription() {
|
||||
return isSubscription;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Indicates whether some other object is "equal to" this one.
|
||||
* The return value is true if and only if {@code o} is an instance of
|
||||
* {@code ListenerInfo} and has equal values for all of its properties.</p>
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!(o instanceof ListenerInfo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ListenerInfo li = (ListenerInfo)o;
|
||||
|
||||
boolean ret = name.equals(li.name) &&
|
||||
(listener == li.listener) &&
|
||||
(isSubscription == li.isSubscription);
|
||||
|
||||
if (filter != null) {
|
||||
ret &= filter.equals(li.filter);
|
||||
} else {
|
||||
ret &= (li.filter == null);
|
||||
}
|
||||
|
||||
if (handback != null) {
|
||||
ret &= handback.equals(li.handback);
|
||||
} else {
|
||||
ret &= (li.handback == null);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode() + listener.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name.toString() + "_" +
|
||||
listener + "_" +
|
||||
filter + "_" +
|
||||
handback + "_" +
|
||||
isSubscription;
|
||||
}
|
||||
|
||||
private final ObjectName name;
|
||||
private final NotificationListener listener;
|
||||
private final NotificationFilter filter;
|
||||
private final Object handback;
|
||||
private final boolean isSubscription;
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.ListenerNotFoundException;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
/**
|
||||
* This interface specifies methods to add and remove notification listeners
|
||||
* on named MBeans.
|
||||
*/
|
||||
public interface NotificationManager {
|
||||
/**
|
||||
* <p>Adds a listener to a registered MBean.
|
||||
* Notifications emitted by the MBean will be forwarded
|
||||
* to the listener.
|
||||
*
|
||||
* @param name The name of the MBean on which the listener should
|
||||
* be added.
|
||||
* @param listener The listener object which will handle the
|
||||
* notifications emitted by the registered MBean.
|
||||
* @param filter The filter object. If filter is null, no
|
||||
* filtering will be performed before handling notifications.
|
||||
* @param handback The context to be sent to the listener when a
|
||||
* notification is emitted.
|
||||
*
|
||||
* @exception InstanceNotFoundException The MBean name provided
|
||||
* does not match any of the registered MBeans.
|
||||
* @exception IOException A communication problem occurred when
|
||||
* talking to the MBean server.
|
||||
*
|
||||
* @see #removeNotificationListener(ObjectName, NotificationListener)
|
||||
* @see #removeNotificationListener(ObjectName, NotificationListener,
|
||||
* NotificationFilter, Object)
|
||||
*/
|
||||
public void addNotificationListener(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException,
|
||||
IOException;
|
||||
|
||||
/**
|
||||
* <p>Removes a listener from a registered MBean.</p>
|
||||
*
|
||||
* <P> If the listener is registered more than once, perhaps with
|
||||
* different filters or callbacks, this method will remove all
|
||||
* those registrations.
|
||||
*
|
||||
* @param name The name of the MBean on which the listener should
|
||||
* be removed.
|
||||
* @param listener The listener to be removed.
|
||||
*
|
||||
* @exception InstanceNotFoundException The MBean name provided
|
||||
* does not match any of the registered MBeans.
|
||||
* @exception ListenerNotFoundException The listener is not
|
||||
* registered in the MBean.
|
||||
* @exception IOException A communication problem occurred when
|
||||
* talking to the MBean server.
|
||||
*
|
||||
* @see #addNotificationListener(ObjectName, NotificationListener,
|
||||
* NotificationFilter, Object)
|
||||
*/
|
||||
public void removeNotificationListener(ObjectName name,
|
||||
NotificationListener listener)
|
||||
throws InstanceNotFoundException,
|
||||
ListenerNotFoundException,
|
||||
IOException;
|
||||
|
||||
/**
|
||||
* <p>Removes a listener from a registered MBean.</p>
|
||||
*
|
||||
* <p>The MBean must have a listener that exactly matches the
|
||||
* given <code>listener</code>, <code>filter</code>, and
|
||||
* <code>handback</code> parameters. If there is more than one
|
||||
* such listener, only one is removed.</p>
|
||||
*
|
||||
* <p>The <code>filter</code> and <code>handback</code> parameters
|
||||
* may be null if and only if they are null in a listener to be
|
||||
* removed.</p>
|
||||
*
|
||||
* @param name The name of the MBean on which the listener should
|
||||
* be removed.
|
||||
* @param listener The listener to be removed.
|
||||
* @param filter The filter that was specified when the listener
|
||||
* was added.
|
||||
* @param handback The handback that was specified when the
|
||||
* listener was added.
|
||||
*
|
||||
* @exception InstanceNotFoundException The MBean name provided
|
||||
* does not match any of the registered MBeans.
|
||||
* @exception ListenerNotFoundException The listener is not
|
||||
* registered in the MBean, or it is not registered with the given
|
||||
* filter and handback.
|
||||
* @exception IOException A communication problem occurred when
|
||||
* talking to the MBean server.
|
||||
*
|
||||
* @see #addNotificationListener(ObjectName, NotificationListener,
|
||||
* NotificationFilter, Object)
|
||||
*
|
||||
*/
|
||||
public void removeNotificationListener(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException,
|
||||
ListenerNotFoundException,
|
||||
IOException;
|
||||
}
|
@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import com.sun.jmx.event.DaemonThreadFactory;
|
||||
import com.sun.jmx.event.RepeatedSingletonJob;
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.rmi.RemoteException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import javax.management.Notification;
|
||||
import javax.management.remote.NotificationResult;
|
||||
import javax.management.remote.TargetedNotification;
|
||||
|
||||
|
||||
/**
|
||||
* This class is used by {@link RMIPushEventRelay}. When
|
||||
* {@link RMIPushEventRelay} calls {@link
|
||||
* EventClientDelegateMBean#addClient(String, Object[], String[])} to get a new
|
||||
* client identifier, it uses this class name as the
|
||||
* first argument to ask {@code EventClientDelegateMBean} to create an object of
|
||||
* this class.
|
||||
* Then {@code EventClientDelegateMBean} forwards client notifications
|
||||
* to this object. This object then continues forwarding the notifications
|
||||
* to the {@code RMIPushEventRelay}.
|
||||
*/
|
||||
public class RMIPushEventForwarder implements EventForwarder {
|
||||
private static final int DEFAULT_BUFFER_SIZE = 6000;
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code RMIPushEventForwarder}.
|
||||
*
|
||||
* @param receiver An RMI stub exported to receive notifications
|
||||
* from this object for its {@link RMIPushEventRelay}.
|
||||
*
|
||||
* @param bufferSize The maximum number of notifications to store
|
||||
* while waiting for the last remote send to complete.
|
||||
*/
|
||||
public RMIPushEventForwarder(RMIPushServer receiver, int bufferSize) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("RMIEventForwarder", "new one");
|
||||
}
|
||||
|
||||
if (bufferSize < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Negative buffer size: " + bufferSize);
|
||||
} else if (bufferSize == 0)
|
||||
bufferSize = DEFAULT_BUFFER_SIZE;
|
||||
|
||||
if (receiver == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
|
||||
this.receiver = receiver;
|
||||
this.buffer = new ArrayBlockingQueue<TargetedNotification>(bufferSize);
|
||||
}
|
||||
|
||||
public void forward(Notification n, Integer listenerId) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("forward", "to the listener: "+listenerId);
|
||||
}
|
||||
synchronized(sendingJob) {
|
||||
TargetedNotification tn = new TargetedNotification(n, listenerId);
|
||||
while (!buffer.offer(tn)) {
|
||||
buffer.remove();
|
||||
passed++;
|
||||
}
|
||||
sendingJob.resume();
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("close", "called");
|
||||
}
|
||||
|
||||
synchronized(sendingJob) {
|
||||
ended = true;
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("setClientId", clientId);
|
||||
}
|
||||
}
|
||||
|
||||
private class SendingJob extends RepeatedSingletonJob {
|
||||
public SendingJob() {
|
||||
super(executor);
|
||||
}
|
||||
|
||||
public boolean isSuspended() {
|
||||
return ended || buffer.isEmpty();
|
||||
}
|
||||
|
||||
public void task() {
|
||||
final long earliest = passed;
|
||||
|
||||
List<TargetedNotification> tns =
|
||||
new ArrayList<TargetedNotification>(buffer.size());
|
||||
synchronized(sendingJob) {
|
||||
buffer.drainTo(tns);
|
||||
passed += tns.size();
|
||||
}
|
||||
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("SendingJob-task", "sending: "+tns.size());
|
||||
}
|
||||
|
||||
if (!tns.isEmpty()) {
|
||||
try {
|
||||
TargetedNotification[] tnArray =
|
||||
new TargetedNotification[tns.size()];
|
||||
tns.toArray(tnArray);
|
||||
receiver.receive(new NotificationResult(earliest, passed, tnArray));
|
||||
} catch (RemoteException e) {
|
||||
if (logger.debugOn()) {
|
||||
logger.debug("SendingJob-task",
|
||||
"Got exception to forward notifs.", e);
|
||||
}
|
||||
|
||||
long currentLost = passed - earliest;
|
||||
if (FetchingEventRelay.isSerialOrClassNotFound(e)) {
|
||||
// send one by one
|
||||
long tmpPassed = earliest;
|
||||
for (TargetedNotification tn : tns) {
|
||||
try {
|
||||
receiver.receive(new NotificationResult(earliest,
|
||||
++tmpPassed, new TargetedNotification[]{tn}));
|
||||
} catch (RemoteException ioee) {
|
||||
logger.trace(
|
||||
"SendingJob-task", "send to remote", ioee);
|
||||
// sends nonFatal notifs?
|
||||
}
|
||||
}
|
||||
|
||||
currentLost = passed - tmpPassed;
|
||||
}
|
||||
|
||||
if (currentLost > 0) { // inform of the lost.
|
||||
try {
|
||||
receiver.receive(new NotificationResult(
|
||||
passed, passed,
|
||||
new TargetedNotification[]{}));
|
||||
} catch (RemoteException ee) {
|
||||
logger.trace(
|
||||
"SendingJob-task", "receiver.receive", ee);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private long passed = 0;
|
||||
|
||||
private static final ExecutorService executor =
|
||||
Executors.newCachedThreadPool(
|
||||
new DaemonThreadFactory("RMIEventForwarder Executor"));
|
||||
private final SendingJob sendingJob = new SendingJob();
|
||||
|
||||
private final BlockingQueue<TargetedNotification> buffer;
|
||||
|
||||
private final RMIPushServer receiver;
|
||||
private boolean ended = false;
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.event", "RMIEventForwarder");
|
||||
}
|
@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
import java.io.IOException;
|
||||
import java.rmi.NoSuchObjectException;
|
||||
import java.rmi.RemoteException;
|
||||
import java.rmi.server.UnicastRemoteObject;
|
||||
import java.rmi.server.RMIClientSocketFactory;
|
||||
import java.rmi.server.RMIServerSocketFactory;
|
||||
import javax.management.MBeanException;
|
||||
import javax.management.remote.NotificationResult;
|
||||
|
||||
/**
|
||||
* This class is an implementation of the {@link EventRelay} interface, using
|
||||
* push mode. It exports an RMI object that {@link RMIPushEventForwarder} uses
|
||||
* to forward notifications.
|
||||
*
|
||||
* @since JMX 2.0
|
||||
*/
|
||||
public class RMIPushEventRelay implements EventRelay {
|
||||
/**
|
||||
* Constructs a default {@code RMIPushEventRelay} object
|
||||
* and exports its {@linkplain RMIPushServer notification
|
||||
* receiver} on any free port. This constructor is equivalent
|
||||
* to {@link #RMIPushEventRelay(EventClientDelegateMBean,
|
||||
* int, RMIClientSocketFactory, RMIServerSocketFactory, int)
|
||||
* RMIPushEventRelay(delegate, 0, null, null, <em><default buffer
|
||||
* size></em>)}.
|
||||
*
|
||||
* @param delegate The {@link EventClientDelegateMBean} proxy to work with.
|
||||
* @throws IOException if failed to communicate with
|
||||
* {@link EventClientDelegateMBean}.
|
||||
* @throws MBeanException if the {@link EventClientDelegateMBean} failed
|
||||
* to create an {@code EventForwarder} for this object.
|
||||
*/
|
||||
public RMIPushEventRelay(EventClientDelegateMBean delegate)
|
||||
throws IOException, MBeanException {
|
||||
this(delegate, 0, null, null, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code RMIPushEventRelay} object and exports its
|
||||
* {@linkplain RMIPushServer notification receiver} on a specified port.
|
||||
*
|
||||
* @param delegate The {@link EventClientDelegateMBean} proxy to work with.
|
||||
* @param port The port used to export an RMI object to receive notifications
|
||||
* from a server. If the port is zero, an anonymous port is used.
|
||||
* @param csf The client socket factory used to export the RMI object.
|
||||
* Can be null.
|
||||
* @param ssf The server socket factory used to export the RMI object.
|
||||
* Can be null.
|
||||
* @param bufferSize The number of notifications held on the server
|
||||
* while waiting for the previous transmission to complete. A value of
|
||||
* zero means the default buffer size.
|
||||
*
|
||||
* @throws IOException if failed to communicate with
|
||||
* {@link EventClientDelegateMBean}.
|
||||
* @throws MBeanException if the {@link EventClientDelegateMBean} failed
|
||||
* to create an {@code EventForwarder} for this object.
|
||||
*
|
||||
* @see RMIPushEventForwarder#RMIPushEventForwarder(RMIPushServer, int)
|
||||
*/
|
||||
public RMIPushEventRelay(EventClientDelegateMBean delegate,
|
||||
int port,
|
||||
RMIClientSocketFactory csf,
|
||||
RMIServerSocketFactory ssf,
|
||||
int bufferSize)
|
||||
throws IOException, MBeanException {
|
||||
|
||||
UnicastRemoteObject.exportObject(exportedReceiver, port, csf, ssf);
|
||||
|
||||
clientId = delegate.addClient(
|
||||
RMIPushEventForwarder.class.getName(),
|
||||
new Object[] {exportedReceiver, bufferSize},
|
||||
new String[] {RMIPushServer.class.getName(),
|
||||
int.class.getName()});
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setEventReceiver(EventReceiver receiver) {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("setEventReceiver", ""+receiver);
|
||||
}
|
||||
synchronized(lock) {
|
||||
this.receiver = receiver;
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("stop", "");
|
||||
}
|
||||
synchronized(lock) {
|
||||
if (stopped) {
|
||||
return;
|
||||
} else {
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
if (clientId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
UnicastRemoteObject.unexportObject(exportedReceiver, true);
|
||||
} catch (NoSuchObjectException nsoe) {
|
||||
logger.fine("RMIPushEventRelay.stop", "unexport", nsoe);
|
||||
// OK: we wanted it unexported, and apparently it already is
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private volatile String clientId;
|
||||
private volatile EventReceiver receiver;
|
||||
|
||||
private RMIPushServer exportedReceiver = new RMIPushServer() {
|
||||
public void receive(NotificationResult nr) throws RemoteException {
|
||||
if (logger.traceOn()) {
|
||||
logger.trace("EventPusherImpl-receive","");
|
||||
}
|
||||
receiver.receive(nr);
|
||||
// Any exception will be sent back to the client.
|
||||
}
|
||||
};
|
||||
|
||||
private boolean stopped = false;
|
||||
|
||||
private final int[] lock = new int[0];
|
||||
|
||||
private static final ClassLogger logger =
|
||||
new ClassLogger("javax.management.event",
|
||||
"PushEventRelay");
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
||||
|
||||
import java.rmi.Remote;
|
||||
import java.rmi.RemoteException;
|
||||
import javax.management.remote.NotificationResult;
|
||||
|
||||
/**
|
||||
* The {@link RMIPushEventRelay} exports an RMI object of this class and
|
||||
* sends a client stub for that object to the associated
|
||||
* {@link RMIPushEventForwarder} in a remote MBean server. The
|
||||
* {@code RMIPushEventForwarder} then sends notifications to the
|
||||
* RMI object.
|
||||
*/
|
||||
public interface RMIPushServer extends Remote {
|
||||
/**
|
||||
* <p>Dispatch the notifications in {@code nr} to the {@link RMIPushEventRelay}
|
||||
* associated with this object.</p>
|
||||
* @param nr the notification result to dispatch.
|
||||
* @throws java.rmi.RemoteException if the remote invocation of this method
|
||||
* failed.
|
||||
*/
|
||||
public void receive(NotificationResult nr) throws RemoteException;
|
||||
}
|
312
jdk/src/share/classes/javax/management/event/package-info.java
Normal file
312
jdk/src/share/classes/javax/management/event/package-info.java
Normal file
@ -0,0 +1,312 @@
|
||||
/**
|
||||
* <p>Defines the <em>Event Service</em>, which provides extended support
|
||||
* for JMX notifications.</p>
|
||||
*
|
||||
* <p>The Event Service provides greater control over
|
||||
* notification handling than the default technique using {@link
|
||||
* javax.management.MBeanServer#addNotificationListener(ObjectName,
|
||||
* NotificationListener, NotificationFilter, Object)
|
||||
* MBeanServer.addNotificationListener} or {@link
|
||||
* javax.management.MBeanServerConnection#addNotificationListener(ObjectName,
|
||||
* NotificationListener, NotificationFilter, Object)
|
||||
* MBeanServerConnection.addNotificationListener}.</p>
|
||||
*
|
||||
* <p>Here are some reasons you may want to use the Event Service:</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>To receive notifications from a set of MBeans defined by an
|
||||
* ObjectName pattern, such as {@code com.example.config:type=Cache,*}.
|
||||
*
|
||||
* <li>When the notification-handling behavior of the connector you are
|
||||
* using does not match your requirements. For example, with the standard
|
||||
* RMI connector you can lose notifications if there are very many of them
|
||||
* in the MBean Server you are connected to, even if you are only listening
|
||||
* for a small proportion of them.
|
||||
*
|
||||
* <li>To change the threading behavior of notification dispatch.
|
||||
*
|
||||
* <li>To define a different transport for notifications, for example to
|
||||
* arrange for them to be delivered through the Java Message Service (<a
|
||||
* href="http://java.sun.com/jms">JMS</a>). The Event Service comes with
|
||||
* one alternative transport as standard, a "push-mode" RMI transport.
|
||||
*
|
||||
* <li>To handle notifications on behalf of MBeans (often virtual) in a
|
||||
* namespace.
|
||||
* </ul>
|
||||
*
|
||||
* <p>The Event Service is new in version 2.0 of the JMX API, which is the
|
||||
* version introduced in version 7 of the Java SE platform. It is not usually
|
||||
* possible to use the Event Service when connecting remotely to an
|
||||
* MBean Server that is running an earlier version.</p>
|
||||
*
|
||||
*
|
||||
* <h3 id="handlingremote">Handling remote notifications with the Event
|
||||
* Service</h3>
|
||||
*
|
||||
* <p>Prior to version 2.0 of the JMX API, every connector
|
||||
* had to include logic to handle notifications. The standard {@linkplain
|
||||
* javax.management.remote.rmi RMI} and JMXMP connectors defined by <a
|
||||
* href="http://jcp.org/en/jsr/detail?id=160">JSR 160</a> handle notifications
|
||||
* in a way that is not always appropriate for applications. Specifically,
|
||||
* the connector server adds one listener to every MBean that might emit
|
||||
* notifications, and adds all received notifications to a fixed-size
|
||||
* buffer. This means that if there are very many notifications, a
|
||||
* remote client may miss some, even if it is only registered for a
|
||||
* very small subset of notifications. Furthermore, since every {@link
|
||||
* javax.management.NotificationBroadcaster NotificationBroadcaster} MBean
|
||||
* gets a listener from the connector server, MBeans cannot usefully optimize
|
||||
* by only sending notifications when there is a listener. Finally, since
|
||||
* the connector server uses just one listener per MBean, MBeans cannot
|
||||
* impose custom behavior per listener, such as security checks or localized
|
||||
* notifications.</p>
|
||||
*
|
||||
* <p>The Event Service does not have these restrictions. The RMI connector
|
||||
* that is included in this version of the JMX API uses the Event Service by
|
||||
* default, although it can be configured to have the previous behavior if
|
||||
* required.</p>
|
||||
*
|
||||
* <p>The Event Service can be used with <em>any</em> connector via the
|
||||
* method {@link javax.management.event.EventClient#getEventClientConnection
|
||||
* EventClient.getEventClientConnection}, like this:</p>
|
||||
*
|
||||
* <pre>
|
||||
* JMXConnector conn = ...;
|
||||
* MBeanServerConnection mbsc = conn.getMBeanServerConnection();
|
||||
* MBeanServerConnection eventMbsc = EventClient.getEventClientConnection(mbsc);
|
||||
* </pre>
|
||||
*
|
||||
* <p>If you add listeners using {@code eventMbsc.addNotificationListener}
|
||||
* instead of {@code mbsc.addNotificationListener}, then they will be handled
|
||||
* by the Event Service rather than by the connector's notification system.</p>
|
||||
*
|
||||
* <p>For the Event Service to work, either the {@link
|
||||
* javax.management.event.EventClientDelegateMBean EventClientDelegateMBean}
|
||||
* must be registered in the MBean Server, or the connector server must
|
||||
* be configured to simulate the existence of this MBean, for example
|
||||
* using {@link javax.management.event.EventClientDelegate#newForwarder()
|
||||
* EventClientDelegate.newForwarder()}. The standard RMI connector is so
|
||||
* configured by default. The {@code EventClientDelegateMBean} documentation
|
||||
* has further details.</p>
|
||||
*
|
||||
*
|
||||
* <h3 id="subscribepattern">Receiving notifications from a set of MBeans</h3>
|
||||
*
|
||||
* <p>The Event Server allows you to receive notifications from every MBean
|
||||
* that matches an {@link javax.management.ObjectName ObjectName} pattern.
|
||||
* For local clients (in the same JVM as the MBean Server), the {@link
|
||||
* javax.management.event.EventSubscriber EventSubscriber} class can be used for
|
||||
* this. For remote clients, or if the same code is to be used locally and
|
||||
* remotely, use an
|
||||
* {@link javax.management.event.EventClient EventClient}.</p>
|
||||
*
|
||||
* <p>EventSubscriber and EventClient correctly handle the case where a new
|
||||
* MBean is registered under a name that matches the pattern. Notifications
|
||||
* from the new MBean will also be received.</p>
|
||||
*
|
||||
* <p>Here is how to receive notifications from all MBeans in a local
|
||||
* {@code MBeanServer} that match {@code com.example.config:type=Cache,*}:</p>
|
||||
*
|
||||
* <pre>
|
||||
* MBeanServer mbs = ...;
|
||||
* NotificationListener listener = ...;
|
||||
* ObjectName pattern = new ObjectName("com.example.config:type=Cache,*");
|
||||
* EventSubscriber esub = EventSubscriber.getEventSubscriber(mbs);
|
||||
* esub.{@link javax.management.event.EventSubscriber#subscribe
|
||||
* subscribe}(pattern, listener, null, null);
|
||||
* </pre>
|
||||
*
|
||||
* <p>Here is how to do the same thing remotely:</p>
|
||||
*
|
||||
* <pre>
|
||||
* MBeanServerConnection mbsc = jmxConnector.getMBeanServerConnection();
|
||||
* EventClient events = new EventClient(mbsc);
|
||||
* NotificationListener listener = ...;
|
||||
* ObjectName pattern = new ObjectName("com.example.config:type=Cache,*");
|
||||
* events.{@link javax.management.event.EventClient#subscribe
|
||||
* subscribe}(pattern, listener, null, null);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <h3 id="threading">Controlling threading behavior for notification
|
||||
* dispatch</h3>
|
||||
*
|
||||
* <p>The EventClient class can be used to control threading of listener
|
||||
* dispatch. For example, to arrange for all listeners to be invoked
|
||||
* in the same thread, you can create an {@code EventClient} like this:</p>
|
||||
*
|
||||
* <pre>
|
||||
* MBeanServerConnection mbsc = ...;
|
||||
* Executor singleThreadExecutor = {@link
|
||||
* java.util.concurrent.Executors#newSingleThreadExecutor()
|
||||
* Executors.newSingleThreadExecutor}();
|
||||
* EventClient events = new EventClient(
|
||||
* mbsc, null, singleThreadExecutor, EventClient.DEFAULT_LEASE_TIMEOUT);
|
||||
* events.addNotificationListener(...);
|
||||
* events.subscribe(...);
|
||||
* </pre>
|
||||
*
|
||||
*
|
||||
* <h3 id="leasing">Leasing</h3>
|
||||
*
|
||||
* <p>The {@code EventClient} uses a <em>lease</em> mechanism to ensure
|
||||
* that resources are eventually released on the server even if the client
|
||||
* does not explicitly clean up. (This can happen through network
|
||||
* partitioning, for example.)</p>
|
||||
*
|
||||
* <p>When an {@code EventClient} registers with the {@code
|
||||
* EventClientDelegateMBean} using one of the {@code addClient} methods,
|
||||
* an initial lease is created with a default expiry time. The {@code
|
||||
* EventClient} requests an explicit lease shortly after that, with a
|
||||
* configurable expiry time. Then the {@code EventClient} periodically
|
||||
* <em>renews</em> the lease before it expires, typically about half way
|
||||
* through the lifetime of the lease. If at any point the lease reaches
|
||||
* the expiry time of the last renewal then it expires, and {@code
|
||||
* EventClient} is unregistered as if it had called the {@link
|
||||
* javax.management.event.EventClientDelegateMBean#removeClient removeClient}
|
||||
* method.</p>
|
||||
*
|
||||
*
|
||||
* <h3 id="transports">Custom notification transports</h3>
|
||||
*
|
||||
* <p>When you create an {@code EventClient}, you can define the transport
|
||||
* that it uses to deliver notifications. The transport might use the
|
||||
* Java Message Service (<a href="http://java.sun.com/jms">JMS</a>) or
|
||||
* any other communication system. Specifying a transport is useful for
|
||||
* example when you want different network behavior from the default, or
|
||||
* different reliability guarantees. The default transport calls {@link
|
||||
* javax.management.event.EventClientDelegateMBean#fetchNotifications
|
||||
* EventClientDelegateMBean.fetchNotifications} repeatedly, which usually means
|
||||
* that there must be a network connection permanently open between the client
|
||||
* and the server. If the same client is connected to many servers this can
|
||||
* cause scalability problems. If notifications are relatively rare, then
|
||||
* JMS or the {@linkplain javax.management.event.RMIPushEventRelay push-mode
|
||||
* RMI transport} may be more suitable.</p>
|
||||
*
|
||||
* <p>A transport is implemented by an {@link javax.management.event.EventRelay
|
||||
* EventRelay} on the client side and a corresponding {@link
|
||||
* javax.management.event.EventForwarder EventForwarder}
|
||||
* on the server side. An example is the {@link
|
||||
* javax.management.event.RMIPushEventRelay RMIPushEventRelay} and its
|
||||
* {@link javax.management.event.RMIPushEventForwarder RMIPushEventForwarder}.</p>
|
||||
*
|
||||
* <p>To use a given transport with an {@code EventClient}, you first create
|
||||
* an instance of its {@code EventRelay}. Typically the {@code EventRelay}'s
|
||||
* constructor will have a parameter of type {@code MBeanServerConnection}
|
||||
* or {@code EventClientDelegateMBean}, so that it can communicate with the
|
||||
* {@code EventClientDelegateMBean} in the server. For example, the {@link
|
||||
* javax.management.event.RMIPushEventForwarder RMIPushEventForwarder}'s constructors
|
||||
* all take an {@code EventClientDelegateMBean} parameter, which is expected to
|
||||
* be a {@linkplain javax.management.JMX#newMBeanProxy(MBeanServerConnection,
|
||||
* ObjectName, Class) proxy} for the {@code EventClientDelegateMBean} in the
|
||||
* server.</p>
|
||||
*
|
||||
* <p>When it is created, the {@code EventRelay} will call
|
||||
* {@link javax.management.event.EventClientDelegateMBean#addClient(String,
|
||||
* Object[], String[]) EventClientDelegateMBean.addClient}. It passes the
|
||||
* name of the {@code EventForwarder} class and its constructor parameters.
|
||||
* The {@code EventClientDelegateMBean} will instantiate this class using
|
||||
* {@link javax.management.MBeanServer#instantiate(String, Object[], String[])
|
||||
* MBeanServer.instantiate}, and it will return a unique <em>client id</em>.</p>
|
||||
*
|
||||
* <p>Then you pass the newly-created {@code EventRelay} to one of the {@code
|
||||
* EventClient} constructors, and you have an {@code EventClient} that uses the
|
||||
* chosen transport.</p>
|
||||
*
|
||||
* <p>For example, when you create an {@code RMIPushEventRelay}, it
|
||||
* uses {@code MBeanServerDelegateMBean.addClient} to create an {@code
|
||||
* RMIEventForwarder} in the server. Notifications will then be delivered
|
||||
* through an RMI communication from the {@code RMIEventForwarder} to the
|
||||
* {@code RMIPushEventRelay}.</p>
|
||||
*
|
||||
*
|
||||
* <h4 id="writingcustomtransport">Writing a custom transport</h4>
|
||||
*
|
||||
* <p>To write a custom transport, you need to understand the sequence
|
||||
* of events when an {@code EventRelay} and its corresponding {@code
|
||||
* EventForwarder} are created, and when a notification is sent from the {@code
|
||||
* EventForwarder} to the {@code EventRelay}.</p>
|
||||
*
|
||||
* <p>When an {@code EventRelay} is created:</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li><p>The {@code EventRelay} must call {@code
|
||||
* EventClientDelegateMBean.addClient} with the name of the {@code
|
||||
* EventForwarder} and the constructor parameters.</p>
|
||||
*
|
||||
* <li><p>{@code EventClientDelegateMBean.addClient} will do the following
|
||||
* steps:</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li>create the {@code EventForwarder} using {@code MBeanServer.instantiate};
|
||||
* <li>allocate a unique client id;
|
||||
* <li>call the new {@code EventForwarder}'s {@link
|
||||
* javax.management.event.EventForwarder#setClientId setClientId} method with
|
||||
* the new client id;
|
||||
* <li>return the client id to the caller.
|
||||
* </ul>
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* <p>When an {@code EventClient} is created with an {@code EventRelay}
|
||||
* parameter, it calls {@link javax.management.event.EventRelay#setEventReceiver
|
||||
* EventRelay.setEventReceiver} with an {@code EventReceiver} that the
|
||||
* {@code EventRelay} will use to deliver notifications.</p>
|
||||
*
|
||||
* <p>When a listener is added using the {@code EventClient}, the
|
||||
* {@code EventRelay} and {@code EventForwarder} are not involved.</p>
|
||||
*
|
||||
* <p>When an MBean emits a notification and a listener has been added
|
||||
* to that MBean using the {@code EventClient}:</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li><p>The {@code EventForwarder}'s
|
||||
* {@link javax.management.event.EventForwarder#forward forward} method
|
||||
* is called with the notification and a <em>listener id</em>.</p>
|
||||
*
|
||||
* <li><p>The {@code EventForwarder} sends the notification and listener id
|
||||
* to the {@code EventRelay} using the custom transport.</p>
|
||||
*
|
||||
* <li><p>The {@code EventRelay} delivers the notification by calling
|
||||
* {@link javax.management.event.EventReceiver#receive EventReceiver.receive}.</p>
|
||||
* </ul>
|
||||
*
|
||||
* <p>When the {@code EventClient} is closed ({@link
|
||||
* javax.management.event.EventClient#close EventClient.close}):</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li><p>The {@code EventClient} calls {@link
|
||||
* javax.management.event.EventRelay#stop EventRelay.stop}.</p>
|
||||
*
|
||||
* <li><p>The {@code EventClient} calls {@link
|
||||
* javax.management.event.EventClientDelegateMBean#removeClient
|
||||
* EventClientDelegateMBean.removeClient}.</p>
|
||||
*
|
||||
* <li><p>The {@code EventClientDelegateMBean} removes any listeners it
|
||||
* had added on behalf of this {@code EventClient}.</p>
|
||||
*
|
||||
* <li><p>The {@code EventClientDelegateMBean} calls
|
||||
* {@link javax.management.event.EventForwarder#close EventForwarder.close}.</p>
|
||||
* </ul>
|
||||
*
|
||||
*
|
||||
* <h4 id="threading">Threading and buffering</h3>
|
||||
*
|
||||
* <p>The {@link javax.management.event.EventForwarder#forward
|
||||
* EventForwarder.forward} method may be called in the thread that the
|
||||
* source MBean is using to send its notification. MBeans can expect
|
||||
* that notification sending does not block. Therefore a {@code forward}
|
||||
* method will typically add the notification to a queue, with a separate
|
||||
* thread that takes notifications off the queue and sends them.</p>
|
||||
*
|
||||
* <p>An {@code EventRelay} does not usually need to buffer notifications
|
||||
* before giving them to
|
||||
* {@link javax.management.event.EventReceiver#receive EventReceiver.receive}.
|
||||
* Although by default each such notification will be sent to potentially-slow
|
||||
* listeners, if this is a problem then an {@code Executor} can be given to
|
||||
* the {@code EventClient} constructor to cause the listeners to be called
|
||||
* in a different thread.</p>
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
|
||||
package javax.management.event;
|
@ -1154,21 +1154,29 @@ public class MLet extends java.net.URLClassLoader
|
||||
*/
|
||||
private synchronized String loadLibraryAsResource(String libname) {
|
||||
try {
|
||||
InputStream is = getResourceAsStream(libname.replace(File.separatorChar,'/'));
|
||||
InputStream is = getResourceAsStream(
|
||||
libname.replace(File.separatorChar,'/'));
|
||||
if (is != null) {
|
||||
File directory = new File(libraryDirectory);
|
||||
directory.mkdirs();
|
||||
File file = File.createTempFile(libname + ".", null, directory);
|
||||
file.deleteOnExit();
|
||||
FileOutputStream fileOutput = new FileOutputStream(file);
|
||||
int c;
|
||||
while ((c = is.read()) != -1) {
|
||||
fileOutput.write(c);
|
||||
}
|
||||
is.close();
|
||||
fileOutput.close();
|
||||
if (file.exists()) {
|
||||
return file.getAbsolutePath();
|
||||
try {
|
||||
File directory = new File(libraryDirectory);
|
||||
directory.mkdirs();
|
||||
File file = File.createTempFile(libname + ".", null,
|
||||
directory);
|
||||
file.deleteOnExit();
|
||||
FileOutputStream fileOutput = new FileOutputStream(file);
|
||||
try {
|
||||
int c;
|
||||
while ((c = is.read()) != -1) {
|
||||
fileOutput.write(c);
|
||||
}
|
||||
} finally {
|
||||
fileOutput.close();
|
||||
}
|
||||
if (file.exists()) {
|
||||
return file.getAbsolutePath();
|
||||
}
|
||||
} finally {
|
||||
is.close();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -373,7 +373,7 @@ public class ModelMBeanInfoSupport extends MBeanInfo implements ModelMBeanInfo {
|
||||
"getDescriptors(String)", "Entry");
|
||||
}
|
||||
|
||||
if ((inDescriptorType == null) || (inDescriptorType.isEmpty())) {
|
||||
if ((inDescriptorType == null) || (inDescriptorType.equals(""))) {
|
||||
inDescriptorType = "all";
|
||||
}
|
||||
|
||||
@ -616,7 +616,7 @@ public class ModelMBeanInfoSupport extends MBeanInfo implements ModelMBeanInfo {
|
||||
inDescriptor = new DescriptorSupport();
|
||||
}
|
||||
|
||||
if ((inDescriptorType == null) || (inDescriptorType.isEmpty())) {
|
||||
if ((inDescriptorType == null) || (inDescriptorType.equals(""))) {
|
||||
inDescriptorType =
|
||||
(String) inDescriptor.getFieldValue("descriptorType");
|
||||
|
||||
|
@ -1123,7 +1123,7 @@ public class RequiredModelMBean
|
||||
if (tracing) {
|
||||
MODELMBEAN_LOGGER.logp(Level.FINER,
|
||||
RequiredModelMBean.class.getName(),"resolveMethod",
|
||||
"resolving " + targetClass + "." + opMethodName);
|
||||
"resolving " + targetClass.getName() + "." + opMethodName);
|
||||
}
|
||||
|
||||
final Class[] argClasses;
|
||||
|
@ -108,7 +108,7 @@ public class RelationService extends NotificationBroadcasterSupport
|
||||
// the value HashMap mapping:
|
||||
// <relation id> -> ArrayList of <role name>
|
||||
// to track where a given MBean is referenced.
|
||||
private Map<ObjectName,Map<String,List<String>>>
|
||||
private final Map<ObjectName,Map<String,List<String>>>
|
||||
myRefedMBeanObjName2RelIdsMap =
|
||||
new HashMap<ObjectName,Map<String,List<String>>>();
|
||||
|
||||
@ -1492,7 +1492,7 @@ public class RelationService extends NotificationBroadcasterSupport
|
||||
// Clones the list of notifications to be able to still receive new
|
||||
// notifications while proceeding those ones
|
||||
List<MBeanServerNotification> localUnregNtfList;
|
||||
synchronized(myUnregNtfList) {
|
||||
synchronized(myRefedMBeanObjName2RelIdsMap) {
|
||||
localUnregNtfList =
|
||||
new ArrayList<MBeanServerNotification>(myUnregNtfList);
|
||||
// Resets list
|
||||
|
@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
package javax.management.remote;
|
||||
|
||||
import java.io.ObjectInputStream;
|
||||
import java.util.Set;
|
||||
import javax.management.Attribute;
|
||||
import javax.management.AttributeList;
|
||||
import javax.management.AttributeNotFoundException;
|
||||
import javax.management.InstanceAlreadyExistsException;
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.IntrospectionException;
|
||||
import javax.management.InvalidAttributeValueException;
|
||||
import javax.management.ListenerNotFoundException;
|
||||
import javax.management.MBeanException;
|
||||
import javax.management.MBeanInfo;
|
||||
import javax.management.MBeanRegistrationException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.NotCompliantMBeanException;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectInstance;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.OperationsException;
|
||||
import javax.management.QueryExp;
|
||||
import javax.management.ReflectionException;
|
||||
import javax.management.loading.ClassLoaderRepository;
|
||||
|
||||
/**
|
||||
* An {@link MBeanServerForwarder} that forwards all {@link MBeanServer}
|
||||
* operations unchanged to the next {@code MBeanServer} in the chain.
|
||||
* This class is typically subclassed to override some but not all methods.
|
||||
*/
|
||||
public class IdentityMBeanServerForwarder implements MBeanServerForwarder {
|
||||
|
||||
private MBeanServer next;
|
||||
|
||||
/**
|
||||
* <p>Construct a forwarder that has no next {@code MBeanServer}.
|
||||
* The resulting object will be unusable until {@link #setMBeanServer
|
||||
* setMBeanServer} is called to establish the next item in the chain.</p>
|
||||
*/
|
||||
public IdentityMBeanServerForwarder() {
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Construct a forwarder that forwards to the given {@code MBeanServer}.
|
||||
* It is not an error for {@code next} to be null, but the resulting object
|
||||
* will be unusable until {@link #setMBeanServer setMBeanServer} is called
|
||||
* to establish the next item in the chain.</p>
|
||||
*/
|
||||
public IdentityMBeanServerForwarder(MBeanServer next) {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
public synchronized MBeanServer getMBeanServer() {
|
||||
return next;
|
||||
}
|
||||
|
||||
public synchronized void setMBeanServer(MBeanServer mbs) {
|
||||
next = mbs;
|
||||
}
|
||||
|
||||
private synchronized MBeanServer next() {
|
||||
return next;
|
||||
}
|
||||
|
||||
public void unregisterMBean(ObjectName name)
|
||||
throws InstanceNotFoundException, MBeanRegistrationException {
|
||||
next().unregisterMBean(name);
|
||||
}
|
||||
|
||||
public AttributeList setAttributes(ObjectName name,
|
||||
AttributeList attributes)
|
||||
throws InstanceNotFoundException, ReflectionException {
|
||||
return next().setAttributes(name, attributes);
|
||||
}
|
||||
|
||||
public void setAttribute(ObjectName name, Attribute attribute)
|
||||
throws InstanceNotFoundException, AttributeNotFoundException,
|
||||
InvalidAttributeValueException, MBeanException,
|
||||
ReflectionException {
|
||||
next().setAttribute(name, attribute);
|
||||
}
|
||||
|
||||
public void removeNotificationListener(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException {
|
||||
next().removeNotificationListener(name, listener, filter, handback);
|
||||
}
|
||||
|
||||
public void removeNotificationListener(ObjectName name,
|
||||
NotificationListener listener)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException {
|
||||
next().removeNotificationListener(name, listener);
|
||||
}
|
||||
|
||||
public void removeNotificationListener(ObjectName name, ObjectName listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException {
|
||||
next().removeNotificationListener(name, listener, filter, handback);
|
||||
}
|
||||
|
||||
public void removeNotificationListener(ObjectName name, ObjectName listener)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException {
|
||||
next().removeNotificationListener(name, listener);
|
||||
}
|
||||
|
||||
public ObjectInstance registerMBean(Object object, ObjectName name)
|
||||
throws InstanceAlreadyExistsException, MBeanRegistrationException,
|
||||
NotCompliantMBeanException {
|
||||
return next().registerMBean(object, name);
|
||||
}
|
||||
|
||||
public Set<ObjectName> queryNames(ObjectName name, QueryExp query) {
|
||||
return next().queryNames(name, query);
|
||||
}
|
||||
|
||||
public Set<ObjectInstance> queryMBeans(ObjectName name, QueryExp query) {
|
||||
return next().queryMBeans(name, query);
|
||||
}
|
||||
|
||||
public boolean isRegistered(ObjectName name) {
|
||||
return next().isRegistered(name);
|
||||
}
|
||||
|
||||
public boolean isInstanceOf(ObjectName name, String className)
|
||||
throws InstanceNotFoundException {
|
||||
return next().isInstanceOf(name, className);
|
||||
}
|
||||
|
||||
public Object invoke(ObjectName name, String operationName, Object[] params,
|
||||
String[] signature)
|
||||
throws InstanceNotFoundException, MBeanException,
|
||||
ReflectionException {
|
||||
return next().invoke(name, operationName, params, signature);
|
||||
}
|
||||
|
||||
public Object instantiate(String className, ObjectName loaderName,
|
||||
Object[] params, String[] signature)
|
||||
throws ReflectionException, MBeanException,
|
||||
InstanceNotFoundException {
|
||||
return next().instantiate(className, loaderName, params, signature);
|
||||
}
|
||||
|
||||
public Object instantiate(String className, Object[] params,
|
||||
String[] signature)
|
||||
throws ReflectionException, MBeanException {
|
||||
return next().instantiate(className, params, signature);
|
||||
}
|
||||
|
||||
public Object instantiate(String className, ObjectName loaderName)
|
||||
throws ReflectionException, MBeanException,
|
||||
InstanceNotFoundException {
|
||||
return next().instantiate(className, loaderName);
|
||||
}
|
||||
|
||||
public Object instantiate(String className)
|
||||
throws ReflectionException, MBeanException {
|
||||
return next().instantiate(className);
|
||||
}
|
||||
|
||||
public ObjectInstance getObjectInstance(ObjectName name)
|
||||
throws InstanceNotFoundException {
|
||||
return next().getObjectInstance(name);
|
||||
}
|
||||
|
||||
public MBeanInfo getMBeanInfo(ObjectName name)
|
||||
throws InstanceNotFoundException, IntrospectionException,
|
||||
ReflectionException {
|
||||
return next().getMBeanInfo(name);
|
||||
}
|
||||
|
||||
public Integer getMBeanCount() {
|
||||
return next().getMBeanCount();
|
||||
}
|
||||
|
||||
public String[] getDomains() {
|
||||
return next().getDomains();
|
||||
}
|
||||
|
||||
public String getDefaultDomain() {
|
||||
return next().getDefaultDomain();
|
||||
}
|
||||
|
||||
public ClassLoaderRepository getClassLoaderRepository() {
|
||||
return next().getClassLoaderRepository();
|
||||
}
|
||||
|
||||
public ClassLoader getClassLoaderFor(ObjectName mbeanName)
|
||||
throws InstanceNotFoundException {
|
||||
return next().getClassLoaderFor(mbeanName);
|
||||
}
|
||||
|
||||
public ClassLoader getClassLoader(ObjectName loaderName)
|
||||
throws InstanceNotFoundException {
|
||||
return next().getClassLoader(loaderName);
|
||||
}
|
||||
|
||||
public AttributeList getAttributes(ObjectName name, String[] attributes)
|
||||
throws InstanceNotFoundException, ReflectionException {
|
||||
return next().getAttributes(name, attributes);
|
||||
}
|
||||
|
||||
public Object getAttribute(ObjectName name, String attribute)
|
||||
throws MBeanException, AttributeNotFoundException,
|
||||
InstanceNotFoundException, ReflectionException {
|
||||
return next().getAttribute(name, attribute);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public ObjectInputStream deserialize(String className,
|
||||
ObjectName loaderName,
|
||||
byte[] data)
|
||||
throws InstanceNotFoundException, OperationsException,
|
||||
ReflectionException {
|
||||
return next().deserialize(className, loaderName, data);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public ObjectInputStream deserialize(String className, byte[] data)
|
||||
throws OperationsException, ReflectionException {
|
||||
return next().deserialize(className, data);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public ObjectInputStream deserialize(ObjectName name, byte[] data)
|
||||
throws InstanceNotFoundException, OperationsException {
|
||||
return next().deserialize(name, data);
|
||||
}
|
||||
|
||||
public ObjectInstance createMBean(String className, ObjectName name,
|
||||
ObjectName loaderName, Object[] params,
|
||||
String[] signature)
|
||||
throws ReflectionException, InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException, MBeanException,
|
||||
NotCompliantMBeanException, InstanceNotFoundException {
|
||||
return next().createMBean(className, name, loaderName, params, signature);
|
||||
}
|
||||
|
||||
public ObjectInstance createMBean(String className, ObjectName name,
|
||||
Object[] params, String[] signature)
|
||||
throws ReflectionException, InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException, MBeanException,
|
||||
NotCompliantMBeanException {
|
||||
return next().createMBean(className, name, params, signature);
|
||||
}
|
||||
|
||||
public ObjectInstance createMBean(String className, ObjectName name,
|
||||
ObjectName loaderName)
|
||||
throws ReflectionException, InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException, MBeanException,
|
||||
NotCompliantMBeanException, InstanceNotFoundException {
|
||||
return next().createMBean(className, name, loaderName);
|
||||
}
|
||||
|
||||
public ObjectInstance createMBean(String className, ObjectName name)
|
||||
throws ReflectionException, InstanceAlreadyExistsException,
|
||||
MBeanRegistrationException, MBeanException,
|
||||
NotCompliantMBeanException {
|
||||
return next().createMBean(className, name);
|
||||
}
|
||||
|
||||
public void addNotificationListener(ObjectName name, ObjectName listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException {
|
||||
next().addNotificationListener(name, listener, filter, handback);
|
||||
}
|
||||
|
||||
public void addNotificationListener(ObjectName name,
|
||||
NotificationListener listener,
|
||||
NotificationFilter filter,
|
||||
Object handback)
|
||||
throws InstanceNotFoundException {
|
||||
next().addNotificationListener(name, listener, filter, handback);
|
||||
}
|
||||
}
|
@ -57,6 +57,26 @@ public interface JMXConnector extends Closeable {
|
||||
public static final String CREDENTIALS =
|
||||
"jmx.remote.credentials";
|
||||
|
||||
/**
|
||||
* <p>Name of the attribute that specifies whether to use the
|
||||
* {@linkplain javax.management.event Event Service} to handle
|
||||
* notifications for this connector. The value associated with
|
||||
* this attribute, if any, is a String, which must be equal,
|
||||
* ignoring case, to {@code "true"} or {@code "false"}.</p>
|
||||
*
|
||||
* <p>Not all connectors will understand this attribute, but the
|
||||
* standard {@linkplain javax.management.remote.rmi.RMIConnector
|
||||
* RMI Connector} does.</p>
|
||||
*
|
||||
* <p>If this attribute is not present, then the system property of the
|
||||
* same name (<code>{@value}</code>) is consulted. If that is not set
|
||||
* either, then the Event Service is not used.</p>
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public static final String USE_EVENT_SERVICE =
|
||||
"jmx.remote.use.event.service";
|
||||
|
||||
/**
|
||||
* <p>Establishes the connection to the connector server. This
|
||||
* method is equivalent to {@link #connect(Map)
|
||||
|
@ -26,17 +26,21 @@
|
||||
|
||||
package javax.management.remote;
|
||||
|
||||
import com.sun.jmx.remote.util.EnvHelp;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import javax.management.MBeanInfo; // for javadoc
|
||||
import javax.management.MBeanNotificationInfo;
|
||||
import javax.management.MBeanRegistration;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.EventClientDelegate;
|
||||
|
||||
/**
|
||||
* <p>Superclass of every connector server. A connector server is
|
||||
@ -75,6 +79,48 @@ public abstract class JMXConnectorServer
|
||||
public static final String AUTHENTICATOR =
|
||||
"jmx.remote.authenticator";
|
||||
|
||||
/**
|
||||
* <p>Name of the attribute that specifies whether this connector
|
||||
* server can delegate notification handling to the
|
||||
* {@linkplain javax.management.event Event Service}.
|
||||
* The value associated with
|
||||
* this attribute, if any, is a String, which must be equal,
|
||||
* ignoring case, to {@code "true"} or {@code "false"}.</p>
|
||||
*
|
||||
* <p>Not all connector servers will understand this attribute, but the
|
||||
* standard {@linkplain javax.management.remote.rmi.RMIConnectorServer
|
||||
* RMI Connector Server} does.</p>
|
||||
*
|
||||
* <p>If this attribute is not present, then the system property of the
|
||||
* same name (<code>{@value}</code>) is consulted. If that is not set
|
||||
* either, then the Event Service is used if the connector server
|
||||
* supports it.</p>
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public static final String DELEGATE_TO_EVENT_SERVICE =
|
||||
"jmx.remote.delegate.event.service";
|
||||
|
||||
/**
|
||||
* <p>Name of the attribute that specifies whether this connector
|
||||
* server simulates the existence of the {@link EventClientDelegate}
|
||||
* MBean. The value associated with this attribute, if any, must
|
||||
* be a string that is equal to {@code "true"} or {@code "false"},
|
||||
* ignoring case. If it is {@code "true"}, then the connector server
|
||||
* will simulate an EventClientDelegate MBean, as described in {@link
|
||||
* EventClientDelegate#newForwarder}. This MBean is needed for {@link
|
||||
* javax.management.event.EventClient EventClient} to function correctly.</p>
|
||||
*
|
||||
* <p>Not all connector servers will understand this attribute, but the
|
||||
* standard {@linkplain javax.management.remote.rmi.RMIConnectorServer
|
||||
* RMI Connector Server} does. For a connector server that understands
|
||||
* this attribute, the default value is {@code "true"}.</p>
|
||||
*
|
||||
* @since 1.7
|
||||
*/
|
||||
public static final String EVENT_CLIENT_DELEGATE_FORWARDER =
|
||||
"jmx.remote.event.client.delegate.forwarder";
|
||||
|
||||
/**
|
||||
* <p>Constructs a connector server that will be registered as an
|
||||
* MBean in the MBean server it is attached to. This constructor
|
||||
@ -89,34 +135,274 @@ public abstract class JMXConnectorServer
|
||||
/**
|
||||
* <p>Constructs a connector server that is attached to the given
|
||||
* MBean server. A connector server that is created in this way
|
||||
* can be registered in a different MBean server.</p>
|
||||
* can be registered in a different MBean server, or not registered
|
||||
* in any MBean server.</p>
|
||||
*
|
||||
* @param mbeanServer the MBean server that this connector server
|
||||
* is attached to. Null if this connector server will be attached
|
||||
* to an MBean server by being registered in it.
|
||||
*/
|
||||
public JMXConnectorServer(MBeanServer mbeanServer) {
|
||||
this.mbeanServer = mbeanServer;
|
||||
insertUserMBeanServer(mbeanServer);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the MBean server that this connector server is
|
||||
* attached to.</p>
|
||||
* attached to, or the first in a chain of user-added
|
||||
* {@link MBeanServerForwarder}s, if any.</p>
|
||||
*
|
||||
* @return the MBean server that this connector server is attached
|
||||
* to, or null if it is not yet attached to an MBean server.
|
||||
*
|
||||
* @see #setMBeanServerForwarder
|
||||
* @see #getSystemMBeanServer
|
||||
*/
|
||||
public synchronized MBeanServer getMBeanServer() {
|
||||
return mbeanServer;
|
||||
return userMBeanServer;
|
||||
}
|
||||
|
||||
public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf)
|
||||
{
|
||||
public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
|
||||
if (mbsf == null)
|
||||
throw new IllegalArgumentException("Invalid null argument: mbsf");
|
||||
|
||||
if (mbeanServer != null) mbsf.setMBeanServer(mbeanServer);
|
||||
mbeanServer = mbsf;
|
||||
if (userMBeanServer != null)
|
||||
mbsf.setMBeanServer(userMBeanServer);
|
||||
insertUserMBeanServer(mbsf);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Remove a forwarder from the chain of forwarders. The forwarder can
|
||||
* be in the system chain or the user chain. On successful return from
|
||||
* this method, the first occurrence in the chain of an object that is
|
||||
* {@linkplain Object#equals equal} to {@code mbsf} will have been
|
||||
* removed.</p>
|
||||
* @param mbsf the forwarder to remove
|
||||
* @throws NoSuchElementException if there is no occurrence of {@code mbsf}
|
||||
* in the chain.
|
||||
* @throws IllegalArgumentException if {@code mbsf} is null.
|
||||
*/
|
||||
public synchronized void removeMBeanServerForwarder(MBeanServerForwarder mbsf) {
|
||||
if (mbsf == null)
|
||||
throw new IllegalArgumentException("Invalid null argument: mbsf");
|
||||
|
||||
MBeanServerForwarder prev = null;
|
||||
MBeanServer curr = systemMBeanServer;
|
||||
while (curr instanceof MBeanServerForwarder && !mbsf.equals(curr)) {
|
||||
prev = (MBeanServerForwarder) curr;
|
||||
curr = prev.getMBeanServer();
|
||||
}
|
||||
if (!(curr instanceof MBeanServerForwarder))
|
||||
throw new NoSuchElementException("MBeanServerForwarder not in chain");
|
||||
MBeanServerForwarder deleted = (MBeanServerForwarder) curr;
|
||||
MBeanServer next = deleted.getMBeanServer();
|
||||
if (prev != null)
|
||||
prev.setMBeanServer(next);
|
||||
if (systemMBeanServer == deleted)
|
||||
systemMBeanServer = next;
|
||||
if (userMBeanServer == deleted)
|
||||
userMBeanServer = next;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set userMBeanServer to mbs and arrange for the end of the chain of
|
||||
* system MBeanServerForwarders to point to it. See the comment before
|
||||
* the systemMBeanServer and userMBeanServer field declarations.
|
||||
*/
|
||||
private void insertUserMBeanServer(MBeanServer mbs) {
|
||||
MBeanServerForwarder lastSystemMBSF = null;
|
||||
for (MBeanServer mbsi = systemMBeanServer;
|
||||
mbsi != userMBeanServer;
|
||||
mbsi = lastSystemMBSF.getMBeanServer()) {
|
||||
lastSystemMBSF = (MBeanServerForwarder) mbsi;
|
||||
}
|
||||
userMBeanServer = mbs;
|
||||
if (lastSystemMBSF == null)
|
||||
systemMBeanServer = mbs;
|
||||
else
|
||||
lastSystemMBSF.setMBeanServer(mbs);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns the first item in the chain of system and then user
|
||||
* forwarders. In the simplest case, a {@code JMXConnectorServer}
|
||||
* is connected directly to an {@code MBeanServer}. But there can
|
||||
* also be a chain of {@link MBeanServerForwarder}s between the two.
|
||||
* This chain consists of two sub-chains: first the <em>system chain</em>
|
||||
* and then the <em>user chain</em>. Incoming requests are given to the
|
||||
* first forwarder in the system chain. Each forwarder can handle
|
||||
* a request itself, or more usually forward it to the next forwarder,
|
||||
* perhaps with some extra behavior such as logging or security
|
||||
* checking before or after the forwarding. The last forwarder in
|
||||
* the system chain is followed by the first forwarder in the user
|
||||
* chain.</p>
|
||||
*
|
||||
* <p>The <em>system chain</em> is usually
|
||||
* defined by a connector server based on the environment Map;
|
||||
* see {@link JMXConnectorServerFactory#newJMXConnectorServer}. Allowing the
|
||||
* connector server to define its forwarders in this way ensures that
|
||||
* they are in the correct order - some forwarders need to be inserted
|
||||
* before others for correct behavior. It is possible to modify the
|
||||
* system chain, for example using {@link #setSystemMBeanServerForwarder} or
|
||||
* {@link #removeMBeanServerForwarder}, but in that case the system
|
||||
* chain is no longer guaranteed to be correct.</p>
|
||||
*
|
||||
* <p>The <em>user chain</em> is defined by calling {@link
|
||||
* #setMBeanServerForwarder} to insert forwarders at the head of the user
|
||||
* chain.</p>
|
||||
*
|
||||
* <p>If there are no forwarders in either chain, then both
|
||||
* {@link #getMBeanServer()} and {@code getSystemMBeanServer()} will
|
||||
* return the {@code MBeanServer} for this connector server. If there
|
||||
* are forwarders in the user chain but not the system chain, then
|
||||
* both methods will return the first forwarder in the user chain.
|
||||
* If there are forwarders in the system chain but not the user chain,
|
||||
* then {@code getSystemMBeanServer()} will return the first forwarder
|
||||
* in the system chain, and {@code getMBeanServer()} will return the
|
||||
* {@code MBeanServer} for this connector server. Finally, if there
|
||||
* are forwarders in each chain then {@code getSystemMBeanServer()}
|
||||
* will return the first forwarder in the system chain, and {@code
|
||||
* getMBeanServer()} will return the first forwarder in the user chain.</p>
|
||||
*
|
||||
* <p>This code illustrates how the chains can be traversed:</p>
|
||||
*
|
||||
* <pre>
|
||||
* JMXConnectorServer cs;
|
||||
* System.out.println("system chain:");
|
||||
* MBeanServer mbs = cs.getSystemMBeanServer();
|
||||
* while (true) {
|
||||
* if (mbs == cs.getMBeanServer())
|
||||
* System.out.println("user chain:");
|
||||
* if (!(mbs instanceof MBeanServerForwarder))
|
||||
* break;
|
||||
* MBeanServerForwarder mbsf = (MBeanServerForwarder) mbs;
|
||||
* System.out.println("--forwarder: " + mbsf);
|
||||
* mbs = mbsf.getMBeanServer();
|
||||
* }
|
||||
* System.out.println("--MBean Server");
|
||||
* </pre>
|
||||
*
|
||||
* @return the first item in the system chain of forwarders.
|
||||
*
|
||||
* @see #setSystemMBeanServerForwarder
|
||||
*/
|
||||
public synchronized MBeanServer getSystemMBeanServer() {
|
||||
return systemMBeanServer;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Inserts an object that intercepts requests for the MBean server
|
||||
* that arrive through this connector server. This object will be
|
||||
* supplied as the <code>MBeanServer</code> for any new connection
|
||||
* created by this connector server. Existing connections are
|
||||
* unaffected.</p>
|
||||
*
|
||||
* <p>This method can be called more than once with different
|
||||
* {@link MBeanServerForwarder} objects. The result is a chain
|
||||
* of forwarders. The last forwarder added is the first in the chain.</p>
|
||||
*
|
||||
* <p>This method modifies the system chain of {@link MBeanServerForwarder}s.
|
||||
* Usually user code should change the user chain instead, via
|
||||
* {@link #setMBeanServerForwarder}.</p>
|
||||
*
|
||||
* <p>Not all connector servers support a system chain of forwarders.
|
||||
* Calling this method on a connector server that does not will produce an
|
||||
* {@link UnsupportedOperationException}.</p>
|
||||
*
|
||||
* <p>Suppose {@code mbs} is the result of {@link #getSystemMBeanServer()}
|
||||
* before calling this method. If {@code mbs} is not null, then
|
||||
* {@code mbsf.setMBeanServer(mbs)} will be called. If doing so
|
||||
* produces an exception, this method throws the same exception without
|
||||
* any other effect. If {@code mbs} is null, or if the call to
|
||||
* {@code mbsf.setMBeanServer(mbs)} succeeds, then this method will
|
||||
* return normally and {@code getSystemMBeanServer()} will then return
|
||||
* {@code mbsf}.</p>
|
||||
*
|
||||
* <p>The result of {@link #getMBeanServer()} is unchanged by this method.</p>
|
||||
*
|
||||
* @param mbsf the new <code>MBeanServerForwarder</code>.
|
||||
*
|
||||
* @throws IllegalArgumentException if the call to {@link
|
||||
* MBeanServerForwarder#setMBeanServer mbsf.setMBeanServer} fails
|
||||
* with <code>IllegalArgumentException</code>, or if
|
||||
* <code>mbsf</code> is null.
|
||||
*
|
||||
* @throws UnsupportedOperationException if
|
||||
* {@link #supportsSystemMBeanServerForwarder} returns false.
|
||||
*
|
||||
* @see #getSystemMBeanServer()
|
||||
*/
|
||||
public synchronized void setSystemMBeanServerForwarder(
|
||||
MBeanServerForwarder mbsf) {
|
||||
if (mbsf == null)
|
||||
throw new IllegalArgumentException("Invalid null argument: mbsf");
|
||||
mustSupportSystemMBSF();
|
||||
|
||||
if (systemMBeanServer != null)
|
||||
mbsf.setMBeanServer(systemMBeanServer);
|
||||
systemMBeanServer = mbsf;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns true if this connector server supports a system chain of
|
||||
* {@link MBeanServerForwarder}s. The default implementation of this
|
||||
* method returns false. Connector servers that do support the system
|
||||
* chain must override this method to return true.
|
||||
*
|
||||
* @return true if this connector server supports the system chain of
|
||||
* forwarders.
|
||||
*/
|
||||
public boolean supportsSystemMBeanServerForwarder() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void mustSupportSystemMBSF() {
|
||||
if (!supportsSystemMBeanServerForwarder()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"System MBeanServerForwarder not supported by this " +
|
||||
"connector server");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Install {@link MBeanServerForwarder}s in the system chain
|
||||
* based on the attributes in the given {@code Map}. A connector
|
||||
* server that {@linkplain #supportsSystemMBeanServerForwarder supports}
|
||||
* a system chain of {@code MBeanServerForwarder}s can call this method
|
||||
* to add forwarders to that chain based on the contents of {@code env}.
|
||||
* In order:</p>
|
||||
*
|
||||
* <ul>
|
||||
*
|
||||
* <li>If {@link #EVENT_CLIENT_DELEGATE_FORWARDER} is absent, or is
|
||||
* present with the value {@code "true"}, then a forwarder with the
|
||||
* functionality of {@link EventClientDelegate#newForwarder} is inserted
|
||||
* at the start of the system chain.</li>
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
* <p>For {@code EVENT_CLIENT_DELEGATE_FORWARDER}, if the
|
||||
* attribute is absent from the {@code Map} and a system property
|
||||
* of the same name is defined, then the value of the system
|
||||
* property is used as if it were in the {@code Map}.
|
||||
*
|
||||
* <p>Attributes in {@code env} that are not listed above are ignored
|
||||
* by this method.</p>
|
||||
*
|
||||
* @throws UnsupportedOperationException if {@link
|
||||
* #supportsSystemMBeanServerForwarder} is false.
|
||||
*/
|
||||
protected void installStandardForwarders(Map<String, ?> env) {
|
||||
mustSupportSystemMBSF();
|
||||
|
||||
// Remember that forwarders must be added in reverse order!
|
||||
|
||||
boolean ecd = EnvHelp.computeBooleanFromString(
|
||||
env, EVENT_CLIENT_DELEGATE_FORWARDER, false, true);
|
||||
|
||||
if (ecd) {
|
||||
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
|
||||
setSystemMBeanServerForwarder(mbsf);
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getConnectionIds() {
|
||||
@ -359,8 +645,8 @@ public abstract class JMXConnectorServer
|
||||
ObjectName name) {
|
||||
if (mbs == null || name == null)
|
||||
throw new NullPointerException("Null MBeanServer or ObjectName");
|
||||
if (mbeanServer == null) {
|
||||
mbeanServer = mbs;
|
||||
if (userMBeanServer == null) {
|
||||
insertUserMBeanServer(mbs);
|
||||
myName = name;
|
||||
}
|
||||
return name;
|
||||
@ -394,10 +680,53 @@ public abstract class JMXConnectorServer
|
||||
myName = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The MBeanServer used by this server to execute a client request.
|
||||
/*
|
||||
* Fields describing the chains of forwarders (MBeanServerForwarders).
|
||||
* In the general case, the forwarders look something like this:
|
||||
*
|
||||
* systemMBeanServer userMBeanServer
|
||||
* | |
|
||||
* v v
|
||||
* mbsf1 -> mbsf2 -> mbsf3 -> mbsf4 -> mbsf5 -> mbs
|
||||
*
|
||||
* Here, each mbsfi is an MBeanServerForwarder, and the arrows
|
||||
* illustrate its getMBeanServer() method. The last MBeanServerForwarder
|
||||
* can point to an MBeanServer that is not instanceof MBeanServerForwarder,
|
||||
* here mbs.
|
||||
*
|
||||
* Initially, the chain can be empty if this JMXConnectorServer was
|
||||
* constructed without an MBeanServer. In this case, both systemMBS
|
||||
* and userMBS will be null. If there is initially an MBeanServer,
|
||||
* then both systemMBS and userMBS will point to it.
|
||||
*
|
||||
* Whenever userMBS is changed, the system chain must be updated. If there
|
||||
* are forwarders in the system chain (between systemMBS and userMBS in the
|
||||
* picture above), then the last one must point to the old value of userMBS
|
||||
* (possibly null). It must be updated to point to the new value. If there
|
||||
* are no forwarders in the system chain, then systemMBS must be updated to
|
||||
* the new value of userMBS. The invariant is that starting from systemMBS
|
||||
* and repeatedly calling MBSF.getMBeanServer() you will end up at
|
||||
* userMBS. The implication is that you will not see any MBeanServer
|
||||
* object on the way that is not also an MBeanServerForwarder.
|
||||
*
|
||||
* The method insertUserMBeanServer contains the logic to change userMBS
|
||||
* and adjust the system chain appropriately.
|
||||
*
|
||||
* If userMBS is null and this JMXConnectorServer is registered in an
|
||||
* MBeanServer, then userMBS becomes that MBeanServer, and the system
|
||||
* chain must be updated as just described.
|
||||
*
|
||||
* When systemMBS is updated, there is no effect on userMBS. The system
|
||||
* chain may contain forwarders even though the user chain is empty
|
||||
* (there is no MBeanServer). In that case an attempt to forward an
|
||||
* incoming request through the chain will fall off the end and fail with a
|
||||
* NullPointerException. Usually a connector server will refuse to start()
|
||||
* if it is not attached to an MBS, so this situation should not arise.
|
||||
*/
|
||||
private MBeanServer mbeanServer = null;
|
||||
|
||||
private MBeanServer userMBeanServer;
|
||||
|
||||
private MBeanServer systemMBeanServer;
|
||||
|
||||
/**
|
||||
* The name used to registered this server in an MBeanServer.
|
||||
|
@ -35,10 +35,8 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
|
||||
/**
|
||||
* <p>Factory to create JMX API connector servers. There
|
||||
@ -172,7 +170,8 @@ public class JMXConnectorServerFactory {
|
||||
* loader MBean name. This class loader is used to deserialize objects in
|
||||
* requests received from the client, possibly after consulting an
|
||||
* MBean-specific class loader. The value associated with this
|
||||
* attribute is an instance of {@link ObjectName}.</p>
|
||||
* attribute is an instance of {@link javax.management.ObjectName
|
||||
* ObjectName}.</p>
|
||||
*/
|
||||
public static final String DEFAULT_CLASS_LOADER_NAME =
|
||||
"jmx.remote.default.class.loader.name";
|
||||
|
@ -105,23 +105,34 @@ public interface JMXConnectorServerMBean {
|
||||
public boolean isActive();
|
||||
|
||||
/**
|
||||
* <p>Adds an object that intercepts requests for the MBean server
|
||||
* <p>Inserts an object that intercepts requests for the MBean server
|
||||
* that arrive through this connector server. This object will be
|
||||
* supplied as the <code>MBeanServer</code> for any new connection
|
||||
* created by this connector server. Existing connections are
|
||||
* unaffected.</p>
|
||||
*
|
||||
* <p>If this connector server is already associated with an
|
||||
* <p>This method can be called more than once with different
|
||||
* {@link MBeanServerForwarder} objects. The result is a chain
|
||||
* of forwarders. The last forwarder added is the first in the chain.
|
||||
* In more detail:</p>
|
||||
*
|
||||
* <ul>
|
||||
* <li><p>If this connector server is already associated with an
|
||||
* <code>MBeanServer</code> object, then that object is given to
|
||||
* {@link MBeanServerForwarder#setMBeanServer
|
||||
* mbsf.setMBeanServer}. If doing so produces an exception, this
|
||||
* method throws the same exception without any other effect.</p>
|
||||
*
|
||||
* <p>If this connector is not already associated with an
|
||||
* <li><p>If this connector is not already associated with an
|
||||
* <code>MBeanServer</code> object, or if the
|
||||
* <code>mbsf.setMBeanServer</code> call just mentioned succeeds,
|
||||
* then <code>mbsf</code> becomes this connector server's
|
||||
* <code>MBeanServer</code>.</p>
|
||||
* </ul>
|
||||
*
|
||||
* <p>A connector server may support two chains of forwarders,
|
||||
* a system chain and a user chain. See {@link
|
||||
* JMXConnectorServer#setSystemMBeanServerForwarder} for details.</p>
|
||||
*
|
||||
* @param mbsf the new <code>MBeanServerForwarder</code>.
|
||||
*
|
||||
@ -129,6 +140,8 @@ public interface JMXConnectorServerMBean {
|
||||
* MBeanServerForwarder#setMBeanServer mbsf.setMBeanServer} fails
|
||||
* with <code>IllegalArgumentException</code>. This includes the
|
||||
* case where <code>mbsf</code> is null.
|
||||
*
|
||||
* @see JMXConnectorServer#setSystemMBeanServerForwarder
|
||||
*/
|
||||
public void setMBeanServerForwarder(MBeanServerForwarder mbsf);
|
||||
|
||||
|
@ -25,10 +25,12 @@
|
||||
|
||||
package javax.management.remote.rmi;
|
||||
|
||||
import com.sun.jmx.mbeanserver.Util;
|
||||
import static com.sun.jmx.mbeanserver.Util.cast;
|
||||
import com.sun.jmx.remote.internal.ServerCommunicatorAdmin;
|
||||
import com.sun.jmx.remote.internal.ServerNotifForwarder;
|
||||
import com.sun.jmx.remote.security.JMXSubjectDomainCombiner;
|
||||
import com.sun.jmx.remote.security.NotificationAccessController;
|
||||
import com.sun.jmx.remote.security.SubjectDelegator;
|
||||
import com.sun.jmx.remote.util.ClassLoaderWithRepository;
|
||||
import com.sun.jmx.remote.util.ClassLogger;
|
||||
@ -36,6 +38,7 @@ import com.sun.jmx.remote.util.EnvHelp;
|
||||
import com.sun.jmx.remote.util.OrderClassLoaders;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.rmi.MarshalledObject;
|
||||
import java.rmi.UnmarshalException;
|
||||
import java.rmi.server.Unreferenced;
|
||||
@ -56,19 +59,24 @@ import javax.management.InstanceAlreadyExistsException;
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.IntrospectionException;
|
||||
import javax.management.InvalidAttributeValueException;
|
||||
import javax.management.JMX;
|
||||
import javax.management.ListenerNotFoundException;
|
||||
import javax.management.MBeanException;
|
||||
import javax.management.MBeanInfo;
|
||||
import javax.management.MBeanRegistrationException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.NotCompliantMBeanException;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.ObjectInstance;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.QueryExp;
|
||||
import javax.management.ReflectionException;
|
||||
import javax.management.RuntimeOperationsException;
|
||||
import javax.management.loading.ClassLoaderRepository;
|
||||
import javax.management.event.EventClientDelegate;
|
||||
import javax.management.event.EventClientDelegateMBean;
|
||||
import javax.management.event.EventClientNotFoundException;
|
||||
import javax.management.event.FetchingEventForwarder;
|
||||
import javax.management.remote.JMXServerErrorException;
|
||||
import javax.management.remote.NotificationResult;
|
||||
import javax.management.remote.TargetedNotification;
|
||||
@ -149,28 +157,16 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
|
||||
new PrivilegedAction<ClassLoaderWithRepository>() {
|
||||
public ClassLoaderWithRepository run() {
|
||||
return new ClassLoaderWithRepository(
|
||||
getClassLoaderRepository(),
|
||||
dcl);
|
||||
mbeanServer.getClassLoaderRepository(),
|
||||
dcl);
|
||||
}
|
||||
});
|
||||
|
||||
serverCommunicatorAdmin = new
|
||||
RMIServerCommunicatorAdmin(EnvHelp.getServerConnectionTimeout(env));
|
||||
|
||||
this.env = env;
|
||||
}
|
||||
|
||||
private synchronized ServerNotifForwarder getServerNotifFwd() {
|
||||
// Lazily created when first use. Mainly when
|
||||
// addNotificationListener is first called.
|
||||
if (serverNotifForwarder == null)
|
||||
serverNotifForwarder =
|
||||
new ServerNotifForwarder(mbeanServer,
|
||||
env,
|
||||
rmiServer.getNotifBuffer(),
|
||||
connectionId);
|
||||
return serverNotifForwarder;
|
||||
}
|
||||
|
||||
public String getConnectionId() throws IOException {
|
||||
// We should call reqIncomming() here... shouldn't we?
|
||||
@ -181,6 +177,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
|
||||
final boolean debug = logger.debugOn();
|
||||
final String idstr = (debug?"["+this.toString()+"]":null);
|
||||
|
||||
final SubscriptionManager mgr;
|
||||
synchronized (this) {
|
||||
if (terminated) {
|
||||
if (debug) logger.debug("close",idstr + " already terminated.");
|
||||
@ -195,11 +192,12 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
|
||||
serverCommunicatorAdmin.terminate();
|
||||
}
|
||||
|
||||
if (serverNotifForwarder != null) {
|
||||
serverNotifForwarder.terminate();
|
||||
}
|
||||
mgr = subscriptionManager;
|
||||
subscriptionManager = null;
|
||||
}
|
||||
|
||||
if (mgr != null) mgr.terminate();
|
||||
|
||||
rmiServer.clientClosed(this);
|
||||
|
||||
if (debug) logger.debug("close",idstr + " closed.");
|
||||
@ -955,8 +953,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
|
||||
int i=0;
|
||||
ClassLoader targetCl;
|
||||
NotificationFilter[] filterValues =
|
||||
new NotificationFilter[names.length];
|
||||
Object params[];
|
||||
new NotificationFilter[names.length];
|
||||
Integer[] ids = new Integer[names.length];
|
||||
final boolean debug=logger.debugOn();
|
||||
|
||||
@ -991,8 +988,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
|
||||
// remove all registered listeners
|
||||
for (int j=0; j<i; j++) {
|
||||
try {
|
||||
getServerNotifFwd().removeNotificationListener(names[j],
|
||||
ids[j]);
|
||||
doRemoveListener(names[j],ids[j]);
|
||||
} catch (Exception eee) {
|
||||
// strange
|
||||
}
|
||||
@ -1240,21 +1236,298 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
|
||||
final long csn = clientSequenceNumber;
|
||||
final int mn = maxNotifications;
|
||||
final long t = timeout;
|
||||
PrivilegedAction<NotificationResult> action =
|
||||
new PrivilegedAction<NotificationResult>() {
|
||||
public NotificationResult run() {
|
||||
return getServerNotifFwd().fetchNotifs(csn, t, mn);
|
||||
|
||||
final PrivilegedExceptionAction<NotificationResult> action =
|
||||
new PrivilegedExceptionAction<NotificationResult>() {
|
||||
public NotificationResult run() throws IOException {
|
||||
return doFetchNotifs(csn, t, mn);
|
||||
}
|
||||
};
|
||||
if (acc == null)
|
||||
return action.run();
|
||||
else
|
||||
return AccessController.doPrivileged(action, acc);
|
||||
try {
|
||||
if (acc == null)
|
||||
return action.run();
|
||||
else
|
||||
return AccessController.doPrivileged(action, acc);
|
||||
} catch (IOException x) {
|
||||
throw x;
|
||||
} catch (RuntimeException x) {
|
||||
throw x;
|
||||
} catch (Exception x) {
|
||||
// should not happen
|
||||
throw new UndeclaredThrowableException(x);
|
||||
}
|
||||
|
||||
} finally {
|
||||
serverCommunicatorAdmin.rspOutgoing();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is an abstraction class that let us use the legacy
|
||||
* ServerNotifForwarder and the new EventClientDelegateMBean
|
||||
* indifferently.
|
||||
**/
|
||||
private static interface SubscriptionManager {
|
||||
public void removeNotificationListener(ObjectName name, Integer id)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException, IOException;
|
||||
public void removeNotificationListener(ObjectName name, Integer[] ids)
|
||||
throws Exception;
|
||||
public NotificationResult fetchNotifications(long csn, long timeout, int maxcount)
|
||||
throws IOException;
|
||||
public Integer addNotificationListener(ObjectName name, NotificationFilter filter)
|
||||
throws InstanceNotFoundException, IOException;
|
||||
public void terminate()
|
||||
throws IOException;
|
||||
}
|
||||
|
||||
/**
|
||||
* A SubscriptionManager that uses a ServerNotifForwarder.
|
||||
**/
|
||||
private static class LegacySubscriptionManager implements SubscriptionManager {
|
||||
private final ServerNotifForwarder forwarder;
|
||||
LegacySubscriptionManager(ServerNotifForwarder forwarder) {
|
||||
this.forwarder = forwarder;
|
||||
}
|
||||
|
||||
public void removeNotificationListener(ObjectName name, Integer id)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException,
|
||||
IOException {
|
||||
forwarder.removeNotificationListener(name,id);
|
||||
}
|
||||
|
||||
public void removeNotificationListener(ObjectName name, Integer[] ids)
|
||||
throws Exception {
|
||||
forwarder.removeNotificationListener(name,ids);
|
||||
}
|
||||
|
||||
public NotificationResult fetchNotifications(long csn, long timeout, int maxcount) {
|
||||
return forwarder.fetchNotifs(csn,timeout,maxcount);
|
||||
}
|
||||
|
||||
public Integer addNotificationListener(ObjectName name,
|
||||
NotificationFilter filter)
|
||||
throws InstanceNotFoundException, IOException {
|
||||
return forwarder.addNotificationListener(name,filter);
|
||||
}
|
||||
|
||||
public void terminate() {
|
||||
forwarder.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A SubscriptionManager that uses an EventClientDelegateMBean.
|
||||
**/
|
||||
private static class EventSubscriptionManager
|
||||
implements SubscriptionManager {
|
||||
private final MBeanServer mbeanServer;
|
||||
private final EventClientDelegateMBean delegate;
|
||||
private final NotificationAccessController notifAC;
|
||||
private final boolean checkNotificationEmission;
|
||||
private final String clientId;
|
||||
private final String connectionId;
|
||||
|
||||
EventSubscriptionManager(
|
||||
MBeanServer mbeanServer,
|
||||
EventClientDelegateMBean delegate,
|
||||
Map<String, ?> env,
|
||||
String clientId,
|
||||
String connectionId) {
|
||||
this.mbeanServer = mbeanServer;
|
||||
this.delegate = delegate;
|
||||
this.notifAC = EnvHelp.getNotificationAccessController(env);
|
||||
this.checkNotificationEmission =
|
||||
EnvHelp.computeBooleanFromString(
|
||||
env, "jmx.remote.x.check.notification.emission", false);
|
||||
this.clientId = clientId;
|
||||
this.connectionId = connectionId;
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial") // no serialVersionUID
|
||||
private class AccessControlFilter implements NotificationFilter {
|
||||
private final NotificationFilter wrapped;
|
||||
private final ObjectName name;
|
||||
|
||||
AccessControlFilter(ObjectName name, NotificationFilter wrapped) {
|
||||
this.name = name;
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
public boolean isNotificationEnabled(Notification notification) {
|
||||
try {
|
||||
if (checkNotificationEmission) {
|
||||
ServerNotifForwarder.checkMBeanPermission(
|
||||
mbeanServer, name, "addNotificationListener");
|
||||
}
|
||||
notifAC.fetchNotification(
|
||||
connectionId, name, notification, getSubject());
|
||||
return (wrapped == null) ? true :
|
||||
wrapped.isNotificationEnabled(notification);
|
||||
} catch (InstanceNotFoundException e) {
|
||||
return false;
|
||||
} catch (SecurityException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Integer addNotificationListener(
|
||||
ObjectName name, NotificationFilter filter)
|
||||
throws InstanceNotFoundException, IOException {
|
||||
if (notifAC != null) {
|
||||
notifAC.addNotificationListener(connectionId, name, getSubject());
|
||||
filter = new AccessControlFilter(name, filter);
|
||||
}
|
||||
try {
|
||||
return delegate.addListener(clientId,name,filter);
|
||||
} catch (EventClientNotFoundException x) {
|
||||
throw new IOException("Unknown clientId: "+clientId,x);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeNotificationListener(ObjectName name, Integer id)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException,
|
||||
IOException {
|
||||
if (notifAC != null)
|
||||
notifAC.removeNotificationListener(connectionId, name, getSubject());
|
||||
try {
|
||||
delegate.removeListenerOrSubscriber(clientId,id);
|
||||
} catch (EventClientNotFoundException x) {
|
||||
throw new IOException("Unknown clientId: "+clientId,x);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeNotificationListener(ObjectName name, Integer[] ids)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException,
|
||||
IOException {
|
||||
if (notifAC != null)
|
||||
notifAC.removeNotificationListener(connectionId, name, getSubject());
|
||||
try {
|
||||
for (Integer id : ids)
|
||||
delegate.removeListenerOrSubscriber(clientId,id);
|
||||
} catch (EventClientNotFoundException x) {
|
||||
throw new IOException("Unknown clientId: "+clientId,x);
|
||||
}
|
||||
}
|
||||
|
||||
public NotificationResult fetchNotifications(long csn, long timeout,
|
||||
int maxcount)
|
||||
throws IOException {
|
||||
try {
|
||||
// For some reason the delegate doesn't accept a negative
|
||||
// sequence number. However legacy clients will always call
|
||||
// fetchNotifications with a negative sequence number, when
|
||||
// they call it for the first time.
|
||||
// In that case, we will use 0 instead.
|
||||
//
|
||||
return delegate.fetchNotifications(
|
||||
clientId, Math.max(csn, 0), maxcount, timeout);
|
||||
} catch (EventClientNotFoundException x) {
|
||||
throw new IOException("Unknown clientId: "+clientId,x);
|
||||
}
|
||||
}
|
||||
|
||||
public void terminate()
|
||||
throws IOException {
|
||||
try {
|
||||
delegate.removeClient(clientId);
|
||||
} catch (EventClientNotFoundException x) {
|
||||
throw new IOException("Unknown clientId: "+clientId,x);
|
||||
}
|
||||
}
|
||||
|
||||
private static Subject getSubject() {
|
||||
return Subject.getSubject(AccessController.getContext());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a SubscriptionManager that uses either the legacy notifications
|
||||
* mechanism (ServerNotifForwarder) or the new event service
|
||||
* (EventClientDelegateMBean) depending on which option was passed in
|
||||
* the connector's map.
|
||||
**/
|
||||
private SubscriptionManager createSubscriptionManager()
|
||||
throws IOException {
|
||||
if (EnvHelp.delegateToEventService(env) &&
|
||||
mbeanServer.isRegistered(EventClientDelegate.OBJECT_NAME)) {
|
||||
final EventClientDelegateMBean mbean =
|
||||
JMX.newMBeanProxy(mbeanServer,
|
||||
EventClientDelegate.OBJECT_NAME,
|
||||
EventClientDelegateMBean.class);
|
||||
String clientId;
|
||||
try {
|
||||
clientId =
|
||||
mbean.addClient(
|
||||
FetchingEventForwarder.class.getName(),
|
||||
new Object[] {EnvHelp.getNotifBufferSize(env)},
|
||||
new String[] {int.class.getName()});
|
||||
} catch (Exception e) {
|
||||
if (e instanceof IOException)
|
||||
throw (IOException) e;
|
||||
else
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
// we're going to call remove client...
|
||||
try {
|
||||
mbean.lease(clientId, Long.MAX_VALUE);
|
||||
} catch (EventClientNotFoundException x) {
|
||||
throw new IOException("Unknown clientId: "+clientId,x);
|
||||
}
|
||||
return new EventSubscriptionManager(mbeanServer, mbean, env,
|
||||
clientId, connectionId);
|
||||
} else {
|
||||
final ServerNotifForwarder serverNotifForwarder =
|
||||
new ServerNotifForwarder(mbeanServer,
|
||||
env,
|
||||
rmiServer.getNotifBuffer(),
|
||||
connectionId);
|
||||
return new LegacySubscriptionManager(serverNotifForwarder);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy creation of a SubscriptionManager.
|
||||
**/
|
||||
private synchronized SubscriptionManager getSubscriptionManager()
|
||||
throws IOException {
|
||||
// Lazily created when first use. Mainly when
|
||||
// addNotificationListener is first called.
|
||||
|
||||
if (subscriptionManager == null) {
|
||||
subscriptionManager = createSubscriptionManager();
|
||||
}
|
||||
return subscriptionManager;
|
||||
}
|
||||
|
||||
// calls SubscriptionManager.
|
||||
private void doRemoveListener(ObjectName name, Integer id)
|
||||
throws InstanceNotFoundException, ListenerNotFoundException,
|
||||
IOException {
|
||||
getSubscriptionManager().removeNotificationListener(name,id);
|
||||
}
|
||||
|
||||
// calls SubscriptionManager.
|
||||
private void doRemoveListener(ObjectName name, Integer[] ids)
|
||||
throws Exception {
|
||||
getSubscriptionManager().removeNotificationListener(name,ids);
|
||||
}
|
||||
|
||||
// calls SubscriptionManager.
|
||||
private NotificationResult doFetchNotifs(long csn, long timeout, int maxcount)
|
||||
throws IOException {
|
||||
return getSubscriptionManager().fetchNotifications(csn, timeout, maxcount);
|
||||
}
|
||||
|
||||
// calls SubscriptionManager.
|
||||
private Integer doAddListener(ObjectName name, NotificationFilter filter)
|
||||
throws InstanceNotFoundException, IOException {
|
||||
return getSubscriptionManager().addNotificationListener(name,filter);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* <p>Returns a string representation of this object. In general,
|
||||
* the <code>toString</code> method returns a string that
|
||||
@ -1313,16 +1586,6 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
|
||||
// private methods
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
private ClassLoaderRepository getClassLoaderRepository() {
|
||||
return
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedAction<ClassLoaderRepository>() {
|
||||
public ClassLoaderRepository run() {
|
||||
return mbeanServer.getClassLoaderRepository();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private ClassLoader getClassLoader(final ObjectName name)
|
||||
throws InstanceNotFoundException {
|
||||
try {
|
||||
@ -1482,9 +1745,8 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
|
||||
return null;
|
||||
|
||||
case ADD_NOTIFICATION_LISTENERS:
|
||||
return getServerNotifFwd().addNotificationListener(
|
||||
(ObjectName)params[0],
|
||||
(NotificationFilter)params[1]);
|
||||
return doAddListener((ObjectName)params[0],
|
||||
(NotificationFilter)params[1]);
|
||||
|
||||
case ADD_NOTIFICATION_LISTENER_OBJECTNAME:
|
||||
mbeanServer.addNotificationListener((ObjectName)params[0],
|
||||
@ -1494,9 +1756,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
|
||||
return null;
|
||||
|
||||
case REMOVE_NOTIFICATION_LISTENER:
|
||||
getServerNotifFwd().removeNotificationListener(
|
||||
(ObjectName)params[0],
|
||||
(Integer[])params[1]);
|
||||
doRemoveListener((ObjectName)params[0],(Integer[])params[1]);
|
||||
return null;
|
||||
|
||||
case REMOVE_NOTIFICATION_LISTENER_OBJECTNAME:
|
||||
@ -1709,23 +1969,21 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
|
||||
private final static int
|
||||
REMOVE_NOTIFICATION_LISTENER = 19;
|
||||
private final static int
|
||||
REMOVE_NOTIFICATION_LISTENER_FILTER_HANDBACK = 20;
|
||||
REMOVE_NOTIFICATION_LISTENER_OBJECTNAME = 20;
|
||||
private final static int
|
||||
REMOVE_NOTIFICATION_LISTENER_OBJECTNAME = 21;
|
||||
REMOVE_NOTIFICATION_LISTENER_OBJECTNAME_FILTER_HANDBACK = 21;
|
||||
private final static int
|
||||
REMOVE_NOTIFICATION_LISTENER_OBJECTNAME_FILTER_HANDBACK = 22;
|
||||
SET_ATTRIBUTE = 22;
|
||||
private final static int
|
||||
SET_ATTRIBUTE = 23;
|
||||
SET_ATTRIBUTES = 23;
|
||||
private final static int
|
||||
SET_ATTRIBUTES = 24;
|
||||
private final static int
|
||||
UNREGISTER_MBEAN = 25;
|
||||
UNREGISTER_MBEAN = 24;
|
||||
|
||||
// SERVER NOTIFICATION
|
||||
//--------------------
|
||||
|
||||
private ServerNotifForwarder serverNotifForwarder;
|
||||
private Map env;
|
||||
private SubscriptionManager subscriptionManager;
|
||||
private Map<String, ?> env;
|
||||
|
||||
// TRACES & DEBUG
|
||||
//---------------
|
||||
|
@ -25,6 +25,9 @@
|
||||
|
||||
package javax.management.remote.rmi;
|
||||
|
||||
import com.sun.jmx.event.DaemonThreadFactory;
|
||||
import com.sun.jmx.event.EventConnection;
|
||||
import com.sun.jmx.mbeanserver.PerThreadGroupPool;
|
||||
import com.sun.jmx.remote.internal.ClientCommunicatorAdmin;
|
||||
import com.sun.jmx.remote.internal.ClientListenerInfo;
|
||||
import com.sun.jmx.remote.internal.ClientNotifForwarder;
|
||||
@ -68,6 +71,12 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.management.Attribute;
|
||||
import javax.management.AttributeList;
|
||||
import javax.management.AttributeNotFoundException;
|
||||
@ -75,6 +84,7 @@ import javax.management.InstanceAlreadyExistsException;
|
||||
import javax.management.InstanceNotFoundException;
|
||||
import javax.management.IntrospectionException;
|
||||
import javax.management.InvalidAttributeValueException;
|
||||
import javax.management.JMX;
|
||||
import javax.management.ListenerNotFoundException;
|
||||
import javax.management.MBeanException;
|
||||
import javax.management.MBeanInfo;
|
||||
@ -92,6 +102,8 @@ import javax.management.ObjectInstance;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.QueryExp;
|
||||
import javax.management.ReflectionException;
|
||||
import javax.management.event.EventClient;
|
||||
import javax.management.event.EventClientDelegateMBean;
|
||||
import javax.management.remote.JMXConnectionNotification;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
@ -280,8 +292,8 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
|
||||
// client-side environment property is set to "true".
|
||||
//
|
||||
boolean checkStub = EnvHelp.computeBooleanFromString(
|
||||
usemap,
|
||||
"jmx.remote.x.check.stub");
|
||||
usemap,
|
||||
"jmx.remote.x.check.stub",false);
|
||||
if (checkStub) checkStub(stub, rmiServerImplStubClass);
|
||||
|
||||
// Connect IIOP Stub if needed.
|
||||
@ -318,6 +330,8 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
|
||||
//
|
||||
connectionId = getConnectionId();
|
||||
|
||||
eventServiceEnabled = EnvHelp.eventServiceEnabled(env);
|
||||
|
||||
Notification connectedNotif =
|
||||
new JMXConnectionNotification(JMXConnectionNotification.OPENED,
|
||||
this,
|
||||
@ -327,6 +341,8 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
|
||||
null);
|
||||
sendNotification(connectedNotif);
|
||||
|
||||
// whether or not event service
|
||||
|
||||
if (tracing) logger.trace("connect",idstr + " done...");
|
||||
} catch (IOException e) {
|
||||
if (tracing)
|
||||
@ -378,13 +394,42 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
|
||||
throw new IOException("Not connected");
|
||||
}
|
||||
|
||||
MBeanServerConnection mbsc = rmbscMap.get(delegationSubject);
|
||||
if (mbsc != null)
|
||||
return mbsc;
|
||||
MBeanServerConnection rmbsc = rmbscMap.get(delegationSubject);
|
||||
if (rmbsc != null) {
|
||||
return rmbsc;
|
||||
}
|
||||
|
||||
mbsc = new RemoteMBeanServerConnection(delegationSubject);
|
||||
rmbscMap.put(delegationSubject, mbsc);
|
||||
return mbsc;
|
||||
rmbsc = new RemoteMBeanServerConnection(delegationSubject);
|
||||
if (eventServiceEnabled) {
|
||||
EventClientDelegateMBean ecd = JMX.newMBeanProxy(
|
||||
rmbsc, EventClientDelegateMBean.OBJECT_NAME,
|
||||
EventClientDelegateMBean.class);
|
||||
EventClient ec = new EventClient(ecd, null, defaultExecutor(), null,
|
||||
EventClient.DEFAULT_LEASE_TIMEOUT);
|
||||
|
||||
rmbsc = EventConnection.Factory.make(rmbsc, ec);
|
||||
ec.addEventClientListener(
|
||||
lostNotifListener, null, null);
|
||||
}
|
||||
rmbscMap.put(delegationSubject, rmbsc);
|
||||
return rmbsc;
|
||||
}
|
||||
|
||||
private static Executor defaultExecutor() {
|
||||
PerThreadGroupPool.Create<ThreadPoolExecutor> create =
|
||||
new PerThreadGroupPool.Create<ThreadPoolExecutor>() {
|
||||
public ThreadPoolExecutor createThreadPool(ThreadGroup group) {
|
||||
ThreadFactory daemonThreadFactory = new DaemonThreadFactory(
|
||||
"RMIConnector listener dispatch %d");
|
||||
ThreadPoolExecutor exec = new ThreadPoolExecutor(
|
||||
1, 10, 1, TimeUnit.SECONDS,
|
||||
new LinkedBlockingDeque<Runnable>(),
|
||||
daemonThreadFactory);
|
||||
exec.allowCoreThreadTimeOut(true);
|
||||
return exec;
|
||||
}
|
||||
};
|
||||
return listenerDispatchThreadPool.getThreadPoolExecutor(create);
|
||||
}
|
||||
|
||||
public void
|
||||
@ -466,6 +511,17 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
|
||||
communicatorAdmin.terminate();
|
||||
}
|
||||
|
||||
// close all EventClient
|
||||
for (MBeanServerConnection rmbsc : rmbscMap.values()) {
|
||||
if (rmbsc instanceof EventConnection) {
|
||||
try {
|
||||
((EventConnection)rmbsc).getEventClient().close();
|
||||
} catch (Exception e) {
|
||||
// OK
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rmiNotifClient != null) {
|
||||
try {
|
||||
rmiNotifClient.terminate();
|
||||
@ -592,18 +648,19 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
|
||||
}
|
||||
|
||||
if (debug) logger.debug("addListenersWithSubjects","registered "
|
||||
+ listenerIDs.length + " listener(s)");
|
||||
+ ((listenerIDs==null)?0:listenerIDs.length)
|
||||
+ " listener(s)");
|
||||
return listenerIDs;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// Implementation of MBeanServerConnection
|
||||
//--------------------------------------------------------------------
|
||||
private class RemoteMBeanServerConnection
|
||||
implements MBeanServerConnection {
|
||||
|
||||
private class RemoteMBeanServerConnection implements MBeanServerConnection {
|
||||
private Subject delegationSubject;
|
||||
|
||||
public EventClient eventClient = null;
|
||||
|
||||
public RemoteMBeanServerConnection() {
|
||||
this(null);
|
||||
}
|
||||
@ -1205,6 +1262,7 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
|
||||
IOException {
|
||||
|
||||
final boolean debug = logger.debugOn();
|
||||
|
||||
if (debug)
|
||||
logger.debug("addNotificationListener" +
|
||||
"(ObjectName,NotificationListener,"+
|
||||
@ -1226,8 +1284,9 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
|
||||
public void removeNotificationListener(ObjectName name,
|
||||
NotificationListener listener)
|
||||
throws InstanceNotFoundException,
|
||||
ListenerNotFoundException,
|
||||
IOException {
|
||||
ListenerNotFoundException,
|
||||
IOException {
|
||||
|
||||
final boolean debug = logger.debugOn();
|
||||
|
||||
if (debug) logger.debug("removeNotificationListener"+
|
||||
@ -1804,6 +1863,26 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
|
||||
terminated = false;
|
||||
|
||||
connectionBroadcaster = new NotificationBroadcasterSupport();
|
||||
|
||||
lostNotifListener =
|
||||
new NotificationListener() {
|
||||
public void handleNotification(Notification n, Object hb) {
|
||||
if (n != null && EventClient.NOTIFS_LOST.equals(n.getType())) {
|
||||
Long lost = (Long)n.getUserData();
|
||||
final String msg =
|
||||
"May have lost up to " + lost +
|
||||
" notification" + (lost.longValue() == 1 ? "" : "s");
|
||||
sendNotification(new JMXConnectionNotification(
|
||||
JMXConnectionNotification.NOTIFS_LOST,
|
||||
RMIConnector.this,
|
||||
connectionId,
|
||||
clientNotifCounter++,
|
||||
msg,
|
||||
lost));
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
@ -2528,6 +2607,11 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
|
||||
|
||||
private transient ClientCommunicatorAdmin communicatorAdmin;
|
||||
|
||||
private boolean eventServiceEnabled;
|
||||
// private transient EventRelay eventRelay;
|
||||
|
||||
private transient NotificationListener lostNotifListener;
|
||||
|
||||
/**
|
||||
* A static WeakReference to an {@link org.omg.CORBA.ORB ORB} to
|
||||
* connect unconnected stubs.
|
||||
@ -2546,4 +2630,7 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
|
||||
private static String strings(final String[] strs) {
|
||||
return objects(strs);
|
||||
}
|
||||
|
||||
private static final PerThreadGroupPool<ThreadPoolExecutor> listenerDispatchThreadPool =
|
||||
PerThreadGroupPool.make();
|
||||
}
|
||||
|
@ -230,6 +230,8 @@ public class RMIConnectorServer extends JMXConnectorServer {
|
||||
|
||||
this.address = url;
|
||||
this.rmiServerImpl = rmiServerImpl;
|
||||
|
||||
installStandardForwarders(this.attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -380,8 +382,8 @@ public class RMIConnectorServer extends JMXConnectorServer {
|
||||
|
||||
try {
|
||||
if (tracing) logger.trace("start", "setting default class loader");
|
||||
defaultClassLoader =
|
||||
EnvHelp.resolveServerClassLoader(attributes, getMBeanServer());
|
||||
defaultClassLoader = EnvHelp.resolveServerClassLoader(
|
||||
attributes, getSystemMBeanServer());
|
||||
} catch (InstanceNotFoundException infc) {
|
||||
IllegalArgumentException x = new
|
||||
IllegalArgumentException("ClassLoader not found: "+infc);
|
||||
@ -396,7 +398,7 @@ public class RMIConnectorServer extends JMXConnectorServer {
|
||||
else
|
||||
rmiServer = newServer();
|
||||
|
||||
rmiServer.setMBeanServer(getMBeanServer());
|
||||
rmiServer.setMBeanServer(getSystemMBeanServer());
|
||||
rmiServer.setDefaultClassLoader(defaultClassLoader);
|
||||
rmiServer.setRMIConnectorServer(this);
|
||||
rmiServer.export();
|
||||
@ -413,7 +415,7 @@ public class RMIConnectorServer extends JMXConnectorServer {
|
||||
|
||||
final boolean rebind = EnvHelp.computeBooleanFromString(
|
||||
attributes,
|
||||
JNDI_REBIND_ATTRIBUTE);
|
||||
JNDI_REBIND_ATTRIBUTE,false);
|
||||
|
||||
if (tracing)
|
||||
logger.trace("start", JNDI_REBIND_ATTRIBUTE + "=" + rebind);
|
||||
@ -590,11 +592,39 @@ public class RMIConnectorServer extends JMXConnectorServer {
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
public synchronized
|
||||
void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
|
||||
@Override
|
||||
public synchronized void setMBeanServerForwarder(MBeanServerForwarder mbsf) {
|
||||
MBeanServer oldSMBS = getSystemMBeanServer();
|
||||
super.setMBeanServerForwarder(mbsf);
|
||||
if (oldSMBS != getSystemMBeanServer())
|
||||
updateMBeanServer();
|
||||
// If the system chain of MBeanServerForwarders is not empty, then
|
||||
// there is no need to call rmiServerImpl.setMBeanServer, because
|
||||
// it is pointing to the head of the system chain and that has not
|
||||
// changed. (The *end* of the system chain will have been changed
|
||||
// to point to mbsf.)
|
||||
}
|
||||
|
||||
private void updateMBeanServer() {
|
||||
if (rmiServerImpl != null)
|
||||
rmiServerImpl.setMBeanServer(getMBeanServer());
|
||||
rmiServerImpl.setMBeanServer(getSystemMBeanServer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setSystemMBeanServerForwarder(
|
||||
MBeanServerForwarder mbsf) {
|
||||
super.setSystemMBeanServerForwarder(mbsf);
|
||||
updateMBeanServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @return true, since this connector server does support a system chain
|
||||
* of forwarders.
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsSystemMBeanServerForwarder() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* We repeat the definitions of connection{Opened,Closed,Failed}
|
||||
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test DynamicWrapperMBeanTest
|
||||
* @bug 6624232
|
||||
* @summary Test the DynamicWrapperMBean interface
|
||||
* @author Eamonn McManus
|
||||
*/
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.StandardMBean;
|
||||
import javax.management.modelmbean.ModelMBeanInfo;
|
||||
import javax.management.modelmbean.ModelMBeanInfoSupport;
|
||||
import javax.management.modelmbean.ModelMBeanOperationInfo;
|
||||
import javax.management.modelmbean.RequiredModelMBean;
|
||||
import static javax.management.StandardMBean.Options;
|
||||
|
||||
public class DynamicWrapperMBeanTest {
|
||||
public static interface WrappedMBean {
|
||||
public void sayHello();
|
||||
}
|
||||
public static class Wrapped implements WrappedMBean {
|
||||
public void sayHello() {
|
||||
System.out.println("Hello");
|
||||
}
|
||||
}
|
||||
|
||||
private static String failure;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (Wrapped.class.getClassLoader() ==
|
||||
StandardMBean.class.getClassLoader()) {
|
||||
throw new Exception(
|
||||
"TEST ERROR: Resource and StandardMBean have same ClassLoader");
|
||||
}
|
||||
|
||||
Options wrappedVisOpts = new Options();
|
||||
wrappedVisOpts.setWrappedObjectVisible(true);
|
||||
Options wrappedInvisOpts = new Options();
|
||||
wrappedInvisOpts.setWrappedObjectVisible(false);
|
||||
assertEquals("Options withWrappedObjectVisible(false)",
|
||||
new Options(), wrappedInvisOpts);
|
||||
|
||||
Wrapped resource = new Wrapped();
|
||||
|
||||
StandardMBean visible =
|
||||
new StandardMBean(resource, WrappedMBean.class, wrappedVisOpts);
|
||||
StandardMBean invisible =
|
||||
new StandardMBean(resource, WrappedMBean.class, wrappedInvisOpts);
|
||||
|
||||
assertEquals("getResource withWrappedObjectVisible(true)",
|
||||
resource, visible.getWrappedObject());
|
||||
assertEquals("getResource withWrappedObjectVisible(false)",
|
||||
invisible, invisible.getWrappedObject());
|
||||
|
||||
System.out.println("===Testing StandardMBean===");
|
||||
|
||||
ObjectName visibleName = new ObjectName("a:type=visible");
|
||||
ObjectName invisibleName = new ObjectName("a:type=invisible");
|
||||
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||
mbs.registerMBean(visible, visibleName);
|
||||
mbs.registerMBean(invisible, invisibleName);
|
||||
|
||||
assertEquals("ClassLoader for visible resource",
|
||||
Wrapped.class.getClassLoader(),
|
||||
mbs.getClassLoaderFor(visibleName));
|
||||
assertEquals("ClassLoader for invisible resource",
|
||||
StandardMBean.class.getClassLoader(),
|
||||
mbs.getClassLoaderFor(invisibleName));
|
||||
|
||||
assertEquals("isInstanceOf(WrappedMBean) for visible wrapped",
|
||||
true, mbs.isInstanceOf(visibleName, WrappedMBean.class.getName()));
|
||||
assertEquals("isInstanceOf(WrappedMBean) for invisible wrapped",
|
||||
false, mbs.isInstanceOf(invisibleName, WrappedMBean.class.getName()));
|
||||
assertEquals("isInstanceOf(StandardMBean) for visible wrapped",
|
||||
false, mbs.isInstanceOf(visibleName, StandardMBean.class.getName()));
|
||||
assertEquals("isInstanceOf(StandardMBean) for invisible wrapped",
|
||||
true, mbs.isInstanceOf(invisibleName, StandardMBean.class.getName()));
|
||||
|
||||
mbs.unregisterMBean(visibleName);
|
||||
mbs.unregisterMBean(invisibleName);
|
||||
|
||||
System.out.println("===Testing RequiredModelMBean===");
|
||||
|
||||
// Godawful Model MBeans...
|
||||
ModelMBeanOperationInfo mmboi = new ModelMBeanOperationInfo(
|
||||
"say hello to the nice man", Wrapped.class.getMethod("sayHello"));
|
||||
ModelMBeanInfo visibleMmbi = new ModelMBeanInfoSupport(
|
||||
Wrapped.class.getName(), "Visible wrapped", null, null,
|
||||
new ModelMBeanOperationInfo[] {mmboi}, null);
|
||||
ModelMBeanInfo invisibleMmbi = new ModelMBeanInfoSupport(
|
||||
Wrapped.class.getName(), "Invisible wrapped", null, null,
|
||||
new ModelMBeanOperationInfo[] {mmboi}, null);
|
||||
RequiredModelMBean visibleRmmb = new RequiredModelMBean(visibleMmbi);
|
||||
RequiredModelMBean invisibleRmmb = new RequiredModelMBean(invisibleMmbi);
|
||||
visibleRmmb.setManagedResource(resource, "VisibleObjectReference");
|
||||
invisibleRmmb.setManagedResource(resource, "ObjectReference");
|
||||
|
||||
mbs.registerMBean(visibleRmmb, visibleName);
|
||||
mbs.registerMBean(invisibleRmmb, invisibleName);
|
||||
|
||||
assertEquals("ClassLoader for visible wrapped",
|
||||
Wrapped.class.getClassLoader(),
|
||||
mbs.getClassLoaderFor(visibleName));
|
||||
assertEquals("ClassLoader for invisible wrapped",
|
||||
StandardMBean.class.getClassLoader(),
|
||||
mbs.getClassLoaderFor(invisibleName));
|
||||
|
||||
assertEquals("isInstanceOf(WrappedMBean) for visible resource",
|
||||
true, mbs.isInstanceOf(visibleName, WrappedMBean.class.getName()));
|
||||
assertEquals("isInstanceOf(WrappedMBean) for invisible resource",
|
||||
false, mbs.isInstanceOf(invisibleName, WrappedMBean.class.getName()));
|
||||
assertEquals("isInstanceOf(RequiredModelMBean) for visible resource",
|
||||
false, mbs.isInstanceOf(visibleName, RequiredModelMBean.class.getName()));
|
||||
assertEquals("isInstanceOf(RequiredModelMBean) for invisible resource",
|
||||
true, mbs.isInstanceOf(invisibleName, RequiredModelMBean.class.getName()));
|
||||
|
||||
if (failure != null)
|
||||
throw new Exception("TEST FAILED: " + failure);
|
||||
}
|
||||
|
||||
private static void assertEquals(String what, Object expect, Object actual) {
|
||||
if (equal(expect, actual))
|
||||
System.out.println("OK: " + what + " = " + expect);
|
||||
else
|
||||
fail(what + " should be " + expect + ", is " + actual);
|
||||
}
|
||||
|
||||
private static boolean equal(Object x, Object y) {
|
||||
if (x == y)
|
||||
return true;
|
||||
if (x == null || y == null)
|
||||
return false;
|
||||
return x.equals(y);
|
||||
}
|
||||
|
||||
private static void fail(String why) {
|
||||
failure = why;
|
||||
System.out.println("FAIL: " + why);
|
||||
}
|
||||
}
|
1410
jdk/test/javax/management/MBeanServer/OldMBeanServerTest.java
Normal file
1410
jdk/test/javax/management/MBeanServer/OldMBeanServerTest.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,371 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test AddRemoveListenerTest.java
|
||||
* @bug 5108776
|
||||
* @summary Basic test for EventClient to see internal thread management.
|
||||
* @author Shanliang JIANG
|
||||
* @run clean AddRemoveListenerTest
|
||||
* @run build AddRemoveListenerTest
|
||||
* @run main AddRemoveListenerTest
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.EventClient;
|
||||
import javax.management.event.EventClientDelegate;
|
||||
import javax.management.event.EventClientDelegateMBean;
|
||||
import javax.management.event.FetchingEventRelay;
|
||||
import javax.management.event.RMIPushEventRelay;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
|
||||
// This thread creates a single MBean that emits a number of parallel
|
||||
// sequences of notifications. Each sequence is distinguished by an id
|
||||
// and each id corresponds to a thread that is filtering the notifications
|
||||
// so it only sees its own ones. The notifications for a given id have
|
||||
// contiguous sequence numbers and each thread checks that the notifications
|
||||
// it receives do indeed have these numbers. If notifications are lost or
|
||||
// if the different sequences interfere with each other then the test will
|
||||
// fail. As an added tweak, a "noise" thread periodically causes notifications
|
||||
// to be emitted that do not correspond to any sequence and do not have any id.
|
||||
public class AddRemoveListenerTest {
|
||||
|
||||
private static MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer();
|
||||
private static ObjectName emitter;
|
||||
private static NotificationSender emitterImpl;
|
||||
private static JMXServiceURL url;
|
||||
private static JMXConnectorServer server;
|
||||
|
||||
private static int toSend = 100;
|
||||
private static final long bigWaiting = 10000;
|
||||
private static int counter = 0;
|
||||
private static int jobs = 10;
|
||||
private static int endedJobs = 0;
|
||||
|
||||
private static volatile String failure;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println(">>> Test on multiple adding/removing listeners.");
|
||||
|
||||
// for 1.5
|
||||
if (System.getProperty("java.version").startsWith("1.5") &&
|
||||
!mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
|
||||
System.out.print("Working on "+System.getProperty("java.version")+
|
||||
" register "+EventClientDelegateMBean.OBJECT_NAME);
|
||||
|
||||
mbeanServer.registerMBean(EventClientDelegate.
|
||||
getEventClientDelegate(mbeanServer),
|
||||
EventClientDelegateMBean.OBJECT_NAME);
|
||||
}
|
||||
|
||||
emitter = new ObjectName("Default:name=NotificationSender");
|
||||
emitterImpl = new NotificationSender();
|
||||
mbeanServer.registerMBean(emitterImpl, emitter);
|
||||
|
||||
String[] types = new String[]{"PushEventRelay", "FetchingEventRelay"};
|
||||
String[] protos = new String[]{"rmi", "iiop", "jmxmp"};
|
||||
for (String prot : protos) {
|
||||
url = new JMXServiceURL(prot, null, 0);
|
||||
|
||||
try {
|
||||
server =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url,
|
||||
null, mbeanServer);
|
||||
server.start();
|
||||
} catch (Exception e) {
|
||||
System.out.println(">>> Skip "+prot+", not supported.");
|
||||
continue;
|
||||
}
|
||||
|
||||
url = server.getAddress();
|
||||
|
||||
// noise
|
||||
Thread noise = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
while (true) {
|
||||
emitterImpl.sendNotif(1, null);
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (Exception e) {
|
||||
// OK
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
noise.setDaemon(true);
|
||||
noise.start();
|
||||
|
||||
try {
|
||||
for (String type: types) {
|
||||
System.out.println("\n\n>>> Testing "+type+" on "+url+" ...");
|
||||
JMXConnector conn = newConn();
|
||||
try {
|
||||
testType(type, conn);
|
||||
} finally {
|
||||
conn.close();
|
||||
System.out.println(">>> Testing "+type+" on "+url+" ... done");
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void testType(String type, JMXConnector conn) throws Exception {
|
||||
Thread[] threads = new Thread[jobs];
|
||||
for (int i=0; i<jobs; i++) {
|
||||
threads[i] = new Thread(new Job(type, conn));
|
||||
threads[i].setDaemon(true);
|
||||
threads[i].start();
|
||||
}
|
||||
|
||||
// to wait
|
||||
long toWait = bigWaiting*jobs;
|
||||
long stopTime = System.currentTimeMillis() + toWait;
|
||||
|
||||
synchronized(AddRemoveListenerTest.class) {
|
||||
while (endedJobs < jobs && toWait > 0 && failure == null) {
|
||||
AddRemoveListenerTest.class.wait(toWait);
|
||||
toWait = stopTime - System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
if (endedJobs != jobs && failure == null) {
|
||||
throw new RuntimeException("Need to set bigger waiting timeout?");
|
||||
}
|
||||
|
||||
endedJobs = 0;
|
||||
}
|
||||
|
||||
public static class Job implements Runnable {
|
||||
public Job(String type, JMXConnector conn) {
|
||||
this.type = type;
|
||||
this.conn = conn;
|
||||
}
|
||||
public void run() {
|
||||
try {
|
||||
test(type, conn);
|
||||
|
||||
synchronized(AddRemoveListenerTest.class) {
|
||||
endedJobs++;
|
||||
if (endedJobs>=jobs) {
|
||||
AddRemoveListenerTest.class.notify();
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException re) {
|
||||
throw re;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final String type;
|
||||
private final JMXConnector conn;
|
||||
}
|
||||
|
||||
private static void test(String type, JMXConnector conn) throws Exception {
|
||||
EventClient ec = newEventClient(type, conn);
|
||||
try {
|
||||
test(type, conn, ec);
|
||||
} finally {
|
||||
ec.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static void test(String type, JMXConnector conn, EventClient ec)
|
||||
throws Exception {
|
||||
String id = getId();
|
||||
|
||||
Listener listener = new Listener(id);
|
||||
Filter filter = new Filter(id);
|
||||
|
||||
System.out.println(">>> ("+id+") To receive notifications "+toSend);
|
||||
ec.addNotificationListener(emitter,
|
||||
listener, filter, null);
|
||||
|
||||
emitterImpl.sendNotif(toSend, id);
|
||||
listener.waitNotifs(bigWaiting, toSend);
|
||||
if (listener.received != toSend) {
|
||||
throw new RuntimeException(">>> ("+id+") Expected to receive: "
|
||||
+toSend+", but got: "+listener.received);
|
||||
}
|
||||
|
||||
listener.clear();
|
||||
ec.removeNotificationListener(emitter, listener, filter, null);
|
||||
|
||||
System.out.println(">>> ("+id+") Repeat adding and removing ...");
|
||||
for (int j=0; j<10; j++) {
|
||||
ec.addNotificationListener(emitter, dummyListener, null, id);
|
||||
Thread.yield(); // allow to start listening
|
||||
ec.removeNotificationListener(emitter, dummyListener, null, id);
|
||||
}
|
||||
|
||||
System.out.println(">>> ("+id+") To receive again notifications "+toSend);
|
||||
ec.addNotificationListener(emitter,
|
||||
listener, filter, null);
|
||||
|
||||
emitterImpl.sendNotif(toSend, id);
|
||||
listener.waitNotifs(bigWaiting, toSend);
|
||||
Thread.yield(); //any duplicated?
|
||||
if (listener.received != toSend) {
|
||||
throw new RuntimeException("("+id+") Expected to receive: "
|
||||
+toSend+", but got: "+listener.received);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
// private classes
|
||||
//--------------------------
|
||||
|
||||
private static class Listener implements NotificationListener {
|
||||
public Listener(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public void handleNotification(Notification notif, Object handback) {
|
||||
if (!id.equals(notif.getUserData())) {
|
||||
System.out.println("("+id+") Filter error, my id is: "+id+
|
||||
", but got "+notif.getUserData());
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
received++;
|
||||
|
||||
if(++sequenceNB != notif.getSequenceNumber()) {
|
||||
fail("(" + id + ") Wrong sequence number, expected: "
|
||||
+sequenceNB+", but got: "+notif.getSequenceNumber());
|
||||
}
|
||||
if (received >= toSend || failure != null) {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void waitNotifs(long timeout, int nb) throws Exception {
|
||||
long toWait = timeout;
|
||||
long stopTime = System.currentTimeMillis() + timeout;
|
||||
synchronized(this) {
|
||||
while (received < nb && toWait > 0 && failure == null) {
|
||||
this.wait(toWait);
|
||||
toWait = stopTime - System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
synchronized(this) {
|
||||
received = 0;
|
||||
sequenceNB = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private String id;
|
||||
private int received = 0;
|
||||
|
||||
private long sequenceNB = -1;
|
||||
}
|
||||
|
||||
private static class Filter implements NotificationFilter {
|
||||
public Filter(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean isNotificationEnabled(Notification n) {
|
||||
return id.equals(n.getUserData());
|
||||
}
|
||||
private String id;
|
||||
}
|
||||
|
||||
private static NotificationListener dummyListener = new NotificationListener() {
|
||||
public void handleNotification(Notification notif, Object handback) {
|
||||
}
|
||||
};
|
||||
|
||||
public static class NotificationSender extends NotificationBroadcasterSupport
|
||||
implements NotificationSenderMBean {
|
||||
|
||||
/**
|
||||
* Send Notification objects.
|
||||
*
|
||||
* @param nb The number of notifications to send
|
||||
*/
|
||||
public void sendNotif(int nb, String userData) {
|
||||
long sequenceNumber = 0;
|
||||
for (int i = 0; i<nb; i++) {
|
||||
Notification notif = new Notification(myType, this, sequenceNumber++);
|
||||
notif.setUserData(userData);
|
||||
sendNotification(notif);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final String myType = "notification.my_notification";
|
||||
}
|
||||
|
||||
public interface NotificationSenderMBean {
|
||||
public void sendNotif(int nb, String userData);
|
||||
}
|
||||
|
||||
private static JMXConnector newConn() throws IOException {
|
||||
return JMXConnectorFactory.connect(url);
|
||||
}
|
||||
|
||||
private static EventClient newEventClient(String type, JMXConnector conn)
|
||||
throws Exception {
|
||||
EventClientDelegateMBean proxy =
|
||||
EventClientDelegate.getProxy(conn.getMBeanServerConnection());
|
||||
if (type.equals("PushEventRelay")) {
|
||||
return new EventClient(proxy,
|
||||
new RMIPushEventRelay(proxy), null, null, 60000);
|
||||
} else if (type.equals("FetchingEventRelay")) {
|
||||
return new EventClient(proxy,
|
||||
new FetchingEventRelay(proxy), null, null, 60000);
|
||||
} else {
|
||||
throw new RuntimeException("Wrong event client type: "+type);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getId() {
|
||||
synchronized(AddRemoveListenerTest.class) {
|
||||
return String.valueOf(counter++);
|
||||
}
|
||||
}
|
||||
|
||||
private static void fail(String msg) {
|
||||
System.out.println("FAIL: " + msg);
|
||||
failure = msg;
|
||||
}
|
||||
}
|
348
jdk/test/javax/management/eventService/CustomForwarderTest.java
Normal file
348
jdk/test/javax/management/eventService/CustomForwarderTest.java
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test CustomForwarderTest
|
||||
* @bug 5108776
|
||||
* @summary Test that a custom EventForwarder can be added
|
||||
* @author Eamonn McManus
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.SocketAddress;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerInvocationHandler;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.EventClient;
|
||||
import javax.management.event.EventClientDelegate;
|
||||
import javax.management.event.EventClientDelegateMBean;
|
||||
import javax.management.event.EventForwarder;
|
||||
import javax.management.event.EventReceiver;
|
||||
import javax.management.event.EventRelay;
|
||||
import javax.management.remote.MBeanServerForwarder;
|
||||
import javax.management.remote.NotificationResult;
|
||||
import javax.management.remote.TargetedNotification;
|
||||
|
||||
public class CustomForwarderTest {
|
||||
public static class UdpEventRelay implements EventRelay {
|
||||
private final EventClientDelegateMBean delegate;
|
||||
private final DatagramSocket socket;
|
||||
private final AtomicBoolean closed = new AtomicBoolean();
|
||||
private final String clientId;
|
||||
private EventReceiver receiver;
|
||||
|
||||
public UdpEventRelay(EventClientDelegateMBean delegate)
|
||||
throws IOException {
|
||||
this.delegate = delegate;
|
||||
this.socket = new DatagramSocket();
|
||||
try {
|
||||
clientId = delegate.addClient(
|
||||
UdpEventForwarder.class.getName(),
|
||||
new Object[] {socket.getLocalSocketAddress()},
|
||||
new String[] {SocketAddress.class.getName()});
|
||||
} catch (IOException e) {
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
final IOException ioe =
|
||||
new IOException("Exception creating EventForwarder");
|
||||
ioe.initCause(e);
|
||||
throw ioe;
|
||||
}
|
||||
Thread t = new Thread(new Receiver());
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
public String getClientId() throws IOException {
|
||||
return clientId;
|
||||
}
|
||||
|
||||
public void setEventReceiver(EventReceiver eventReceiver) {
|
||||
this.receiver = eventReceiver;
|
||||
}
|
||||
|
||||
public void stop() throws IOException {
|
||||
closed.set(true);
|
||||
socket.close();
|
||||
}
|
||||
|
||||
private class Receiver implements Runnable {
|
||||
public void run() {
|
||||
byte[] buf = new byte[1024];
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length);
|
||||
while (true) {
|
||||
try {
|
||||
socket.receive(packet);
|
||||
} catch (IOException e) {
|
||||
if (closed.get()) {
|
||||
System.out.println("Receiver got exception: " + e);
|
||||
System.out.println("Normal because it has been closed");
|
||||
return;
|
||||
} else {
|
||||
System.err.println("UNEXPECTED EXCEPTION IN RECEIVER:");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
try {
|
||||
ByteArrayInputStream bin = new ByteArrayInputStream(buf);
|
||||
ObjectInputStream oin = new ObjectInputStream(bin);
|
||||
NotificationResult nr = (NotificationResult)
|
||||
oin.readObject();
|
||||
receiver.receive(nr);
|
||||
} catch (Exception e) {
|
||||
System.err.println("UNEXPECTED EXCEPTION IN RECEIVER:");
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class UdpEventForwarder implements EventForwarder {
|
||||
private final DatagramSocket socket;
|
||||
private final AtomicLong seqNo = new AtomicLong(0);
|
||||
private static volatile boolean drop;
|
||||
|
||||
public UdpEventForwarder(SocketAddress addr) throws IOException {
|
||||
this.socket = new DatagramSocket();
|
||||
socket.connect(addr);
|
||||
}
|
||||
|
||||
public static void setDrop(boolean drop) {
|
||||
UdpEventForwarder.drop = drop;
|
||||
}
|
||||
|
||||
public void forward(Notification n, Integer listenerId) throws IOException {
|
||||
long nextSeqNo = seqNo.incrementAndGet();
|
||||
long thisSeqNo = nextSeqNo - 1;
|
||||
TargetedNotification tn = new TargetedNotification(n, listenerId);
|
||||
NotificationResult nr = new NotificationResult(
|
||||
thisSeqNo, nextSeqNo, new TargetedNotification[] {tn});
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oout = new ObjectOutputStream(bout);
|
||||
oout.writeObject(nr);
|
||||
oout.close();
|
||||
byte[] bytes = bout.toByteArray();
|
||||
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
|
||||
if (!drop)
|
||||
socket.send(packet);
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
socket.close();
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) throws IOException {
|
||||
// Nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
public static interface EmptyMBean {}
|
||||
|
||||
public static class Empty
|
||||
extends NotificationBroadcasterSupport implements EmptyMBean {
|
||||
public void send(Notification n) {
|
||||
super.sendNotification(n);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
|
||||
mbsf.setMBeanServer(mbs);
|
||||
mbs = mbsf;
|
||||
|
||||
// for 1.5
|
||||
if (System.getProperty("java.version").startsWith("1.5") &&
|
||||
!mbs.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
|
||||
System.out.print("Working on "+System.getProperty("java.version")+
|
||||
" register "+EventClientDelegateMBean.OBJECT_NAME);
|
||||
|
||||
mbs.registerMBean(EventClientDelegate.
|
||||
getEventClientDelegate(mbs),
|
||||
EventClientDelegateMBean.OBJECT_NAME);
|
||||
}
|
||||
|
||||
ObjectName name = new ObjectName("a:b=c");
|
||||
Empty mbean = new Empty();
|
||||
mbs.registerMBean(mbean, name);
|
||||
|
||||
EventClientDelegateMBean delegate = (EventClientDelegateMBean)
|
||||
MBeanServerInvocationHandler.newProxyInstance(
|
||||
mbs,
|
||||
EventClientDelegateMBean.OBJECT_NAME,
|
||||
EventClientDelegateMBean.class,
|
||||
false);
|
||||
EventRelay relay = new UdpEventRelay(delegate);
|
||||
EventClient client = new EventClient(delegate, relay, null, null, 0L);
|
||||
|
||||
final Semaphore lostCountSema = new Semaphore(0);
|
||||
NotificationListener lostListener = new NotificationListener() {
|
||||
public void handleNotification(Notification notification, Object handback) {
|
||||
if (notification.getType().equals(EventClient.NOTIFS_LOST)) {
|
||||
System.out.println("Got lost-notifs notif: count=" +
|
||||
notification.getUserData());
|
||||
lostCountSema.release(((Long) notification.getUserData()).intValue());
|
||||
} else
|
||||
System.out.println("Mysterious EventClient notif: " + notification);
|
||||
}
|
||||
};
|
||||
client.addEventClientListener(lostListener, null, null);
|
||||
|
||||
final BlockingQueue<Notification> notifQueue =
|
||||
new ArrayBlockingQueue<Notification>(10);
|
||||
NotificationListener countListener = new NotificationListener() {
|
||||
public void handleNotification(Notification notification, Object handback) {
|
||||
System.out.println("Received: " + notification);
|
||||
notifQueue.add(notification);
|
||||
if (!"tiddly".equals(handback)) {
|
||||
System.err.println("TEST FAILED: bad handback: " + handback);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
final AtomicInteger filterCount = new AtomicInteger(0);
|
||||
NotificationFilter countFilter = new NotificationFilter() {
|
||||
private static final long serialVersionUID = 1234L;
|
||||
|
||||
public boolean isNotificationEnabled(Notification notification) {
|
||||
System.out.println("Filter called for: " + notification);
|
||||
filterCount.incrementAndGet();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
client.addNotificationListener(name, countListener, countFilter, "tiddly");
|
||||
|
||||
assertEquals("Initial notif count", 0, notifQueue.size());
|
||||
assertEquals("Initial filter count", 0, filterCount.get());
|
||||
|
||||
Notification n = nextNotif(name);
|
||||
mbean.send(n);
|
||||
|
||||
System.out.println("Waiting for notification to arrive...");
|
||||
|
||||
Notification n1 = notifQueue.poll(10, TimeUnit.SECONDS);
|
||||
|
||||
assertEquals("Received notif", n, n1);
|
||||
assertEquals("Notif queue size after receive", 0, notifQueue.size());
|
||||
assertEquals("Filter count after notif", 1, filterCount.get());
|
||||
assertEquals("Lost notif count", 0, lostCountSema.availablePermits());
|
||||
|
||||
System.out.println("Dropping notifs");
|
||||
|
||||
UdpEventForwarder.setDrop(true);
|
||||
for (int i = 0; i < 3; i++)
|
||||
mbean.send(nextNotif(name));
|
||||
UdpEventForwarder.setDrop(false);
|
||||
|
||||
Thread.sleep(2);
|
||||
assertEquals("Notif queue size after drops", 0, notifQueue.size());
|
||||
|
||||
System.out.println("Turning off dropping and sending a notif");
|
||||
n = nextNotif(name);
|
||||
mbean.send(n);
|
||||
|
||||
System.out.println("Waiting for dropped notifications to be detected...");
|
||||
boolean acquired = lostCountSema.tryAcquire(3, 5, TimeUnit.SECONDS);
|
||||
assertEquals("Correct count of lost notifs", true, acquired);
|
||||
|
||||
n1 = notifQueue.poll(10, TimeUnit.SECONDS);
|
||||
assertEquals("Received non-dropped notif", n, n1);
|
||||
|
||||
assertEquals("Notif queue size", 0, notifQueue.size());
|
||||
assertEquals("Filter count after drops", 5, filterCount.get());
|
||||
|
||||
Thread.sleep(10);
|
||||
assertEquals("Further lost-notifs", 0, lostCountSema.availablePermits());
|
||||
|
||||
client.close();
|
||||
|
||||
System.out.println("TEST PASSED");
|
||||
}
|
||||
|
||||
private static AtomicLong nextSeqNo = new AtomicLong(0);
|
||||
private static Notification nextNotif(ObjectName name) {
|
||||
long n = nextSeqNo.incrementAndGet();
|
||||
return new Notification("type", name, n, "" + n);
|
||||
}
|
||||
|
||||
private static void assertEquals(String what, Object expected, Object got) {
|
||||
if (equals(expected, got))
|
||||
System.out.println(what + " = " + expected + ", as expected");
|
||||
else {
|
||||
Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
|
||||
for (Thread t : traces.keySet()) {
|
||||
System.out.println(t.getName());
|
||||
for (StackTraceElement elmt : traces.get(t)) {
|
||||
System.out.println(" " + elmt);
|
||||
}
|
||||
}
|
||||
throw new RuntimeException(
|
||||
"TEST FAILED: " + what + " is " + got + "; should be " +
|
||||
expected);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean equals(Object expected, Object got) {
|
||||
if (!(expected instanceof Notification))
|
||||
return expected.equals(got);
|
||||
if (expected.getClass() != got.getClass())
|
||||
return false;
|
||||
// Notification doesn't override Object.equals so two distinct
|
||||
// notifs are never equal even if they have the same contents.
|
||||
// Although the test doesn't serialize the notifs, if at some
|
||||
// stage it did then it would fail because the deserialized notif
|
||||
// was not equal to the original one. Therefore we compare enough
|
||||
// notif fields to detect when notifs really are different.
|
||||
Notification en = (Notification) expected;
|
||||
Notification gn = (Notification) got;
|
||||
return (en.getType().equals(gn.getType()) &&
|
||||
en.getSource().equals(gn.getSource()) &&
|
||||
en.getSequenceNumber() == gn.getSequenceNumber());
|
||||
}
|
||||
}
|
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 5108776
|
||||
* @summary Test that the various Executor parameters in an EventClient do
|
||||
* what they are supposed to.
|
||||
* @author Eamonn McManus
|
||||
*/
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.EventClient;
|
||||
import javax.management.event.EventClientDelegate;
|
||||
import javax.management.event.EventClientDelegateMBean;
|
||||
import javax.management.event.FetchingEventRelay;
|
||||
import javax.management.remote.MBeanServerForwarder;
|
||||
|
||||
public class EventClientExecutorTest {
|
||||
private static volatile String failure;
|
||||
private static final Set testedPrefixes = new HashSet();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Executor fetchExecutor = Executors.newSingleThreadExecutor(
|
||||
new NamedThreadFactory("FETCH"));
|
||||
Executor listenerExecutor = Executors.newSingleThreadExecutor(
|
||||
new NamedThreadFactory("LISTENER"));
|
||||
ScheduledExecutorService leaseScheduler =
|
||||
Executors.newSingleThreadScheduledExecutor(
|
||||
new NamedThreadFactory("LEASE"));
|
||||
|
||||
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
||||
MBeanServerForwarder mbsf = EventClientDelegate.newForwarder();
|
||||
mbsf.setMBeanServer(mbs);
|
||||
mbs = mbsf;
|
||||
|
||||
EventClientDelegateMBean ecd = EventClientDelegate.getProxy(mbs);
|
||||
ecd = (EventClientDelegateMBean) Proxy.newProxyInstance(
|
||||
EventClientDelegateMBean.class.getClassLoader(),
|
||||
new Class<?>[] {EventClientDelegateMBean.class},
|
||||
new DelegateCheckIH(ecd));
|
||||
|
||||
ObjectName mbeanName = new ObjectName("d:type=Notifier");
|
||||
Notifier notifier = new Notifier();
|
||||
mbs.registerMBean(notifier, mbeanName);
|
||||
|
||||
FetchingEventRelay eventRelay = new FetchingEventRelay(
|
||||
ecd, fetchExecutor);
|
||||
EventClient ec = new EventClient(
|
||||
ecd, eventRelay, listenerExecutor, leaseScheduler, 1000L);
|
||||
NotificationListener checkListener = new NotificationListener() {
|
||||
public void handleNotification(Notification notification,
|
||||
Object handback) {
|
||||
assertThreadName("listener dispatch", "LISTENER");
|
||||
}
|
||||
};
|
||||
ec.addNotificationListener(mbeanName, checkListener, null, null);
|
||||
|
||||
mbs.invoke(mbeanName, "send", null, null);
|
||||
|
||||
// Now wait until we have seen all three thread types.
|
||||
long deadline = System.currentTimeMillis() + 5000;
|
||||
synchronized (testedPrefixes) {
|
||||
while (testedPrefixes.size() < 3 && failure == null) {
|
||||
long remain = deadline - System.currentTimeMillis();
|
||||
if (remain <= 0) {
|
||||
fail("Timed out waiting for all three thread types to show, " +
|
||||
"saw only " + testedPrefixes);
|
||||
break;
|
||||
}
|
||||
try {
|
||||
testedPrefixes.wait(remain);
|
||||
} catch (InterruptedException e) {
|
||||
fail("Unexpected InterruptedException");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We deliberately don't close the EventClient to check that it has
|
||||
// not created any non-daemon threads.
|
||||
|
||||
if (failure != null)
|
||||
throw new Exception("TEST FAILED: " + failure);
|
||||
else
|
||||
System.out.println("TEST PASSED");
|
||||
}
|
||||
|
||||
public static interface NotifierMBean {
|
||||
public void send();
|
||||
}
|
||||
|
||||
public static class Notifier extends NotificationBroadcasterSupport
|
||||
implements NotifierMBean {
|
||||
public void send() {
|
||||
Notification n = new Notification("a.b.c", this, 0L);
|
||||
sendNotification(n);
|
||||
}
|
||||
}
|
||||
|
||||
static void fail(String why) {
|
||||
System.out.println("FAIL: " + why);
|
||||
failure = why;
|
||||
}
|
||||
|
||||
static void assertThreadName(String what, String prefix) {
|
||||
String name = Thread.currentThread().getName();
|
||||
if (!name.startsWith(prefix)) {
|
||||
fail("Wrong thread for " + what + ": " + name);
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (testedPrefixes) {
|
||||
if (testedPrefixes.add(prefix))
|
||||
testedPrefixes.notify();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DelegateCheckIH implements InvocationHandler {
|
||||
private final EventClientDelegateMBean ecd;
|
||||
|
||||
public DelegateCheckIH(EventClientDelegateMBean ecd) {
|
||||
this.ecd = ecd;
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Throwable {
|
||||
String methodName = method.getName();
|
||||
if (methodName.equals("fetchNotifications"))
|
||||
assertThreadName("fetchNotifications", "FETCH");
|
||||
else if (methodName.equals("lease"))
|
||||
assertThreadName("lease renewal", "LEASE");
|
||||
try {
|
||||
return method.invoke(ecd, args);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class NamedThreadFactory implements ThreadFactory {
|
||||
private final String namePrefix;
|
||||
private int count;
|
||||
|
||||
NamedThreadFactory(String namePrefix) {
|
||||
this.namePrefix = namePrefix;
|
||||
}
|
||||
|
||||
public synchronized Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r);
|
||||
t.setName(namePrefix + " " + ++count);
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,289 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 5108776
|
||||
* @summary Test that the EventClientDelegate MBean does not require extra
|
||||
* permissions compared with plain addNotificationListener.
|
||||
* @author Eamonn McManus
|
||||
* @run main/othervm -Dxjava.security.debug=policy,access,failure EventDelegateSecurityTest
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.AllPermission;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.management.MBeanPermission;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.EventClient;
|
||||
import javax.management.remote.JMXAuthenticator;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXPrincipal;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.MBeanServerForwarder;
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
public class EventDelegateSecurityTest {
|
||||
private static final BlockingQueue<Notification> notifQ =
|
||||
new SynchronousQueue<Notification>();
|
||||
|
||||
private static volatile long seqNo;
|
||||
private static volatile long expectSeqNo;
|
||||
|
||||
private static class QueueListener implements NotificationListener {
|
||||
public void handleNotification(Notification notification,
|
||||
Object handback) {
|
||||
try {
|
||||
notifQ.put(notification);
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static final NotificationListener queueListener = new QueueListener();
|
||||
|
||||
public static interface SenderMBean {
|
||||
public void send();
|
||||
}
|
||||
|
||||
public static class Sender
|
||||
extends NotificationBroadcasterSupport implements SenderMBean {
|
||||
public void send() {
|
||||
Notification n = new Notification("x", this, seqNo++);
|
||||
sendNotification(n);
|
||||
}
|
||||
}
|
||||
|
||||
private static class LimitInvocationHandler implements InvocationHandler {
|
||||
private MBeanServer nextMBS;
|
||||
private final Set<String> allowedMethods = new HashSet<String>();
|
||||
|
||||
void allow(String... names) {
|
||||
synchronized (allowedMethods) {
|
||||
allowedMethods.addAll(Arrays.asList(names));
|
||||
}
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method m, Object[] args)
|
||||
throws Throwable {
|
||||
System.out.println(
|
||||
"filter: " + m.getName() +
|
||||
((args == null) ? "[]" : Arrays.deepToString(args)));
|
||||
String name = m.getName();
|
||||
|
||||
if (name.equals("getMBeanServer"))
|
||||
return nextMBS;
|
||||
|
||||
if (name.equals("setMBeanServer")) {
|
||||
nextMBS = (MBeanServer) args[0];
|
||||
return null;
|
||||
}
|
||||
|
||||
if (m.getDeclaringClass() == Object.class ||
|
||||
allowedMethods.contains(name)) {
|
||||
try {
|
||||
return m.invoke(nextMBS, args);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
} else {
|
||||
System.out.println("...refused");
|
||||
throw new SecurityException(
|
||||
"Method refused: " + m.getDeclaringClass().getName() +
|
||||
"." + m.getName() +
|
||||
((args == null) ? "[]" : Arrays.deepToString(args)));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static interface MakeConnectorServer {
|
||||
public JMXConnectorServer make(JMXServiceURL url) throws IOException;
|
||||
}
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
JMXPrincipal rootPrincipal = new JMXPrincipal("root");
|
||||
Subject rootSubject = new Subject();
|
||||
rootSubject.getPrincipals().add(rootPrincipal);
|
||||
Subject.doAsPrivileged(rootSubject, new PrivilegedExceptionAction<Void>() {
|
||||
public Void run() throws Exception {
|
||||
mainAsRoot();
|
||||
return null;
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
private static void mainAsRoot() throws Exception {
|
||||
AccessControlContext acc = AccessController.getContext();
|
||||
Subject subject = Subject.getSubject(acc);
|
||||
System.out.println("Subject: " + subject);
|
||||
final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||
ObjectName name = new ObjectName("a:b=c");
|
||||
mbs.registerMBean(new Sender(), name);
|
||||
|
||||
System.out.println("Test with no installed security");
|
||||
test(mbs, name, new MakeConnectorServer() {
|
||||
public JMXConnectorServer make(JMXServiceURL url) throws IOException {
|
||||
return
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, null, null);
|
||||
}
|
||||
});
|
||||
|
||||
System.out.println("Test with filtering MBeanServerForwarder");
|
||||
LimitInvocationHandler limitIH = new LimitInvocationHandler();
|
||||
// We allow getClassLoaderRepository because the ConnectorServer
|
||||
// calls it so any real checking MBeanServerForwarder must accept it.
|
||||
limitIH.allow(
|
||||
"addNotificationListener", "removeNotificationListener",
|
||||
"getClassLoaderRepository"
|
||||
);
|
||||
final MBeanServerForwarder limitMBSF = (MBeanServerForwarder)
|
||||
Proxy.newProxyInstance(
|
||||
MBeanServerForwarder.class.getClassLoader(),
|
||||
new Class<?>[] {MBeanServerForwarder.class},
|
||||
limitIH);
|
||||
// We go to considerable lengths to ensure that the ConnectorServer has
|
||||
// no MBeanServer when the EventClientDelegate forwarder is activated,
|
||||
// so that the calls it makes when it is later linked to an MBeanServer
|
||||
// go through the limitMBSF.
|
||||
test(mbs, name, new MakeConnectorServer() {
|
||||
public JMXConnectorServer make(JMXServiceURL url) throws IOException {
|
||||
JMXConnectorServer cs =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, null, null);
|
||||
limitMBSF.setMBeanServer(mbs);
|
||||
cs.setMBeanServerForwarder(limitMBSF);
|
||||
return cs;
|
||||
}
|
||||
});
|
||||
|
||||
final File policyFile =
|
||||
File.createTempFile("EventDelegateSecurityTest", ".policy");
|
||||
PrintWriter pw = new PrintWriter(policyFile);
|
||||
String JMXPrincipal = JMXPrincipal.class.getName();
|
||||
String AllPermission = AllPermission.class.getName();
|
||||
String MBeanPermission = MBeanPermission.class.getName();
|
||||
pw.println("grant principal " + JMXPrincipal + " \"root\" {");
|
||||
pw.println(" permission " + AllPermission + ";");
|
||||
pw.println("};");
|
||||
pw.println("grant principal " + JMXPrincipal + " \"user\" {");
|
||||
pw.println(" permission " + MBeanPermission + " \"*\", " +
|
||||
" \"addNotificationListener\";");
|
||||
pw.println(" permission " + MBeanPermission + " \"*\", " +
|
||||
" \"removeNotificationListener\";");
|
||||
pw.println("};");
|
||||
pw.close();
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
policyFile.delete();
|
||||
}
|
||||
});
|
||||
System.setProperty("java.security.policy", policyFile.getAbsolutePath());
|
||||
System.setSecurityManager(new SecurityManager());
|
||||
test(mbs, name, new MakeConnectorServer() {
|
||||
public JMXConnectorServer make(JMXServiceURL url) throws IOException {
|
||||
Map<String, Object> env = new HashMap<String, Object>();
|
||||
env.put(JMXConnectorServer.AUTHENTICATOR, new JMXAuthenticator() {
|
||||
public Subject authenticate(Object credentials) {
|
||||
Subject s = new Subject();
|
||||
s.getPrincipals().add(new JMXPrincipal("user"));
|
||||
return s;
|
||||
}
|
||||
});
|
||||
return
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, env, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void test(MBeanServer mbs, ObjectName name) throws Exception {
|
||||
test(mbs, name, null);
|
||||
}
|
||||
|
||||
private static void test(
|
||||
MBeanServer mbs, ObjectName name, MakeConnectorServer make)
|
||||
throws Exception {
|
||||
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///");
|
||||
JMXConnectorServer cs = make.make(url);
|
||||
ObjectName csName = new ObjectName("a:type=ConnectorServer");
|
||||
mbs.registerMBean(cs, csName);
|
||||
cs.start();
|
||||
try {
|
||||
JMXServiceURL addr = cs.getAddress();
|
||||
JMXConnector cc = JMXConnectorFactory.connect(addr);
|
||||
MBeanServerConnection mbsc = cc.getMBeanServerConnection();
|
||||
test(mbs, mbsc, name);
|
||||
cc.close();
|
||||
mbs.unregisterMBean(csName);
|
||||
} finally {
|
||||
cs.stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static void test(
|
||||
MBeanServer mbs, MBeanServerConnection mbsc, ObjectName name)
|
||||
throws Exception {
|
||||
EventClient ec = new EventClient(mbsc);
|
||||
ec.addNotificationListener(name, queueListener, null, null);
|
||||
mbs.invoke(name, "send", null, null);
|
||||
|
||||
Notification n = notifQ.poll(5, TimeUnit.SECONDS);
|
||||
if (n == null)
|
||||
throw new Exception("FAILED: notif not delivered");
|
||||
if (n.getSequenceNumber() != expectSeqNo) {
|
||||
throw new Exception(
|
||||
"FAILED: notif seqno " + n.getSequenceNumber() +
|
||||
" should be " + expectSeqNo);
|
||||
}
|
||||
expectSeqNo++;
|
||||
|
||||
ec.removeNotificationListener(name, queueListener);
|
||||
ec.close();
|
||||
}
|
||||
}
|
221
jdk/test/javax/management/eventService/EventManagerTest.java
Normal file
221
jdk/test/javax/management/eventService/EventManagerTest.java
Normal file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test EventManagerTest.java 1.8 08/01/22
|
||||
* @bug 5108776
|
||||
* @summary Basic test for EventManager.
|
||||
* @author Shanliang JIANG
|
||||
* @run clean EventManagerTest
|
||||
* @run build EventManagerTest
|
||||
* @run main EventManagerTest
|
||||
*/
|
||||
|
||||
import javax.management.MBeanNotificationInfo;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.*;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class EventManagerTest {
|
||||
private static MBeanServer mbeanServer;
|
||||
private static ObjectName emitter;
|
||||
private static JMXServiceURL url;
|
||||
private static JMXConnectorServer server;
|
||||
private static JMXConnector conn;
|
||||
private static MBeanServerConnection client;
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println(">>> EventManagerTest-main basic tests ...");
|
||||
mbeanServer = MBeanServerFactory.createMBeanServer();
|
||||
|
||||
// for 1.5
|
||||
if (System.getProperty("java.version").startsWith("1.5") &&
|
||||
!mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
|
||||
System.out.print("Working on "+System.getProperty("java.version")+
|
||||
" register "+EventClientDelegateMBean.OBJECT_NAME);
|
||||
|
||||
mbeanServer.registerMBean(EventClientDelegate.
|
||||
getEventClientDelegate(mbeanServer),
|
||||
EventClientDelegateMBean.OBJECT_NAME);
|
||||
}
|
||||
|
||||
emitter = new ObjectName("Default:name=NotificationEmitter");
|
||||
|
||||
url = new JMXServiceURL("rmi", null, 0) ;
|
||||
server =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer);
|
||||
server.start();
|
||||
|
||||
url = server.getAddress();
|
||||
conn = JMXConnectorFactory.connect(url, null);
|
||||
client = conn.getMBeanServerConnection();
|
||||
|
||||
mbeanServer.registerMBean(new NotificationEmitter(), emitter);
|
||||
|
||||
boolean succeed;
|
||||
|
||||
System.out.println(">>> EventManagerTest-main: using the fetching EventRelay...");
|
||||
succeed = test(new EventClient(client));
|
||||
|
||||
System.out.println(">>> EventManagerTest-main: using the pushing EventRelay...");
|
||||
EventClientDelegateMBean ecd = EventClientDelegate.getProxy(client);
|
||||
succeed &= test(new EventClient(ecd,
|
||||
new RMIPushEventRelay(ecd),
|
||||
null, null,
|
||||
EventClient.DEFAULT_LEASE_TIMEOUT));
|
||||
|
||||
conn.close();
|
||||
server.stop();
|
||||
|
||||
if (succeed) {
|
||||
System.out.println(">>> EventManagerTest-main: PASSE!");
|
||||
} else {
|
||||
System.out.println("\n>>> EventManagerTest-main: FAILED!");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean test(EventClient efClient) throws Exception {
|
||||
// add listener from the client side
|
||||
Listener listener = new Listener();
|
||||
efClient.subscribe(emitter, listener, null, null);
|
||||
|
||||
// ask to send notifs
|
||||
Object[] params = new Object[] {new Integer(sendNB)};
|
||||
String[] signatures = new String[] {"java.lang.Integer"};
|
||||
client.invoke(emitter, "sendNotifications", params, signatures);
|
||||
|
||||
// waiting
|
||||
long toWait = 6000;
|
||||
long stopTime = System.currentTimeMillis() + toWait;
|
||||
|
||||
synchronized(listener) {
|
||||
while(listener.received < sendNB && toWait > 0) {
|
||||
listener.wait(toWait);
|
||||
toWait = stopTime - System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
// clean
|
||||
System.out.println(">>> EventManagerTest-test: cleaning...");
|
||||
efClient.unsubscribe(emitter, listener);
|
||||
efClient.close();
|
||||
|
||||
if (listener.received != sendNB) {
|
||||
System.out.println(">>> EventManagerTest-test: FAILED! Expected to receive "+sendNB+", but got "+listener.received);
|
||||
|
||||
return false;
|
||||
} else if (listener.seqErr > 0) {
|
||||
System.out.println(">>> EventManagerTest-test: FAILED! The receiving sequence is not correct.");
|
||||
|
||||
return false;
|
||||
} else {
|
||||
System.out.println(">>> EventManagerTest-test: got all expected "+listener.received);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Listener implements NotificationListener {
|
||||
public int received = 0;
|
||||
public int seqErr = 0;
|
||||
|
||||
private long lastSeq = -1;
|
||||
|
||||
public void handleNotification(Notification notif, Object handback) {
|
||||
if (!myType.equals(notif.getType())) {
|
||||
System.out.println(">>> EventManagerTest-Listener: got unexpected notif: "+notif);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (lastSeq == -1) {
|
||||
lastSeq = notif.getSequenceNumber();
|
||||
} else if (notif.getSequenceNumber() - lastSeq++ != 1) {
|
||||
seqErr++;
|
||||
}
|
||||
|
||||
//System.out.println(">>> EventManagerTest-Listener: got notif "+notif.getSequenceNumber());
|
||||
|
||||
synchronized(this) {
|
||||
if (++received >= sendNB) {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class NotificationEmitter extends NotificationBroadcasterSupport
|
||||
implements NotificationEmitterMBean {
|
||||
|
||||
public MBeanNotificationInfo[] getNotificationInfo() {
|
||||
final String[] ntfTypes = {myType};
|
||||
|
||||
final MBeanNotificationInfo[] ntfInfoArray = {
|
||||
new MBeanNotificationInfo(ntfTypes,
|
||||
"javax.management.Notification",
|
||||
"Notifications sent by the NotificationEmitter")};
|
||||
|
||||
return ntfInfoArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Notification objects.
|
||||
*
|
||||
* @param nb The number of notifications to send
|
||||
*/
|
||||
public void sendNotifications(Integer nb) {
|
||||
Notification notif;
|
||||
for (int i=1; i<=nb.intValue(); i++) {
|
||||
notif = new Notification(myType, this, count++);
|
||||
notif.setUserData("jsl");
|
||||
//System.out.println(">>> EventManagerService-NotificationEmitter-sendNotifications: "+i);
|
||||
|
||||
sendNotification(notif);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface NotificationEmitterMBean {
|
||||
public void sendNotifications(Integer nb);
|
||||
}
|
||||
|
||||
private static int sendNB = 120;
|
||||
private static long count = 0;
|
||||
|
||||
private static final String myType = "notification.my_notification";
|
||||
}
|
276
jdk/test/javax/management/eventService/FetchingTest.java
Normal file
276
jdk/test/javax/management/eventService/FetchingTest.java
Normal file
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 5108776
|
||||
* @summary Basic test for EventClient.
|
||||
* @author Shanliang JIANG
|
||||
* @run clean FetchingTest MyFetchingEventForwarder
|
||||
* @run build FetchingTest MyFetchingEventForwarder
|
||||
* @run main FetchingTest MyFetchingEventForwarder
|
||||
*/
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.EventClient;
|
||||
import javax.management.event.EventClientDelegate;
|
||||
import javax.management.event.EventClientDelegateMBean;
|
||||
import javax.management.event.FetchingEventRelay;
|
||||
import javax.management.event.RMIPushEventForwarder;
|
||||
import javax.management.event.RMIPushServer;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
public class FetchingTest {
|
||||
private static MBeanServer mbeanServer;
|
||||
private static ObjectName emitter;
|
||||
private static JMXServiceURL url;
|
||||
private static JMXConnectorServer server;
|
||||
private static JMXConnector conn;
|
||||
private static MBeanServerConnection client;
|
||||
private static long WAITING_TIME = 6000;
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
System.out.println(">>> FetchingTest-main basic tests ...");
|
||||
mbeanServer = MBeanServerFactory.createMBeanServer();
|
||||
|
||||
// for 1.5
|
||||
if (System.getProperty("java.version").startsWith("1.5") &&
|
||||
!mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
|
||||
System.out.print("Working on "+System.getProperty("java.version")+
|
||||
" register "+EventClientDelegateMBean.OBJECT_NAME);
|
||||
|
||||
mbeanServer.registerMBean(EventClientDelegate.
|
||||
getEventClientDelegate(mbeanServer),
|
||||
EventClientDelegateMBean.OBJECT_NAME);
|
||||
}
|
||||
|
||||
emitter = new ObjectName("Default:name=NotificationEmitter");
|
||||
mbeanServer.registerMBean(new NotificationEmitter(), emitter);
|
||||
boolean succeed = true;
|
||||
|
||||
final String[] protos = new String[] {"rmi", "iiop", "jmxmp"};
|
||||
for (String proto : protos) {
|
||||
System.out.println(">>> FetchingTest-main: testing on "+proto);
|
||||
|
||||
try {
|
||||
url = new JMXServiceURL(proto, null, 0) ;
|
||||
server = JMXConnectorServerFactory.
|
||||
newJMXConnectorServer(url, null, mbeanServer);
|
||||
server.start();
|
||||
} catch (Exception e) {
|
||||
// OK
|
||||
System.out.println(">>> FetchingTest-main: skip the proto "+proto);
|
||||
continue;
|
||||
}
|
||||
|
||||
url = server.getAddress();
|
||||
conn = JMXConnectorFactory.connect(url, null);
|
||||
client = conn.getMBeanServerConnection();
|
||||
|
||||
succeed &= test();
|
||||
|
||||
conn.close();
|
||||
server.stop();
|
||||
|
||||
System.out.println(
|
||||
">>> FetchingTest-main: testing on "+proto+" done.");
|
||||
}
|
||||
|
||||
if (succeed) {
|
||||
System.out.println(">>> FetchingTest-main: PASSED!");
|
||||
} else {
|
||||
System.out.println("\n>>> FetchingTest-main: FAILED!");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean test() throws Exception {
|
||||
System.out.println(">>> FetchingTest-test: " +
|
||||
"using the default fetching forwarder ...");
|
||||
EventClient eventClient =
|
||||
new EventClient(client);
|
||||
|
||||
Listener listener = new Listener();
|
||||
eventClient.addNotificationListener(emitter, listener, null, null);
|
||||
|
||||
// ask to send notifs
|
||||
Object[] params = new Object[] {new Integer(sendNB)};
|
||||
String[] signatures = new String[] {"java.lang.Integer"};
|
||||
conn.getMBeanServerConnection().invoke(emitter,
|
||||
"sendNotifications", params, signatures);
|
||||
|
||||
if (listener.waitNotif(WAITING_TIME) != sendNB) {
|
||||
System.out.println(
|
||||
">>> FetchingTest-test: FAILED! Expected to receive "+
|
||||
sendNB+", but got "+listener.received);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
System.out.println(
|
||||
">>> ListenerTest-test: got all expected "+listener.received);
|
||||
//eventClient.removeNotificationListener(emitter, listener);
|
||||
eventClient.close();
|
||||
|
||||
System.out.println(">>> FetchingTest-test: " +
|
||||
"using a user specific List ...");
|
||||
|
||||
FetchingEventRelay fer = new FetchingEventRelay(
|
||||
EventClientDelegate.getProxy(client),
|
||||
1000, 1000L, 1000, null,
|
||||
MyFetchingEventForwarder.class.getName(),
|
||||
null, null);
|
||||
|
||||
eventClient = new EventClient(
|
||||
EventClientDelegate.getProxy(client), fer, null, null, 10000);
|
||||
|
||||
eventClient.addNotificationListener(emitter, listener, null, null);
|
||||
listener.received = 0;
|
||||
|
||||
conn.getMBeanServerConnection().invoke(emitter,
|
||||
"sendNotifications", params, signatures);
|
||||
|
||||
if (listener.waitNotif(WAITING_TIME) != sendNB) {
|
||||
System.out.println(
|
||||
">>> FetchingTest-test: FAILED! Expected to receive "+
|
||||
sendNB+", but got "+listener.received);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
System.out.println(
|
||||
">>> FetchingTest-test: got all expected "+listener.received);
|
||||
|
||||
if (!MyFetchingEventForwarder.shared.isUsed()) {
|
||||
System.out.println(
|
||||
">>> FetchingTest-test: FAILED! The user specific list" +
|
||||
"is not used!");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
System.out.println(">>> Negative test to add an EventClient" +
|
||||
" with a non EventForwarder object.");
|
||||
try {
|
||||
MyFetchingEventForwarder.shared.setAgain();
|
||||
|
||||
System.out.println(
|
||||
">>> FetchingTest-test: FAILED! No expected exception" +
|
||||
"when setting the list after the forwarder started.");
|
||||
|
||||
return false;
|
||||
} catch (IllegalStateException ise) {
|
||||
// OK
|
||||
System.out.println(
|
||||
">>> FetchingTest-test: Got expected exception: " + ise);
|
||||
}
|
||||
|
||||
eventClient.close();
|
||||
|
||||
try {
|
||||
fer = new FetchingEventRelay(
|
||||
EventClientDelegate.getProxy(client),
|
||||
1000, 1000L, 1000, null,
|
||||
Object.class.getName(),
|
||||
null, null);
|
||||
|
||||
eventClient = new EventClient(
|
||||
EventClientDelegate.getProxy(client), fer, null, null, 10000);
|
||||
|
||||
System.out.println(
|
||||
">>> FetchingTest-test: FAILED! No expected exception" +
|
||||
"when creating an illegal EventForwarder");
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// OK
|
||||
// iae.printStackTrace();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class Listener implements NotificationListener {
|
||||
public void handleNotification(Notification notif, Object handback) {
|
||||
synchronized(this) {
|
||||
if (++received >= sendNB) {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
//System.out.println(">>> FetchingTest-Listener: received = "+received);
|
||||
}
|
||||
|
||||
public int waitNotif(long timeout) throws Exception {
|
||||
synchronized(this) {
|
||||
long stopTime = System.currentTimeMillis() + timeout;
|
||||
long toWait = timeout;
|
||||
while (toWait > 0 && received < sendNB) {
|
||||
this.wait(toWait);
|
||||
toWait = stopTime - System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
return received;
|
||||
}
|
||||
|
||||
public static int received = 0;
|
||||
}
|
||||
|
||||
public static class NotificationEmitter extends NotificationBroadcasterSupport
|
||||
implements NotificationEmitterMBean {
|
||||
|
||||
public void sendNotifications(Integer nb) {
|
||||
System.out.println(
|
||||
">>> FetchingTest-NotificationEmitter-sendNotifications: "+nb);
|
||||
Notification notif;
|
||||
for (int i=1; i<=nb.intValue(); i++) {
|
||||
notif = new Notification(myType, this, count++);
|
||||
sendNotification(notif);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface NotificationEmitterMBean {
|
||||
public void sendNotifications(Integer nb);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static int sendNB = 20;
|
||||
private static int count = 0;
|
||||
|
||||
private static final String myType = "notification.my_notification";
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6717789
|
||||
* @summary Check that a lock is not held when a LeaseManager expires.
|
||||
* @author Eamonn McManus
|
||||
*/
|
||||
|
||||
import com.sun.jmx.event.LeaseManager;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class LeaseManagerDeadlockTest {
|
||||
public static String failure;
|
||||
public static LeaseManager leaseManager;
|
||||
public static Semaphore callbackThreadCompleted = new Semaphore(0);
|
||||
public static Object lock = new Object();
|
||||
|
||||
public static Runnable triggerDeadlock = new Runnable() {
|
||||
public void run() {
|
||||
Runnable pingLeaseManager = new Runnable() {
|
||||
public void run() {
|
||||
System.out.println("Ping thread starts");
|
||||
synchronized (lock) {
|
||||
leaseManager.lease(1);
|
||||
}
|
||||
System.out.println("Ping thread completes");
|
||||
}
|
||||
};
|
||||
Thread t = new Thread(pingLeaseManager);
|
||||
t.start();
|
||||
try {
|
||||
Thread.sleep(10); // enough time for ping thread to grab lock
|
||||
synchronized (lock) {
|
||||
t.join();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
fail(e.toString());
|
||||
}
|
||||
System.out.println("Callback thread completes");
|
||||
callbackThreadCompleted.release();
|
||||
}
|
||||
};
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// Also test that we can shorten the lease from its initial value.
|
||||
leaseManager = new LeaseManager(triggerDeadlock, 1000000);
|
||||
leaseManager.lease(1L);
|
||||
|
||||
boolean callbackRan =
|
||||
callbackThreadCompleted.tryAcquire(3, TimeUnit.SECONDS);
|
||||
|
||||
if (!callbackRan) {
|
||||
fail("Callback did not complete - probable deadlock");
|
||||
ThreadMXBean threads = ManagementFactory.getThreadMXBean();
|
||||
System.out.println(Arrays.toString(threads.findDeadlockedThreads()));
|
||||
System.out.println("PRESS RETURN");
|
||||
System.in.read();
|
||||
}
|
||||
|
||||
if (failure == null)
|
||||
System.out.println("TEST PASSED");
|
||||
else
|
||||
throw new Exception("TEST FAILED: " + failure);
|
||||
}
|
||||
|
||||
public static void fail(String why) {
|
||||
System.out.println("TEST FAILS: " + why);
|
||||
failure = why;
|
||||
}
|
||||
}
|
361
jdk/test/javax/management/eventService/LeaseTest.java
Normal file
361
jdk/test/javax/management/eventService/LeaseTest.java
Normal file
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test LeaseTest.java 1.6 08/01/22
|
||||
* @bug 5108776
|
||||
* @summary Basic test for Event service leasing.
|
||||
* @author Shanliang JIANG
|
||||
* @run clean LeaseTest
|
||||
* @run build LeaseTest
|
||||
* @run main LeaseTest
|
||||
*/
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.management.ListenerNotFoundException;
|
||||
import javax.management.MBeanNotificationInfo;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.EventClient;
|
||||
import javax.management.event.EventClientDelegate;
|
||||
import javax.management.event.EventClientDelegateMBean;
|
||||
import javax.management.event.EventClientNotFoundException;
|
||||
import javax.management.event.FetchingEventRelay;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
public class LeaseTest {
|
||||
|
||||
private static MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer();
|
||||
private static List<Notification> notifList = new ArrayList<Notification>();
|
||||
private static ObjectName emitter;
|
||||
private static NotificationEmitter emitterImpl;
|
||||
private static JMXServiceURL url;
|
||||
private static JMXConnectorServer server;
|
||||
private static JMXConnector conn;
|
||||
private static Listener listener = new Listener();
|
||||
|
||||
private static long leaseTime = 100;
|
||||
private static final int multiple = 5;
|
||||
private static final long bigWaiting = 6000;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println(">>> Test the event service lease");
|
||||
|
||||
// for 1.5
|
||||
if (System.getProperty("java.version").startsWith("1.5") &&
|
||||
!mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
|
||||
System.out.print("Working on "+System.getProperty("java.version")+
|
||||
" register "+EventClientDelegateMBean.OBJECT_NAME);
|
||||
|
||||
mbeanServer.registerMBean(EventClientDelegate.
|
||||
getEventClientDelegate(mbeanServer),
|
||||
EventClientDelegateMBean.OBJECT_NAME);
|
||||
}
|
||||
|
||||
System.setProperty("com.sun.event.lease.time",
|
||||
String.valueOf(leaseTime));
|
||||
emitter = new ObjectName("Default:name=NotificationEmitter");
|
||||
emitterImpl = new NotificationEmitter();
|
||||
mbeanServer.registerMBean(emitterImpl, emitter);
|
||||
|
||||
String[] types = new String[]{"PushingEventRelay", "FetchingEventRelay"};
|
||||
String[] protos = new String[]{"rmi", "iiop", "jmxmp"};
|
||||
for (String prot : protos) {
|
||||
url = new JMXServiceURL(prot, null, 0);
|
||||
|
||||
try {
|
||||
server =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url,
|
||||
null, mbeanServer);
|
||||
server.start();
|
||||
} catch (Exception e) {
|
||||
System.out.println(">>> Skip "+prot+", not support.");
|
||||
continue;
|
||||
}
|
||||
|
||||
url = server.getAddress();
|
||||
|
||||
try {
|
||||
for (String type: types) {
|
||||
test(type);
|
||||
}
|
||||
} finally {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void test(String type) throws Exception {
|
||||
System.out.println("\n\n>>> Testing "+type+" on "+url+" ...");
|
||||
newConn();
|
||||
EventClient ec = newEventClient(type);
|
||||
|
||||
ec.addNotificationListener(emitter,
|
||||
listener, null, null);
|
||||
|
||||
System.out.println(">>> Send a notification and should receive it.");
|
||||
emitterImpl.sendNotif(++counter);
|
||||
|
||||
if (!waitNotif(bigWaiting, counter)) {
|
||||
throw new RuntimeException(">>> Failed to receive notif.");
|
||||
}
|
||||
|
||||
System.out.println(">>> Sleep 3 times of requested lease time.");
|
||||
Thread.sleep(leaseTime*3);
|
||||
System.out.println(">>> Send again a notification and should receive it.");
|
||||
emitterImpl.sendNotif(++counter);
|
||||
|
||||
if (!waitNotif(bigWaiting, counter)) {
|
||||
throw new RuntimeException(">>> Failed to receive notif.");
|
||||
}
|
||||
|
||||
System.out.println(">>> Close the client connection: "+
|
||||
conn.getConnectionId());
|
||||
conn.close();
|
||||
|
||||
System.out.println(">>> Waiting lease timeout to do clean.");
|
||||
|
||||
if (!emitterImpl.waitingClean(leaseTime*multiple)) {
|
||||
throw new RuntimeException(
|
||||
">>> The event lease failed to do clean: "+
|
||||
emitterImpl.listenerSize);
|
||||
} else {
|
||||
System.out.println(">>> The listener has been removed.");
|
||||
}
|
||||
|
||||
// Check that the client id has indeed been removed, by trying to
|
||||
// remove it again, which should fail.
|
||||
newConn();
|
||||
try {
|
||||
EventClientDelegateMBean proxy =
|
||||
EventClientDelegate.getProxy(conn.getMBeanServerConnection());
|
||||
proxy.removeClient(ec.getEventRelay().getClientId());
|
||||
|
||||
throw new RuntimeException(
|
||||
">>> The client id is not removed.");
|
||||
} catch (EventClientNotFoundException ecnfe) {
|
||||
// OK
|
||||
System.out.println(">>> The client id has been removed.");
|
||||
}
|
||||
conn.close();
|
||||
|
||||
System.out.println(">>> Reconnect to the server.");
|
||||
newConn();
|
||||
|
||||
System.out.println(">>> Create a new EventClient and add the listeners" +
|
||||
" in the failed EventClient into new EventClient");
|
||||
EventClient newEC = newEventClient(type);
|
||||
newEC.addListeners(ec.getListeners());
|
||||
// We expect ec.close() to get IOException because we closed the
|
||||
// underlying connection.
|
||||
try {
|
||||
ec.close();
|
||||
throw new RuntimeException(">>> EventClient.close did not throw " +
|
||||
"expected IOException");
|
||||
} catch (IOException e) {
|
||||
System.out.println(">>> EventClient.close threw expected exception: " + e);
|
||||
}
|
||||
|
||||
emitterImpl.sendNotif(++counter);
|
||||
|
||||
if (!waitNotif(bigWaiting, counter)) {
|
||||
throw new RuntimeException(">>> The event client failed to add " +
|
||||
"all old registered listeners after re-connection.");
|
||||
} else {
|
||||
System.out.println(">>> Successfully received notification from" +
|
||||
" new EventClient.");
|
||||
}
|
||||
|
||||
System.out.println(">>> Clean the failed EventClient.");
|
||||
ec.close();
|
||||
if (ec.getListeners().size() != 0) {
|
||||
throw new RuntimeException(">>> The event client fails to do clean.");
|
||||
}
|
||||
|
||||
System.out.println(">>> Clean the new EventClient.");
|
||||
newEC.close();
|
||||
if (newEC.getListeners().size() != 0) {
|
||||
throw new RuntimeException(">>> The event client fails to do clean.");
|
||||
}
|
||||
|
||||
conn.close();
|
||||
System.out.println(">>> Testing "+type+" on "+url+" ... done");
|
||||
}
|
||||
|
||||
private static boolean waitNotif(long time, int sequenceNumber)
|
||||
throws Exception {
|
||||
synchronized(notifList) {
|
||||
if (search(sequenceNumber)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
long stopTime = System.currentTimeMillis() + time;
|
||||
long toWait = time;
|
||||
while (toWait > 0) {
|
||||
notifList.wait(toWait);
|
||||
|
||||
if (search(sequenceNumber)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
toWait = stopTime - System.currentTimeMillis();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean search(int sequenceNumber) {
|
||||
while(notifList.size() > 0) {
|
||||
Notification n = notifList.remove(0);
|
||||
if (n.getSequenceNumber() == sequenceNumber) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
// private classes
|
||||
//--------------------------
|
||||
|
||||
private static class Listener implements NotificationListener {
|
||||
public void handleNotification(Notification notif, Object handback) {
|
||||
synchronized (notifList) {
|
||||
notifList.add(notif);
|
||||
notifList.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class NotificationEmitter extends NotificationBroadcasterSupport
|
||||
implements NotificationEmitterMBean {
|
||||
|
||||
public MBeanNotificationInfo[] getNotificationInfo() {
|
||||
final String[] ntfTypes = {myType};
|
||||
|
||||
final MBeanNotificationInfo[] ntfInfoArray = {
|
||||
new MBeanNotificationInfo(ntfTypes,
|
||||
"javax.management.Notification",
|
||||
"Notifications sent by the NotificationEmitter")};
|
||||
|
||||
return ntfInfoArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Notification objects.
|
||||
*
|
||||
* @param nb The number of notifications to send
|
||||
*/
|
||||
public void sendNotif(int sequenceNumber) {
|
||||
Notification notif = new Notification(myType, this, sequenceNumber);
|
||||
sendNotification(notif);
|
||||
}
|
||||
|
||||
public void addNotificationListener(NotificationListener listener,
|
||||
NotificationFilter filter, Object handback) {
|
||||
super.addNotificationListener(listener, filter, handback);
|
||||
|
||||
listenerSize++;
|
||||
}
|
||||
|
||||
public void removeNotificationListener(NotificationListener listener)
|
||||
throws ListenerNotFoundException {
|
||||
super.removeNotificationListener(listener);
|
||||
listenerSize--;
|
||||
|
||||
synchronized(this) {
|
||||
if (listenerSize == 0) {
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeNotificationListener(NotificationListener listener,
|
||||
NotificationFilter filter, Object handback)
|
||||
throws ListenerNotFoundException {
|
||||
super.removeNotificationListener(listener, filter, handback);
|
||||
listenerSize--;
|
||||
|
||||
synchronized(this) {
|
||||
if (listenerSize == 0) {
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean waitingClean(long timeout) throws Exception {
|
||||
synchronized(this) {
|
||||
long stopTime = System.currentTimeMillis() + timeout;
|
||||
long toWait = timeout;
|
||||
while (listenerSize != 0 && toWait > 0) {
|
||||
this.wait(toWait);
|
||||
toWait = stopTime - System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
return listenerSize == 0;
|
||||
}
|
||||
|
||||
public int listenerSize = 0;
|
||||
|
||||
private final String myType = "notification.my_notification";
|
||||
}
|
||||
|
||||
public interface NotificationEmitterMBean {
|
||||
public void sendNotif(int sequenceNumber);
|
||||
}
|
||||
|
||||
private static void newConn() throws IOException {
|
||||
conn = JMXConnectorFactory.connect(url);
|
||||
}
|
||||
|
||||
private static EventClient newEventClient(String type) throws Exception {
|
||||
EventClientDelegateMBean proxy =
|
||||
EventClientDelegate.getProxy(conn.getMBeanServerConnection());
|
||||
if (type.equals("PushingEventRelay")) {
|
||||
return new EventClient(proxy,
|
||||
new FetchingEventRelay(proxy), null, null, leaseTime);
|
||||
} else if (type.equals("FetchingEventRelay")) {
|
||||
return new EventClient(proxy,
|
||||
new FetchingEventRelay(proxy), null, null, leaseTime);
|
||||
} else {
|
||||
throw new RuntimeException("Wrong event client type: "+type);
|
||||
}
|
||||
}
|
||||
|
||||
private static int counter = 0;
|
||||
}
|
224
jdk/test/javax/management/eventService/ListenerTest.java
Normal file
224
jdk/test/javax/management/eventService/ListenerTest.java
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test ListenerTest.java 1.7 08/01/22
|
||||
* @bug 5108776
|
||||
* @summary Basic test for EventClient.
|
||||
* @author Shanliang JIANG
|
||||
* @run clean ListenerTest
|
||||
* @run build ListenerTest
|
||||
* @run main ListenerTest
|
||||
*/
|
||||
|
||||
import javax.management.MBeanNotificationInfo;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.*;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class ListenerTest {
|
||||
private static MBeanServer mbeanServer;
|
||||
private static ObjectName emitter;
|
||||
private static JMXServiceURL url;
|
||||
private static JMXConnectorServer server;
|
||||
private static JMXConnector conn;
|
||||
private static MBeanServerConnection client;
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
System.out.println(">>> ListenerTest-main basic tests ...");
|
||||
mbeanServer = MBeanServerFactory.createMBeanServer();
|
||||
|
||||
// for 1.5
|
||||
if (System.getProperty("java.version").startsWith("1.5") &&
|
||||
!mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
|
||||
System.out.print("Working on "+System.getProperty("java.version")+
|
||||
" register "+EventClientDelegateMBean.OBJECT_NAME);
|
||||
|
||||
mbeanServer.registerMBean(EventClientDelegate.
|
||||
getEventClientDelegate(mbeanServer),
|
||||
EventClientDelegateMBean.OBJECT_NAME);
|
||||
}
|
||||
|
||||
emitter = new ObjectName("Default:name=NotificationEmitter");
|
||||
|
||||
url = new JMXServiceURL("rmi", null, 0) ;
|
||||
server =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer);
|
||||
server.start();
|
||||
|
||||
url = server.getAddress();
|
||||
conn = JMXConnectorFactory.connect(url, null);
|
||||
client = conn.getMBeanServerConnection();
|
||||
|
||||
mbeanServer.registerMBean(new NotificationEmitter(), emitter);
|
||||
|
||||
boolean succeed;
|
||||
|
||||
System.out.println(">>> ListenerTest-main: using the fetching EventRelay...");
|
||||
succeed = test(new EventClient(client));
|
||||
|
||||
System.out.println(">>> ListenerTest-main: using the pushing EventRelay...");
|
||||
EventClientDelegateMBean ecd = EventClientDelegate.getProxy(client);
|
||||
succeed &= test(new EventClient(ecd,
|
||||
new RMIPushEventRelay(ecd),
|
||||
null, null,
|
||||
EventClient.DEFAULT_LEASE_TIMEOUT));
|
||||
|
||||
conn.close();
|
||||
server.stop();
|
||||
|
||||
if (succeed) {
|
||||
System.out.println(">>> ListenerTest-main: PASSED!");
|
||||
} else {
|
||||
System.out.println("\n>>> ListenerTest-main: FAILED!");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean test(EventClient efClient) throws Exception {
|
||||
// add listener from the client side
|
||||
Listener listener = new Listener();
|
||||
efClient.addNotificationListener(emitter, listener, null, null);
|
||||
|
||||
// ask to send notifs
|
||||
Object[] params = new Object[] {new Integer(sendNB)};
|
||||
String[] signatures = new String[] {"java.lang.Integer"};
|
||||
client.invoke(emitter, "sendNotifications", params, signatures);
|
||||
|
||||
// waiting
|
||||
long toWait = 6000;
|
||||
long stopTime = System.currentTimeMillis() + toWait;
|
||||
|
||||
synchronized(listener) {
|
||||
while(listener.received < sendNB && toWait > 0) {
|
||||
listener.wait(toWait);
|
||||
toWait = stopTime - System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
// clean
|
||||
efClient.removeNotificationListener(emitter, listener, null, null);
|
||||
efClient.close();
|
||||
|
||||
if (listener.received != sendNB) {
|
||||
System.out.println(">>> ListenerTest-test: FAILED! Expected to receive "+sendNB+", but got "+listener.received);
|
||||
|
||||
return false;
|
||||
} else if (listener.seqErr > 0) {
|
||||
System.out.println(">>> ListenerTest-test: FAILED! The receiving sequence is not correct.");
|
||||
|
||||
return false;
|
||||
} else {
|
||||
System.out.println(">>> ListenerTest-test: got all expected "+listener.received);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Listener implements NotificationListener {
|
||||
public int received = 0;
|
||||
public int seqErr = 0;
|
||||
|
||||
private long lastSeq = -1;
|
||||
|
||||
public void handleNotification(Notification notif, Object handback) {
|
||||
if (!myType.equals(notif.getType())) {
|
||||
System.out.println(">>> EventManagerTest-Listener: got unexpected notif: "+notif);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (lastSeq == -1) {
|
||||
lastSeq = notif.getSequenceNumber();
|
||||
} else if (notif.getSequenceNumber() - lastSeq++ != 1) {
|
||||
seqErr++;
|
||||
}
|
||||
|
||||
System.out.println(">>> ListenerTest-Listener: got notif "+notif.getSequenceNumber());
|
||||
|
||||
synchronized(this) {
|
||||
if (++received >= sendNB) {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(">>> ListenerTest-Listener: received = "+received);
|
||||
}
|
||||
}
|
||||
|
||||
public static class NotificationEmitter extends NotificationBroadcasterSupport
|
||||
implements NotificationEmitterMBean {
|
||||
|
||||
public MBeanNotificationInfo[] getNotificationInfo() {
|
||||
final String[] ntfTypes = {myType};
|
||||
|
||||
final MBeanNotificationInfo[] ntfInfoArray = {
|
||||
new MBeanNotificationInfo(ntfTypes,
|
||||
"javax.management.Notification",
|
||||
"Notifications sent by the NotificationEmitter")};
|
||||
|
||||
return ntfInfoArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Notification objects.
|
||||
*
|
||||
* @param nb The number of notifications to send
|
||||
*/
|
||||
public void sendNotifications(Integer nb) {
|
||||
Notification notif;
|
||||
for (int i=1; i<=nb.intValue(); i++) {
|
||||
notif = new Notification(myType, this, count++);
|
||||
//System.out.println(">>> ListenerTest-NotificationEmitter-sendNotifications: "+i);
|
||||
|
||||
sendNotification(notif);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public interface NotificationEmitterMBean {
|
||||
public void sendNotifications(Integer nb);
|
||||
}
|
||||
|
||||
private static int sendNB = 20;
|
||||
private static int count = 0;
|
||||
|
||||
private static final String myType = "notification.my_notification";
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* MyList.java
|
||||
*
|
||||
* Created on Oct 23, 2007, 2:45:57 PM
|
||||
*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @author sjiang
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import javax.management.event.FetchingEventForwarder;
|
||||
|
||||
public class MyFetchingEventForwarder extends FetchingEventForwarder {
|
||||
|
||||
public MyFetchingEventForwarder() {
|
||||
super(1000);
|
||||
shared = this;
|
||||
setList(myList);
|
||||
}
|
||||
|
||||
public void setAgain() {
|
||||
setList(myList);
|
||||
}
|
||||
|
||||
public void setClientId(String clientId) throws IOException {
|
||||
used = true;
|
||||
super.setClientId(clientId);
|
||||
}
|
||||
|
||||
public boolean isUsed() {
|
||||
return used;
|
||||
}
|
||||
|
||||
private class MyList<TargetedNotification>
|
||||
extends ArrayList<TargetedNotification> {
|
||||
|
||||
public boolean add(TargetedNotification e) {
|
||||
used = true;
|
||||
|
||||
return super.add(e);
|
||||
}
|
||||
}
|
||||
|
||||
public MyList myList = new MyList();
|
||||
public static MyFetchingEventForwarder shared;
|
||||
private boolean used = false;
|
||||
}
|
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* @test NotSerializableNotifTest.java 1.5 08/01/22
|
||||
* @bug 5108776
|
||||
* @summary Basic test for EventClient.
|
||||
* @author Shanliang JIANG
|
||||
* @run clean NotSerializableNotifTest
|
||||
* @run build NotSerializableNotifTest
|
||||
* @run main NotSerializableNotifTest
|
||||
*/
|
||||
|
||||
|
||||
// JMX imports
|
||||
//
|
||||
import javax.management.* ;
|
||||
import javax.management.event.EventClient;
|
||||
import javax.management.event.EventClientDelegate;
|
||||
import javax.management.event.EventClientDelegateMBean;
|
||||
import javax.management.event.EventRelay;
|
||||
import javax.management.event.FetchingEventRelay;
|
||||
|
||||
import javax.management.remote.*;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
public class NotSerializableNotifTest {
|
||||
private static MBeanServer mbeanServer =
|
||||
MBeanServerFactory.createMBeanServer();
|
||||
private static ObjectName emitter;
|
||||
private static int port = 2468;
|
||||
|
||||
private static String[] protocols;
|
||||
|
||||
private static final int sentNotifs = 50;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println(">>> Test to send a not serializable notification");
|
||||
|
||||
// for 1.5
|
||||
if (System.getProperty("java.version").startsWith("1.5") &&
|
||||
!mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
|
||||
System.out.print("Working on "+System.getProperty("java.version")+
|
||||
" register "+EventClientDelegateMBean.OBJECT_NAME);
|
||||
|
||||
mbeanServer.registerMBean(EventClientDelegate.
|
||||
getEventClientDelegate(mbeanServer),
|
||||
EventClientDelegateMBean.OBJECT_NAME);
|
||||
}
|
||||
|
||||
NotificationEmitter nm = new NotificationEmitter();
|
||||
emitter = new ObjectName("Default:name=NotificationEmitter");
|
||||
mbeanServer.registerMBean(nm, emitter);
|
||||
String proto = "rmi";
|
||||
|
||||
System.out.println(">>> Test for protocol " + proto);
|
||||
|
||||
JMXServiceURL url = new JMXServiceURL(proto, null, 0);
|
||||
|
||||
JMXConnectorServer server =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer);
|
||||
|
||||
server.start();
|
||||
|
||||
url = server.getAddress();
|
||||
JMXConnector conn = JMXConnectorFactory.connect(url, null);
|
||||
MBeanServerConnection client = conn.getMBeanServerConnection();
|
||||
|
||||
EventClientDelegateMBean ecd = EventClientDelegate.getProxy(client);
|
||||
EventRelay eventRelay = new FetchingEventRelay(
|
||||
ecd,
|
||||
FetchingEventRelay.DEFAULT_BUFFER_SIZE,
|
||||
10,
|
||||
FetchingEventRelay.DEFAULT_MAX_NOTIFICATIONS,
|
||||
null);
|
||||
EventClient ec = new EventClient(ecd, eventRelay, null, null,
|
||||
EventClient.DEFAULT_LEASE_TIMEOUT);
|
||||
|
||||
// add listener from the client side
|
||||
Listener listener = new Listener();
|
||||
ec.addNotificationListener(emitter, listener, null, null);
|
||||
|
||||
LostListener lostListener = new LostListener();
|
||||
ec.addEventClientListener(lostListener, null, null);
|
||||
|
||||
// ask to send one not serializable notif
|
||||
System.out.println(">>> sending not serializable notifs ...");
|
||||
|
||||
Object[] params = new Object[] {new Integer(sentNotifs)};
|
||||
String[] signatures = new String[] {"java.lang.Integer"};
|
||||
client.invoke(emitter, "sendNotserializableNotifs", params, signatures);
|
||||
|
||||
// nm.sendNotserializableNotifs(sentNotifs);
|
||||
// nm.sendNotifications(1);
|
||||
|
||||
// waiting
|
||||
synchronized(lostListener) {
|
||||
if (lostListener.lostCount != sentNotifs) {
|
||||
lostListener.wait(6000);
|
||||
}
|
||||
}
|
||||
|
||||
Thread.sleep(100);
|
||||
|
||||
if (lostListener.lostCount != sentNotifs) {
|
||||
System.out.println(">>> FAILED. Expected "+sentNotifs+", but got "+lostListener.lostCount);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
System.out.println(">>> Passed.");
|
||||
|
||||
ec.close();
|
||||
conn.close();
|
||||
server.stop();
|
||||
}
|
||||
|
||||
|
||||
//--------------------------
|
||||
// private classes
|
||||
//--------------------------
|
||||
private static class Listener implements NotificationListener {
|
||||
public void handleNotification(Notification n, Object handback) {
|
||||
System.out.println(">>> Listener: receive: "+n);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class LostListener implements NotificationListener {
|
||||
public void handleNotification(Notification n, Object handback) {
|
||||
if (!EventClient.NOTIFS_LOST.equals(n.getType())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(n.getUserData() instanceof Long)) {
|
||||
System.out.println(">>> Listener: JMXConnectionNotification userData " +
|
||||
"not a Long: " + n.getUserData());
|
||||
System.exit(1);
|
||||
} else {
|
||||
int lost = ((Long) n.getUserData()).intValue();
|
||||
lostCount += lost;
|
||||
if (lostCount >= sentNotifs) {
|
||||
synchronized(this) {
|
||||
this.notifyAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private int lostCount = 0;
|
||||
}
|
||||
|
||||
public static class NotificationEmitter extends NotificationBroadcasterSupport
|
||||
implements NotificationEmitterMBean {
|
||||
|
||||
public MBeanNotificationInfo[] getNotificationInfo() {
|
||||
final String[] ntfTypes = {myType};
|
||||
|
||||
final MBeanNotificationInfo[] ntfInfoArray = {
|
||||
new MBeanNotificationInfo(ntfTypes,
|
||||
"javax.management.Notification",
|
||||
"Notifications sent by the NotificationEmitter")};
|
||||
|
||||
return ntfInfoArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send not serializable Notifications.
|
||||
*
|
||||
* @param nb The number of notifications to send
|
||||
*/
|
||||
public void sendNotserializableNotifs(Integer nb) {
|
||||
|
||||
Notification notif;
|
||||
for (int i=1; i<=nb.intValue(); i++) {
|
||||
notif = new Notification(myType, this, i);
|
||||
|
||||
notif.setUserData(new Object());
|
||||
sendNotification(notif);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Notification objects.
|
||||
*
|
||||
* @param nb The number of notifications to send
|
||||
*/
|
||||
public void sendNotifications(Integer nb) {
|
||||
Notification notif;
|
||||
for (int i=1; i<=nb.intValue(); i++) {
|
||||
notif = new Notification(myType, this, i);
|
||||
|
||||
sendNotification(notif);
|
||||
}
|
||||
}
|
||||
|
||||
private final String myType = "notification.my_notification";
|
||||
}
|
||||
|
||||
public interface NotificationEmitterMBean {
|
||||
public void sendNotifications(Integer nb);
|
||||
|
||||
public void sendNotserializableNotifs(Integer nb);
|
||||
}
|
||||
}
|
184
jdk/test/javax/management/eventService/PublishTest.java
Normal file
184
jdk/test/javax/management/eventService/PublishTest.java
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.*;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class PublishTest {
|
||||
private static MBeanServer mbeanServer;
|
||||
private static EventManager eventManager;
|
||||
private static ObjectName emitter;
|
||||
private static JMXServiceURL url;
|
||||
private static JMXConnectorServer server;
|
||||
private static JMXConnector conn;
|
||||
private static MBeanServerConnection client;
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println(">>> PublishTest-main basic tests ...");
|
||||
mbeanServer = MBeanServerFactory.createMBeanServer();
|
||||
|
||||
// for 1.5
|
||||
if (System.getProperty("java.version").startsWith("1.5") &&
|
||||
!mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
|
||||
System.out.print("Working on "+System.getProperty("java.version")+
|
||||
" register "+EventClientDelegateMBean.OBJECT_NAME);
|
||||
|
||||
mbeanServer.registerMBean(EventClientDelegate.
|
||||
getEventClientDelegate(mbeanServer),
|
||||
EventClientDelegateMBean.OBJECT_NAME);
|
||||
}
|
||||
|
||||
eventManager = EventManager.getEventManager(mbeanServer);
|
||||
|
||||
emitter = new ObjectName("Default:name=NotificationEmitter");
|
||||
|
||||
url = new JMXServiceURL("rmi", null, 0) ;
|
||||
server =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer);
|
||||
server.start();
|
||||
|
||||
url = server.getAddress();
|
||||
conn = JMXConnectorFactory.connect(url, null);
|
||||
client = conn.getMBeanServerConnection();
|
||||
|
||||
boolean succeed;
|
||||
|
||||
System.out.println(">>> PublishTest-main: using the fetching EventRelay...");
|
||||
succeed = test(new EventClient(client));
|
||||
|
||||
System.out.println(">>> PublishTest-main: using the pushing EventRelay...");
|
||||
succeed &= test(new EventClient(client,
|
||||
new RMIPushEventRelay(EventClientDelegate.getProxy(client)),
|
||||
null,
|
||||
EventClient.DEFAULT_LEASE_TIMEOUT));
|
||||
|
||||
conn.close();
|
||||
server.stop();
|
||||
|
||||
if (succeed) {
|
||||
System.out.println(">>> PublishTest-main: PASSE!");
|
||||
} else {
|
||||
System.out.println("\n>>> PublishTest-main: FAILED!");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean test(EventClient efClient) throws Exception {
|
||||
// add listener from the client side
|
||||
Listener listener = new Listener();
|
||||
efClient.subscribe(emitter, listener, null, null);
|
||||
|
||||
ObjectName other = new ObjectName("Default:name=other");
|
||||
// publish notifs
|
||||
for (int i=0; i<sendNB; i++) {
|
||||
Notification notif = new Notification(myType, emitter, count++);
|
||||
Notification notif2 = new Notification(myType, other, 0);
|
||||
//System.out.println(">>> EventManagerService-NotificationEmitter-sendNotifications: "+i);
|
||||
|
||||
eventManager.publish(emitter, notif);
|
||||
eventManager.publish(other, notif2); // should not received
|
||||
}
|
||||
|
||||
// waiting
|
||||
long toWait = 6000;
|
||||
long stopTime = System.currentTimeMillis() + toWait;
|
||||
|
||||
synchronized(listener) {
|
||||
while(listener.received < sendNB && toWait > 0) {
|
||||
listener.wait(toWait);
|
||||
toWait = stopTime - System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
// clean
|
||||
efClient.unsubscribe(emitter, listener);
|
||||
efClient.close();
|
||||
|
||||
if (listener.received != sendNB) {
|
||||
System.out.println(">>> PublishTest-test: FAILED! Expected to receive "+sendNB+", but got "+listener.received);
|
||||
|
||||
return false;
|
||||
} else if (listener.seqErr > 0) {
|
||||
System.out.println(">>> PublishTest-test: FAILED! The receiving sequence is not correct.");
|
||||
|
||||
return false;
|
||||
} else {
|
||||
System.out.println(">>> PublishTest-test: got all expected "+listener.received);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Listener implements NotificationListener {
|
||||
public int received = 0;
|
||||
public int seqErr = 0;
|
||||
|
||||
private long lastSeq = -1;
|
||||
|
||||
public void handleNotification(Notification notif, Object handback) {
|
||||
if (!myType.equals(notif.getType())) {
|
||||
System.out.println(">>> PublishTest-Listener: got unexpected notif: "+notif);
|
||||
System.exit(1);
|
||||
} else if (!emitter.equals(notif.getSource())) {
|
||||
System.out.println(">>> PublishTest-Listener: unknown ObjectName: "+notif.getSource());
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (lastSeq == -1) {
|
||||
lastSeq = notif.getSequenceNumber();
|
||||
} else if (notif.getSequenceNumber() - lastSeq++ != 1) {
|
||||
seqErr++;
|
||||
}
|
||||
|
||||
System.out.println(">>> PublishTest-Listener: got notif "+notif.getSequenceNumber());
|
||||
|
||||
synchronized(this) {
|
||||
if (++received >= sendNB) {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println(">>> PublishTest-Listener: received = "+received);
|
||||
}
|
||||
}
|
||||
|
||||
private static int sendNB = 20;
|
||||
private static long count = 0;
|
||||
|
||||
private static final String myType = "notification.my_notification";
|
||||
}
|
@ -0,0 +1,488 @@
|
||||
/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test ReconnectableJMXConnector
|
||||
* @bug 5108776
|
||||
* @summary Check that the Event Service can be used to build a
|
||||
* ReconnectableJMXConnector.
|
||||
* @author Eamonn McManus
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import javax.management.ListenerNotFoundException;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.EventClient;
|
||||
import javax.management.remote.JMXConnectionNotification;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
/*
|
||||
* This test checks that it is possible to use the Event Service to create
|
||||
* a "reconnectable connector".
|
||||
*
|
||||
* In the JMX Remote API, we deliberately specified that a connector client
|
||||
* (JMXConnector) that encounters a network failure is then permanently broken.
|
||||
* The idea being that adding recovery logic to the basic connector client
|
||||
* would make it much more complicated and less reliable, and the logic would
|
||||
* in any case never correspond to what a given situation needs. Some of
|
||||
* the tough questions are: Should the connector try to mask the failure by
|
||||
* blocking operations until the failure is resolved? How long should the
|
||||
* connector try to reestablish the connection before giving up? Rather than
|
||||
* try to solve this problem in the connector, we suggested that people who
|
||||
* wanted to recover from network failures could implement the JMXConnector
|
||||
* interface themselves so that it forwards to a wrapped JMXConnector that can
|
||||
* be replaced in case of network failure.
|
||||
*
|
||||
* This works fine except that the connector client has state,
|
||||
* in the form of listeners added by the user through the
|
||||
* MBeanServerConnection.addNotificationListener method. It's possible
|
||||
* for the wrapper to keep track of these listeners as well as forwarding
|
||||
* them to the wrapped JMXConnector, so that it can reapply them to
|
||||
* a replacement JMXConnector after failure recover. But it's quite
|
||||
* tricky, particularly because of the two- and four-argument versions of
|
||||
* removeNotificationListener.
|
||||
*
|
||||
* The Event Service can take care of this for you through the EventClient
|
||||
* class. Listeners added through that class are implemented in a way that
|
||||
* doesn't require the connector client to maintain any state, so they should
|
||||
* continue to work transparently after replacing the wrapped JMXConnector.
|
||||
* This test is a proof of concept that shows it works. Quite a number of
|
||||
* details would need to be changed to build a reliable reconnectable
|
||||
* connector.
|
||||
*
|
||||
* The test simulates network failure by rewrapping the wrapped JMXConnector's
|
||||
* MBeanServerConnection (MBSC) in a "breakable" MBSC which we can cause
|
||||
* to stop working. We do this in two phases. The first phase suspends
|
||||
* any MBSC calls just at the point where they would return to the caller.
|
||||
* The goal here is to block an EventClientDelegateMBean.fetchNotifications
|
||||
* operation when it has received notifications but not yet delivered them
|
||||
* to the EventClient. This is the most delicate point where a breakage
|
||||
* can occur, because the EventClientDelegate must not drop those notifs
|
||||
* from its buffer until another fetchNotifs call arrives with a later
|
||||
* sequence number (which is an implicit ack of the previous set of
|
||||
* notifs). Once the fetchNotifs call is suspended, we "kill" the MBSC,
|
||||
* causing it to throw IOException from this and any other calls. That
|
||||
* triggers the reconnect logic, which will make a new MBSC and issue
|
||||
* the same fetchNotifs call to it.
|
||||
*
|
||||
* The test could be improved by synchronizing explicitly between the
|
||||
* breakable MBSC and the mainline, so we only proceed to kill the MBSC
|
||||
* when we are sure that the fetchNotifs call is blocked. As it is,
|
||||
* we have a small delay which both ensures that no notifs are delivered
|
||||
* while the connection is suspended, and if the machine is fast enough
|
||||
* allows the fetchNotifs call to reach the blocking point.
|
||||
*/
|
||||
public class ReconnectableConnectorTest {
|
||||
private static class ReconnectableJMXConnector implements JMXConnector {
|
||||
private final JMXServiceURL url;
|
||||
private AtomicReference<JMXConnector> wrappedJMXC =
|
||||
new AtomicReference<JMXConnector>();
|
||||
private AtomicReference<MBeanServerConnection> wrappedMBSC =
|
||||
new AtomicReference<MBeanServerConnection>();
|
||||
private final NotificationBroadcasterSupport broadcaster =
|
||||
new NotificationBroadcasterSupport();
|
||||
private final Lock connectLock = new ReentrantLock();
|
||||
|
||||
ReconnectableJMXConnector(JMXServiceURL url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
private class ReconnectIH implements InvocationHandler {
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Throwable {
|
||||
try {
|
||||
return method.invoke(wrappedMBSC.get(), args);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (e.getCause() instanceof IOException) {
|
||||
connect();
|
||||
try {
|
||||
return method.invoke(wrappedMBSC.get(),args);
|
||||
} catch (InvocationTargetException ee) {
|
||||
throw ee.getCause();
|
||||
}
|
||||
}
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FailureListener implements NotificationListener {
|
||||
public void handleNotification(Notification n, Object h) {
|
||||
String type = n.getType();
|
||||
if (type.equals(JMXConnectionNotification.FAILED)) {
|
||||
try {
|
||||
connect();
|
||||
} catch (IOException e) {
|
||||
broadcaster.sendNotification(n);
|
||||
}
|
||||
} else if (type.equals(JMXConnectionNotification.NOTIFS_LOST))
|
||||
broadcaster.sendNotification(n);
|
||||
}
|
||||
}
|
||||
|
||||
public void connect() throws IOException {
|
||||
connectLock.lock();
|
||||
try {
|
||||
connectWithLock();
|
||||
} finally {
|
||||
connectLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void connectWithLock() throws IOException {
|
||||
MBeanServerConnection mbsc = wrappedMBSC.get();
|
||||
if (mbsc != null) {
|
||||
try {
|
||||
mbsc.getDefaultDomain();
|
||||
return; // the connection works
|
||||
} catch (IOException e) {
|
||||
// OK: the connection doesn't work, so make a new one
|
||||
}
|
||||
}
|
||||
// This is where we would need to add the fancy logic that
|
||||
// allows the connection to keep failing for a while
|
||||
// before giving up.
|
||||
JMXConnector jmxc = JMXConnectorFactory.connect(url);
|
||||
jmxc.addConnectionNotificationListener(
|
||||
new FailureListener(), null, null);
|
||||
wrappedJMXC.set(jmxc);
|
||||
if (false)
|
||||
wrappedMBSC.set(jmxc.getMBeanServerConnection());
|
||||
else {
|
||||
mbsc = jmxc.getMBeanServerConnection();
|
||||
InvocationHandler ih = new BreakableIH(mbsc);
|
||||
mbsc = (MBeanServerConnection) Proxy.newProxyInstance(
|
||||
MBeanServerConnection.class.getClassLoader(),
|
||||
new Class<?>[] {MBeanServerConnection.class},
|
||||
ih);
|
||||
wrappedMBSC.set(mbsc);
|
||||
}
|
||||
}
|
||||
|
||||
private BreakableIH breakableIH() {
|
||||
MBeanServerConnection mbsc = wrappedMBSC.get();
|
||||
return (BreakableIH) Proxy.getInvocationHandler(mbsc);
|
||||
}
|
||||
|
||||
void suspend() {
|
||||
BreakableIH ih = breakableIH();
|
||||
ih.suspend();
|
||||
}
|
||||
|
||||
void kill() throws IOException {
|
||||
BreakableIH ih = breakableIH();
|
||||
wrappedJMXC.get().close();
|
||||
ih.kill();
|
||||
}
|
||||
|
||||
public void connect(Map<String, ?> env) throws IOException {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
private final AtomicReference<MBeanServerConnection> mbscRef =
|
||||
new AtomicReference<MBeanServerConnection>();
|
||||
|
||||
public MBeanServerConnection getMBeanServerConnection()
|
||||
throws IOException {
|
||||
connect();
|
||||
// Synchro here is not strictly correct: two threads could make
|
||||
// an MBSC at the same time. OK for a test but beware for real
|
||||
// code.
|
||||
MBeanServerConnection mbsc = mbscRef.get();
|
||||
if (mbsc != null)
|
||||
return mbsc;
|
||||
mbsc = (MBeanServerConnection) Proxy.newProxyInstance(
|
||||
MBeanServerConnection.class.getClassLoader(),
|
||||
new Class<?>[] {MBeanServerConnection.class},
|
||||
new ReconnectIH());
|
||||
mbsc = EventClient.getEventClientConnection(mbsc);
|
||||
mbscRef.set(mbsc);
|
||||
return mbsc;
|
||||
}
|
||||
|
||||
public MBeanServerConnection getMBeanServerConnection(
|
||||
Subject delegationSubject) throws IOException {
|
||||
throw new UnsupportedOperationException("Not supported yet.");
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
wrappedJMXC.get().close();
|
||||
}
|
||||
|
||||
public void addConnectionNotificationListener(
|
||||
NotificationListener l, NotificationFilter f, Object h) {
|
||||
broadcaster.addNotificationListener(l, f, h);
|
||||
}
|
||||
|
||||
public void removeConnectionNotificationListener(NotificationListener l)
|
||||
throws ListenerNotFoundException {
|
||||
broadcaster.removeNotificationListener(l);
|
||||
}
|
||||
|
||||
public void removeConnectionNotificationListener(
|
||||
NotificationListener l, NotificationFilter f, Object h)
|
||||
throws ListenerNotFoundException {
|
||||
broadcaster.removeNotificationListener(l, f, h);
|
||||
}
|
||||
|
||||
public String getConnectionId() throws IOException {
|
||||
return wrappedJMXC.get().getConnectionId();
|
||||
}
|
||||
}
|
||||
|
||||
// InvocationHandler that allows us to perform a two-phase "break" of
|
||||
// an object. The first phase suspends the object, so that calls to
|
||||
// it are blocked just before they return. The second phase unblocks
|
||||
// suspended threads and causes them to throw IOException.
|
||||
private static class BreakableIH implements InvocationHandler {
|
||||
private final Object wrapped;
|
||||
private final Holder<String> state = new Holder<String>("running");
|
||||
|
||||
BreakableIH(Object wrapped) {
|
||||
this.wrapped = wrapped;
|
||||
}
|
||||
|
||||
void suspend() {
|
||||
state.set("suspended");
|
||||
}
|
||||
|
||||
void kill() {
|
||||
state.set("killed");
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args)
|
||||
throws Throwable {
|
||||
Object result;
|
||||
try {
|
||||
result = method.invoke(wrapped, args);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
String s = state.get();
|
||||
if (s.equals("suspended"))
|
||||
state.waitUntilEqual("killed", 3, TimeUnit.SECONDS);
|
||||
else if (s.equals("killed"))
|
||||
throw new IOException("Broken");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Holder<T> {
|
||||
private T held;
|
||||
private Lock lock = new ReentrantLock();
|
||||
private Condition changed = lock.newCondition();
|
||||
|
||||
Holder(T value) {
|
||||
lock.lock();
|
||||
this.held = value;
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
void waitUntilEqual(T value, long timeout, TimeUnit units)
|
||||
throws InterruptedException {
|
||||
long millis = units.toMillis(timeout);
|
||||
long stop = System.currentTimeMillis() + millis;
|
||||
Date stopDate = new Date(stop);
|
||||
lock.lock();
|
||||
try {
|
||||
while (!value.equals(held)) {
|
||||
boolean ok = changed.awaitUntil(stopDate);
|
||||
if (!ok)
|
||||
throw new InterruptedException("Timed out");
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void set(T value) {
|
||||
lock.lock();
|
||||
try {
|
||||
held = value;
|
||||
changed.signalAll();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
T get() {
|
||||
lock.lock();
|
||||
try {
|
||||
return held;
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class StoreListener implements NotificationListener {
|
||||
final BlockingQueue<Notification> queue =
|
||||
new ArrayBlockingQueue<Notification>(100);
|
||||
|
||||
public void handleNotification(Notification n, Object h) {
|
||||
queue.add(n);
|
||||
}
|
||||
|
||||
Notification nextNotification(long time, TimeUnit units)
|
||||
throws InterruptedException {
|
||||
Notification n = queue.poll(time, units);
|
||||
if (n == null)
|
||||
throw new NoSuchElementException("Notification wait timed out");
|
||||
return n;
|
||||
}
|
||||
|
||||
int notifCount() {
|
||||
return queue.size();
|
||||
}
|
||||
}
|
||||
|
||||
public static interface SenderMBean {}
|
||||
public static class Sender
|
||||
extends NotificationBroadcasterSupport implements SenderMBean {
|
||||
private AtomicLong seqNo = new AtomicLong(0);
|
||||
|
||||
void send() {
|
||||
Notification n =
|
||||
new Notification("type", this, seqNo.getAndIncrement());
|
||||
sendNotification(n);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
||||
Sender sender = new Sender();
|
||||
ObjectName name = new ObjectName("a:b=c");
|
||||
mbs.registerMBean(sender, name);
|
||||
|
||||
System.out.println("Creating connector server");
|
||||
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///");
|
||||
JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(
|
||||
url, null, mbs);
|
||||
cs.start();
|
||||
|
||||
StoreListener csListener = new StoreListener();
|
||||
cs.addNotificationListener(csListener, null, null);
|
||||
|
||||
System.out.println("Creating reconnectable client");
|
||||
JMXServiceURL addr = cs.getAddress();
|
||||
ReconnectableJMXConnector cc = new ReconnectableJMXConnector(addr);
|
||||
MBeanServerConnection mbsc = cc.getMBeanServerConnection();
|
||||
|
||||
System.out.println("Checking server has sent new-client notif");
|
||||
Notification csn = csListener.nextNotification(1, TimeUnit.SECONDS);
|
||||
assertEquals("CS notif type",
|
||||
JMXConnectionNotification.OPENED, csn.getType());
|
||||
|
||||
StoreListener listener = new StoreListener();
|
||||
mbsc.addNotificationListener(name, listener, null, null);
|
||||
|
||||
System.out.println("Sending 10 notifs and checking they are received");
|
||||
for (int i = 0; i < 10; i++)
|
||||
sender.send();
|
||||
checkNotifs(listener, 0, 10);
|
||||
|
||||
System.out.println("Suspending the fetchNotifs operation");
|
||||
cc.suspend();
|
||||
System.out.println("Sending a notif while fetchNotifs is suspended");
|
||||
sender.send();
|
||||
System.out.println("Brief wait before checking no notif is received");
|
||||
Thread.sleep(2);
|
||||
// dumpThreads();
|
||||
assertEquals("notif queue while connector suspended",
|
||||
0, listener.notifCount());
|
||||
assertEquals("connector server notif queue while connector suspended",
|
||||
0, csListener.notifCount());
|
||||
|
||||
System.out.println("Breaking the connection so fetchNotifs will fail over");
|
||||
cc.kill();
|
||||
|
||||
System.out.println("Checking that client has reconnected");
|
||||
csn = csListener.nextNotification(1, TimeUnit.SECONDS);
|
||||
assertEquals("First CS notif type after kill",
|
||||
JMXConnectionNotification.CLOSED, csn.getType());
|
||||
csn = csListener.nextNotification(1, TimeUnit.SECONDS);
|
||||
assertEquals("Second CS notif type after kill",
|
||||
JMXConnectionNotification.OPENED, csn.getType());
|
||||
|
||||
System.out.println("Checking that suspended notif has been received");
|
||||
checkNotifs(listener, 10, 11);
|
||||
}
|
||||
|
||||
private static void checkNotifs(
|
||||
StoreListener sl, long start, long stop)
|
||||
throws Exception {
|
||||
for (long i = start; i < stop; i++) {
|
||||
Notification n = sl.nextNotification(1, TimeUnit.SECONDS);
|
||||
assertEquals("received sequence number", i, n.getSequenceNumber());
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertEquals(String what, Object expect, Object actual)
|
||||
throws Exception {
|
||||
if (!expect.equals(actual)) {
|
||||
fail(what + " should be " + expect + " but is " + actual);
|
||||
}
|
||||
}
|
||||
|
||||
private static void fail(String why) throws Exception {
|
||||
throw new Exception("TEST FAILED: " + why);
|
||||
}
|
||||
|
||||
private static void dumpThreads() {
|
||||
System.out.println("Thread stack dump");
|
||||
Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
|
||||
for (Map.Entry<Thread, StackTraceElement[]> entry : traces.entrySet()) {
|
||||
Thread t = entry.getKey();
|
||||
System.out.println("===Thread " + t.getName() + "===");
|
||||
for (StackTraceElement ste : entry.getValue())
|
||||
System.out.println(" " + ste);
|
||||
}
|
||||
}
|
||||
}
|
364
jdk/test/javax/management/eventService/SharingThreadTest.java
Normal file
364
jdk/test/javax/management/eventService/SharingThreadTest.java
Normal file
@ -0,0 +1,364 @@
|
||||
/*/*
|
||||
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test SharingThreadTest.java 1.3 08/01/22
|
||||
* @bug 5108776
|
||||
* @summary Basic test for EventClient to see internal thread management.
|
||||
* @author Shanliang JIANG
|
||||
* @run clean SharingThreadTest
|
||||
* @run build SharingThreadTest
|
||||
* @run main SharingThreadTest
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.EventClient;
|
||||
import javax.management.event.EventClientDelegate;
|
||||
import javax.management.event.EventClientDelegateMBean;
|
||||
import javax.management.event.FetchingEventRelay;
|
||||
import javax.management.event.RMIPushEventRelay;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
|
||||
public class SharingThreadTest {
|
||||
|
||||
private static MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer();
|
||||
private static List<Notification> notifList = new ArrayList<Notification>();
|
||||
private static ObjectName emitter;
|
||||
private static NotificationEmitter emitterImpl;
|
||||
private static JMXServiceURL url;
|
||||
private static JMXConnectorServer server;
|
||||
private static JMXConnector conn;
|
||||
|
||||
|
||||
private static int toSend = 10;
|
||||
private static long sequenceNumber = 0;
|
||||
private static final long bigWaiting = 6000;
|
||||
private static int counter = 0;
|
||||
private static int jobs = 10;
|
||||
private static int endedJobs = 0;
|
||||
|
||||
private static Executor sharedExecutor = new ThreadPoolExecutor(0, 1, 1000,
|
||||
TimeUnit.MILLISECONDS, new ArrayBlockingQueue(jobs));
|
||||
//Executors.newFixedThreadPool(1);
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println(">>> Test on sharing threads for multiple EventClient.");
|
||||
|
||||
// for 1.5
|
||||
if (System.getProperty("java.version").startsWith("1.5") &&
|
||||
!mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
|
||||
System.out.print("Working on "+System.getProperty("java.version")+
|
||||
" register "+EventClientDelegateMBean.OBJECT_NAME);
|
||||
|
||||
mbeanServer.registerMBean(EventClientDelegate.
|
||||
getEventClientDelegate(mbeanServer),
|
||||
EventClientDelegateMBean.OBJECT_NAME);
|
||||
|
||||
sharedExecutor = new ThreadPoolExecutor(1, 1, 1000,
|
||||
TimeUnit.MILLISECONDS, new ArrayBlockingQueue(jobs));
|
||||
}
|
||||
|
||||
emitter = new ObjectName("Default:name=NotificationEmitter");
|
||||
emitterImpl = new NotificationEmitter();
|
||||
mbeanServer.registerMBean(emitterImpl, emitter);
|
||||
|
||||
String[] types = new String[]{"PushEventRelay", "FetchingEventRelay"};
|
||||
String[] protos = new String[]{"rmi", "iiop", "jmxmp"};
|
||||
for (String prot : protos) {
|
||||
url = new JMXServiceURL(prot, null, 0);
|
||||
|
||||
try {
|
||||
server =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url,
|
||||
null, mbeanServer);
|
||||
server.start();
|
||||
} catch (Exception e) {
|
||||
System.out.println(">>> Skip "+prot+", not support.");
|
||||
continue;
|
||||
}
|
||||
|
||||
url = server.getAddress();
|
||||
|
||||
// noise
|
||||
Thread noise = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
while (true) {
|
||||
emitterImpl.sendNotif(1, null);
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (Exception e) {
|
||||
// OK
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
noise.setDaemon(true);
|
||||
noise.start();
|
||||
|
||||
Thread[] threads = new Thread[jobs];
|
||||
try {
|
||||
for (String type: types) {
|
||||
System.out.println("\n\n>>> Testing "+type+" on "+url+" ...");
|
||||
newConn();
|
||||
for (int i=0; i<jobs; i++) {
|
||||
threads[i] = new Thread(new Job(type));
|
||||
threads[i].setDaemon(true);
|
||||
threads[i].start();
|
||||
}
|
||||
|
||||
// to wait
|
||||
long toWait = bigWaiting*jobs;
|
||||
long stopTime = System.currentTimeMillis() + toWait;
|
||||
|
||||
synchronized(SharingThreadTest.class) {
|
||||
while (endedJobs < jobs && toWait > 0) {
|
||||
SharingThreadTest.class.wait(toWait);
|
||||
toWait = stopTime - System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
|
||||
if (endedJobs != jobs) {
|
||||
throw new RuntimeException("Need to set bigger waiting timeout?");
|
||||
}
|
||||
|
||||
endedJobs = 0;
|
||||
conn.close();
|
||||
System.out.println(">>> Testing "+type+" on "+url+" ... done");
|
||||
}
|
||||
} finally {
|
||||
server.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Job implements Runnable {
|
||||
public Job(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
public void run() {
|
||||
try {
|
||||
test(type);
|
||||
|
||||
synchronized(SharingThreadTest.class) {
|
||||
endedJobs++;
|
||||
if (endedJobs>=jobs) {
|
||||
SharingThreadTest.class.notify();
|
||||
}
|
||||
}
|
||||
} catch (RuntimeException re) {
|
||||
throw re;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final String type;
|
||||
}
|
||||
|
||||
private static void test(String type) throws Exception {
|
||||
String id = getId();
|
||||
|
||||
Listener listener = new Listener(id);
|
||||
Filter filter = new Filter(id);
|
||||
|
||||
//newConn();
|
||||
EventClient ec = newEventClient(type);
|
||||
|
||||
System.out.println(">>> ("+id+") To receive notifications "+toSend);
|
||||
ec.addNotificationListener(emitter,
|
||||
listener, filter, null);
|
||||
|
||||
emitterImpl.sendNotif(toSend, id);
|
||||
listener.waitNotifs(bigWaiting, toSend);
|
||||
if (listener.received != toSend) {
|
||||
throw new RuntimeException(">>> ("+id+") Expected to receive: "
|
||||
+toSend+", but got: "+listener.received);
|
||||
}
|
||||
|
||||
// not close the EventClient to keep using thread
|
||||
//ec.close();
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
// private classes
|
||||
//--------------------------
|
||||
|
||||
private static class Listener implements NotificationListener {
|
||||
public Listener(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public void handleNotification(Notification notif, Object handback) {
|
||||
if (!id.equals(notif.getUserData())) {
|
||||
System.out.println("("+id+") Filter error, my id is: "+id+
|
||||
", but got "+notif.getUserData());
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println("("+id+") received "+notif.getSequenceNumber());
|
||||
synchronized (notifList) {
|
||||
received++;
|
||||
|
||||
if (sequenceNB < 0) {
|
||||
sequenceNB = notif.getSequenceNumber();
|
||||
} else if(++sequenceNB != notif.getSequenceNumber()) {
|
||||
throw new RuntimeException("Wrong sequence number, expecte: "
|
||||
+sequenceNB+", but got: "+notif.getSequenceNumber());
|
||||
}
|
||||
if (received >= toSend) {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void waitNotifs(long timeout, int nb) throws Exception {
|
||||
long toWait = timeout;
|
||||
long stopTime = System.currentTimeMillis() + timeout;
|
||||
synchronized(this) {
|
||||
while (received < nb && toWait > 0) {
|
||||
this.wait(toWait);
|
||||
toWait = stopTime - System.currentTimeMillis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
synchronized(this) {
|
||||
received = 0;
|
||||
sequenceNB = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private String id;
|
||||
private int received = 0;
|
||||
|
||||
private long sequenceNB = -1;
|
||||
}
|
||||
|
||||
private static class Filter implements NotificationFilter {
|
||||
public Filter(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean isNotificationEnabled(Notification n) {
|
||||
return id.equals(n.getUserData());
|
||||
}
|
||||
private String id;
|
||||
}
|
||||
|
||||
private static NotificationListener dummyListener = new NotificationListener() {
|
||||
public void handleNotification(Notification notif, Object handback) {
|
||||
}
|
||||
};
|
||||
|
||||
public static class NotificationEmitter extends NotificationBroadcasterSupport
|
||||
implements NotificationEmitterMBean {
|
||||
|
||||
/**
|
||||
* Send Notification objects.
|
||||
*
|
||||
* @param nb The number of notifications to send
|
||||
*/
|
||||
public void sendNotif(int nb, String userData) {
|
||||
new Thread(new SendJob(nb, userData)).start();
|
||||
}
|
||||
|
||||
private class SendJob implements Runnable {
|
||||
public SendJob(int nb, String userData) {
|
||||
this.nb = nb;
|
||||
this.userData = userData;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if (userData != null) {
|
||||
System.out.println(">>> ("+userData+") sending "+nb);
|
||||
}
|
||||
for (int i = 0; i<nb; i++) {
|
||||
Notification notif = new Notification(myType, emitter,
|
||||
sequenceNumber++);
|
||||
notif.setUserData(userData);
|
||||
sendNotification(notif);
|
||||
Thread.yield();
|
||||
try {
|
||||
Thread.sleep(1);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
if (userData != null) {
|
||||
System.out.println(">>> ("+userData+") sending done");
|
||||
}
|
||||
}
|
||||
private int nb;
|
||||
private String userData;
|
||||
}
|
||||
private final String myType = "notification.my_notification";
|
||||
}
|
||||
|
||||
public interface NotificationEmitterMBean {
|
||||
public void sendNotif(int nb, String userData);
|
||||
}
|
||||
|
||||
private static void newConn() throws IOException {
|
||||
conn = JMXConnectorFactory.connect(url);
|
||||
}
|
||||
|
||||
private static EventClient newEventClient(String type) throws Exception {
|
||||
EventClientDelegateMBean proxy =
|
||||
EventClientDelegate.getProxy(conn.getMBeanServerConnection());
|
||||
if (type.equals("PushEventRelay")) {
|
||||
return new EventClient(proxy,
|
||||
new RMIPushEventRelay(proxy), sharedExecutor, null, 600);
|
||||
} else if (type.equals("FetchingEventRelay")) {
|
||||
return new EventClient(proxy,
|
||||
new FetchingEventRelay(proxy,
|
||||
FetchingEventRelay.DEFAULT_BUFFER_SIZE,
|
||||
10,
|
||||
FetchingEventRelay.DEFAULT_MAX_NOTIFICATIONS,
|
||||
sharedExecutor),
|
||||
null, null, 600);
|
||||
} else {
|
||||
throw new RuntimeException("Wrong event client type: "+type);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getId() {
|
||||
synchronized(SharingThreadTest.class) {
|
||||
return String.valueOf(counter++);
|
||||
}
|
||||
}
|
||||
}
|
127
jdk/test/javax/management/eventService/SubscribeTest.java
Normal file
127
jdk/test/javax/management/eventService/SubscribeTest.java
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 5108776
|
||||
* @summary Test that EventSubscriber.subscribe works
|
||||
* @author Eamonn McManus
|
||||
*/
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.EventSubscriber;
|
||||
|
||||
public class SubscribeTest {
|
||||
private static class CountListener implements NotificationListener {
|
||||
volatile int count;
|
||||
|
||||
public void handleNotification(Notification n, Object h) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SwitchFilter implements NotificationFilter {
|
||||
volatile boolean enabled;
|
||||
|
||||
public boolean isNotificationEnabled(Notification n) {
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface SenderMBean {}
|
||||
|
||||
public static class Sender extends NotificationBroadcasterSupport
|
||||
implements SenderMBean {
|
||||
void send() {
|
||||
Notification n = new Notification("type", this, 1L);
|
||||
sendNotification(n);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||
ObjectName name1 = new ObjectName("d:type=Sender,id=1");
|
||||
ObjectName name2 = new ObjectName("d:type=Sender,id=2");
|
||||
ObjectName pattern = new ObjectName("d:type=Sender,*");
|
||||
Sender sender1 = new Sender();
|
||||
Sender sender2 = new Sender();
|
||||
mbs.registerMBean(sender1, name1);
|
||||
mbs.registerMBean(sender2, name2);
|
||||
|
||||
EventSubscriber sub = EventSubscriber.getEventSubscriber(mbs);
|
||||
|
||||
System.out.println("Single subscribe covering both MBeans");
|
||||
CountListener listen1 = new CountListener();
|
||||
sub.subscribe(pattern, listen1, null, null);
|
||||
sender1.send();
|
||||
assertEquals("Notifs after sender1 send", 1, listen1.count);
|
||||
sender2.send();
|
||||
assertEquals("Notifs after sender2 send", 2, listen1.count);
|
||||
|
||||
System.out.println("Unsubscribe");
|
||||
sub.unsubscribe(pattern, listen1);
|
||||
sender1.send();
|
||||
assertEquals("Notifs after sender1 send", 2, listen1.count);
|
||||
|
||||
System.out.println("Subscribe twice to same MBean with same listener " +
|
||||
"but different filters");
|
||||
SwitchFilter filter1 = new SwitchFilter();
|
||||
sub.subscribe(name1, listen1, null, null);
|
||||
sub.subscribe(name1, listen1, filter1, null);
|
||||
listen1.count = 0;
|
||||
sender1.send();
|
||||
// switch is off, so only one notif expected
|
||||
assertEquals("Notifs after sender1 send", 1, listen1.count);
|
||||
filter1.enabled = true;
|
||||
sender1.send();
|
||||
// switch is on, so two more notifs expected
|
||||
assertEquals("Notifs after sender1 send", 3, listen1.count);
|
||||
|
||||
System.out.println("Remove those subscriptions");
|
||||
sub.unsubscribe(name1, listen1);
|
||||
sender1.send();
|
||||
assertEquals("Notifs after sender1 send", 3, listen1.count);
|
||||
}
|
||||
|
||||
private static void assertEquals(String what, Object expected, Object actual)
|
||||
throws Exception {
|
||||
if (!equal(expected, actual)) {
|
||||
String msg = "Expected " + expected + "; got " + actual;
|
||||
throw new Exception("TEST FAILED: " + what + ": " + msg);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean equal(Object x, Object y) {
|
||||
if (x == y)
|
||||
return true;
|
||||
if (x == null)
|
||||
return false;
|
||||
return (x.equals(y));
|
||||
}
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* @test UsingEventService.java 1.10 08/01/22
|
||||
* @bug 5108776
|
||||
* @summary Basic test for EventManager.
|
||||
* @author Shanliang JIANG
|
||||
* @run clean UsingEventService
|
||||
* @run build UsingEventService
|
||||
* @run main UsingEventService
|
||||
*/
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.event.EventConsumer;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
public class UsingEventService {
|
||||
private static JMXServiceURL url;
|
||||
private static JMXConnectorServer server;
|
||||
private static JMXConnector conn;
|
||||
private static MBeanServerConnection client;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
if (System.getProperty("java.version").startsWith("1.5")) {
|
||||
System.out.println(">>> UsingEventService-main not available for JDK1.5, bye");
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectName oname = new ObjectName("test:t=t");
|
||||
Notification n = new Notification("", oname, 0);
|
||||
|
||||
System.out.println(">>> UsingEventService-main basic tests ...");
|
||||
MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer();
|
||||
|
||||
url = new JMXServiceURL("rmi", null, 0) ;
|
||||
JMXConnectorServer server =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbeanServer);
|
||||
server.start();
|
||||
url = server.getAddress();
|
||||
|
||||
System.out.println(">>> UsingEventService-main test to not use the event service...");
|
||||
conn = JMXConnectorFactory.connect(url, null);
|
||||
client = conn.getMBeanServerConnection();
|
||||
|
||||
System.out.println(">>> UsingEventService-main test to use the event service...");
|
||||
Map env = new HashMap(1);
|
||||
env.put("jmx.remote.use.event.service", "true");
|
||||
conn = JMXConnectorFactory.connect(url, env);
|
||||
client = conn.getMBeanServerConnection();
|
||||
|
||||
((EventConsumer)client).subscribe(oname, listener, null, null);
|
||||
|
||||
System.out.println(">>> UsingEventService-main using event service as expected!");
|
||||
|
||||
System.out.println(">>> UsingEventService-main test to use" +
|
||||
" the event service with system property...");
|
||||
|
||||
System.setProperty("jmx.remote.use.event.service", "true");
|
||||
conn = JMXConnectorFactory.connect(url, null);
|
||||
client = conn.getMBeanServerConnection();
|
||||
|
||||
((EventConsumer)client).subscribe(oname, listener, null, null);
|
||||
|
||||
System.out.println("" +
|
||||
">>> UsingEventService-main using event service as expected!");
|
||||
|
||||
System.out.println(">>> Happy bye bye!");
|
||||
}
|
||||
|
||||
private final static NotificationListener listener = new NotificationListener() {
|
||||
public void handleNotification(Notification n, Object hk) {
|
||||
//
|
||||
}
|
||||
};
|
||||
}
|
@ -32,17 +32,19 @@
|
||||
*/
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MonitorInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.management.Attribute;
|
||||
import javax.management.JMX;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.StandardMBean;
|
||||
import javax.management.openmbean.CompositeData;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
@ -50,6 +52,58 @@ import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
|
||||
public class GenericArrayTypeTest {
|
||||
// A version of java.lang.management.MonitorInfo so we can run this test
|
||||
// on JDK 5, where that class didn't exist.
|
||||
public static class MonitorInfo {
|
||||
private final String className;
|
||||
private final int identityHashCode;
|
||||
private final int lockedStackDepth;
|
||||
private final StackTraceElement lockedStackFrame;
|
||||
|
||||
public MonitorInfo(
|
||||
String className, int identityHashCode,
|
||||
int lockedStackDepth, StackTraceElement lockedStackFrame) {
|
||||
this.className = className;
|
||||
this.identityHashCode = identityHashCode;
|
||||
this.lockedStackDepth = lockedStackDepth;
|
||||
this.lockedStackFrame = lockedStackFrame;
|
||||
}
|
||||
|
||||
public static MonitorInfo from(CompositeData cd) {
|
||||
try {
|
||||
CompositeData stecd = (CompositeData) cd.get("lockedStackFrame");
|
||||
StackTraceElement ste = new StackTraceElement(
|
||||
(String) stecd.get("className"),
|
||||
(String) stecd.get("methodName"),
|
||||
(String) stecd.get("fileName"),
|
||||
(Integer) stecd.get("lineNumber"));
|
||||
return new MonitorInfo(
|
||||
(String) cd.get("className"),
|
||||
(Integer) cd.get("identityHashCode"),
|
||||
(Integer) cd.get("lockedStackDepth"),
|
||||
ste);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return className;
|
||||
}
|
||||
|
||||
public int getIdentityHashCode() {
|
||||
return identityHashCode;
|
||||
}
|
||||
|
||||
public int getLockedStackDepth() {
|
||||
return lockedStackDepth;
|
||||
}
|
||||
|
||||
public StackTraceElement getLockedStackFrame() {
|
||||
return lockedStackFrame;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface TestMXBean {
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
||||
* @bug 6482247
|
||||
* @summary Test that creating MXBeans does not introduce memory leaks.
|
||||
* @author Eamonn McManus
|
||||
* @run build LeakTest
|
||||
* @run build LeakTest RandomMXBeanTest
|
||||
* @run main LeakTest
|
||||
*/
|
||||
|
||||
|
@ -86,7 +86,8 @@ public class MBeanOperationInfoTest {
|
||||
if (error > 0) {
|
||||
System.out.println("\nTEST FAILED");
|
||||
throw new Exception("TEST FAILED: " + error + " wrong return types");
|
||||
} else if (tested != returnTypes.length) {
|
||||
} else if (tested != returnTypes.length &&
|
||||
!System.getProperty("java.specification.version").equals("1.5")) {
|
||||
System.out.println("\nTEST FAILED");
|
||||
throw new Exception("TEST FAILED: " + tested + " cases tested, " +
|
||||
returnTypes.length + " expected");
|
||||
|
@ -149,7 +149,7 @@ public class MXBeanTest {
|
||||
if (mbai.getName().equals("Ints")
|
||||
&& mbai.isReadable() && !mbai.isWritable()
|
||||
&& mbai.getDescriptor().getFieldValue("openType")
|
||||
.equals(new ArrayType(SimpleType.INTEGER, true))
|
||||
.equals(new ArrayType<int[]>(SimpleType.INTEGER, true))
|
||||
&& attrs[0].getType().equals("[I"))
|
||||
success("MBeanAttributeInfo");
|
||||
else
|
||||
|
@ -46,7 +46,8 @@ public class ThreadMXBeanTest {
|
||||
long[] ids1 = proxy.getAllThreadIds();
|
||||
|
||||
// Add some random ids to the list so we'll get back null ThreadInfo
|
||||
long[] ids2 = Arrays.copyOf(ids1, ids1.length + 10);
|
||||
long[] ids2 = new long[ids1.length + 10];
|
||||
System.arraycopy(ids1, 0, ids2, 0, ids1.length);
|
||||
Random r = new Random();
|
||||
for (int i = ids1.length; i < ids2.length; i++)
|
||||
ids2[i] = Math.abs(r.nextLong());
|
||||
|
@ -83,20 +83,20 @@ public interface TigerMXBean {
|
||||
Tuiseal opEnum(Tuiseal x, Tuiseal y);
|
||||
|
||||
List<String> StringList = Arrays.asList(new String[] {"a", "b", "x"});
|
||||
ArrayType StringListType =
|
||||
ArrayType<?> StringListType =
|
||||
MerlinMXBean.ArrayTypeMaker.make(1, SimpleType.STRING);
|
||||
List<String> getStringList();
|
||||
void setStringList(List<String> x);
|
||||
List<String> opStringList(List<String> x, List<String> y);
|
||||
|
||||
Set<String> StringSet = new HashSet(StringList);
|
||||
ArrayType StringSetType = StringListType;
|
||||
Set<String> StringSet = new HashSet<String>(StringList);
|
||||
ArrayType<?> StringSetType = StringListType;
|
||||
Set<String> getStringSet();
|
||||
void setStringSet(Set<String> x);
|
||||
Set<String> opStringSet(Set<String> x, Set<String> y);
|
||||
|
||||
SortedSet<String> SortedStringSet = new TreeSet(StringList);
|
||||
ArrayType SortedStringSetType = StringListType;
|
||||
SortedSet<String> SortedStringSet = new TreeSet<String>(StringList);
|
||||
ArrayType<?> SortedStringSetType = StringListType;
|
||||
SortedSet<String> getSortedStringSet();
|
||||
void setSortedStringSet(SortedSet<String> x);
|
||||
SortedSet<String> opSortedStringSet(SortedSet<String> x,
|
||||
@ -119,7 +119,7 @@ public interface TigerMXBean {
|
||||
Map<String,List<String>> y);
|
||||
|
||||
SortedMap<String,String> XSortedMap =
|
||||
new TreeMap(Collections.singletonMap("foo", "bar"));
|
||||
new TreeMap<String,String>(Collections.singletonMap("foo", "bar"));
|
||||
String XSortedMapTypeName =
|
||||
"java.util.SortedMap<java.lang.String, java.lang.String>";
|
||||
CompositeType XSortedMapRowType = MerlinMXBean.CompositeTypeMaker.make(
|
||||
@ -137,8 +137,8 @@ public interface TigerMXBean {
|
||||
|
||||
// For bug 6319960, try constructing Set and Map with non-Comparable
|
||||
|
||||
Set<Point> PointSet = new HashSet(Collections.singleton(Point));
|
||||
ArrayType PointSetType =
|
||||
Set<Point> PointSet = new HashSet<Point>(Collections.singleton(Point));
|
||||
ArrayType<?> PointSetType =
|
||||
MerlinMXBean.ArrayTypeMaker.make(1, PointType);
|
||||
Set<Point> getPointSet();
|
||||
void setPointSet(Set<Point> x);
|
||||
|
@ -98,7 +98,7 @@ public class QueryNotifFilterTest {
|
||||
this.queryString = queryString;
|
||||
}
|
||||
abstract boolean apply(MBeanServer mbs, ObjectName name) throws Exception;
|
||||
@Override
|
||||
//@Override - doesn't override in JDK5
|
||||
public boolean apply(ObjectName name) {
|
||||
try {
|
||||
return apply(getMBeanServer(), name);
|
||||
|
@ -31,11 +31,16 @@
|
||||
* @run main CloseServerTest
|
||||
*/
|
||||
|
||||
import com.sun.jmx.remote.util.EnvHelp;
|
||||
import java.net.MalformedURLException;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.management.*;
|
||||
import javax.management.remote.*;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
public class CloseServerTest {
|
||||
private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
|
||||
@ -131,40 +136,54 @@ public class CloseServerTest {
|
||||
|
||||
server.stop();
|
||||
|
||||
// with a client listener, but close the server first
|
||||
System.out.println(">>> Open, start a server, create a client, add a listener, close the server then the client.");
|
||||
server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
|
||||
server.start();
|
||||
List<Map<String, String>> envs = Arrays.asList(
|
||||
Collections.singletonMap(
|
||||
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false"),
|
||||
Collections.singletonMap(
|
||||
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "true"));
|
||||
|
||||
addr = server.getAddress();
|
||||
client = JMXConnectorFactory.newJMXConnector(addr, null);
|
||||
client.connect(null);
|
||||
for (Map<String, String> env : envs) {
|
||||
System.out.println(
|
||||
">>>>>>>> " + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE +
|
||||
" = " + env.get(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE));
|
||||
|
||||
mserver = client.getMBeanServerConnection();
|
||||
mserver.addNotificationListener(delegateName, dummyListener, null, null);
|
||||
// with a client listener, but close the server first
|
||||
System.out.println(">>> Open, start a server, create a client, " +
|
||||
"add a listener, close the server then the client.");
|
||||
server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
|
||||
server.start();
|
||||
|
||||
server.stop();
|
||||
addr = server.getAddress();
|
||||
client = JMXConnectorFactory.newJMXConnector(addr, null);
|
||||
client.connect(null);
|
||||
|
||||
mserver = client.getMBeanServerConnection();
|
||||
mserver.addNotificationListener(delegateName, dummyListener, null, null);
|
||||
|
||||
server.stop();
|
||||
|
||||
try {
|
||||
client.close();
|
||||
} catch (Exception e) {
|
||||
// ok, it is because the server has been closed.
|
||||
}
|
||||
|
||||
// with a client listener, but close the client first
|
||||
System.out.println(">>> Open, start a server, create a client, " +
|
||||
"add a listener, close the client then the server.");
|
||||
server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
|
||||
server.start();
|
||||
|
||||
addr = server.getAddress();
|
||||
client = JMXConnectorFactory.newJMXConnector(addr, null);
|
||||
client.connect(null);
|
||||
|
||||
mserver = client.getMBeanServerConnection();
|
||||
mserver.addNotificationListener(delegateName, dummyListener, null, null);
|
||||
|
||||
try {
|
||||
client.close();
|
||||
} catch (Exception e) {
|
||||
// ok, it is because the server has been closed.
|
||||
server.stop();
|
||||
}
|
||||
|
||||
// with a client listener, but close the client first
|
||||
System.out.println(">>> Open, start a server, create a client, add a listener, close the client then the server.");
|
||||
server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
|
||||
server.start();
|
||||
|
||||
addr = server.getAddress();
|
||||
client = JMXConnectorFactory.newJMXConnector(addr, null);
|
||||
client.connect(null);
|
||||
|
||||
mserver = client.getMBeanServerConnection();
|
||||
mserver.addNotificationListener(delegateName, dummyListener, null, null);
|
||||
|
||||
client.close();
|
||||
server.stop();
|
||||
} catch (MalformedURLException e) {
|
||||
System.out.println(">>> Skipping unsupported URL " + u);
|
||||
return true;
|
||||
|
@ -37,6 +37,7 @@ import java.util.HashMap;
|
||||
|
||||
import javax.management.*;
|
||||
import javax.management.remote.*;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
public class DeadLockTest {
|
||||
private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
|
||||
@ -72,6 +73,9 @@ public class DeadLockTest {
|
||||
// disable the client ping
|
||||
env.put("jmx.remote.x.client.connection.check.period", "0");
|
||||
|
||||
// ensure we are not internally using the Event Service on the server
|
||||
env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
|
||||
|
||||
try {
|
||||
u = new JMXServiceURL(proto, null, 0);
|
||||
server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
|
||||
|
@ -50,6 +50,8 @@ import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import com.sun.jmx.remote.util.EnvHelp;
|
||||
import java.util.Collections;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
public class IdleTimeoutTest {
|
||||
public static void main(String[] args) throws Exception {
|
||||
@ -88,8 +90,13 @@ public class IdleTimeoutTest {
|
||||
|
||||
private static long getIdleTimeout(MBeanServer mbs, JMXServiceURL url)
|
||||
throws Exception {
|
||||
// If the connector server is using the Event Service, then connections
|
||||
// never time out. This is by design.
|
||||
Map<String, String> env =
|
||||
Collections.singletonMap(
|
||||
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
|
||||
JMXConnectorServer server =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
|
||||
server.start();
|
||||
try {
|
||||
url = server.getAddress();
|
||||
@ -164,6 +171,7 @@ public class IdleTimeoutTest {
|
||||
|
||||
Map idleMap = new HashMap();
|
||||
idleMap.put(EnvHelp.SERVER_CONNECTION_TIMEOUT, new Long(timeout));
|
||||
idleMap.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
|
||||
JMXConnectorServer server =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url,idleMap,mbs);
|
||||
|
||||
|
@ -35,6 +35,8 @@
|
||||
import java.net.MalformedURLException;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
@ -47,12 +49,14 @@ import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
/**
|
||||
* VM shutdown hook. Test that the hook is called less than 5 secs
|
||||
* after expected exit.
|
||||
*/
|
||||
class TimeChecker extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
System.out.println("shutdown hook called");
|
||||
long elapsedTime =
|
||||
@ -81,12 +85,15 @@ public class RMIExitTest {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Start test");
|
||||
Runtime.getRuntime().addShutdownHook(new TimeChecker());
|
||||
test();
|
||||
test(false);
|
||||
test(true);
|
||||
exitStartTime = System.currentTimeMillis();
|
||||
System.out.println("End test");
|
||||
}
|
||||
|
||||
private static void test() {
|
||||
private static void test(boolean eventService) {
|
||||
System.out.println(
|
||||
"---testing with" + (eventService ? "" : "out") + " Event Service");
|
||||
try {
|
||||
JMXServiceURL u = new JMXServiceURL("rmi", null, 0);
|
||||
JMXConnectorServer server;
|
||||
@ -105,8 +112,11 @@ public class RMIExitTest {
|
||||
}
|
||||
};
|
||||
|
||||
Map<String, String> env = Collections.singletonMap(
|
||||
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
|
||||
Boolean.toString(eventService));
|
||||
server = JMXConnectorServerFactory.newJMXConnectorServer(u,
|
||||
null,
|
||||
env,
|
||||
mbs);
|
||||
server.start();
|
||||
|
||||
|
@ -33,10 +33,10 @@
|
||||
|
||||
import java.util.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.management.*;
|
||||
import javax.management.remote.*;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
public class ReconnectTest {
|
||||
private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
|
||||
@ -48,6 +48,7 @@ public class ReconnectTest {
|
||||
String timeout = "1000";
|
||||
env.put("jmx.remote.x.server.connection.timeout", timeout);
|
||||
env.put("jmx.remote.x.client.connection.check.period", timeout);
|
||||
env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Random;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.remote.IdentityMBeanServerForwarder;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.MBeanServerForwarder;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6218920
|
||||
* @summary Tests manipulation of MBeanServerForwarder chains.
|
||||
* @author Eamonn McManus
|
||||
*/
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
public class ForwarderChainTest {
|
||||
private static final TestMBeanServerForwarder[] forwarders =
|
||||
new TestMBeanServerForwarder[10];
|
||||
static {
|
||||
for (int i = 0; i < forwarders.length; i++)
|
||||
forwarders[i] = new TestMBeanServerForwarder(i);
|
||||
}
|
||||
|
||||
private static class TestMBeanServerForwarder
|
||||
extends IdentityMBeanServerForwarder {
|
||||
private final int index;
|
||||
volatile int defaultDomainCount;
|
||||
|
||||
TestMBeanServerForwarder(int index) {
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultDomain() {
|
||||
defaultDomainCount++;
|
||||
return super.getDefaultDomain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "forwarders[" + index + "]";
|
||||
}
|
||||
}
|
||||
|
||||
private static String failure;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
System.out.println("===Test with newly created, unattached server===");
|
||||
|
||||
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///");
|
||||
JMXConnectorServer cs = new RMIConnectorServer(url, null);
|
||||
test(cs, null);
|
||||
|
||||
System.out.println("===Test with server attached to MBS===");
|
||||
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
||||
cs = new RMIConnectorServer(url, null, mbs);
|
||||
test(cs, mbs);
|
||||
|
||||
System.out.println("===Remove any leftover forwarders===");
|
||||
while (cs.getSystemMBeanServer() instanceof MBeanServerForwarder) {
|
||||
MBeanServerForwarder mbsf =
|
||||
(MBeanServerForwarder) cs.getSystemMBeanServer();
|
||||
cs.removeMBeanServerForwarder(mbsf);
|
||||
}
|
||||
expectChain(cs, "U", mbs);
|
||||
|
||||
System.out.println("===Ensure forwarders are called===");
|
||||
cs.setMBeanServerForwarder(forwarders[0]);
|
||||
cs.setSystemMBeanServerForwarder(forwarders[1]);
|
||||
expectChain(cs, "1U0", mbs);
|
||||
cs.start();
|
||||
if (forwarders[0].defaultDomainCount != 0 ||
|
||||
forwarders[1].defaultDomainCount != 0) {
|
||||
fail("defaultDomainCount not zero");
|
||||
}
|
||||
JMXServiceURL addr = cs.getAddress();
|
||||
JMXConnector cc = JMXConnectorFactory.connect(addr);
|
||||
MBeanServerConnection mbsc = cc.getMBeanServerConnection();
|
||||
mbsc.getDefaultDomain();
|
||||
cc.close();
|
||||
cs.stop();
|
||||
for (boolean system : new boolean[] {false, true}) {
|
||||
TestMBeanServerForwarder mbsf = system ? forwarders[1] : forwarders[0];
|
||||
if (mbsf.defaultDomainCount != 1) {
|
||||
fail((system ? "System" : "User") + " forwarder called " +
|
||||
mbsf.defaultDomainCount + " times");
|
||||
}
|
||||
}
|
||||
|
||||
if (failure == null)
|
||||
System.out.println("TEST PASSED");
|
||||
else
|
||||
throw new Exception("TEST FAILED: " + failure);
|
||||
}
|
||||
|
||||
private static void test(JMXConnectorServer cs, MBeanServer end) {
|
||||
// A newly-created connector server might have system forwarders,
|
||||
// so get rid of those.
|
||||
while (cs.getSystemMBeanServer() != cs.getMBeanServer())
|
||||
cs.removeMBeanServerForwarder((MBeanServerForwarder) cs.getSystemMBeanServer());
|
||||
|
||||
expectChain(cs, "U", end);
|
||||
|
||||
System.out.println("Add a user forwarder");
|
||||
cs.setMBeanServerForwarder(forwarders[0]);
|
||||
expectChain(cs, "U0", end);
|
||||
|
||||
System.out.println("Add another user forwarder");
|
||||
cs.setMBeanServerForwarder(forwarders[1]);
|
||||
expectChain(cs, "U10", end);
|
||||
|
||||
System.out.println("Add a system forwarder");
|
||||
cs.setSystemMBeanServerForwarder(forwarders[2]);
|
||||
expectChain(cs, "2U10", end);
|
||||
|
||||
System.out.println("Add another user forwarder");
|
||||
cs.setMBeanServerForwarder(forwarders[3]);
|
||||
expectChain(cs, "2U310", end);
|
||||
|
||||
System.out.println("Add another system forwarder");
|
||||
cs.setSystemMBeanServerForwarder(forwarders[4]);
|
||||
expectChain(cs, "42U310", end);
|
||||
|
||||
System.out.println("Remove the first user forwarder");
|
||||
cs.removeMBeanServerForwarder(forwarders[3]);
|
||||
expectChain(cs, "42U10", end);
|
||||
|
||||
System.out.println("Remove the last user forwarder");
|
||||
cs.removeMBeanServerForwarder(forwarders[0]);
|
||||
expectChain(cs, "42U1", end);
|
||||
|
||||
System.out.println("Remove the first system forwarder");
|
||||
cs.removeMBeanServerForwarder(forwarders[4]);
|
||||
expectChain(cs, "2U1", end);
|
||||
|
||||
System.out.println("Remove the last system forwarder");
|
||||
cs.removeMBeanServerForwarder(forwarders[2]);
|
||||
expectChain(cs, "U1", end);
|
||||
|
||||
System.out.println("Remove the last forwarder");
|
||||
cs.removeMBeanServerForwarder(forwarders[1]);
|
||||
expectChain(cs, "U", end);
|
||||
|
||||
System.out.println("---Doing random manipulations---");
|
||||
// In this loop we pick one of the forwarders at random each time.
|
||||
// If it is already in the chain, then we remove it. If it is not
|
||||
// in the chain, then we do one of three things: try to remove it
|
||||
// (expecting an exception); add it to the user chain; or add it
|
||||
// to the system chain.
|
||||
// A subtle point is that if there is no MBeanServer then
|
||||
// cs.setMBeanServerForwarder(mbsf) does not change mbsf.getMBeanServer().
|
||||
// Since we're recycling a random forwarder[i], we explicitly
|
||||
// call mbsf.setMBeanServer(null) in this case.
|
||||
String chain = "U";
|
||||
Random r = new Random();
|
||||
for (int i = 0; i < 50; i++) {
|
||||
int fwdi = r.nextInt(10);
|
||||
MBeanServerForwarder mbsf = forwarders[fwdi];
|
||||
char c = (char) ('0' + fwdi);
|
||||
int ci = chain.indexOf(c);
|
||||
if (ci >= 0) {
|
||||
System.out.println("Remove " + c);
|
||||
cs.removeMBeanServerForwarder(mbsf);
|
||||
chain = chain.substring(0, ci) + chain.substring(ci + 1);
|
||||
} else {
|
||||
switch (r.nextInt(3)) {
|
||||
case 0: { // try to remove it
|
||||
try {
|
||||
System.out.println("Try to remove absent " + c);
|
||||
cs.removeMBeanServerForwarder(mbsf);
|
||||
fail("Remove succeeded but should not have");
|
||||
return;
|
||||
} catch (NoSuchElementException e) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1: { // add it to the user chain
|
||||
System.out.println("Add " + c + " to user chain");
|
||||
if (cs.getMBeanServer() == null)
|
||||
mbsf.setMBeanServer(null);
|
||||
cs.setMBeanServerForwarder(mbsf);
|
||||
int postu = chain.indexOf('U') + 1;
|
||||
chain = chain.substring(0, postu) + c +
|
||||
chain.substring(postu);
|
||||
break;
|
||||
}
|
||||
case 2: { // add it to the system chain
|
||||
System.out.println("Add " + c + " to system chain");
|
||||
if (cs.getSystemMBeanServer() == null)
|
||||
mbsf.setMBeanServer(null);
|
||||
cs.setSystemMBeanServerForwarder(mbsf);
|
||||
chain = c + chain;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
expectChain(cs, chain, end);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the forwarder chain has the expected contents. The forwarders
|
||||
* are encoded as a string. For example, "12U34" means that the system
|
||||
* chain contains forwarders[1] followed by forwarders[2], and the user
|
||||
* chain contains forwarders[3] followed by forwarders[4]. Since the
|
||||
* user chain is attached to the end of the system chain, another way to
|
||||
* look at this is that the U marks the transition from one to the other.
|
||||
*
|
||||
* After traversing the chains, we should be pointing at "end".
|
||||
*/
|
||||
private static void expectChain(
|
||||
JMXConnectorServer cs, String chain, MBeanServer end) {
|
||||
System.out.println("...expected chain: " + chain);
|
||||
MBeanServer curr = cs.getSystemMBeanServer();
|
||||
int i = 0;
|
||||
while (i < chain.length()) {
|
||||
char c = chain.charAt(i);
|
||||
if (c == 'U') {
|
||||
if (cs.getMBeanServer() != curr) {
|
||||
fail("User chain should have started here: " + curr);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
int fwdi = c - '0';
|
||||
MBeanServerForwarder forwarder = forwarders[fwdi];
|
||||
if (curr != forwarder) {
|
||||
fail("Expected forwarder " + c + " here: " + curr);
|
||||
return;
|
||||
}
|
||||
curr = ((MBeanServerForwarder) curr).getMBeanServer();
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (curr != end) {
|
||||
fail("End of chain is " + curr + ", should be " + end);
|
||||
return;
|
||||
}
|
||||
System.out.println("...OK");
|
||||
}
|
||||
|
||||
private static void fail(String msg) {
|
||||
System.out.println("FAILED: " + msg);
|
||||
failure = msg;
|
||||
}
|
||||
}
|
@ -0,0 +1,177 @@
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.event.EventClientDelegate;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 6663757
|
||||
* @summary Tests standard MBeanServerForwarders introduced by connector server
|
||||
* options.
|
||||
* @author Eamonn McManus
|
||||
*/
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.MBeanServerForwarder;
|
||||
|
||||
public class StandardForwardersTest {
|
||||
private static String failure;
|
||||
|
||||
private static class Forwarder {
|
||||
private final String attribute;
|
||||
private final boolean defaultEnabled;
|
||||
private final Class<?> expectedClass;
|
||||
|
||||
public Forwarder(String attribute, boolean defaultEnabled,
|
||||
Class<?> expectedClass) {
|
||||
this.attribute = attribute;
|
||||
this.defaultEnabled = defaultEnabled;
|
||||
this.expectedClass = expectedClass;
|
||||
}
|
||||
}
|
||||
|
||||
private static enum Status {DISABLED, ENABLED, DEFAULT}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||
|
||||
MBeanServerForwarder ecdFwd =
|
||||
EventClientDelegate.newForwarder();
|
||||
Forwarder ecd = new Forwarder(
|
||||
JMXConnectorServer.EVENT_CLIENT_DELEGATE_FORWARDER, true,
|
||||
ecdFwd.getClass());
|
||||
|
||||
Forwarder[] forwarders = {ecd};
|
||||
|
||||
// Now go through every combination of forwarders. Each forwarder
|
||||
// may be explicitly enabled, explicitly disabled, or left to its
|
||||
// default value.
|
||||
int nStatus = Status.values().length;
|
||||
int limit = (int) Math.pow(nStatus, forwarders.length);
|
||||
for (int i = 0; i < limit; i++) {
|
||||
Status[] status = new Status[forwarders.length];
|
||||
int ii = i;
|
||||
for (int j = 0; j < status.length; j++) {
|
||||
status[j] = Status.values()[ii % nStatus];
|
||||
ii /= nStatus;
|
||||
}
|
||||
Map<String, String> env = new HashMap<String, String>();
|
||||
String test = "";
|
||||
for (int j = 0; j < status.length; j++) {
|
||||
if (!test.equals(""))
|
||||
test += "; ";
|
||||
test += forwarders[j].attribute;
|
||||
switch (status[j]) {
|
||||
case DISABLED:
|
||||
test += "=false";
|
||||
env.put(forwarders[j].attribute, "false");
|
||||
break;
|
||||
case ENABLED:
|
||||
test += "=true";
|
||||
env.put(forwarders[j].attribute, "true");
|
||||
break;
|
||||
case DEFAULT:
|
||||
test += "=default(" + forwarders[j].defaultEnabled + ")";
|
||||
break;
|
||||
}
|
||||
}
|
||||
boolean consistent = isConsistent(env);
|
||||
test += "; (" + (consistent ? "" : "in") + "consistent)";
|
||||
System.out.println(test);
|
||||
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///");
|
||||
try {
|
||||
JMXConnectorServer cs =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
|
||||
if (!consistent) {
|
||||
fail("Inconsistent attributes should have been rejected " +
|
||||
"but were not");
|
||||
}
|
||||
checkForwarders(cs, forwarders, status);
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (consistent) {
|
||||
fail("Consistent attributes provoked IllegalArgumentException");
|
||||
e.printStackTrace(System.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (failure == null)
|
||||
System.out.println("TEST PASSED");
|
||||
else
|
||||
throw new Exception(failure);
|
||||
}
|
||||
|
||||
// Check that the classes of the forwarders in the system chain correspond
|
||||
// to what we expect given the options we have passed. This check is a bit
|
||||
// superficial in the sense that a forwarder might be for example a
|
||||
// SingleMBeanForwarderHandler but that doesn't prove that it is the
|
||||
// right Single MBean. Nevertheless the test should expose any severe
|
||||
// wrongness.
|
||||
//
|
||||
// The check here makes some assumptions that could become untrue in the
|
||||
// future. First, it assumes that the forwarders that are added have
|
||||
// exactly the classes that are in the Forwarder[] array. So for example
|
||||
// the forwarder for CONTEXT_FORWARDER must be of the same class as an
|
||||
// explicit call to ClientContext.newContextForwarder. The spec doesn't
|
||||
// require that - it only requires that the forwarder have the same
|
||||
// behaviour. The second assumption is that the connector server doesn't
|
||||
// add any forwarders of its own into the system chain, and again the spec
|
||||
// doesn't disallow that.
|
||||
private static void checkForwarders(
|
||||
JMXConnectorServer cs, Forwarder[] forwarders, Status[] status) {
|
||||
List<Class<?>> expectedClasses = new ArrayList<Class<?>>();
|
||||
for (int i = 0; i < forwarders.length; i++) {
|
||||
if (status[i] == Status.ENABLED ||
|
||||
(status[i] == Status.DEFAULT && forwarders[i].defaultEnabled))
|
||||
expectedClasses.add(forwarders[i].expectedClass);
|
||||
}
|
||||
MBeanServer stop = cs.getMBeanServer();
|
||||
List<Class<?>> foundClasses = new ArrayList<Class<?>>();
|
||||
for (MBeanServer mbs = cs.getSystemMBeanServer(); mbs != stop;
|
||||
mbs = ((MBeanServerForwarder) mbs).getMBeanServer())
|
||||
foundClasses.add(mbs.getClass());
|
||||
if (!expectedClasses.equals(foundClasses)) {
|
||||
fail("Incorrect forwarder chain: expected " + expectedClasses +
|
||||
"; found " + foundClasses);
|
||||
}
|
||||
}
|
||||
|
||||
// env is consistent if either (a) localizer is not enabled or (b)
|
||||
// localizer is enabled and context is enabled.
|
||||
// Neither of those is present in this codebase so env is always consistent.
|
||||
private static boolean isConsistent(Map<String, String> env) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void fail(String why) {
|
||||
System.out.println("FAILED: " + why);
|
||||
failure = why;
|
||||
}
|
||||
}
|
@ -44,13 +44,33 @@
|
||||
We also test objects that are of known class but not serializable.
|
||||
The test cases are similar.
|
||||
*/
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
import javax.management.*;
|
||||
import javax.management.loading.*;
|
||||
import javax.management.remote.*;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.NotSerializableException;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import javax.management.Attribute;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationFilter;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.remote.JMXConnectionNotification;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
import org.omg.CORBA.MARSHAL;
|
||||
|
||||
public class MissingClassTest {
|
||||
private static final int NNOTIFS = 50;
|
||||
@ -84,7 +104,6 @@ public class MissingClassTest {
|
||||
serverLoader.loadClass("$ClientUnknown$").newInstance();
|
||||
|
||||
final String[] protos = {"rmi", /*"iiop",*/ "jmxmp"};
|
||||
// iiop commented out until bug 4935098 is fixed
|
||||
boolean ok = true;
|
||||
for (int i = 0; i < protos.length; i++) {
|
||||
try {
|
||||
@ -105,7 +124,16 @@ public class MissingClassTest {
|
||||
}
|
||||
|
||||
private static boolean test(String proto) throws Exception {
|
||||
System.out.println("Testing for proto " + proto);
|
||||
boolean ok = true;
|
||||
for (boolean eventService : new boolean[] {false, true})
|
||||
ok &= test(proto, eventService);
|
||||
return ok;
|
||||
}
|
||||
|
||||
private static boolean test(String proto, boolean eventService)
|
||||
throws Exception {
|
||||
System.out.println("Testing for proto " + proto + " with" +
|
||||
(eventService ? "" : "out") + " Event Service");
|
||||
|
||||
boolean ok = true;
|
||||
|
||||
@ -117,6 +145,8 @@ public class MissingClassTest {
|
||||
Map serverMap = new HashMap();
|
||||
serverMap.put(JMXConnectorServerFactory.DEFAULT_CLASS_LOADER,
|
||||
serverLoader);
|
||||
serverMap.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
|
||||
Boolean.toString(eventService));
|
||||
|
||||
// make sure no auto-close at server side
|
||||
serverMap.put("jmx.remote.x.server.connection.timeout", "888888888");
|
||||
@ -155,6 +185,8 @@ public class MissingClassTest {
|
||||
ok = false;
|
||||
} catch (IOException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof MARSHAL) // see CR 4935098
|
||||
cause = cause.getCause();
|
||||
if (cause instanceof ClassNotFoundException) {
|
||||
System.out.println("Success: got an IOException wrapping " +
|
||||
"a ClassNotFoundException");
|
||||
@ -177,6 +209,8 @@ public class MissingClassTest {
|
||||
ok = false;
|
||||
} catch (IOException e) {
|
||||
Throwable wrapped = e.getCause();
|
||||
if (wrapped instanceof MARSHAL) // see CR 4935098
|
||||
wrapped = wrapped.getCause();
|
||||
if (wrapped instanceof ClassNotFoundException) {
|
||||
System.out.println("Success: got an IOException wrapping " +
|
||||
"a ClassNotFoundException: " +
|
||||
@ -228,6 +262,8 @@ public class MissingClassTest {
|
||||
ok = false;
|
||||
} catch (IOException e) {
|
||||
Throwable cause = e.getCause();
|
||||
if (cause instanceof MARSHAL) // see CR 4935098
|
||||
cause = cause.getCause();
|
||||
if (cause instanceof ClassNotFoundException) {
|
||||
System.out.println("Success: got an IOException " +
|
||||
"wrapping a ClassNotFoundException");
|
||||
@ -461,12 +497,13 @@ public class MissingClassTest {
|
||||
while ((remain = deadline - System.currentTimeMillis()) >= 0) {
|
||||
synchronized (result) {
|
||||
if (result.failed
|
||||
|| (result.knownCount == NNOTIFS
|
||||
&& result.lostCount == NNOTIFS*2))
|
||||
|| (result.knownCount >= NNOTIFS
|
||||
&& result.lostCount >= NNOTIFS*2))
|
||||
break;
|
||||
result.wait(remain);
|
||||
}
|
||||
}
|
||||
Thread.sleep(2); // allow any spurious extra notifs to arrive
|
||||
if (result.failed) {
|
||||
System.out.println("TEST FAILS: Notification strangeness");
|
||||
return false;
|
||||
@ -476,6 +513,11 @@ public class MissingClassTest {
|
||||
"got NOTIFS_LOST for unknown and " +
|
||||
"unserializable ones");
|
||||
return true;
|
||||
} else if (result.knownCount >= NNOTIFS
|
||||
|| result.lostCount >= NNOTIFS*2) {
|
||||
System.out.println("TEST FAILS: Received too many notifs: " +
|
||||
"known=" + result.knownCount + "; lost=" + result.lostCount);
|
||||
return false;
|
||||
} else {
|
||||
System.out.println("TEST FAILS: Timed out without receiving " +
|
||||
"all notifs: known=" + result.knownCount +
|
||||
|
@ -33,10 +33,12 @@
|
||||
*/
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import javax.management.*;
|
||||
import javax.management.remote.*;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
public class AddRemoveTest {
|
||||
private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
|
||||
@ -69,9 +71,16 @@ public class AddRemoveTest {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean test(String proto)
|
||||
private static boolean test(String proto) throws Exception {
|
||||
boolean ok = test(proto, false);
|
||||
ok &= test(proto, true);
|
||||
return ok;
|
||||
}
|
||||
|
||||
private static boolean test(String proto, boolean eventService)
|
||||
throws Exception {
|
||||
System.out.println(">>> Test for protocol " + proto);
|
||||
System.out.println(">>> Test for protocol " + proto + " with" +
|
||||
(eventService ? "" : "out") + " event service");
|
||||
JMXServiceURL u = new JMXServiceURL(proto, null, 0);
|
||||
JMXConnectorServer server;
|
||||
JMXServiceURL addr;
|
||||
@ -89,7 +98,10 @@ public class AddRemoveTest {
|
||||
|
||||
try {
|
||||
// with a client listener, but close the server first
|
||||
server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
|
||||
Map<String, String> env = Collections.singletonMap(
|
||||
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
|
||||
Boolean.toString(eventService));
|
||||
server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
|
||||
server.start();
|
||||
|
||||
addr = server.getAddress();
|
||||
|
@ -31,11 +31,12 @@
|
||||
* @run main DiffHBTest
|
||||
*/
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import javax.management.*;
|
||||
import javax.management.remote.*;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
/**
|
||||
* This test registeres an unique listener with two different handbacks,
|
||||
@ -48,11 +49,6 @@ public class DiffHBTest {
|
||||
private static ObjectName delegateName;
|
||||
private static ObjectName timerName;
|
||||
|
||||
public static int received = 0;
|
||||
public static final int[] receivedLock = new int[0];
|
||||
public static Notification receivedNotif = null;
|
||||
|
||||
public static Object receivedHB = null;
|
||||
public static final String[] hbs = new String[] {"0", "1"};
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
@ -61,162 +57,174 @@ public class DiffHBTest {
|
||||
delegateName = new ObjectName("JMImplementation:type=MBeanServerDelegate");
|
||||
timerName = new ObjectName("MBean:name=Timer");
|
||||
|
||||
boolean ok = true;
|
||||
String errors = "";
|
||||
|
||||
for (int i = 0; i < protocols.length; i++) {
|
||||
try {
|
||||
if (!test(protocols[i])) {
|
||||
System.out.println(">>> Test failed for " + protocols[i]);
|
||||
ok = false;
|
||||
final String s = test(protocols[i]);
|
||||
if (s != null) {
|
||||
if ("".equals(errors)) {
|
||||
errors = "Failed to " + protocols[i] + ": "+s;
|
||||
} else {
|
||||
System.out.println(">>> Test successed for " + protocols[i]);
|
||||
errors = "\tFailed to " + protocols[i] + ": "+s;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println(">>> Test failed for " + protocols[i]);
|
||||
e.printStackTrace(System.out);
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
System.out.println(">>> Test passed");
|
||||
if ("".equals(errors)) {
|
||||
System.out.println(">>> Passed!");
|
||||
} else {
|
||||
System.out.println(">>> TEST FAILED");
|
||||
System.exit(1);
|
||||
System.out.println(">>> Failed!");
|
||||
|
||||
throw new RuntimeException(errors);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean test(String proto) throws Exception {
|
||||
System.out.println(">>> Test for protocol " + proto);
|
||||
private static String test(String proto) throws Exception {
|
||||
String ret = null;
|
||||
for (boolean eventService : new boolean[] {false, true}) {
|
||||
String s = test(proto, eventService);
|
||||
if (s != null) {
|
||||
if (ret == null)
|
||||
ret = s;
|
||||
else
|
||||
ret = ret + "; " + s;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static String test(String proto, boolean eventService)
|
||||
throws Exception {
|
||||
System.out.println(">>> Test for protocol " + proto + " with" +
|
||||
(eventService ? "" : "out") + " event service");
|
||||
JMXServiceURL u = new JMXServiceURL(proto, null, 0);
|
||||
JMXConnectorServer server;
|
||||
JMXServiceURL addr;
|
||||
JMXConnector client;
|
||||
MBeanServerConnection mserver;
|
||||
|
||||
final NotificationListener dummyListener = new NotificationListener() {
|
||||
public void handleNotification(Notification n, Object o) {
|
||||
synchronized(receivedLock) {
|
||||
if (n == null) {
|
||||
System.out.println(">>> Got a null notification.");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// check number
|
||||
if (received > 2) {
|
||||
System.out.println(">>> Expect to receive 2 notifs, but get "+received);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (received == 0) { // first time
|
||||
receivedNotif = n;
|
||||
receivedHB = o;
|
||||
|
||||
if (!hbs[0].equals(o) && !hbs[1].equals(o)) {
|
||||
System.out.println(">>> Unkown handback: "+o);
|
||||
System.exit(1);
|
||||
}
|
||||
} else { // second time
|
||||
if (!receivedNotif.equals(n)) {
|
||||
System.out.println(">>> Not get same notif twice.");
|
||||
System.exit(1);
|
||||
} else if (!hbs[0].equals(o) && !hbs[1].equals(o)) { // validate handback
|
||||
System.out.println(">>> Unkown handback: "+o);
|
||||
System.exit(1);
|
||||
} else if (receivedHB.equals(o)) {
|
||||
System.out.println(">>> Got same handback twice: "+o);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
++received;
|
||||
|
||||
if (received == 2) {
|
||||
receivedLock.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
|
||||
Map<String, String> env = Collections.singletonMap(
|
||||
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
|
||||
Boolean.toString(eventService));
|
||||
server =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
|
||||
server.start();
|
||||
|
||||
addr = server.getAddress();
|
||||
client = JMXConnectorFactory.newJMXConnector(addr, null);
|
||||
client.connect(null);
|
||||
|
||||
mserver = client.getMBeanServerConnection();
|
||||
|
||||
mserver.addNotificationListener(delegateName, dummyListener, null, hbs[0]);
|
||||
mserver.addNotificationListener(delegateName, dummyListener, null, hbs[1]);
|
||||
|
||||
for (int i=0; i<20; i++) {
|
||||
synchronized(receivedLock) {
|
||||
received = 0;
|
||||
}
|
||||
|
||||
mserver.createMBean("javax.management.timer.Timer", timerName);
|
||||
|
||||
synchronized(receivedLock) {
|
||||
if (received != 2) {
|
||||
long remainingTime = waitingTime;
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
while (received != 2 && remainingTime > 0) {
|
||||
receivedLock.wait(remainingTime);
|
||||
remainingTime = waitingTime -
|
||||
(System.currentTimeMillis() - startTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (received != 2) {
|
||||
System.out.println(">>> Expected 2 notifis, but received "+received);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
synchronized(receivedLock) {
|
||||
received = 0;
|
||||
}
|
||||
|
||||
mserver.unregisterMBean(timerName);
|
||||
|
||||
synchronized(receivedLock) {
|
||||
if (received != 2) {
|
||||
|
||||
long remainingTime = waitingTime;
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
while (received != 2 && remainingTime >0) {
|
||||
receivedLock.wait(remainingTime);
|
||||
remainingTime = waitingTime -
|
||||
(System.currentTimeMillis() - startTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (received != 2) {
|
||||
System.out.println(">>> Expected 2 notifis, but received "+received);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mserver.removeNotificationListener(delegateName, dummyListener);
|
||||
|
||||
client.close();
|
||||
|
||||
server.stop();
|
||||
|
||||
} catch (MalformedURLException e) {
|
||||
System.out.println(">>> Skipping unsupported URL " + u);
|
||||
return true;
|
||||
JMXServiceURL addr = server.getAddress();
|
||||
client = JMXConnectorFactory.connect(addr, null);
|
||||
} catch (Exception e) {
|
||||
// not support
|
||||
System.out.println(">>> not support: " + proto);
|
||||
return null;
|
||||
}
|
||||
|
||||
return true;
|
||||
MBeanServerConnection mserver = client.getMBeanServerConnection();
|
||||
|
||||
System.out.print(">>>\t");
|
||||
for (int i=0; i<5; i++) {
|
||||
System.out.print(i + "\t");
|
||||
final MyListener dummyListener = new MyListener();
|
||||
mserver.addNotificationListener(
|
||||
delegateName, dummyListener, null, hbs[0]);
|
||||
mserver.addNotificationListener(
|
||||
delegateName, dummyListener, null, hbs[1]);
|
||||
|
||||
mserver.createMBean("javax.management.timer.Timer", timerName);
|
||||
|
||||
long remainingTime = waitingTime;
|
||||
final long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
synchronized(dummyListener) {
|
||||
while (!dummyListener.done && remainingTime > 0) {
|
||||
dummyListener.wait(remainingTime);
|
||||
remainingTime = waitingTime -
|
||||
(System.currentTimeMillis() - startTime);
|
||||
}
|
||||
|
||||
if (dummyListener.errorInfo != null) {
|
||||
return dummyListener.errorInfo;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
//System.out.println("Unregister: "+i);
|
||||
mserver.unregisterMBean(timerName);
|
||||
mserver.removeNotificationListener(delegateName, dummyListener);
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("");
|
||||
client.close();
|
||||
server.stop();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private final static long waitingTime = 10000;
|
||||
private static class MyListener implements NotificationListener {
|
||||
public boolean done = false;
|
||||
public String errorInfo = null;
|
||||
|
||||
private int received = 0;
|
||||
private MBeanServerNotification receivedNotif = null;
|
||||
private Object receivedHB = null;
|
||||
public void handleNotification(Notification n, Object o) {
|
||||
if (!(n instanceof MBeanServerNotification)) {
|
||||
failed("Received an unexpected notification: "+n);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hbs[0].equals(o) && !hbs[1].equals(o)) {
|
||||
failed("Unkown handback: "+o);
|
||||
return;
|
||||
}
|
||||
|
||||
// what we need
|
||||
final MBeanServerNotification msn = (MBeanServerNotification)n;
|
||||
if (!(MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(
|
||||
msn.getType())) ||
|
||||
!msn.getMBeanName().equals(timerName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized(this) {
|
||||
received++;
|
||||
|
||||
if (received == 1) { // first time
|
||||
receivedNotif = msn;
|
||||
receivedHB = o;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (received > 2) {
|
||||
failed("Expect to receive 2 notifs, but get "+received);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// second time
|
||||
if (receivedHB.equals(o)) {
|
||||
failed("Got same handback twice: "+o);
|
||||
} else if(!hbs[0].equals(o) && !hbs[1].equals(o)) {
|
||||
failed("Unknown handback: "+o);
|
||||
} else if (receivedNotif.getSequenceNumber() !=
|
||||
msn.getSequenceNumber()) {
|
||||
failed("expected to receive:\n"
|
||||
+receivedNotif
|
||||
+"\n but got\n"+msn);
|
||||
}
|
||||
|
||||
// passed
|
||||
done = true;
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
private void failed(String errorInfo) {
|
||||
this.errorInfo = errorInfo;
|
||||
done = true;
|
||||
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
private final static long waitingTime = 2000;
|
||||
}
|
||||
|
@ -29,11 +29,12 @@
|
||||
* @author Shanliang JIANG
|
||||
* @run clean EmptyDomainNotificationTest
|
||||
* @run build EmptyDomainNotificationTest
|
||||
* @run main EmptyDomainNotificationTest
|
||||
* @run main EmptyDomainNotificationTest classic
|
||||
* @run main EmptyDomainNotificationTest event
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MBeanServerFactory;
|
||||
@ -46,6 +47,7 @@ import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
public class EmptyDomainNotificationTest {
|
||||
|
||||
@ -80,11 +82,25 @@ public class EmptyDomainNotificationTest {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
String type = args[0];
|
||||
boolean eventService;
|
||||
if (type.equals("classic"))
|
||||
eventService = false;
|
||||
else if (type.equals("event"))
|
||||
eventService = true;
|
||||
else
|
||||
throw new IllegalArgumentException(type);
|
||||
|
||||
final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
|
||||
|
||||
final JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
|
||||
|
||||
JMXConnectorServer server = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
|
||||
Map<String, String> env = Collections.singletonMap(
|
||||
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
|
||||
Boolean.toString(eventService));
|
||||
|
||||
JMXConnectorServer server =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
|
||||
server.start();
|
||||
|
||||
JMXConnector client = JMXConnectorFactory.connect(server.getAddress(), null);
|
||||
|
@ -51,18 +51,29 @@
|
||||
* been compiled by the second.
|
||||
*/
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import javax.management.*;
|
||||
import javax.management.remote.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.MalformedObjectNameException;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
public class ListenerScaleTest {
|
||||
private static final int WARMUP_WITH_ONE_MBEAN = 1000;
|
||||
private static final int NOTIFS_TO_TIME = 100;
|
||||
private static final int EXTRA_MBEANS = 20000;
|
||||
|
||||
private static final MBeanServer mbs =
|
||||
ManagementFactory.getPlatformMBeanServer();
|
||||
private static final ObjectName testObjectName;
|
||||
static {
|
||||
try {
|
||||
@ -87,7 +98,7 @@ public class ListenerScaleTest {
|
||||
}
|
||||
};
|
||||
|
||||
private static final long timeNotif() {
|
||||
private static final long timeNotif(MBeanServer mbs) {
|
||||
try {
|
||||
startTime = System.nanoTime();
|
||||
nnotifs = 0;
|
||||
@ -117,12 +128,20 @@ public class ListenerScaleTest {
|
||||
};
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
|
||||
test(false);
|
||||
test(true);
|
||||
}
|
||||
|
||||
private static void test(boolean eventService) throws Exception {
|
||||
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
||||
Sender sender = new Sender();
|
||||
mbs.registerMBean(sender, testObjectName);
|
||||
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
|
||||
Map<String, String> env = Collections.singletonMap(
|
||||
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
|
||||
Boolean.toString(eventService));
|
||||
JMXConnectorServer cs =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
|
||||
cs.start();
|
||||
JMXServiceURL addr = cs.getAddress();
|
||||
JMXConnector cc = JMXConnectorFactory.connect(addr);
|
||||
@ -140,7 +159,7 @@ public class ListenerScaleTest {
|
||||
mbsc.addNotificationListener(testObjectName, timingListener, null, null);
|
||||
long singleMBeanTime = 0;
|
||||
for (int i = 0; i < WARMUP_WITH_ONE_MBEAN; i++)
|
||||
singleMBeanTime = timeNotif();
|
||||
singleMBeanTime = timeNotif(mbs);
|
||||
if (singleMBeanTime == 0)
|
||||
singleMBeanTime = 1;
|
||||
System.out.println("Time with a single MBean: " + singleMBeanTime + "ns");
|
||||
@ -165,7 +184,7 @@ public class ListenerScaleTest {
|
||||
}
|
||||
System.out.println();
|
||||
System.out.println("Timing a notification send now");
|
||||
long manyMBeansTime = timeNotif();
|
||||
long manyMBeansTime = timeNotif(mbs);
|
||||
System.out.println("Time with many MBeans: " + manyMBeansTime + "ns");
|
||||
double ratio = (double) manyMBeansTime / singleMBeanTime;
|
||||
if (ratio > 100.0)
|
||||
|
@ -31,11 +31,11 @@
|
||||
* @run main NotifBufferSizePropertyNameTest
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import javax.management.*;
|
||||
import javax.management.remote.*;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
/**
|
||||
* This class tests also the size of a server notification buffer.
|
||||
@ -88,6 +88,9 @@ public class NotifBufferSizePropertyNameTest {
|
||||
private static void test(Map env) throws Exception {
|
||||
final MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
||||
|
||||
env = new HashMap((env == null) ? Collections.emptyMap() : env);
|
||||
env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
|
||||
|
||||
mbs.registerMBean(new NotificationEmitter(), oname);
|
||||
JMXConnectorServer server = JMXConnectorServerFactory.newJMXConnectorServer(
|
||||
url,
|
||||
|
@ -22,7 +22,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test NotifReconnectDeadlockTest
|
||||
* @test
|
||||
* @bug 6199899
|
||||
* @summary Tests reconnection done by a fetching notif thread.
|
||||
* @author Shanliang JIANG
|
||||
@ -31,11 +31,21 @@
|
||||
* @run main NotifReconnectDeadlockTest
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import javax.management.*;
|
||||
import javax.management.remote.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.Notification;
|
||||
import javax.management.NotificationBroadcasterSupport;
|
||||
import javax.management.NotificationListener;
|
||||
import javax.management.ObjectName;
|
||||
import javax.management.remote.JMXConnectionNotification;
|
||||
import javax.management.remote.JMXConnector;
|
||||
import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
/**
|
||||
* "This test checks for a bug whereby reconnection did not work if (a) it was
|
||||
@ -64,6 +74,7 @@ public class NotifReconnectDeadlockTest {
|
||||
Map env = new HashMap(2);
|
||||
env.put("jmx.remote.x.server.connection.timeout", new Long(serverTimeout));
|
||||
env.put("jmx.remote.x.client.connection.check.period", new Long(Long.MAX_VALUE));
|
||||
env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
|
||||
|
||||
final MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
||||
|
||||
|
@ -156,7 +156,8 @@ public class NotificationAccessControllerTest {
|
||||
List<Notification> received,
|
||||
List<ObjectName> expected) {
|
||||
if (received.size() != size) {
|
||||
echo("Error: expecting " + size + " notifications");
|
||||
echo("Error: expecting " + size + " notifications, got " +
|
||||
received.size());
|
||||
return 1;
|
||||
} else {
|
||||
for (Notification n : received) {
|
||||
|
@ -32,6 +32,8 @@
|
||||
*/
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import javax.management.MBeanServerFactory;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.MBeanServerConnection;
|
||||
@ -44,6 +46,7 @@ import javax.management.remote.JMXConnectorFactory;
|
||||
import javax.management.remote.JMXConnectorServer;
|
||||
import javax.management.remote.JMXConnectorServerFactory;
|
||||
import javax.management.remote.JMXServiceURL;
|
||||
import javax.management.remote.rmi.RMIConnectorServer;
|
||||
|
||||
public class NotificationBufferCreationTest {
|
||||
private static final MBeanServer mbs =
|
||||
@ -86,6 +89,8 @@ public class NotificationBufferCreationTest {
|
||||
JMXServiceURL u = null;
|
||||
try {
|
||||
u = new JMXServiceURL(protocol, null, 0);
|
||||
Map<String, String> env = Collections.singletonMap(
|
||||
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
|
||||
server =
|
||||
JMXConnectorServerFactory.newJMXConnectorServer(u,
|
||||
null,
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user