This commit is contained in:
Tim Bell 2008-08-07 18:02:28 -07:00
commit 6f9ae46ab8
120 changed files with 17355 additions and 741 deletions

View File

@ -155,6 +155,7 @@ CORE_PKGS = \
javax.lang.model.type \
javax.lang.model.util \
javax.management \
javax.management.event \
javax.management.loading \
javax.management.monitor \
javax.management.relation \

View File

@ -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");
}

View 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");
}

View File

@ -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();
}

View 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);
}
}
}

View 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");
}

View 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");
}

View 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");
}

View 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");
}

View File

@ -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");
}

View File

@ -453,12 +453,13 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
final ResourceContext context =
unregisterFromRepository(resource, instance, name);
try {
if (instance instanceof MBeanRegistration)
postDeregisterInvoke((MBeanRegistration) instance);
postDeregisterInvoke(name,(MBeanRegistration) instance);
} finally {
context.done();
}
}
public ObjectInstance getObjectInstance(ObjectName name)
throws InstanceNotFoundException {
@ -989,10 +990,12 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
registerFailed = false;
registered = true;
} finally {
postRegister(mbean, registered, registerFailed);
try {
postRegister(logicalName, mbean, registered, registerFailed);
} finally {
if (registered) context.done();
}
}
context.done();
return new ObjectInstance(logicalName, classname);
}
@ -1051,7 +1054,8 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
private static void postRegister(
DynamicMBean mbean, boolean registrationDone, boolean registerFailed) {
ObjectName logicalName, DynamicMBean mbean,
boolean registrationDone, boolean registerFailed) {
if (registerFailed && mbean instanceof DynamicMBean2)
((DynamicMBean2) mbean).registerFailed();
@ -1059,11 +1063,19 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
if (mbean instanceof MBeanRegistration)
((MBeanRegistration) mbean).postRegister(registrationDone);
} catch (RuntimeException e) {
MBEANSERVER_LOGGER.fine("While registering MBean ["+logicalName+
"]: " + "Exception thrown by postRegister: " +
"rethrowing <"+e+">, but keeping the MBean registered");
throw new RuntimeMBeanException(e,
"RuntimeException thrown in postRegister method");
"RuntimeException thrown in postRegister method: "+
"rethrowing <"+e+">, but keeping the MBean registered");
} catch (Error er) {
MBEANSERVER_LOGGER.fine("While registering MBean ["+logicalName+
"]: " + "Error thrown by postRegister: " +
"rethrowing <"+er+">, but keeping the MBean registered");
throw new RuntimeErrorException(er,
"Error thrown in postRegister method");
"Error thrown in postRegister method: "+
"rethrowing <"+er+">, but keeping the MBean registered");
}
}
@ -1076,15 +1088,28 @@ public class DefaultMBeanServerInterceptor implements MBeanServerInterceptor {
}
}
private static void postDeregisterInvoke(MBeanRegistration moi) {
private static void postDeregisterInvoke(ObjectName mbean,
MBeanRegistration moi) {
try {
moi.postDeregister();
} catch (RuntimeException e) {
MBEANSERVER_LOGGER.fine("While unregistering MBean ["+mbean+
"]: " + "Exception thrown by postDeregister: " +
"rethrowing <"+e+">, although the MBean is succesfully " +
"unregistered");
throw new RuntimeMBeanException(e,
"RuntimeException thrown in postDeregister method");
"RuntimeException thrown in postDeregister method: "+
"rethrowing <"+e+
">, although the MBean is sucessfully unregistered");
} catch (Error er) {
MBEANSERVER_LOGGER.fine("While unregistering MBean ["+mbean+
"]: " + "Error thrown by postDeregister: " +
"rethrowing <"+er+">, although the MBean is succesfully " +
"unregistered");
throw new RuntimeErrorException(er,
"Error thrown in postDeregister method");
"Error thrown in postDeregister method: "+
"rethrowing <"+er+
">, although the MBean is sucessfully unregistered");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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);
}
}

View File

@ -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>

View File

@ -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

View File

@ -25,7 +25,6 @@
package com.sun.jmx.mbeanserver;
import static com.sun.jmx.mbeanserver.Util.*;
import javax.management.Attribute;
import javax.management.AttributeList;

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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) {
@ -333,6 +333,12 @@ public class ServerNotifForwarder {
private void checkMBeanPermission(final ObjectName name,
final String actions)
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) {

View File

@ -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);
final BufferedInputStream bis = new BufferedInputStream(fis);
try {
userCredentials = new Properties();
userCredentials.load(bis);
} finally {
bis.close();
}
} finally {
fis.close();
}

View File

@ -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 {
@ -349,6 +349,23 @@ public class EnvHelp {
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);
}
/**
* Get an integer-valued attribute with name <code>name</code>
* from <code>env</code>. If <code>env</code> is null, or does
@ -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) {}

View File

@ -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;
}

View File

@ -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);
}
}
}

View File

@ -25,6 +25,7 @@
package java.util;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A facility for threads to schedule tasks for future execution in a
@ -92,12 +93,12 @@ public class Timer {
* and the timer thread consumes, executing timer tasks as appropriate,
* and removing them from the queue when they're obsolete.
*/
private TaskQueue queue = new TaskQueue();
private final TaskQueue queue = new TaskQueue();
/**
* The timer thread.
*/
private TimerThread thread = new TimerThread(queue);
private final TimerThread thread = new TimerThread(queue);
/**
* This object causes the timer's task execution thread to exit
@ -106,7 +107,7 @@ public class Timer {
* Timer as such a finalizer would be susceptible to a subclass's
* finalizer forgetting to call it.
*/
private Object threadReaper = new Object() {
private final Object threadReaper = new Object() {
protected void finalize() throws Throwable {
synchronized(queue) {
thread.newTasksMayBeScheduled = false;
@ -116,12 +117,11 @@ public class Timer {
};
/**
* This ID is used to generate thread names. (It could be replaced
* by an AtomicInteger as soon as they become available.)
* This ID is used to generate thread names.
*/
private static int nextSerialNumber = 0;
private static synchronized int serialNumber() {
return nextSerialNumber++;
private final static AtomicInteger nextSerialNumber = new AtomicInteger(0);
private static int serialNumber() {
return nextSerialNumber.getAndIncrement();
}
/**
@ -387,6 +387,11 @@ public class Timer {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
// Constrain value of period sufficiently to prevent numeric
// overflow while still being effectively infinitely large.
if (Math.abs(period) > (Long.MAX_VALUE >> 1))
period >>= 1;
synchronized(queue) {
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");

View File

@ -223,8 +223,7 @@ public class ScheduledThreadPoolExecutor
}
public long getDelay(TimeUnit unit) {
long d = unit.convert(time - now(), TimeUnit.NANOSECONDS);
return d;
return unit.convert(time - now(), TimeUnit.NANOSECONDS);
}
public int compareTo(Delayed other) {
@ -264,7 +263,7 @@ public class ScheduledThreadPoolExecutor
if (p > 0)
time += p;
else
time = now() - p;
time = triggerTime(-p);
}
public boolean cancel(boolean mayInterruptIfRunning) {
@ -472,6 +471,38 @@ public class ScheduledThreadPoolExecutor
new DelayedWorkQueue(), threadFactory, handler);
}
/**
* Returns the trigger time of a delayed action.
*/
private long triggerTime(long delay, TimeUnit unit) {
return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));
}
/**
* Returns the trigger time of a delayed action.
*/
long triggerTime(long delay) {
return now() +
((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
/**
* Constrains the values of all delays in the queue to be within
* Long.MAX_VALUE of each other, to avoid overflow in compareTo.
* This may occur if a task is eligible to be dequeued, but has
* not yet been, while some other task is added with a delay of
* Long.MAX_VALUE.
*/
private long overflowFree(long delay) {
Delayed head = (Delayed) super.getQueue().peek();
if (head != null) {
long headDelay = head.getDelay(TimeUnit.NANOSECONDS);
if (headDelay < 0 && (delay - headDelay < 0))
delay = Long.MAX_VALUE + headDelay;
}
return delay;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
@ -481,10 +512,9 @@ public class ScheduledThreadPoolExecutor
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (delay < 0) delay = 0;
long triggerTime = now() + unit.toNanos(delay);
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null, triggerTime));
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
@ -498,10 +528,9 @@ public class ScheduledThreadPoolExecutor
TimeUnit unit) {
if (callable == null || unit == null)
throw new NullPointerException();
if (delay < 0) delay = 0;
long triggerTime = now() + unit.toNanos(delay);
RunnableScheduledFuture<V> t = decorateTask(callable,
new ScheduledFutureTask<V>(callable, triggerTime));
new ScheduledFutureTask<V>(callable,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
@ -519,12 +548,10 @@ public class ScheduledThreadPoolExecutor
throw new NullPointerException();
if (period <= 0)
throw new IllegalArgumentException();
if (initialDelay < 0) initialDelay = 0;
long triggerTime = now() + unit.toNanos(initialDelay);
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
@ -545,12 +572,10 @@ public class ScheduledThreadPoolExecutor
throw new NullPointerException();
if (delay <= 0)
throw new IllegalArgumentException();
if (initialDelay < 0) initialDelay = 0;
long triggerTime = now() + unit.toNanos(initialDelay);
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime,
triggerTime(initialDelay, unit),
unit.toNanos(-delay));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;

View File

@ -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++) {

View File

@ -158,7 +158,19 @@ public interface MBeanRegistration {
/**
* Allows the MBean to perform any operations needed after having been
* registered in the MBean server or after the registration has failed.
*
* <p>If the implementation of this method throws a {@link RuntimeException}
* or an {@link Error}, the MBean Server will rethrow those inside
* a {@link RuntimeMBeanException} or {@link RuntimeErrorException},
* respectively. However, throwing an exception in {@code postRegister}
* will not change the state of the MBean:
* if the MBean was already registered ({@code registrationDone} is
* {@code true}), the MBean will remain registered. </p>
* <p>This might be confusing for the code calling {@code createMBean()}
* or {@code registerMBean()}, as such code might assume that MBean
* registration has failed when such an exception is raised.
* Therefore it is recommended that implementations of
* {@code postRegister} do not throw Runtime Exceptions or Errors if it
* can be avoided.</p>
* @param registrationDone Indicates whether or not the MBean has
* been successfully registered in the MBean server. The value
* false means that the registration phase has failed.
@ -178,6 +190,17 @@ public interface MBeanRegistration {
/**
* Allows the MBean to perform any operations needed after having been
* unregistered in the MBean server.
* <p>If the implementation of this method throws a {@link RuntimeException}
* or an {@link Error}, the MBean Server will rethrow those inside
* a {@link RuntimeMBeanException} or {@link RuntimeErrorException},
* respectively. However, throwing an excepption in {@code postDeregister}
* will not change the state of the MBean:
* the MBean was already successfully deregistered and will remain so. </p>
* <p>This might be confusing for the code calling
* {@code unregisterMBean()}, as it might assume that MBean deregistration
* has failed. Therefore it is recommended that implementations of
* {@code postDeregister} do not throw Runtime Exceptions or Errors if it
* can be avoided.</p>
*/
public void postDeregister();

View File

@ -50,8 +50,8 @@ import javax.management.loading.ClassLoaderRepository;
* server. A Java object cannot be registered in the MBean server
* unless it is a JMX compliant MBean.</p>
*
* <p id="notif">When an MBean is registered or unregistered in the MBean server
* a {@link javax.management.MBeanServerNotification
* <p id="notif">When an MBean is registered or unregistered in the
* MBean server a {@link javax.management.MBeanServerNotification
* MBeanServerNotification} Notification is emitted. To register an
* object as listener to MBeanServerNotifications you should call the
* MBean server method {@link #addNotificationListener
@ -262,6 +262,8 @@ public interface MBeanServer extends MBeanServerConnection {
* {@inheritDoc}
* <p>If this method successfully creates an MBean, a notification
* is sent as described <a href="#notif">above</a>.</p>
*
* @throws RuntimeOperationsException {@inheritDoc}
*/
public ObjectInstance createMBean(String className, ObjectName name)
throws ReflectionException, InstanceAlreadyExistsException,
@ -272,6 +274,8 @@ public interface MBeanServer extends MBeanServerConnection {
* {@inheritDoc}
* <p>If this method successfully creates an MBean, a notification
* is sent as described <a href="#notif">above</a>.</p>
*
* @throws RuntimeOperationsException {@inheritDoc}
*/
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName)
@ -283,6 +287,8 @@ public interface MBeanServer extends MBeanServerConnection {
* {@inheritDoc}
* <p>If this method successfully creates an MBean, a notification
* is sent as described <a href="#notif">above</a>.</p>
*
* @throws RuntimeOperationsException {@inheritDoc}
*/
public ObjectInstance createMBean(String className, ObjectName name,
Object params[], String signature[])
@ -294,6 +300,8 @@ public interface MBeanServer extends MBeanServerConnection {
* {@inheritDoc}
* <p>If this method successfully creates an MBean, a notification
* is sent as described <a href="#notif">above</a>.</p>
*
* @throws RuntimeOperationsException {@inheritDoc}
*/
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName, Object params[],
@ -328,11 +336,30 @@ public interface MBeanServer extends MBeanServerConnection {
* <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
* @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
* <CODE>RuntimeException</CODE>, the <CODE>registerMBean<CODE> method will
* throw a <CODE>RuntimeMBeanException</CODE>, although the MBean
* registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>registerMBean<CODE> method
* threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception RuntimeErrorException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
* <CODE>Error</CODE>, the <CODE>registerMBean<CODE> method will
* throw a <CODE>RuntimeErrorException</CODE>, although the MBean
* registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>registerMBean<CODE> method
* threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception NotCompliantMBeanException This object is not a JMX
* compliant MBean
* @exception RuntimeOperationsException Wraps a
* <CODE>java.lang.IllegalArgumentException</CODE>: The object
* passed in parameter is null or no object name is specified.
* @see javax.management.MBeanRegistration
*/
public ObjectInstance registerMBean(Object object, ObjectName name)
throws InstanceAlreadyExistsException, MBeanRegistrationException,
@ -343,6 +370,8 @@ public interface MBeanServer extends MBeanServerConnection {
*
* <p>If this method successfully unregisters an MBean, a notification
* is sent as described <a href="#notif">above</a>.</p>
*
* @throws RuntimeOperationsException {@inheritDoc}
*/
public void unregisterMBean(ObjectName name)
throws InstanceNotFoundException, MBeanRegistrationException;
@ -358,6 +387,9 @@ public interface MBeanServer extends MBeanServerConnection {
public Set<ObjectName> queryNames(ObjectName name, QueryExp query);
// doc comment inherited from MBeanServerConnection
/**
* @throws RuntimeOperationsException {@inheritDoc}
*/
public boolean isRegistered(ObjectName name);
/**
@ -370,21 +402,33 @@ public interface MBeanServer extends MBeanServerConnection {
public Integer getMBeanCount();
// doc comment inherited from MBeanServerConnection
/**
* @throws RuntimeOperationsException {@inheritDoc}
*/
public Object getAttribute(ObjectName name, String attribute)
throws MBeanException, AttributeNotFoundException,
InstanceNotFoundException, ReflectionException;
// doc comment inherited from MBeanServerConnection
/**
* @throws RuntimeOperationsException {@inheritDoc}
*/
public AttributeList getAttributes(ObjectName name, String[] attributes)
throws InstanceNotFoundException, ReflectionException;
// doc comment inherited from MBeanServerConnection
/**
* @throws RuntimeOperationsException {@inheritDoc}
*/
public void setAttribute(ObjectName name, Attribute attribute)
throws InstanceNotFoundException, AttributeNotFoundException,
InvalidAttributeValueException, MBeanException,
ReflectionException;
// doc comment inherited from MBeanServerConnection
/**
* @throws RuntimeOperationsException {@inheritDoc}
*/
public AttributeList setAttributes(ObjectName name,
AttributeList attributes)
throws InstanceNotFoundException, ReflectionException;
@ -401,14 +445,23 @@ 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,
Object handback)
throws InstanceNotFoundException;
// doc comment inherited from MBeanServerConnection
/**
* {@inheritDoc}
* @throws RuntimeOperationsException {@inheritDoc}
*/
public void addNotificationListener(ObjectName name,
ObjectName listener,
NotificationFilter filter,

View File

@ -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
@ -75,6 +76,24 @@ public interface MBeanServerConnection {
* <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
* @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
* <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
* throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception RuntimeErrorException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
* <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
* throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception MBeanException The constructor of the MBean has
* thrown an exception
* @exception NotCompliantMBeanException This class is not a JMX
@ -86,7 +105,7 @@ public interface MBeanServerConnection {
* is specified for the MBean.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
*
* @see javax.management.MBeanRegistration
*/
public ObjectInstance createMBean(String className, ObjectName name)
throws ReflectionException, InstanceAlreadyExistsException,
@ -129,6 +148,24 @@ public interface MBeanServerConnection {
* <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
* @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
* <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
* throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception RuntimeErrorException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
* <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
* throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception MBeanException The constructor of the MBean has
* thrown an exception
* @exception NotCompliantMBeanException This class is not a JMX
@ -142,6 +179,7 @@ public interface MBeanServerConnection {
* is specified for the MBean.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
* @see javax.management.MBeanRegistration
*/
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName)
@ -185,6 +223,24 @@ public interface MBeanServerConnection {
* <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
* @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
* <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
* throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception RuntimeErrorException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
* <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
* throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception MBeanException The constructor of the MBean has
* thrown an exception
* @exception NotCompliantMBeanException This class is not a JMX
@ -196,7 +252,7 @@ public interface MBeanServerConnection {
* is specified for the MBean.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
*
* @see javax.management.MBeanRegistration
*/
public ObjectInstance createMBean(String className, ObjectName name,
Object params[], String signature[])
@ -239,6 +295,24 @@ public interface MBeanServerConnection {
* <CODE>preRegister</CODE> (<CODE>MBeanRegistration</CODE>
* interface) method of the MBean has thrown an exception. The
* MBean will not be registered.
* @exception RuntimeMBeanException If the <CODE>postRegister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
* <CODE>RuntimeException</CODE>, the <CODE>createMBean<CODE> method will
* throw a <CODE>RuntimeMBeanException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception RuntimeErrorException If the <CODE>postRegister</CODE> method
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
* <CODE>Error</CODE>, the <CODE>createMBean<CODE> method will
* throw a <CODE>RuntimeErrorException</CODE>, although the MBean creation
* and registration succeeded. In such a case, the MBean will be actually
* registered even though the <CODE>createMBean<CODE> method
* threw an exception. Note that <CODE>RuntimeErrorException</CODE> can
* also be thrown by <CODE>preRegister</CODE>, in which case the MBean
* will not be registered.
* @exception MBeanException The constructor of the MBean has
* thrown an exception
* @exception NotCompliantMBeanException This class is not a JMX
@ -252,7 +326,7 @@ public interface MBeanServerConnection {
* is specified for the MBean.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
*
* @see javax.management.MBeanRegistration
*/
public ObjectInstance createMBean(String className, ObjectName name,
ObjectName loaderName, Object params[],
@ -275,6 +349,24 @@ public interface MBeanServerConnection {
* @exception MBeanRegistrationException The preDeregister
* ((<CODE>MBeanRegistration</CODE> interface) method of the MBean
* has thrown an exception.
* @exception RuntimeMBeanException If the <CODE>postDeregister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws a
* <CODE>RuntimeException</CODE>, the <CODE>unregisterMBean<CODE> method
* will throw a <CODE>RuntimeMBeanException</CODE>, although the MBean
* unregistration succeeded. In such a case, the MBean will be actually
* unregistered even though the <CODE>unregisterMBean<CODE> method
* threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
* also be thrown by <CODE>preDeregister</CODE>, in which case the MBean
* will remain registered.
* @exception RuntimeErrorException If the <CODE>postDeregister</CODE>
* (<CODE>MBeanRegistration</CODE> interface) method of the MBean throws an
* <CODE>Error</CODE>, the <CODE>unregisterMBean<CODE> method will
* throw a <CODE>RuntimeErrorException</CODE>, although the MBean
* unregistration succeeded. In such a case, the MBean will be actually
* unregistered even though the <CODE>unregisterMBean<CODE> method
* threw an exception. Note that <CODE>RuntimeMBeanException</CODE> can
* also be thrown by <CODE>preDeregister</CODE>, in which case the MBean
* will remain registered.
* @exception RuntimeOperationsException Wraps a
* <CODE>java.lang.IllegalArgumentException</CODE>: The object
* name in parameter is null or the MBean you are when trying to
@ -282,7 +374,7 @@ public interface MBeanServerConnection {
* MBeanServerDelegate} MBean.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
*
* @see javax.management.MBeanRegistration
*/
public void unregisterMBean(ObjectName name)
throws InstanceNotFoundException, MBeanRegistrationException,
@ -585,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,
@ -727,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,

View File

@ -43,27 +43,27 @@ package javax.management;
/* Serial version */
private static final long serialVersionUID = 2876477500475969677L;
/**
* Notification type denoting that an MBean has been registered. Value is "JMX.mbean.registered".
* Notification type denoting that an MBean has been registered.
* Value is "JMX.mbean.registered".
*/
public static final String REGISTRATION_NOTIFICATION = "JMX.mbean.registered" ;
public static final String REGISTRATION_NOTIFICATION =
"JMX.mbean.registered";
/**
* Notification type denoting that an MBean has been unregistered. Value is "JMX.mbean.unregistered".
* Notification type denoting that an MBean has been unregistered.
* Value is "JMX.mbean.unregistered".
*/
public static final String UNREGISTRATION_NOTIFICATION = "JMX.mbean.unregistered" ;
public static final String UNREGISTRATION_NOTIFICATION =
"JMX.mbean.unregistered";
/**
* @serial The object names of the MBeans concerned by this notification
*/
private final ObjectName objectName;
/**
* Creates an MBeanServerNotification object specifying object names of
* the MBeans that caused the notification and the specified notification type.
* the MBeans that caused the notification and the specified notification
* type.
*
* @param type A string denoting the type of the
* notification. Set it to one these values: {@link
@ -73,10 +73,12 @@ package javax.management;
* for forwarding MBean server notification.
* @param sequenceNumber A sequence number that can be used to order
* received notifications.
* @param objectName The object name of the MBean that caused the notification.
* @param objectName The object name of the MBean that caused the
* notification.
*
*/
public MBeanServerNotification(String type, Object source, long sequenceNumber, ObjectName objectName ) {
public MBeanServerNotification(String type, Object source,
long sequenceNumber, ObjectName objectName) {
super(type, source, sequenceNumber);
this.objectName = objectName;
}
@ -90,4 +92,10 @@ package javax.management;
return objectName;
}
@Override
public String toString() {
return super.toString() + "[mbeanName=" + objectName + "]";
}
}

View File

@ -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
@ -1081,9 +1081,10 @@ public interface Node {
MXBean is determined as follows.</p>
<ul>
<li><p>If an {@link JMX.MBeanOptions} argument is supplied to
<li><p>If a {@link JMX.MBeanOptions} argument is supplied to
the {@link StandardMBean} constructor that makes an MXBean,
or to the {@link JMX#newMXBeanProxy JMX.newMXBeanProxy}
or to the {@link JMX#newMBeanProxy(MBeanServerConnection,
ObjectName, Class, JMX.MBeanOptions) JMX.newMBeanProxy}
method, and the {@code MBeanOptions} object defines a non-null
{@code MXBeanMappingFactory}, then that is the value of
<code><em>f</em></code>.</p></li>

View File

@ -38,9 +38,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.QueryExp;
/**
* <p>Represents the object name of an MBean, or a pattern that can
@ -1160,9 +1157,19 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
//
//in.defaultReadObject();
final ObjectInputStream.GetField fields = in.readFields();
cn = (String)fields.get("domain", "default")+
":"+
String propListString =
(String)fields.get("propertyListString", "");
// 6616825: take care of property patterns
final boolean propPattern =
fields.get("propertyPattern" , false);
if (propPattern) {
propListString =
(propListString.length()==0?"*":(propListString+",*"));
}
cn = (String)fields.get("domain", "default")+
":"+ propListString;
} else {
// Read an object serialized in the new serial form
//
@ -1796,6 +1803,7 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
* @return True if <code>object</code> is an ObjectName whose
* canonical form is equal to that of this ObjectName.
*/
@Override
public boolean equals(Object object) {
// same object case
@ -1819,6 +1827,7 @@ public class ObjectName implements Comparable<ObjectName>, QueryExp {
* Returns a hash code for this object name.
*
*/
@Override
public int hashCode() {
return _canonicalName.hashCode();
}

View File

@ -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;

View File

@ -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) { }
/**

File diff suppressed because it is too large Load Diff

View File

@ -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>>();
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}

View 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;
}

View File

@ -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>>();
}

View File

@ -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");
}

View File

@ -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");
}

View 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;
}

View File

@ -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;
}

View File

@ -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");
}

View File

@ -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>&lt;default buffer
* size&gt;</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");
}

View File

@ -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;
}

View 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;

View File

@ -1154,22 +1154,30 @@ 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) {
try {
File directory = new File(libraryDirectory);
directory.mkdirs();
File file = File.createTempFile(libname + ".", null, directory);
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);
}
is.close();
} finally {
fileOutput.close();
}
if (file.exists()) {
return file.getAbsolutePath();
}
} finally {
is.close();
}
}
} catch (Exception e) {
MLET_LOGGER.logp(Level.FINEST, MLet.class.getName(),

View File

@ -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");

View File

@ -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;

View File

@ -108,6 +108,9 @@ import java.lang.reflect.Type;
* <p>If we are unable to modify the {@code MyLinkedList} class,
* we can define an {@link MXBeanMappingFactory}. See the documentation
* of that class for further details.</p>
*
* @see <a href="../MXBean.html#custom">MXBean specification, section
* "Custom MXBean type mappings"</a>
*/
public abstract class MXBeanMapping {
private final Type javaType;

View File

@ -82,6 +82,9 @@ import java.lang.reflect.Type;
* appears in, or we can supply the factory to a {@link
* javax.management.StandardMBean StandardMBean} constructor or MXBean
* proxy.</p>
*
* @see <a href="../MXBean.html#custom">MXBean specification, section
* "Custom MXBean type mappings"</a>
*/
public abstract class MXBeanMappingFactory {
/**

View File

@ -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

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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.

View File

@ -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";

View File

@ -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);

View File

@ -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(),
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.");
@ -956,7 +954,6 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
ClassLoader targetCl;
NotificationFilter[] filterValues =
new NotificationFilter[names.length];
Object params[];
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);
}
};
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,8 +1745,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
return null;
case ADD_NOTIFICATION_LISTENERS:
return getServerNotifFwd().addNotificationListener(
(ObjectName)params[0],
return doAddListener((ObjectName)params[0],
(NotificationFilter)params[1]);
case ADD_NOTIFICATION_LISTENER_OBJECTNAME:
@ -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
//---------------

View File

@ -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;
@ -281,7 +293,7 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
//
boolean checkStub = EnvHelp.computeBooleanFromString(
usemap,
"jmx.remote.x.check.stub");
"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,"+
@ -1228,6 +1286,7 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
throws InstanceNotFoundException,
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();
}

View File

@ -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}

View File

@ -25,7 +25,7 @@
#
# @test Solaris32AndSolaris64Test.sh
# @bug 4478312 4780570 4913748
# @bug 4478312 4780570 4913748 6730273
# @summary Test debugging with mixed 32/64bit VMs.
# @author Tim Bell
# Based on test/java/awt/TEMPLATE/AutomaticShellTest.sh
@ -177,8 +177,14 @@ filename=$TESTCLASSES/@debuggeeVMOptions
if [ ! -r ${filename} ] ; then
filename=$TESTCLASSES/../@debuggeeVMOptions
fi
# Remove -d32, -d64 if present, and remove -XX:[+-]UseCompressedOops
# if present since it is illegal in 32 bit mode.
if [ -r ${filename} ] ; then
DEBUGGEEFLAGS=`cat ${filename} | sed -e 's/-d32//g' -e 's/-d64//g'`
DEBUGGEEFLAGS=`cat ${filename} | sed \
-e 's/-d32//g' \
-e 's/-d64//g' \
-e 's/-XX:.UseCompressedOops//g' \
`
fi
#

View File

@ -60,8 +60,8 @@ public class ThreadMXBeanProxy {
thread.setDaemon(true);
thread.start();
// wait until myThread acquires mutex
while (!mutex.isLocked()) {
// wait until myThread acquires mutex and lock owner is set.
while (!(mutex.isLocked() && mutex.getLockOwner() == thread)) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
@ -73,17 +73,17 @@ public class ThreadMXBeanProxy {
// validate the local access
ThreadInfo[] infos = getThreadMXBean().getThreadInfo(ids, true, true);
if (ids.length != 1) {
if (infos.length != 1) {
throw new RuntimeException("Returned ThreadInfo[] of length=" +
ids.length + ". Expected to be 1.");
infos.length + ". Expected to be 1.");
}
thread.checkThreadInfo(infos[0]);
// validate the remote access
infos = mbean.getThreadInfo(ids, true, true);
if (ids.length != 1) {
if (infos.length != 1) {
throw new RuntimeException("Returned ThreadInfo[] of length=" +
ids.length + ". Expected to be 1.");
infos.length + ". Expected to be 1.");
}
thread.checkThreadInfo(infos[0]);
@ -160,8 +160,7 @@ public class ThreadMXBeanProxy {
LockInfo[] syncs = info.getLockedSynchronizers();
if (syncs.length != OWNED_SYNCS) {
throw new RuntimeException("Number of locked syncs = " +
syncs.length +
" not matched. Expected: " + OWNED_SYNCS);
syncs.length + " not matched. Expected: " + OWNED_SYNCS);
}
AbstractOwnableSynchronizer s = mutex.getSync();
String lockName = s.getClass().getName();
@ -174,7 +173,6 @@ public class ThreadMXBeanProxy {
throw new RuntimeException("LockInfo: " + syncs[0] +
" IdentityHashCode not matched. Expected: " + hcode);
}
}
}
static class Mutex implements Lock, java.io.Serializable {
@ -214,6 +212,10 @@ public class ThreadMXBeanProxy {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
protected Thread getLockOwner() {
return getExclusiveOwnerThread();
}
}
// The sync object does all the hard work. We just forward to it.
@ -232,6 +234,8 @@ public class ThreadMXBeanProxy {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public Thread getLockOwner() { return sync.getLockOwner(); }
public AbstractOwnableSynchronizer getSync() { return sync; }
}
}

View File

@ -0,0 +1,115 @@
/*
* 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 6730507
* @summary java.util.Timer schedule delay Long.MAX_VALUE causes task to execute multiple times
* @author Chris Hegarty
* @author Martin Buchholz
*/
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public class DelayOverflow
{
void scheduleNow(Timer timer, TimerTask task, int how) {
switch (how) {
case 0 :
timer.schedule(task, new Date(), Long.MAX_VALUE);
break;
case 1:
timer.schedule(task, 0L, Long.MAX_VALUE);
break;
case 2:
timer.scheduleAtFixedRate(task, new Date(), Long.MAX_VALUE);
break;
case 3:
timer.scheduleAtFixedRate(task, 0L, Long.MAX_VALUE);
break;
default:
fail(String.valueOf(how));
}
}
void sleep(long millis) {
try { Thread.sleep(millis); }
catch (Throwable t) { unexpected(t); }
}
/** Checks that scheduledExecutionTime returns a "recent" time. */
void checkScheduledExecutionTime(TimerTask task) {
long t = System.currentTimeMillis()
- task.scheduledExecutionTime();
check(t >= 0 && t < 1000 * 600);
}
void test(String[] args) throws Throwable {
for (int how=0; how<4; how++) {
final CountDownLatch done = new CountDownLatch(1);
final AtomicInteger count = new AtomicInteger(0);
final Timer timer = new Timer();
final TimerTask task = new TimerTask() {
@Override
public void run() {
checkScheduledExecutionTime(this);
count.incrementAndGet();
done.countDown();
}};
scheduleNow(timer, task, how);
done.await();
equal(count.get(), 1);
checkScheduledExecutionTime(task);
if (new java.util.Random().nextBoolean())
sleep(10);
check(task.cancel());
timer.cancel();
checkScheduledExecutionTime(task);
}
}
//--------------------- Infrastructure ---------------------------
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View File

@ -0,0 +1,161 @@
/*
* 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 6725789
* @summary Check for long overflow in task time comparison.
*/
import java.util.concurrent.*;
public class DelayOverflow {
static void waitForNanoTimeTick() {
for (long t0 = System.nanoTime(); t0 == System.nanoTime(); )
;
}
void scheduleNow(ScheduledThreadPoolExecutor pool,
Runnable r, int how) {
switch (how) {
case 0:
pool.schedule(r, 0, TimeUnit.MILLISECONDS);
break;
case 1:
pool.schedule(Executors.callable(r), 0, TimeUnit.DAYS);
break;
case 2:
pool.scheduleWithFixedDelay(r, 0, 1000, TimeUnit.NANOSECONDS);
break;
case 3:
pool.scheduleAtFixedRate(r, 0, 1000, TimeUnit.MILLISECONDS);
break;
default:
fail(String.valueOf(how));
}
}
void scheduleAtTheEndOfTime(ScheduledThreadPoolExecutor pool,
Runnable r, int how) {
switch (how) {
case 0:
pool.schedule(r, Long.MAX_VALUE, TimeUnit.MILLISECONDS);
break;
case 1:
pool.schedule(Executors.callable(r), Long.MAX_VALUE, TimeUnit.DAYS);
break;
case 2:
pool.scheduleWithFixedDelay(r, Long.MAX_VALUE, 1000, TimeUnit.NANOSECONDS);
break;
case 3:
pool.scheduleAtFixedRate(r, Long.MAX_VALUE, 1000, TimeUnit.MILLISECONDS);
break;
default:
fail(String.valueOf(how));
}
}
/**
* Attempts to test exhaustively and deterministically, all 20
* possible ways that one task can be scheduled in the maximal
* distant future, while at the same time an existing tasks's time
* has already expired.
*/
void test(String[] args) throws Throwable {
for (int nowHow = 0; nowHow < 4; nowHow++) {
for (int thenHow = 0; thenHow < 4; thenHow++) {
final ScheduledThreadPoolExecutor pool
= new ScheduledThreadPoolExecutor(1);
final CountDownLatch runLatch = new CountDownLatch(1);
final CountDownLatch busyLatch = new CountDownLatch(1);
final CountDownLatch proceedLatch = new CountDownLatch(1);
final Runnable notifier = new Runnable() {
public void run() { runLatch.countDown(); }};
final Runnable neverRuns = new Runnable() {
public void run() { fail(); }};
final Runnable keepPoolBusy = new Runnable() {
public void run() {
try {
busyLatch.countDown();
proceedLatch.await();
} catch (Throwable t) { unexpected(t); }
}};
pool.schedule(keepPoolBusy, 0, TimeUnit.SECONDS);
busyLatch.await();
scheduleNow(pool, notifier, nowHow);
waitForNanoTimeTick();
scheduleAtTheEndOfTime(pool, neverRuns, thenHow);
proceedLatch.countDown();
check(runLatch.await(10L, TimeUnit.SECONDS));
equal(runLatch.getCount(), 0L);
pool.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
pool.shutdown();
}
final int nowHowCopy = nowHow;
final ScheduledThreadPoolExecutor pool
= new ScheduledThreadPoolExecutor(1);
final CountDownLatch runLatch = new CountDownLatch(1);
final Runnable notifier = new Runnable() {
public void run() { runLatch.countDown(); }};
final Runnable scheduleNowScheduler = new Runnable() {
public void run() {
try {
scheduleNow(pool, notifier, nowHowCopy);
waitForNanoTimeTick();
} catch (Throwable t) { unexpected(t); }
}};
pool.scheduleWithFixedDelay(scheduleNowScheduler,
0, Long.MAX_VALUE,
TimeUnit.NANOSECONDS);
check(runLatch.await(10L, TimeUnit.SECONDS));
equal(runLatch.getCount(), 0L);
pool.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
pool.shutdown();
}
}
//--------------------- Infrastructure ---------------------------
volatile int passed = 0, failed = 0;
void pass() {passed++;}
void fail() {failed++; Thread.dumpStack();}
void fail(String msg) {System.err.println(msg); fail();}
void unexpected(Throwable t) {failed++; t.printStackTrace();}
void check(boolean cond) {if (cond) pass(); else fail();}
void equal(Object x, Object y) {
if (x == null ? y == null : x.equals(y)) pass();
else fail(x + " not equal to " + y);}
public static void main(String[] args) throws Throwable {
Class<?> k = new Object(){}.getClass().getEnclosingClass();
try {k.getMethod("instanceMain",String[].class)
.invoke( k.newInstance(), (Object) args);}
catch (Throwable e) {throw e.getCause();}}
public void instanceMain(String[] args) throws Throwable {
try {test(args);} catch (Throwable t) {unexpected(t);}
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
if (failed > 0) throw new AssertionError("Some tests failed");}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,132 @@
/*
* 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 6689505
* @summary Checks that MBeanServerNotification.toString contains the
* MBean name.
* @author Daniel Fuchs
* @compile MBeanServerNotificationTest.java
* @run main MBeanServerNotificationTest
*/
import com.sun.jmx.mbeanserver.Util;
import javax.management.*;
import java.util.concurrent.*;
public class MBeanServerNotificationTest {
final static String[] names = {
":type=Wombat", "wombat:type=Wombat",null,
};
public static void main(String[] args) throws Exception {
System.out.println("Test that MBeanServerNotification.toString " +
"contains the name of the MBean being registered " +
"or unregistered.");
int failures = 0;
final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
for (String str:names) {
try {
final ObjectName name = (str==null)?null:new ObjectName(str);
failures+=test(mbs, name, name!=null);
} catch(Exception x) {
x.printStackTrace(System.out);
System.out.println("Test failed for: "+str);
failures++;
}
}
if (failures == 0)
System.out.println("Test passed");
else {
System.out.println("TEST FAILED: " + failures + " failure(s)");
System.exit(1);
}
}
private static enum Registration {
REGISTER(MBeanServerNotification.REGISTRATION_NOTIFICATION),
UNREGISTER(MBeanServerNotification.UNREGISTRATION_NOTIFICATION);
final String type;
private Registration(String type) {this.type = type;}
public int test(MBeanServerNotification n, ObjectName name) {
int failures = 0;
System.out.println("Testing: "+n);
if (!n.toString().endsWith("[type="+type+
"][message="+n.getMessage()+
"][mbeanName="+name+"]")) {
System.err.println("Test failed for "+ type+
" ["+name+"]: "+n);
failures++;
}
return failures;
}
public MBeanServerNotification create(ObjectName name) {
return new MBeanServerNotification(type,
MBeanServerDelegate.DELEGATE_NAME, next(), name);
}
private static long next = 0;
private static synchronized long next() {return next++;}
}
private static int test(MBeanServer mbs, ObjectName name,
boolean register)
throws Exception {
System.out.println("--------" + name + "--------");
int failures = 0;
for (Registration reg : Registration.values()) {
failures = reg.test(reg.create(name), name);
}
if (!register) return failures;
final ArrayBlockingQueue<Notification> queue =
new ArrayBlockingQueue<Notification>(10);
final NotificationListener listener = new NotificationListener() {
public void handleNotification(Notification notification,
Object handback) {
try {
queue.put(notification);
} catch(Exception x) {
x.printStackTrace(System.out);
}
}
};
mbs.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME,
listener, null, name);
final ObjectInstance oi = mbs.registerMBean(new Wombat(), name);
try {
failures+=Registration.REGISTER.test((MBeanServerNotification)
queue.poll(2, TimeUnit.SECONDS), oi.getObjectName());
} finally {
mbs.unregisterMBean(oi.getObjectName());
failures+=Registration.UNREGISTER.test((MBeanServerNotification)
queue.poll(2, TimeUnit.SECONDS), oi.getObjectName());
}
return failures;
}
public static interface WombatMBean {}
public static class Wombat implements WombatMBean {}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,516 @@
/*
* 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 6730926
* @summary Check behaviour of MBeanServer when postRegister and postDeregister
* throw exceptions.
* @author Daniel Fuchs
* @compile PostExceptionTest.java
* @run main PostExceptionTest
*/
import javax.management.*;
import java.io.Serializable;
import java.net.URL;
import java.util.EnumSet;
import javax.management.loading.MLet;
public class PostExceptionTest {
/**
* A test case where we instantiate an ExceptionalWombatMBean (or a
* subclass of it) which will throw the exception {@code t} from within
* the methods indicated by {@code where}
*/
public static class Case {
public final Throwable t;
public final EnumSet<WHERE> where;
public Case(Throwable t,EnumSet<WHERE> where) {
this.t=t; this.where=where;
}
}
// Various methods to create an instance of Case in a single line
// --------------------------------------------------------------
public static Case caze(Throwable t, WHERE w) {
return new Case(t,EnumSet.of(w));
}
public static Case caze(Throwable t, EnumSet<WHERE> where) {
return new Case(t,where);
}
public static Case caze(Throwable t, WHERE w, WHERE... rest) {
return new Case(t,EnumSet.of(w,rest));
}
/**
* Here is the list of our test cases:
*/
public static Case[] cases ={
caze(new RuntimeException(),WHERE.PREREGISTER),
caze(new RuntimeException(),WHERE.POSTREGISTER),
caze(new RuntimeException(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER),
caze(new RuntimeException(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER),
caze(new Exception(),WHERE.PREREGISTER),
caze(new Exception(),WHERE.POSTREGISTER),
caze(new Exception(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER),
caze(new Exception(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER),
caze(new Error(),WHERE.PREREGISTER),
caze(new Error(),WHERE.POSTREGISTER),
caze(new Error(),WHERE.POSTREGISTER, WHERE.PREDEREGISTER),
caze(new Error(),WHERE.POSTREGISTER, WHERE.POSTDEREGISTER),
caze(new RuntimeException(),EnumSet.allOf(WHERE.class)),
caze(new Exception(),EnumSet.allOf(WHERE.class)),
caze(new Error(),EnumSet.allOf(WHERE.class)),
};
public static void main(String[] args) throws Exception {
System.out.println("Test behaviour of MBeanServer when postRegister " +
"or postDeregister throw exceptions");
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
int failures = 0;
final ObjectName n = new ObjectName("test:type=Wombat");
// We're going to test each cases, using each of the 4 createMBean
// forms + registerMBean in turn to create the MBean.
// Wich method is used to create the MBean is indicated by "how"
//
for (Case caze:cases) {
for (CREATE how : CREATE.values()) {
failures+=test(mbs,n,how,caze.t,caze.where);
}
}
if (failures == 0)
System.out.println("Test passed");
else {
System.out.println("TEST FAILED: " + failures + " failure(s)");
System.exit(1);
}
}
// Execute a test case composed of:
// mbs: The MBeanServer where the MBean will be registered,
// name: The name of that MBean
// how: How will the MBean be created/registered (which MBeanServer
// method)
// t: The exception/error that the MBean will throw
// where: In which pre/post register/deregister method the exception/error
// will be thrown
//
private static int test(MBeanServer mbs, ObjectName name, CREATE how,
Throwable t, EnumSet<WHERE> where)
throws Exception {
System.out.println("-------<"+how+"> / <"+t+"> / "+ where + "-------");
int failures = 0;
ObjectInstance oi = null;
Exception reg = null; // exception thrown by create/register
Exception unreg = null; // exception thrown by unregister
try {
// Create the MBean
oi = how.create(t, where, mbs, name);
} catch (Exception xx) {
reg=xx;
}
final ObjectName n = (oi==null)?name:oi.getObjectName();
final boolean isRegistered = mbs.isRegistered(n);
try {
// If the MBean is registered, unregister it
if (isRegistered) mbs.unregisterMBean(n);
} catch (Exception xxx) {
unreg=xxx;
}
final boolean isUnregistered = !mbs.isRegistered(n);
if (!isUnregistered) {
// if the MBean is still registered (preDeregister threw an
// exception) signify to the MBean that it now should stop
// throwing anaything and unregister it.
JMX.newMBeanProxy(mbs, n, ExceptionalWombatMBean.class).end();
mbs.unregisterMBean(n);
}
// Now analyze the result. If we didn't ask the MBean to throw any
// exception then reg should be null.
if (where.isEmpty() && reg!=null) {
System.out.println("Unexpected registration exception: "+
reg);
throw new RuntimeException("Unexpected registration exception: "+
reg,reg);
}
// If we didn't ask the MBean to throw any exception then unreg should
// also be null.
if (where.isEmpty() && unreg!=null) {
System.out.println("Unexpected unregistration exception: "+
unreg);
throw new RuntimeException("Unexpected unregistration exception: "+
unreg,unreg);
}
// If we asked the MBean to throw an exception in either of preRegister
// or postRegister, then reg should not be null.
if ((where.contains(WHERE.PREREGISTER)
|| where.contains(WHERE.POSTREGISTER))&& reg==null) {
System.out.println("Expected registration exception not " +
"thrown by "+where);
throw new RuntimeException("Expected registration exception not " +
"thrown by "+where);
}
// If we asked the MBean not to throw any exception in preRegister
// then the MBean should have been registered, unregisterMBean should
// have been called.
// If we asked the MBean to throw an exception in either of preDeregister
// or postDeregister, then unreg should not be null.
if ((where.contains(WHERE.PREDEREGISTER)
|| where.contains(WHERE.POSTDEREGISTER))&& unreg==null
&& !where.contains(WHERE.PREREGISTER)) {
System.out.println("Expected unregistration exception not " +
"thrown by "+where);
throw new RuntimeException("Expected unregistration exception not " +
"thrown by "+where);
}
// If we asked the MBean to throw an exception in preRegister
// then the MBean should not have been registered.
if (where.contains(WHERE.PREREGISTER)) {
if (isRegistered) {
System.out.println("MBean is still registered [" +
where+
"]: "+name+" / "+reg);
throw new RuntimeException("MBean is still registered [" +
where+
"]: "+name+" / "+reg,reg);
}
}
// If we asked the MBean not to throw an exception in preRegister,
// but to throw an exception in postRegister, then the MBean should
// have been registered.
if (where.contains(WHERE.POSTREGISTER) &&
!where.contains(WHERE.PREREGISTER)) {
if (!isRegistered) {
System.out.println("MBean is already unregistered [" +
where+
"]: "+name+" / "+reg);
throw new RuntimeException("MBean is already unregistered [" +
where+
"]: "+name+" / "+reg,reg);
}
}
// If we asked the MBean to throw an exception in preRegister,
// check that the exception we caught was as expected.
//
if (where.contains(WHERE.PREREGISTER)) {
WHERE.PREREGISTER.check(reg, t);
} else if (where.contains(WHERE.POSTREGISTER)) {
// If we asked the MBean to throw an exception in postRegister,
// check that the exception we caught was as expected.
// We don't do this check if we asked the MBean to also throw an
// exception in pre register, because postRegister will not have
// been called.
WHERE.POSTREGISTER.check(reg, t);
}
if (!isRegistered) return failures;
// The MBean was registered, so unregisterMBean was called. Check
// unregisterMBean exceptions...
//
// If we asked the MBean to throw an exception in preDeregister
// then the MBean should not have been deregistered.
if (where.contains(WHERE.PREDEREGISTER)) {
if (isUnregistered) {
System.out.println("MBean is already unregistered [" +
where+
"]: "+name+" / "+unreg);
throw new RuntimeException("MBean is already unregistered [" +
where+
"]: "+name+" / "+unreg,unreg);
}
}
// If we asked the MBean not to throw an exception in preDeregister,
// but to throw an exception in postDeregister, then the MBean should
// have been deregistered.
if (where.contains(WHERE.POSTDEREGISTER) &&
!where.contains(WHERE.PREDEREGISTER)) {
if (!isUnregistered) {
System.out.println("MBean is not unregistered [" +
where+
"]: "+name+" / "+unreg);
throw new RuntimeException("MBean is not unregistered [" +
where+
"]: "+name+" / "+unreg,unreg);
}
}
// If we asked the MBean to throw an exception in preDeregister,
// check that the exception we caught was as expected.
//
if (where.contains(WHERE.PREDEREGISTER)) {
WHERE.PREDEREGISTER.check(unreg, t);
} else if (where.contains(WHERE.POSTDEREGISTER)) {
// If we asked the MBean to throw an exception in postDeregister,
// check that the exception we caught was as expected.
// We don't do this check if we asked the MBean to also throw an
// exception in pre register, because postRegister will not have
// been called.
WHERE.POSTDEREGISTER.check(unreg, t);
}
return failures;
}
/**
* This enum lists the 4 methods in MBeanRegistration.
*/
public static enum WHERE {
PREREGISTER, POSTREGISTER, PREDEREGISTER, POSTDEREGISTER;
// Checks that an exception thrown by the MBeanServer correspond to
// what is expected when an MBean throws an exception in this
// MBeanRegistration method ("this" is one of the 4 enum values above)
//
public void check(Exception thrown, Throwable t)
throws Exception {
if (t instanceof RuntimeException) {
if (!(thrown instanceof RuntimeMBeanException)) {
System.out.println("Expected RuntimeMBeanException, got "+
thrown);
throw new Exception("Expected RuntimeMBeanException, got "+
thrown);
}
} else if (t instanceof Error) {
if (!(thrown instanceof RuntimeErrorException)) {
System.out.println("Expected RuntimeErrorException, got "+
thrown);
throw new Exception("Expected RuntimeErrorException, got "+
thrown);
}
} else if (t instanceof Exception) {
if (EnumSet.of(POSTDEREGISTER,POSTREGISTER).contains(this)) {
if (!(thrown instanceof RuntimeMBeanException)) {
System.out.println("Expected RuntimeMBeanException, got "+
thrown);
throw new Exception("Expected RuntimeMBeanException, got "+
thrown);
}
if (! (thrown.getCause() instanceof RuntimeException)) {
System.out.println("Bad cause: " +
"expected RuntimeException, " +
"got <"+thrown.getCause()+">");
throw new Exception("Bad cause: " +
"expected RuntimeException, " +
"got <"+thrown.getCause()+">");
}
}
if (EnumSet.of(PREDEREGISTER,PREREGISTER).contains(this)) {
if (!(thrown instanceof MBeanRegistrationException)) {
System.out.println("Expected " +
"MBeanRegistrationException, got "+
thrown);
throw new Exception("Expected " +
"MBeanRegistrationException, got "+
thrown);
}
if (! (thrown.getCause() instanceof Exception)) {
System.out.println("Bad cause: " +
"expected Exception, " +
"got <"+thrown.getCause()+">");
throw new Exception("Bad cause: " +
"expected Exception, " +
"got <"+thrown.getCause()+">");
}
}
}
}
}
/**
* This enum lists the 5 methods to create and register an
* ExceptionalWombat MBean
*/
public static enum CREATE {
CREATE1() {
// Creates an ExceptionalWombat MBean using createMBean form #1
public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
MBeanServer server, ObjectName name) throws Exception {
ExceptionallyHackyWombat.t = t;
ExceptionallyHackyWombat.w = where;
return server.createMBean(
ExceptionallyHackyWombat.class.getName(),
name);
}
},
CREATE2() {
// Creates an ExceptionalWombat MBean using createMBean form #2
public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
MBeanServer server, ObjectName name) throws Exception {
ExceptionallyHackyWombat.t = t;
ExceptionallyHackyWombat.w = where;
final ObjectName loaderName = registerMLet(server);
return server.createMBean(
ExceptionallyHackyWombat.class.getName(),
name, loaderName);
}
},
CREATE3() {
// Creates an ExceptionalWombat MBean using createMBean form #3
public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
MBeanServer server, ObjectName name) throws Exception {
final Object[] params = {t, where};
final String[] signature = {Throwable.class.getName(),
EnumSet.class.getName()
};
return server.createMBean(
ExceptionalWombat.class.getName(), name,
params, signature);
}
},
CREATE4() {
// Creates an ExceptionalWombat MBean using createMBean form #4
public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
MBeanServer server, ObjectName name) throws Exception {
final Object[] params = {t, where};
final String[] signature = {Throwable.class.getName(),
EnumSet.class.getName()
};
return server.createMBean(
ExceptionalWombat.class.getName(), name,
registerMLet(server), params, signature);
}
},
REGISTER() {
// Creates an ExceptionalWombat MBean using registerMBean
public ObjectInstance create(Throwable t, EnumSet<WHERE> where,
MBeanServer server, ObjectName name) throws Exception {
final ExceptionalWombat wombat =
new ExceptionalWombat(t, where);
return server.registerMBean(wombat, name);
}
};
// Creates an ExceptionalWombat MBean using the method denoted by this
// Enum value - one of CREATE1, CREATE2, CREATE3, CREATE4, or REGISTER.
public abstract ObjectInstance create(Throwable t, EnumSet<WHERE> where,
MBeanServer server, ObjectName name) throws Exception;
// This is a bit of a hack - we use an MLet that delegates to the
// System ClassLoader so that we can use createMBean form #2 and #3
// while still using the same class loader (system).
// This is necessary to make the ExceptionallyHackyWombatMBean work ;-)
//
public ObjectName registerMLet(MBeanServer server) throws Exception {
final ObjectName name = new ObjectName("test:type=MLet");
if (server.isRegistered(name)) {
return name;
}
final MLet mlet = new MLet(new URL[0],
ClassLoader.getSystemClassLoader());
return server.registerMBean(mlet, name).getObjectName();
}
}
/**
*A Wombat MBean that can throw exceptions or errors in any of the
* MBeanRegistration methods.
*/
public static interface ExceptionalWombatMBean {
// Tells the MBean to stop throwing exceptions - we sometime
// need to call this at the end of the test so that we can
// actually unregister the MBean.
public void end();
}
/**
*A Wombat MBean that can throw exceptions or errors in any of the
* MBeanRegistration methods.
*/
public static class ExceptionalWombat
implements ExceptionalWombatMBean, MBeanRegistration {
private final Throwable throwable;
private final EnumSet<WHERE> where;
private volatile boolean end=false;
public ExceptionalWombat(Throwable t, EnumSet<WHERE> where) {
this.throwable=t; this.where=where;
}
private Exception doThrow() {
if (throwable instanceof Error)
throw (Error)throwable;
if (throwable instanceof RuntimeException)
throw (RuntimeException)throwable;
return (Exception)throwable;
}
public ObjectName preRegister(MBeanServer server, ObjectName name)
throws Exception {
if (!end && where.contains(WHERE.PREREGISTER))
throw doThrow();
return name;
}
public void postRegister(Boolean registrationDone) {
if (!end && where.contains(WHERE.POSTREGISTER))
throw new RuntimeException(doThrow());
}
public void preDeregister() throws Exception {
if (!end && where.contains(WHERE.PREDEREGISTER))
throw doThrow();
}
public void postDeregister() {
if (!end && where.contains(WHERE.POSTREGISTER))
throw new RuntimeException(doThrow());
}
public void end() {
this.end=true;
}
}
/**
* This is a big ugly hack to call createMBean form #1 and #2 - where
* the empty constructor is used. Since we still want to supply parameters
* to the ExceptionalWombat super class, we temporarily store these
* parameter value in a static volatile before calling create MBean.
* Of course this only works because our test is sequential and single
* threaded, and nobody but our test uses this ExceptionallyHackyWombat.
*/
public static class ExceptionallyHackyWombat extends ExceptionalWombat {
public static volatile Throwable t;
public static volatile EnumSet<WHERE> w;
public ExceptionallyHackyWombat() {
super(t,w);
}
}
}

View File

@ -23,9 +23,9 @@
/*
* @test
* @bug 6211220
* @bug 6211220 6616825
* @summary Test that jmx.serial.form=1.0 works for ObjectName
* @author Eamonn McManus
* @author Eamonn McManus, Daniel Fuchs
* @run clean SerialCompatTest
* @run build SerialCompatTest
* @run main/othervm SerialCompatTest
@ -36,6 +36,192 @@ import java.util.*;
import javax.management.ObjectName;
public class SerialCompatTest {
public static void check6211220() throws Exception {
ObjectName on = new ObjectName("a:b=c");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(on);
oos.close();
byte[] bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
ObjectName on1 = (ObjectName) ois.readObject();
// if the bug is present, these will get NullPointerException
for (int i = 0; i <= 11; i++) {
String msg = "6211220 case(" + i + ")";
try {
switch (i) {
case 0:
check(msg, on1.getDomain().equals("a"));
break;
case 1:
check(msg, on1.getCanonicalName().equals("a:b=c"));
break;
case 2:
check(msg, on1.getKeyPropertyListString()
.equals("b=c"));
break;
case 3:
check(msg, on1.getCanonicalKeyPropertyListString()
.equals("b=c"));
break;
case 4:
check(msg, on1.getKeyProperty("b").equals("c"));
break;
case 5:
check(msg, on1.getKeyPropertyList()
.equals(Collections.singletonMap("b", "c")));
break;
case 6:
check(msg, !on1.isDomainPattern());
break;
case 7:
check(msg, !on1.isPattern());
break;
case 8:
check(msg, !on1.isPropertyPattern());
break;
case 9:
check(msg, on1.equals(on));
break;
case 10:
check(msg, on.equals(on1));
break;
case 11:
check(msg, on1.apply(on));
break;
default:
throw new Exception(msg + ": Test incorrect");
}
} catch (Exception e) {
System.out.println(msg + ": Test failed with exception:");
e.printStackTrace(System.out);
failed = true;
}
}
if (failed) {
throw new Exception("Some tests for 6211220 failed");
} else {
System.out.println("All tests for 6211220 passed");
}
}
static void checkName(String testname, ObjectName on)
throws Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(on);
oos.close();
byte[] bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
ObjectName on1 = (ObjectName) ois.readObject();
// if the bug is present, these will get NullPointerException
for (int i = 0; i <= 11; i++) {
String msg = testname + " case(" + i + ")";
try {
switch (i) {
case 0:
check(msg, on1.getDomain().equals(on.getDomain()));
break;
case 1:
check(msg, on1.getCanonicalName().
equals(on.getCanonicalName()));
break;
case 2:
check(msg, on1.getKeyPropertyListString().
equals(on.getKeyPropertyListString()));
break;
case 3:
check(msg, on1.getCanonicalKeyPropertyListString().
equals(on.getCanonicalKeyPropertyListString()));
break;
case 4:
for (Object ko : on1.getKeyPropertyList().keySet()) {
final String key = (String) ko;
check(msg, on1.getKeyProperty(key).
equals(on.getKeyProperty(key)));
}
for (Object ko : on.getKeyPropertyList().keySet()) {
final String key = (String) ko;
check(msg, on1.getKeyProperty(key).
equals(on.getKeyProperty(key)));
}
case 5:
check(msg, on1.getKeyPropertyList()
.equals(on.getKeyPropertyList()));
break;
case 6:
check(msg, on1.isDomainPattern()==on.isDomainPattern());
break;
case 7:
check(msg, on1.isPattern() == on.isPattern());
break;
case 8:
check(msg,
on1.isPropertyPattern()==on.isPropertyPattern());
break;
case 9:
check(msg, on1.equals(on));
break;
case 10:
check(msg, on.equals(on1));
break;
case 11:
if (!on.isPattern()) {
check(msg, on1.apply(on));
}
break;
default:
throw new Exception("Test incorrect: case: " + i);
}
} catch (Exception e) {
System.out.println("Test (" + i + ") failed with exception:");
e.printStackTrace(System.out);
failed = true;
}
}
}
private static String[] names6616825 = {
"a:b=c", "a:b=c,*", "*:*", ":*", ":b=c", ":b=c,*",
"a:*,b=c", ":*", ":*,b=c", "*x?:k=\"x\\*z\"", "*x?:k=\"x\\*z\",*",
"*x?:*,k=\"x\\*z\"", "*x?:k=\"x\\*z\",*,b=c"
};
static void check6616825() throws Exception {
System.out.println("Testing 616825");
for (String n : names6616825) {
final ObjectName on;
try {
on = new ObjectName(n);
} catch (Exception x) {
failed = true;
System.out.println("Unexpected failure for 6616825 [" + n +
"]: " + x);
x.printStackTrace(System.out);
continue;
}
try {
checkName("616825 " + n, on);
} catch (Exception x) {
failed = true;
System.out.println("6616825 failed for [" + n + "]: " + x);
x.printStackTrace(System.out);
}
}
if (failed) {
throw new Exception("Some tests for 6616825 failed");
} else {
System.out.println("All tests for 6616825 passed");
}
}
public static void main(String[] args) throws Exception {
System.setProperty("jmx.serial.form", "1.0");
@ -50,68 +236,29 @@ public class SerialCompatTest {
// new serial form has no fields, uses writeObject
}
ObjectName on = new ObjectName("a:b=c");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(on);
oos.close();
byte[] bytes = bos.toByteArray();
ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bis);
ObjectName on1 = (ObjectName) ois.readObject();
// if the bug is present, these will get NullPointerException
for (int i = 0; i <= 11; i++) {
try {
switch (i) {
case 0:
check(on1.getDomain().equals("a")); break;
case 1:
check(on1.getCanonicalName().equals("a:b=c")); break;
case 2:
check(on1.getKeyPropertyListString().equals("b=c")); break;
case 3:
check(on1.getCanonicalKeyPropertyListString().equals("b=c"));
break;
case 4:
check(on1.getKeyProperty("b").equals("c")); break;
case 5:
check(on1.getKeyPropertyList()
.equals(Collections.singletonMap("b", "c"))); break;
case 6:
check(!on1.isDomainPattern()); break;
case 7:
check(!on1.isPattern()); break;
case 8:
check(!on1.isPropertyPattern()); break;
case 9:
check(on1.equals(on)); break;
case 10:
check(on.equals(on1)); break;
case 11:
check(on1.apply(on)); break;
default:
throw new Exception("Test incorrect: case: " + i);
}
} catch (Exception e) {
System.out.println("Test failed with exception:");
e.printStackTrace(System.out);
failed = true;
check6211220();
} catch (Exception x) {
System.err.println(x.getMessage());
}
try {
check6616825();
} catch (Exception x) {
System.err.println(x.getMessage());
}
if (failed)
if (failed) {
throw new Exception("Some tests failed");
else
} else {
System.out.println("All tests passed");
}
}
private static void check(boolean condition) {
private static void check(String msg, boolean condition) {
if (!condition) {
new Throwable("Test failed").printStackTrace(System.out);
new Throwable("Test failed " + msg).printStackTrace(System.out);
failed = true;
}
}
private static boolean failed;
}

View File

@ -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;
}
}

View 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());
}
}

View File

@ -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;
}
}
}

View File

@ -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();
}
}

View 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";
}

View 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";
}

View File

@ -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;
}
}

View 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;
}

View 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";
}

View File

@ -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;
}

View File

@ -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);
}
}

View 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";
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,365 @@
/*/*
* 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.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 ObjectName emitter;
private static NotificationEmitter emitterImpl;
private static JMXServiceURL url;
private static JMXConnectorServer server;
private static int toSend = 10;
private static final long bigWaiting = 6000;
private static int counter = 0;
private static int jobs = 10;
private static int endedJobs = 0;
private static volatile String failure;
private static Executor sharedExecutor = new ThreadPoolExecutor(0, 1, 1000,
TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(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<Runnable>(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();
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(SharingThreadTest.class) {
while (endedJobs < jobs && toWait > 0 && failure == null) {
SharingThreadTest.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(SharingThreadTest.class) {
endedJobs++;
if (endedJobs>=jobs) {
SharingThreadTest.class.notify();
}
}
} catch (RuntimeException re) {
re.printStackTrace(System.out);
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 {
String id = getId();
Listener listener = new Listener(id);
Filter filter = new Filter(id);
//newConn();
EventClient ec = newEventClient(type, conn);
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);
}
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 (this) {
received++;
if (sequenceNB < 0) {
sequenceNB = notif.getSequenceNumber();
} else 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();
}
}
}
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;
}
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);
}
long sequenceNumber = 0;
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 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), 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++);
}
}
private static void fail(String msg) {
System.out.println("FAIL: " + msg);
failure = msg;
}
}

View 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));
}
}

View File

@ -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) {
//
}
};
}

View File

@ -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 {

View File

@ -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
*/

View File

@ -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");

View File

@ -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

View File

@ -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());

View File

@ -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);

View File

@ -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);

Some files were not shown because too many files have changed in this diff Show More