5108776: Add reliable event handling to the JMX API

6218920: API bug - impossible to delete last MBeanServerForwarder on a connector

Reviewed-by: emcmanus
This commit is contained in:
Shanliang Jiang 2008-07-31 15:31:13 +02:00
parent 22260fb95d
commit cf105cf085
104 changed files with 15871 additions and 580 deletions

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

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) {
@ -332,7 +332,13 @@ public class ServerNotifForwarder {
*/
private void checkMBeanPermission(final ObjectName name,
final String actions)
throws InstanceNotFoundException, SecurityException {
throws InstanceNotFoundException, SecurityException {
checkMBeanPermission(mbeanServer, name, actions);
}
public static void checkMBeanPermission(
final MBeanServer mbs, final ObjectName name, final String actions)
throws InstanceNotFoundException, SecurityException {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
AccessControlContext acc = AccessController.getContext();
@ -342,7 +348,7 @@ public class ServerNotifForwarder {
new PrivilegedExceptionAction<ObjectInstance>() {
public ObjectInstance run()
throws InstanceNotFoundException {
return mbeanServer.getObjectInstance(name);
return mbs.getObjectInstance(name);
}
});
} catch (PrivilegedActionException e) {
@ -364,14 +370,12 @@ public class ServerNotifForwarder {
TargetedNotification tn) {
try {
if (checkNotificationEmission) {
checkMBeanPermission(name, "addNotificationListener");
checkMBeanPermission(
name, "addNotificationListener");
}
if (notificationAccessController != null) {
notificationAccessController.fetchNotification(
connectionId,
name,
tn.getNotification(),
Subject.getSubject(AccessController.getContext()));
connectionId, name, tn.getNotification(), getSubject());
}
return true;
} catch (SecurityException e) {

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

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 {
@ -346,7 +346,24 @@ public class EnvHelp {
*/
public static long getFetchTimeout(Map env) {
return getIntegerAttribute(env, FETCH_TIMEOUT, 60000L, 0,
Long.MAX_VALUE);
Long.MAX_VALUE);
}
/**
* <p>Name of the attribute that specifies an object that will check
* accesses to add/removeNotificationListener and also attempts to
* receive notifications. The value associated with this attribute
* should be a <code>NotificationAccessController</code> object.
* The default value is null.</p>
* This field is not public because of its com.sun dependency.
*/
public static final String NOTIF_ACCESS_CONTROLLER =
"com.sun.jmx.remote.notification.access.controller";
public static NotificationAccessController getNotificationAccessController(
Map env) {
return (env == null) ? null :
(NotificationAccessController) env.get(NOTIF_ACCESS_CONTROLLER);
}
/**
@ -470,24 +487,24 @@ public class EnvHelp {
}
/**
The value of this attribute, if present, is a string specifying
what other attributes should not appear in
JMXConnectorServer.getAttributes(). It is a space-separated
list of attribute patterns, where each pattern is either an
attribute name, or an attribute prefix followed by a "*"
character. The "*" has no special significance anywhere except
at the end of a pattern. By default, this list is added to the
list defined by {@link #DEFAULT_HIDDEN_ATTRIBUTES} (which
uses the same format). If the value of this attribute begins
with an "=", then the remainder of the string defines the
complete list of attribute patterns.
* The value of this attribute, if present, is a string specifying
* what other attributes should not appear in
* JMXConnectorServer.getAttributes(). It is a space-separated
* list of attribute patterns, where each pattern is either an
* attribute name, or an attribute prefix followed by a "*"
* character. The "*" has no special significance anywhere except
* at the end of a pattern. By default, this list is added to the
* list defined by {@link #DEFAULT_HIDDEN_ATTRIBUTES} (which
* uses the same format). If the value of this attribute begins
* with an "=", then the remainder of the string defines the
* complete list of attribute patterns.
*/
public static final String HIDDEN_ATTRIBUTES =
"jmx.remote.x.hidden.attributes";
/**
Default list of attributes not to show.
@see #HIDDEN_ATTRIBUTES
* Default list of attributes not to show.
* @see #HIDDEN_ATTRIBUTES
*/
/* This list is copied directly from the spec, plus
java.naming.security.*. Most of the attributes here would have
@ -651,6 +668,8 @@ public class EnvHelp {
* @param env the environment map.
* @param prop the name of the property in the environment map whose
* returned string value must be converted into a boolean value.
* @param systemProperty if true, consult a system property of the
* same name if there is no entry in the environment map.
*
* @return
* <ul>
@ -671,16 +690,73 @@ public class EnvHelp {
* @throws ClassCastException if {@code env.get(prop)} cannot be cast
* to {@code String}.
*/
public static boolean computeBooleanFromString(Map env, String prop)
throws IllegalArgumentException, ClassCastException {
public static boolean computeBooleanFromString(
Map env, String prop, boolean systemProperty) {
if (env == null)
throw new IllegalArgumentException("env map cannot be null");
// returns a default value of 'false' if no property is found...
return computeBooleanFromString(env,prop,systemProperty,false);
}
/**
* Computes a boolean value from a string value retrieved from a
* property in the given map.
*
* @param env the environment map.
* @param prop the name of the property in the environment map whose
* returned string value must be converted into a boolean value.
* @param systemProperty if true, consult a system property of the
* same name if there is no entry in the environment map.
* @param defaultValue a default value to return in case no property
* was defined.
*
* @return
* <ul>
* <li>{@code defaultValue} if {@code env.get(prop)} is {@code null}
* and {@code systemProperty} is {@code false}</li>
* <li>{@code defaultValue} if {@code env.get(prop)} is {@code null}
* and {@code systemProperty} is {@code true} and
* {@code System.getProperty(prop)} is {@code null}</li>
* <li>{@code false} if {@code env.get(prop)} is {@code null}
* and {@code systemProperty} is {@code true} and
* {@code System.getProperty(prop).equalsIgnoreCase("false")}
* is {@code true}</li>
* <li>{@code true} if {@code env.get(prop)} is {@code null}
* and {@code systemProperty} is {@code true} and
* {@code System.getProperty(prop).equalsIgnoreCase("true")}
* is {@code true}</li>
* <li>{@code false} if
* {@code ((String)env.get(prop)).equalsIgnoreCase("false")}
* is {@code true}</li>
* <li>{@code true} if
* {@code ((String)env.get(prop)).equalsIgnoreCase("true")}
* is {@code true}</li>
* </ul>
*
* @throws IllegalArgumentException if {@code env} is {@code null} or
* {@code env.get(prop)} is not {@code null} and
* {@code ((String)env.get(prop)).equalsIgnoreCase("false")} and
* {@code ((String)env.get(prop)).equalsIgnoreCase("true")} are
* {@code false}.
* @throws ClassCastException if {@code env.get(prop)} cannot be cast
* to {@code String}.
*/
public static boolean computeBooleanFromString(
Map env, String prop, boolean systemProperty, boolean defaultValue) {
if (env == null)
throw new IllegalArgumentException("env map cannot be null");
String stringBoolean = (String) env.get(prop);
if (stringBoolean == null && systemProperty) {
stringBoolean =
AccessController.doPrivileged(new GetPropertyAction(prop));
}
if (stringBoolean == null)
return false;
return defaultValue;
else if (stringBoolean.equalsIgnoreCase("true"))
return true;
else if (stringBoolean.equalsIgnoreCase("false"))
@ -703,6 +779,65 @@ public class EnvHelp {
return new Hashtable<K, V>(m);
}
/**
* Returns true if the parameter JMXConnector.USE_EVENT_SERVICE is set to a
* String equals "true" by ignoring case in the map or in the System.
*/
public static boolean eventServiceEnabled(Map env) {
return computeBooleanFromString(env, JMXConnector.USE_EVENT_SERVICE, true);
}
/**
* Returns true if the parameter JMXConnectorServer.DELEGATE_TO_EVENT_SERVICE
* is set to a String equals "true" (ignores case).
* If the property DELEGATE_TO_EVENT_SERVICE is not set, returns
* a default value of "true".
*/
public static boolean delegateToEventService(Map env) {
return computeBooleanFromString(env,
JMXConnectorServer.DELEGATE_TO_EVENT_SERVICE, true, true);
}
// /**
// * <p>Name of the attribute that specifies an EventRelay object to use.
// */
// public static final String EVENT_RELAY =
// "jmx.remote.x.event.relay";
//
//
// /**
// * Returns an EventRelay object. The default one is FetchingEventRelay.
// * If {@code EVENT_RELAY} is specified in {@code env} as a key,
// * its value will be returned as an EventRelay object, if the value is
// * not of type {@code EventRelay}, the default {@code FetchingEventRelay}
// * will be returned.
// * If {@code EVENT_RELAY} is not specified but {@code ENABLE_EVENT_RELAY}
// * is specified as a key and its value is <code true>, the default {@code FetchingEventRelay}
// * will be returned.
// */
// public static EventRelay getEventRelay(Map env) {
// Map info = env == null ?
// Collections.EMPTY_MAP : env;
//
// Object o = env.get(EVENT_RELAY);
// if (o instanceof EventRelay) {
// return (EventRelay)o;
// } else if (o != null) {
// logger.warning("getEventRelay",
// "The user specified object is not an EventRelay object, " +
// "using the default class FetchingEventRelay.");
//
// return new FetchingEventRelay();
// }
//
// if (enableEventRelay(env)) {
// return new FetchingEventRelay();
// }
//
// return null;
// }
private static final class SinkOutputStream extends OutputStream {
public void write(byte[] b, int off, int len) {}
public void write(int b) {}

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

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

@ -420,7 +420,13 @@ public interface MBeanServer extends MBeanServerConnection {
// doc comment inherited from MBeanServerConnection
public String[] getDomains();
// doc comment inherited from MBeanServerConnection
// doc comment inherited from MBeanServerConnection, plus:
/**
* {@inheritDoc}
* If the source of the notification
* is a reference to an MBean object, the MBean server will replace it
* by that MBean's ObjectName. Otherwise the source is unchanged.
*/
public void addNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,

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
@ -676,32 +677,7 @@ public interface MBeanServerConnection {
public String[] getDomains()
throws IOException;
/**
* <p>Adds a listener to a registered MBean.</p>
*
* <P> A notification emitted by an MBean will be forwarded by the
* MBeanServer to the listener. If the source of the notification
* is a reference to an MBean object, the MBean server will replace it
* by that MBean's ObjectName. Otherwise the source is unchanged.
*
* @param name The name of the MBean on which the listener should
* be added.
* @param listener The listener object which will handle the
* notifications emitted by the registered MBean.
* @param filter The filter object. If filter is null, no
* filtering will be performed before handling notifications.
* @param handback The context to be sent to the listener when a
* notification is emitted.
*
* @exception InstanceNotFoundException The MBean name provided
* does not match any of the registered MBeans.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
*
* @see #removeNotificationListener(ObjectName, NotificationListener)
* @see #removeNotificationListener(ObjectName, NotificationListener,
* NotificationFilter, Object)
*/
// doc inherited from NotificationManager
public void addNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,
@ -818,65 +794,13 @@ public interface MBeanServerConnection {
throws InstanceNotFoundException, ListenerNotFoundException,
IOException;
/**
* <p>Removes a listener from a registered MBean.</p>
*
* <P> If the listener is registered more than once, perhaps with
* different filters or callbacks, this method will remove all
* those registrations.
*
* @param name The name of the MBean on which the listener should
* be removed.
* @param listener The listener to be removed.
*
* @exception InstanceNotFoundException The MBean name provided
* does not match any of the registered MBeans.
* @exception ListenerNotFoundException The listener is not
* registered in the MBean.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
*
* @see #addNotificationListener(ObjectName, NotificationListener,
* NotificationFilter, Object)
*/
// doc inherited from NotificationManager
public void removeNotificationListener(ObjectName name,
NotificationListener listener)
throws InstanceNotFoundException, ListenerNotFoundException,
IOException;
/**
* <p>Removes a listener from a registered MBean.</p>
*
* <p>The MBean must have a listener that exactly matches the
* given <code>listener</code>, <code>filter</code>, and
* <code>handback</code> parameters. If there is more than one
* such listener, only one is removed.</p>
*
* <p>The <code>filter</code> and <code>handback</code> parameters
* may be null if and only if they are null in a listener to be
* removed.</p>
*
* @param name The name of the MBean on which the listener should
* be removed.
* @param listener The listener to be removed.
* @param filter The filter that was specified when the listener
* was added.
* @param handback The handback that was specified when the
* listener was added.
*
* @exception InstanceNotFoundException The MBean name provided
* does not match any of the registered MBeans.
* @exception ListenerNotFoundException The listener is not
* registered in the MBean, or it is not registered with the given
* filter and handback.
* @exception IOException A communication problem occurred when
* talking to the MBean server.
*
* @see #addNotificationListener(ObjectName, NotificationListener,
* NotificationFilter, Object)
*
*/
// doc inherited from NotificationManager
public void removeNotificationListener(ObjectName name,
NotificationListener listener,
NotificationFilter filter,

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

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,21 +1154,29 @@ public class MLet extends java.net.URLClassLoader
*/
private synchronized String loadLibraryAsResource(String libname) {
try {
InputStream is = getResourceAsStream(libname.replace(File.separatorChar,'/'));
InputStream is = getResourceAsStream(
libname.replace(File.separatorChar,'/'));
if (is != null) {
File directory = new File(libraryDirectory);
directory.mkdirs();
File file = File.createTempFile(libname + ".", null, directory);
file.deleteOnExit();
FileOutputStream fileOutput = new FileOutputStream(file);
int c;
while ((c = is.read()) != -1) {
fileOutput.write(c);
}
is.close();
fileOutput.close();
if (file.exists()) {
return file.getAbsolutePath();
try {
File directory = new File(libraryDirectory);
directory.mkdirs();
File file = File.createTempFile(libname + ".", null,
directory);
file.deleteOnExit();
FileOutputStream fileOutput = new FileOutputStream(file);
try {
int c;
while ((c = is.read()) != -1) {
fileOutput.write(c);
}
} finally {
fileOutput.close();
}
if (file.exists()) {
return file.getAbsolutePath();
}
} finally {
is.close();
}
}
} catch (Exception e) {

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,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(),
dcl);
mbeanServer.getClassLoaderRepository(),
dcl);
}
});
serverCommunicatorAdmin = new
RMIServerCommunicatorAdmin(EnvHelp.getServerConnectionTimeout(env));
this.env = env;
}
private synchronized ServerNotifForwarder getServerNotifFwd() {
// Lazily created when first use. Mainly when
// addNotificationListener is first called.
if (serverNotifForwarder == null)
serverNotifForwarder =
new ServerNotifForwarder(mbeanServer,
env,
rmiServer.getNotifBuffer(),
connectionId);
return serverNotifForwarder;
}
public String getConnectionId() throws IOException {
// We should call reqIncomming() here... shouldn't we?
@ -181,6 +177,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
final boolean debug = logger.debugOn();
final String idstr = (debug?"["+this.toString()+"]":null);
final SubscriptionManager mgr;
synchronized (this) {
if (terminated) {
if (debug) logger.debug("close",idstr + " already terminated.");
@ -195,11 +192,12 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
serverCommunicatorAdmin.terminate();
}
if (serverNotifForwarder != null) {
serverNotifForwarder.terminate();
}
mgr = subscriptionManager;
subscriptionManager = null;
}
if (mgr != null) mgr.terminate();
rmiServer.clientClosed(this);
if (debug) logger.debug("close",idstr + " closed.");
@ -955,8 +953,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
int i=0;
ClassLoader targetCl;
NotificationFilter[] filterValues =
new NotificationFilter[names.length];
Object params[];
new NotificationFilter[names.length];
Integer[] ids = new Integer[names.length];
final boolean debug=logger.debugOn();
@ -991,8 +988,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
// remove all registered listeners
for (int j=0; j<i; j++) {
try {
getServerNotifFwd().removeNotificationListener(names[j],
ids[j]);
doRemoveListener(names[j],ids[j]);
} catch (Exception eee) {
// strange
}
@ -1240,21 +1236,298 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
final long csn = clientSequenceNumber;
final int mn = maxNotifications;
final long t = timeout;
PrivilegedAction<NotificationResult> action =
new PrivilegedAction<NotificationResult>() {
public NotificationResult run() {
return getServerNotifFwd().fetchNotifs(csn, t, mn);
final PrivilegedExceptionAction<NotificationResult> action =
new PrivilegedExceptionAction<NotificationResult>() {
public NotificationResult run() throws IOException {
return doFetchNotifs(csn, t, mn);
}
};
if (acc == null)
return action.run();
else
return AccessController.doPrivileged(action, acc);
try {
if (acc == null)
return action.run();
else
return AccessController.doPrivileged(action, acc);
} catch (IOException x) {
throw x;
} catch (RuntimeException x) {
throw x;
} catch (Exception x) {
// should not happen
throw new UndeclaredThrowableException(x);
}
} finally {
serverCommunicatorAdmin.rspOutgoing();
}
}
/**
* This is an abstraction class that let us use the legacy
* ServerNotifForwarder and the new EventClientDelegateMBean
* indifferently.
**/
private static interface SubscriptionManager {
public void removeNotificationListener(ObjectName name, Integer id)
throws InstanceNotFoundException, ListenerNotFoundException, IOException;
public void removeNotificationListener(ObjectName name, Integer[] ids)
throws Exception;
public NotificationResult fetchNotifications(long csn, long timeout, int maxcount)
throws IOException;
public Integer addNotificationListener(ObjectName name, NotificationFilter filter)
throws InstanceNotFoundException, IOException;
public void terminate()
throws IOException;
}
/**
* A SubscriptionManager that uses a ServerNotifForwarder.
**/
private static class LegacySubscriptionManager implements SubscriptionManager {
private final ServerNotifForwarder forwarder;
LegacySubscriptionManager(ServerNotifForwarder forwarder) {
this.forwarder = forwarder;
}
public void removeNotificationListener(ObjectName name, Integer id)
throws InstanceNotFoundException, ListenerNotFoundException,
IOException {
forwarder.removeNotificationListener(name,id);
}
public void removeNotificationListener(ObjectName name, Integer[] ids)
throws Exception {
forwarder.removeNotificationListener(name,ids);
}
public NotificationResult fetchNotifications(long csn, long timeout, int maxcount) {
return forwarder.fetchNotifs(csn,timeout,maxcount);
}
public Integer addNotificationListener(ObjectName name,
NotificationFilter filter)
throws InstanceNotFoundException, IOException {
return forwarder.addNotificationListener(name,filter);
}
public void terminate() {
forwarder.terminate();
}
}
/**
* A SubscriptionManager that uses an EventClientDelegateMBean.
**/
private static class EventSubscriptionManager
implements SubscriptionManager {
private final MBeanServer mbeanServer;
private final EventClientDelegateMBean delegate;
private final NotificationAccessController notifAC;
private final boolean checkNotificationEmission;
private final String clientId;
private final String connectionId;
EventSubscriptionManager(
MBeanServer mbeanServer,
EventClientDelegateMBean delegate,
Map<String, ?> env,
String clientId,
String connectionId) {
this.mbeanServer = mbeanServer;
this.delegate = delegate;
this.notifAC = EnvHelp.getNotificationAccessController(env);
this.checkNotificationEmission =
EnvHelp.computeBooleanFromString(
env, "jmx.remote.x.check.notification.emission", false);
this.clientId = clientId;
this.connectionId = connectionId;
}
@SuppressWarnings("serial") // no serialVersionUID
private class AccessControlFilter implements NotificationFilter {
private final NotificationFilter wrapped;
private final ObjectName name;
AccessControlFilter(ObjectName name, NotificationFilter wrapped) {
this.name = name;
this.wrapped = wrapped;
}
public boolean isNotificationEnabled(Notification notification) {
try {
if (checkNotificationEmission) {
ServerNotifForwarder.checkMBeanPermission(
mbeanServer, name, "addNotificationListener");
}
notifAC.fetchNotification(
connectionId, name, notification, getSubject());
return (wrapped == null) ? true :
wrapped.isNotificationEnabled(notification);
} catch (InstanceNotFoundException e) {
return false;
} catch (SecurityException e) {
return false;
}
}
}
public Integer addNotificationListener(
ObjectName name, NotificationFilter filter)
throws InstanceNotFoundException, IOException {
if (notifAC != null) {
notifAC.addNotificationListener(connectionId, name, getSubject());
filter = new AccessControlFilter(name, filter);
}
try {
return delegate.addListener(clientId,name,filter);
} catch (EventClientNotFoundException x) {
throw new IOException("Unknown clientId: "+clientId,x);
}
}
public void removeNotificationListener(ObjectName name, Integer id)
throws InstanceNotFoundException, ListenerNotFoundException,
IOException {
if (notifAC != null)
notifAC.removeNotificationListener(connectionId, name, getSubject());
try {
delegate.removeListenerOrSubscriber(clientId,id);
} catch (EventClientNotFoundException x) {
throw new IOException("Unknown clientId: "+clientId,x);
}
}
public void removeNotificationListener(ObjectName name, Integer[] ids)
throws InstanceNotFoundException, ListenerNotFoundException,
IOException {
if (notifAC != null)
notifAC.removeNotificationListener(connectionId, name, getSubject());
try {
for (Integer id : ids)
delegate.removeListenerOrSubscriber(clientId,id);
} catch (EventClientNotFoundException x) {
throw new IOException("Unknown clientId: "+clientId,x);
}
}
public NotificationResult fetchNotifications(long csn, long timeout,
int maxcount)
throws IOException {
try {
// For some reason the delegate doesn't accept a negative
// sequence number. However legacy clients will always call
// fetchNotifications with a negative sequence number, when
// they call it for the first time.
// In that case, we will use 0 instead.
//
return delegate.fetchNotifications(
clientId, Math.max(csn, 0), maxcount, timeout);
} catch (EventClientNotFoundException x) {
throw new IOException("Unknown clientId: "+clientId,x);
}
}
public void terminate()
throws IOException {
try {
delegate.removeClient(clientId);
} catch (EventClientNotFoundException x) {
throw new IOException("Unknown clientId: "+clientId,x);
}
}
private static Subject getSubject() {
return Subject.getSubject(AccessController.getContext());
}
}
/**
* Creates a SubscriptionManager that uses either the legacy notifications
* mechanism (ServerNotifForwarder) or the new event service
* (EventClientDelegateMBean) depending on which option was passed in
* the connector's map.
**/
private SubscriptionManager createSubscriptionManager()
throws IOException {
if (EnvHelp.delegateToEventService(env) &&
mbeanServer.isRegistered(EventClientDelegate.OBJECT_NAME)) {
final EventClientDelegateMBean mbean =
JMX.newMBeanProxy(mbeanServer,
EventClientDelegate.OBJECT_NAME,
EventClientDelegateMBean.class);
String clientId;
try {
clientId =
mbean.addClient(
FetchingEventForwarder.class.getName(),
new Object[] {EnvHelp.getNotifBufferSize(env)},
new String[] {int.class.getName()});
} catch (Exception e) {
if (e instanceof IOException)
throw (IOException) e;
else
throw new IOException(e);
}
// we're going to call remove client...
try {
mbean.lease(clientId, Long.MAX_VALUE);
} catch (EventClientNotFoundException x) {
throw new IOException("Unknown clientId: "+clientId,x);
}
return new EventSubscriptionManager(mbeanServer, mbean, env,
clientId, connectionId);
} else {
final ServerNotifForwarder serverNotifForwarder =
new ServerNotifForwarder(mbeanServer,
env,
rmiServer.getNotifBuffer(),
connectionId);
return new LegacySubscriptionManager(serverNotifForwarder);
}
}
/**
* Lazy creation of a SubscriptionManager.
**/
private synchronized SubscriptionManager getSubscriptionManager()
throws IOException {
// Lazily created when first use. Mainly when
// addNotificationListener is first called.
if (subscriptionManager == null) {
subscriptionManager = createSubscriptionManager();
}
return subscriptionManager;
}
// calls SubscriptionManager.
private void doRemoveListener(ObjectName name, Integer id)
throws InstanceNotFoundException, ListenerNotFoundException,
IOException {
getSubscriptionManager().removeNotificationListener(name,id);
}
// calls SubscriptionManager.
private void doRemoveListener(ObjectName name, Integer[] ids)
throws Exception {
getSubscriptionManager().removeNotificationListener(name,ids);
}
// calls SubscriptionManager.
private NotificationResult doFetchNotifs(long csn, long timeout, int maxcount)
throws IOException {
return getSubscriptionManager().fetchNotifications(csn, timeout, maxcount);
}
// calls SubscriptionManager.
private Integer doAddListener(ObjectName name, NotificationFilter filter)
throws InstanceNotFoundException, IOException {
return getSubscriptionManager().addNotificationListener(name,filter);
}
/**
* <p>Returns a string representation of this object. In general,
* the <code>toString</code> method returns a string that
@ -1313,16 +1586,6 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
// private methods
//------------------------------------------------------------------------
private ClassLoaderRepository getClassLoaderRepository() {
return
AccessController.doPrivileged(
new PrivilegedAction<ClassLoaderRepository>() {
public ClassLoaderRepository run() {
return mbeanServer.getClassLoaderRepository();
}
});
}
private ClassLoader getClassLoader(final ObjectName name)
throws InstanceNotFoundException {
try {
@ -1482,9 +1745,8 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
return null;
case ADD_NOTIFICATION_LISTENERS:
return getServerNotifFwd().addNotificationListener(
(ObjectName)params[0],
(NotificationFilter)params[1]);
return doAddListener((ObjectName)params[0],
(NotificationFilter)params[1]);
case ADD_NOTIFICATION_LISTENER_OBJECTNAME:
mbeanServer.addNotificationListener((ObjectName)params[0],
@ -1494,9 +1756,7 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
return null;
case REMOVE_NOTIFICATION_LISTENER:
getServerNotifFwd().removeNotificationListener(
(ObjectName)params[0],
(Integer[])params[1]);
doRemoveListener((ObjectName)params[0],(Integer[])params[1]);
return null;
case REMOVE_NOTIFICATION_LISTENER_OBJECTNAME:
@ -1709,23 +1969,21 @@ public class RMIConnectionImpl implements RMIConnection, Unreferenced {
private final static int
REMOVE_NOTIFICATION_LISTENER = 19;
private final static int
REMOVE_NOTIFICATION_LISTENER_FILTER_HANDBACK = 20;
REMOVE_NOTIFICATION_LISTENER_OBJECTNAME = 20;
private final static int
REMOVE_NOTIFICATION_LISTENER_OBJECTNAME = 21;
REMOVE_NOTIFICATION_LISTENER_OBJECTNAME_FILTER_HANDBACK = 21;
private final static int
REMOVE_NOTIFICATION_LISTENER_OBJECTNAME_FILTER_HANDBACK = 22;
SET_ATTRIBUTE = 22;
private final static int
SET_ATTRIBUTE = 23;
SET_ATTRIBUTES = 23;
private final static int
SET_ATTRIBUTES = 24;
private final static int
UNREGISTER_MBEAN = 25;
UNREGISTER_MBEAN = 24;
// SERVER NOTIFICATION
//--------------------
private ServerNotifForwarder serverNotifForwarder;
private Map env;
private SubscriptionManager subscriptionManager;
private Map<String, ?> env;
// TRACES & DEBUG
//---------------

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;
@ -280,8 +292,8 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
// client-side environment property is set to "true".
//
boolean checkStub = EnvHelp.computeBooleanFromString(
usemap,
"jmx.remote.x.check.stub");
usemap,
"jmx.remote.x.check.stub",false);
if (checkStub) checkStub(stub, rmiServerImplStubClass);
// Connect IIOP Stub if needed.
@ -318,6 +330,8 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
//
connectionId = getConnectionId();
eventServiceEnabled = EnvHelp.eventServiceEnabled(env);
Notification connectedNotif =
new JMXConnectionNotification(JMXConnectionNotification.OPENED,
this,
@ -327,6 +341,8 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
null);
sendNotification(connectedNotif);
// whether or not event service
if (tracing) logger.trace("connect",idstr + " done...");
} catch (IOException e) {
if (tracing)
@ -378,13 +394,42 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
throw new IOException("Not connected");
}
MBeanServerConnection mbsc = rmbscMap.get(delegationSubject);
if (mbsc != null)
return mbsc;
MBeanServerConnection rmbsc = rmbscMap.get(delegationSubject);
if (rmbsc != null) {
return rmbsc;
}
mbsc = new RemoteMBeanServerConnection(delegationSubject);
rmbscMap.put(delegationSubject, mbsc);
return mbsc;
rmbsc = new RemoteMBeanServerConnection(delegationSubject);
if (eventServiceEnabled) {
EventClientDelegateMBean ecd = JMX.newMBeanProxy(
rmbsc, EventClientDelegateMBean.OBJECT_NAME,
EventClientDelegateMBean.class);
EventClient ec = new EventClient(ecd, null, defaultExecutor(), null,
EventClient.DEFAULT_LEASE_TIMEOUT);
rmbsc = EventConnection.Factory.make(rmbsc, ec);
ec.addEventClientListener(
lostNotifListener, null, null);
}
rmbscMap.put(delegationSubject, rmbsc);
return rmbsc;
}
private static Executor defaultExecutor() {
PerThreadGroupPool.Create<ThreadPoolExecutor> create =
new PerThreadGroupPool.Create<ThreadPoolExecutor>() {
public ThreadPoolExecutor createThreadPool(ThreadGroup group) {
ThreadFactory daemonThreadFactory = new DaemonThreadFactory(
"RMIConnector listener dispatch %d");
ThreadPoolExecutor exec = new ThreadPoolExecutor(
1, 10, 1, TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(),
daemonThreadFactory);
exec.allowCoreThreadTimeOut(true);
return exec;
}
};
return listenerDispatchThreadPool.getThreadPoolExecutor(create);
}
public void
@ -466,6 +511,17 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
communicatorAdmin.terminate();
}
// close all EventClient
for (MBeanServerConnection rmbsc : rmbscMap.values()) {
if (rmbsc instanceof EventConnection) {
try {
((EventConnection)rmbsc).getEventClient().close();
} catch (Exception e) {
// OK
}
}
}
if (rmiNotifClient != null) {
try {
rmiNotifClient.terminate();
@ -592,18 +648,19 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
}
if (debug) logger.debug("addListenersWithSubjects","registered "
+ listenerIDs.length + " listener(s)");
+ ((listenerIDs==null)?0:listenerIDs.length)
+ " listener(s)");
return listenerIDs;
}
//--------------------------------------------------------------------
// Implementation of MBeanServerConnection
//--------------------------------------------------------------------
private class RemoteMBeanServerConnection
implements MBeanServerConnection {
private class RemoteMBeanServerConnection implements MBeanServerConnection {
private Subject delegationSubject;
public EventClient eventClient = null;
public RemoteMBeanServerConnection() {
this(null);
}
@ -1205,6 +1262,7 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
IOException {
final boolean debug = logger.debugOn();
if (debug)
logger.debug("addNotificationListener" +
"(ObjectName,NotificationListener,"+
@ -1226,8 +1284,9 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
public void removeNotificationListener(ObjectName name,
NotificationListener listener)
throws InstanceNotFoundException,
ListenerNotFoundException,
IOException {
ListenerNotFoundException,
IOException {
final boolean debug = logger.debugOn();
if (debug) logger.debug("removeNotificationListener"+
@ -1804,6 +1863,26 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
terminated = false;
connectionBroadcaster = new NotificationBroadcasterSupport();
lostNotifListener =
new NotificationListener() {
public void handleNotification(Notification n, Object hb) {
if (n != null && EventClient.NOTIFS_LOST.equals(n.getType())) {
Long lost = (Long)n.getUserData();
final String msg =
"May have lost up to " + lost +
" notification" + (lost.longValue() == 1 ? "" : "s");
sendNotification(new JMXConnectionNotification(
JMXConnectionNotification.NOTIFS_LOST,
RMIConnector.this,
connectionId,
clientNotifCounter++,
msg,
lost));
}
}
};
}
//--------------------------------------------------------------------
@ -2528,6 +2607,11 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
private transient ClientCommunicatorAdmin communicatorAdmin;
private boolean eventServiceEnabled;
// private transient EventRelay eventRelay;
private transient NotificationListener lostNotifListener;
/**
* A static WeakReference to an {@link org.omg.CORBA.ORB ORB} to
* connect unconnected stubs.
@ -2546,4 +2630,7 @@ public class RMIConnector implements JMXConnector, Serializable, JMXAddressable
private static String strings(final String[] strs) {
return objects(strs);
}
private static final PerThreadGroupPool<ThreadPoolExecutor> listenerDispatchThreadPool =
PerThreadGroupPool.make();
}

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

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

File diff suppressed because it is too large Load Diff

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,364 @@
/*/*
* Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
/*
* @test SharingThreadTest.java 1.3 08/01/22
* @bug 5108776
* @summary Basic test for EventClient to see internal thread management.
* @author Shanliang JIANG
* @run clean SharingThreadTest
* @run build SharingThreadTest
* @run main SharingThreadTest
*/
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.event.EventClient;
import javax.management.event.EventClientDelegate;
import javax.management.event.EventClientDelegateMBean;
import javax.management.event.FetchingEventRelay;
import javax.management.event.RMIPushEventRelay;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
public class SharingThreadTest {
private static MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer();
private static List<Notification> notifList = new ArrayList<Notification>();
private static ObjectName emitter;
private static NotificationEmitter emitterImpl;
private static JMXServiceURL url;
private static JMXConnectorServer server;
private static JMXConnector conn;
private static int toSend = 10;
private static long sequenceNumber = 0;
private static final long bigWaiting = 6000;
private static int counter = 0;
private static int jobs = 10;
private static int endedJobs = 0;
private static Executor sharedExecutor = new ThreadPoolExecutor(0, 1, 1000,
TimeUnit.MILLISECONDS, new ArrayBlockingQueue(jobs));
//Executors.newFixedThreadPool(1);
public static void main(String[] args) throws Exception {
System.out.println(">>> Test on sharing threads for multiple EventClient.");
// for 1.5
if (System.getProperty("java.version").startsWith("1.5") &&
!mbeanServer.isRegistered(EventClientDelegateMBean.OBJECT_NAME)) {
System.out.print("Working on "+System.getProperty("java.version")+
" register "+EventClientDelegateMBean.OBJECT_NAME);
mbeanServer.registerMBean(EventClientDelegate.
getEventClientDelegate(mbeanServer),
EventClientDelegateMBean.OBJECT_NAME);
sharedExecutor = new ThreadPoolExecutor(1, 1, 1000,
TimeUnit.MILLISECONDS, new ArrayBlockingQueue(jobs));
}
emitter = new ObjectName("Default:name=NotificationEmitter");
emitterImpl = new NotificationEmitter();
mbeanServer.registerMBean(emitterImpl, emitter);
String[] types = new String[]{"PushEventRelay", "FetchingEventRelay"};
String[] protos = new String[]{"rmi", "iiop", "jmxmp"};
for (String prot : protos) {
url = new JMXServiceURL(prot, null, 0);
try {
server =
JMXConnectorServerFactory.newJMXConnectorServer(url,
null, mbeanServer);
server.start();
} catch (Exception e) {
System.out.println(">>> Skip "+prot+", not support.");
continue;
}
url = server.getAddress();
// noise
Thread noise = new Thread(new Runnable() {
public void run() {
while (true) {
emitterImpl.sendNotif(1, null);
try {
Thread.sleep(10);
} catch (Exception e) {
// OK
}
}
}
});
noise.setDaemon(true);
noise.start();
Thread[] threads = new Thread[jobs];
try {
for (String type: types) {
System.out.println("\n\n>>> Testing "+type+" on "+url+" ...");
newConn();
for (int i=0; i<jobs; i++) {
threads[i] = new Thread(new Job(type));
threads[i].setDaemon(true);
threads[i].start();
}
// to wait
long toWait = bigWaiting*jobs;
long stopTime = System.currentTimeMillis() + toWait;
synchronized(SharingThreadTest.class) {
while (endedJobs < jobs && toWait > 0) {
SharingThreadTest.class.wait(toWait);
toWait = stopTime - System.currentTimeMillis();
}
}
if (endedJobs != jobs) {
throw new RuntimeException("Need to set bigger waiting timeout?");
}
endedJobs = 0;
conn.close();
System.out.println(">>> Testing "+type+" on "+url+" ... done");
}
} finally {
server.stop();
}
}
}
public static class Job implements Runnable {
public Job(String type) {
this.type = type;
}
public void run() {
try {
test(type);
synchronized(SharingThreadTest.class) {
endedJobs++;
if (endedJobs>=jobs) {
SharingThreadTest.class.notify();
}
}
} catch (RuntimeException re) {
throw re;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private final String type;
}
private static void test(String type) throws Exception {
String id = getId();
Listener listener = new Listener(id);
Filter filter = new Filter(id);
//newConn();
EventClient ec = newEventClient(type);
System.out.println(">>> ("+id+") To receive notifications "+toSend);
ec.addNotificationListener(emitter,
listener, filter, null);
emitterImpl.sendNotif(toSend, id);
listener.waitNotifs(bigWaiting, toSend);
if (listener.received != toSend) {
throw new RuntimeException(">>> ("+id+") Expected to receive: "
+toSend+", but got: "+listener.received);
}
// not close the EventClient to keep using thread
//ec.close();
}
//--------------------------
// private classes
//--------------------------
private static class Listener implements NotificationListener {
public Listener(String id) {
this.id = id;
}
public void handleNotification(Notification notif, Object handback) {
if (!id.equals(notif.getUserData())) {
System.out.println("("+id+") Filter error, my id is: "+id+
", but got "+notif.getUserData());
System.exit(1);
}
System.out.println("("+id+") received "+notif.getSequenceNumber());
synchronized (notifList) {
received++;
if (sequenceNB < 0) {
sequenceNB = notif.getSequenceNumber();
} else if(++sequenceNB != notif.getSequenceNumber()) {
throw new RuntimeException("Wrong sequence number, expecte: "
+sequenceNB+", but got: "+notif.getSequenceNumber());
}
if (received >= toSend) {
this.notify();
}
}
}
public void waitNotifs(long timeout, int nb) throws Exception {
long toWait = timeout;
long stopTime = System.currentTimeMillis() + timeout;
synchronized(this) {
while (received < nb && toWait > 0) {
this.wait(toWait);
toWait = stopTime - System.currentTimeMillis();
}
}
}
public void clear() {
synchronized(this) {
received = 0;
sequenceNB = -1;
}
}
private String id;
private int received = 0;
private long sequenceNB = -1;
}
private static class Filter implements NotificationFilter {
public Filter(String id) {
this.id = id;
}
public boolean isNotificationEnabled(Notification n) {
return id.equals(n.getUserData());
}
private String id;
}
private static NotificationListener dummyListener = new NotificationListener() {
public void handleNotification(Notification notif, Object handback) {
}
};
public static class NotificationEmitter extends NotificationBroadcasterSupport
implements NotificationEmitterMBean {
/**
* Send Notification objects.
*
* @param nb The number of notifications to send
*/
public void sendNotif(int nb, String userData) {
new Thread(new SendJob(nb, userData)).start();
}
private class SendJob implements Runnable {
public SendJob(int nb, String userData) {
this.nb = nb;
this.userData = userData;
}
public void run() {
if (userData != null) {
System.out.println(">>> ("+userData+") sending "+nb);
}
for (int i = 0; i<nb; i++) {
Notification notif = new Notification(myType, emitter,
sequenceNumber++);
notif.setUserData(userData);
sendNotification(notif);
Thread.yield();
try {
Thread.sleep(1);
} catch (Exception e) {}
}
if (userData != null) {
System.out.println(">>> ("+userData+") sending done");
}
}
private int nb;
private String userData;
}
private final String myType = "notification.my_notification";
}
public interface NotificationEmitterMBean {
public void sendNotif(int nb, String userData);
}
private static void newConn() throws IOException {
conn = JMXConnectorFactory.connect(url);
}
private static EventClient newEventClient(String type) throws Exception {
EventClientDelegateMBean proxy =
EventClientDelegate.getProxy(conn.getMBeanServerConnection());
if (type.equals("PushEventRelay")) {
return new EventClient(proxy,
new RMIPushEventRelay(proxy), sharedExecutor, null, 600);
} else if (type.equals("FetchingEventRelay")) {
return new EventClient(proxy,
new FetchingEventRelay(proxy,
FetchingEventRelay.DEFAULT_BUFFER_SIZE,
10,
FetchingEventRelay.DEFAULT_MAX_NOTIFICATIONS,
sharedExecutor),
null, null, 600);
} else {
throw new RuntimeException("Wrong event client type: "+type);
}
}
private static String getId() {
synchronized(SharingThreadTest.class) {
return String.valueOf(counter++);
}
}
}

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

View File

@ -31,11 +31,16 @@
* @run main CloseServerTest
*/
import com.sun.jmx.remote.util.EnvHelp;
import java.net.MalformedURLException;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.management.*;
import javax.management.remote.*;
import javax.management.remote.rmi.RMIConnectorServer;
public class CloseServerTest {
private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
@ -131,40 +136,54 @@ public class CloseServerTest {
server.stop();
// with a client listener, but close the server first
System.out.println(">>> Open, start a server, create a client, add a listener, close the server then the client.");
server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
server.start();
List<Map<String, String>> envs = Arrays.asList(
Collections.singletonMap(
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false"),
Collections.singletonMap(
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "true"));
addr = server.getAddress();
client = JMXConnectorFactory.newJMXConnector(addr, null);
client.connect(null);
for (Map<String, String> env : envs) {
System.out.println(
">>>>>>>> " + RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE +
" = " + env.get(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE));
mserver = client.getMBeanServerConnection();
mserver.addNotificationListener(delegateName, dummyListener, null, null);
// with a client listener, but close the server first
System.out.println(">>> Open, start a server, create a client, " +
"add a listener, close the server then the client.");
server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
server.start();
server.stop();
addr = server.getAddress();
client = JMXConnectorFactory.newJMXConnector(addr, null);
client.connect(null);
mserver = client.getMBeanServerConnection();
mserver.addNotificationListener(delegateName, dummyListener, null, null);
server.stop();
try {
client.close();
} catch (Exception e) {
// ok, it is because the server has been closed.
}
// with a client listener, but close the client first
System.out.println(">>> Open, start a server, create a client, " +
"add a listener, close the client then the server.");
server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
server.start();
addr = server.getAddress();
client = JMXConnectorFactory.newJMXConnector(addr, null);
client.connect(null);
mserver = client.getMBeanServerConnection();
mserver.addNotificationListener(delegateName, dummyListener, null, null);
try {
client.close();
} catch (Exception e) {
// ok, it is because the server has been closed.
server.stop();
}
// with a client listener, but close the client first
System.out.println(">>> Open, start a server, create a client, add a listener, close the client then the server.");
server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
server.start();
addr = server.getAddress();
client = JMXConnectorFactory.newJMXConnector(addr, null);
client.connect(null);
mserver = client.getMBeanServerConnection();
mserver.addNotificationListener(delegateName, dummyListener, null, null);
client.close();
server.stop();
} catch (MalformedURLException e) {
System.out.println(">>> Skipping unsupported URL " + u);
return true;

View File

@ -37,6 +37,7 @@ import java.util.HashMap;
import javax.management.*;
import javax.management.remote.*;
import javax.management.remote.rmi.RMIConnectorServer;
public class DeadLockTest {
private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
@ -72,6 +73,9 @@ public class DeadLockTest {
// disable the client ping
env.put("jmx.remote.x.client.connection.check.period", "0");
// ensure we are not internally using the Event Service on the server
env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
try {
u = new JMXServiceURL(proto, null, 0);
server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);

View File

@ -50,6 +50,8 @@ import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import com.sun.jmx.remote.util.EnvHelp;
import java.util.Collections;
import javax.management.remote.rmi.RMIConnectorServer;
public class IdleTimeoutTest {
public static void main(String[] args) throws Exception {
@ -88,8 +90,13 @@ public class IdleTimeoutTest {
private static long getIdleTimeout(MBeanServer mbs, JMXServiceURL url)
throws Exception {
// If the connector server is using the Event Service, then connections
// never time out. This is by design.
Map<String, String> env =
Collections.singletonMap(
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
JMXConnectorServer server =
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
server.start();
try {
url = server.getAddress();
@ -164,6 +171,7 @@ public class IdleTimeoutTest {
Map idleMap = new HashMap();
idleMap.put(EnvHelp.SERVER_CONNECTION_TIMEOUT, new Long(timeout));
idleMap.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
JMXConnectorServer server =
JMXConnectorServerFactory.newJMXConnectorServer(url,idleMap,mbs);

View File

@ -35,6 +35,8 @@
import java.net.MalformedURLException;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import javax.management.MBeanServerFactory;
import javax.management.MBeanServer;
import javax.management.ObjectName;
@ -47,12 +49,14 @@ import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.rmi.RMIConnectorServer;
/**
* VM shutdown hook. Test that the hook is called less than 5 secs
* after expected exit.
*/
class TimeChecker extends Thread {
@Override
public void run() {
System.out.println("shutdown hook called");
long elapsedTime =
@ -81,12 +85,15 @@ public class RMIExitTest {
public static void main(String[] args) {
System.out.println("Start test");
Runtime.getRuntime().addShutdownHook(new TimeChecker());
test();
test(false);
test(true);
exitStartTime = System.currentTimeMillis();
System.out.println("End test");
}
private static void test() {
private static void test(boolean eventService) {
System.out.println(
"---testing with" + (eventService ? "" : "out") + " Event Service");
try {
JMXServiceURL u = new JMXServiceURL("rmi", null, 0);
JMXConnectorServer server;
@ -105,8 +112,11 @@ public class RMIExitTest {
}
};
Map<String, String> env = Collections.singletonMap(
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
Boolean.toString(eventService));
server = JMXConnectorServerFactory.newJMXConnectorServer(u,
null,
env,
mbs);
server.start();

View File

@ -33,10 +33,10 @@
import java.util.*;
import java.net.MalformedURLException;
import java.io.IOException;
import javax.management.*;
import javax.management.remote.*;
import javax.management.remote.rmi.RMIConnectorServer;
public class ReconnectTest {
private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
@ -48,6 +48,7 @@ public class ReconnectTest {
String timeout = "1000";
env.put("jmx.remote.x.server.connection.timeout", timeout);
env.put("jmx.remote.x.client.connection.check.period", timeout);
env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
}
public static void main(String[] args) throws Exception {

View File

@ -0,0 +1,274 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
import java.util.NoSuchElementException;
import java.util.Random;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerFactory;
import javax.management.remote.IdentityMBeanServerForwarder;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.MBeanServerForwarder;
/*
* @test
* @bug 6218920
* @summary Tests manipulation of MBeanServerForwarder chains.
* @author Eamonn McManus
*/
import javax.management.remote.rmi.RMIConnectorServer;
public class ForwarderChainTest {
private static final TestMBeanServerForwarder[] forwarders =
new TestMBeanServerForwarder[10];
static {
for (int i = 0; i < forwarders.length; i++)
forwarders[i] = new TestMBeanServerForwarder(i);
}
private static class TestMBeanServerForwarder
extends IdentityMBeanServerForwarder {
private final int index;
volatile int defaultDomainCount;
TestMBeanServerForwarder(int index) {
this.index = index;
}
@Override
public String getDefaultDomain() {
defaultDomainCount++;
return super.getDefaultDomain();
}
@Override
public String toString() {
return "forwarders[" + index + "]";
}
}
private static String failure;
public static void main(String[] args) throws Exception {
System.out.println("===Test with newly created, unattached server===");
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///");
JMXConnectorServer cs = new RMIConnectorServer(url, null);
test(cs, null);
System.out.println("===Test with server attached to MBS===");
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
cs = new RMIConnectorServer(url, null, mbs);
test(cs, mbs);
System.out.println("===Remove any leftover forwarders===");
while (cs.getSystemMBeanServer() instanceof MBeanServerForwarder) {
MBeanServerForwarder mbsf =
(MBeanServerForwarder) cs.getSystemMBeanServer();
cs.removeMBeanServerForwarder(mbsf);
}
expectChain(cs, "U", mbs);
System.out.println("===Ensure forwarders are called===");
cs.setMBeanServerForwarder(forwarders[0]);
cs.setSystemMBeanServerForwarder(forwarders[1]);
expectChain(cs, "1U0", mbs);
cs.start();
if (forwarders[0].defaultDomainCount != 0 ||
forwarders[1].defaultDomainCount != 0) {
fail("defaultDomainCount not zero");
}
JMXServiceURL addr = cs.getAddress();
JMXConnector cc = JMXConnectorFactory.connect(addr);
MBeanServerConnection mbsc = cc.getMBeanServerConnection();
mbsc.getDefaultDomain();
cc.close();
cs.stop();
for (boolean system : new boolean[] {false, true}) {
TestMBeanServerForwarder mbsf = system ? forwarders[1] : forwarders[0];
if (mbsf.defaultDomainCount != 1) {
fail((system ? "System" : "User") + " forwarder called " +
mbsf.defaultDomainCount + " times");
}
}
if (failure == null)
System.out.println("TEST PASSED");
else
throw new Exception("TEST FAILED: " + failure);
}
private static void test(JMXConnectorServer cs, MBeanServer end) {
// A newly-created connector server might have system forwarders,
// so get rid of those.
while (cs.getSystemMBeanServer() != cs.getMBeanServer())
cs.removeMBeanServerForwarder((MBeanServerForwarder) cs.getSystemMBeanServer());
expectChain(cs, "U", end);
System.out.println("Add a user forwarder");
cs.setMBeanServerForwarder(forwarders[0]);
expectChain(cs, "U0", end);
System.out.println("Add another user forwarder");
cs.setMBeanServerForwarder(forwarders[1]);
expectChain(cs, "U10", end);
System.out.println("Add a system forwarder");
cs.setSystemMBeanServerForwarder(forwarders[2]);
expectChain(cs, "2U10", end);
System.out.println("Add another user forwarder");
cs.setMBeanServerForwarder(forwarders[3]);
expectChain(cs, "2U310", end);
System.out.println("Add another system forwarder");
cs.setSystemMBeanServerForwarder(forwarders[4]);
expectChain(cs, "42U310", end);
System.out.println("Remove the first user forwarder");
cs.removeMBeanServerForwarder(forwarders[3]);
expectChain(cs, "42U10", end);
System.out.println("Remove the last user forwarder");
cs.removeMBeanServerForwarder(forwarders[0]);
expectChain(cs, "42U1", end);
System.out.println("Remove the first system forwarder");
cs.removeMBeanServerForwarder(forwarders[4]);
expectChain(cs, "2U1", end);
System.out.println("Remove the last system forwarder");
cs.removeMBeanServerForwarder(forwarders[2]);
expectChain(cs, "U1", end);
System.out.println("Remove the last forwarder");
cs.removeMBeanServerForwarder(forwarders[1]);
expectChain(cs, "U", end);
System.out.println("---Doing random manipulations---");
// In this loop we pick one of the forwarders at random each time.
// If it is already in the chain, then we remove it. If it is not
// in the chain, then we do one of three things: try to remove it
// (expecting an exception); add it to the user chain; or add it
// to the system chain.
// A subtle point is that if there is no MBeanServer then
// cs.setMBeanServerForwarder(mbsf) does not change mbsf.getMBeanServer().
// Since we're recycling a random forwarder[i], we explicitly
// call mbsf.setMBeanServer(null) in this case.
String chain = "U";
Random r = new Random();
for (int i = 0; i < 50; i++) {
int fwdi = r.nextInt(10);
MBeanServerForwarder mbsf = forwarders[fwdi];
char c = (char) ('0' + fwdi);
int ci = chain.indexOf(c);
if (ci >= 0) {
System.out.println("Remove " + c);
cs.removeMBeanServerForwarder(mbsf);
chain = chain.substring(0, ci) + chain.substring(ci + 1);
} else {
switch (r.nextInt(3)) {
case 0: { // try to remove it
try {
System.out.println("Try to remove absent " + c);
cs.removeMBeanServerForwarder(mbsf);
fail("Remove succeeded but should not have");
return;
} catch (NoSuchElementException e) {
}
break;
}
case 1: { // add it to the user chain
System.out.println("Add " + c + " to user chain");
if (cs.getMBeanServer() == null)
mbsf.setMBeanServer(null);
cs.setMBeanServerForwarder(mbsf);
int postu = chain.indexOf('U') + 1;
chain = chain.substring(0, postu) + c +
chain.substring(postu);
break;
}
case 2: { // add it to the system chain
System.out.println("Add " + c + " to system chain");
if (cs.getSystemMBeanServer() == null)
mbsf.setMBeanServer(null);
cs.setSystemMBeanServerForwarder(mbsf);
chain = c + chain;
break;
}
}
}
expectChain(cs, chain, end);
}
}
/*
* Check that the forwarder chain has the expected contents. The forwarders
* are encoded as a string. For example, "12U34" means that the system
* chain contains forwarders[1] followed by forwarders[2], and the user
* chain contains forwarders[3] followed by forwarders[4]. Since the
* user chain is attached to the end of the system chain, another way to
* look at this is that the U marks the transition from one to the other.
*
* After traversing the chains, we should be pointing at "end".
*/
private static void expectChain(
JMXConnectorServer cs, String chain, MBeanServer end) {
System.out.println("...expected chain: " + chain);
MBeanServer curr = cs.getSystemMBeanServer();
int i = 0;
while (i < chain.length()) {
char c = chain.charAt(i);
if (c == 'U') {
if (cs.getMBeanServer() != curr) {
fail("User chain should have started here: " + curr);
return;
}
} else {
int fwdi = c - '0';
MBeanServerForwarder forwarder = forwarders[fwdi];
if (curr != forwarder) {
fail("Expected forwarder " + c + " here: " + curr);
return;
}
curr = ((MBeanServerForwarder) curr).getMBeanServer();
}
i++;
}
if (curr != end) {
fail("End of chain is " + curr + ", should be " + end);
return;
}
System.out.println("...OK");
}
private static void fail(String msg) {
System.out.println("FAILED: " + msg);
failure = msg;
}
}

View File

@ -0,0 +1,177 @@
/*
* Copyright 2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.management.MBeanServer;
import javax.management.event.EventClientDelegate;
import javax.management.remote.JMXConnectorServer;
/*
* @test
* @bug 6663757
* @summary Tests standard MBeanServerForwarders introduced by connector server
* options.
* @author Eamonn McManus
*/
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.MBeanServerForwarder;
public class StandardForwardersTest {
private static String failure;
private static class Forwarder {
private final String attribute;
private final boolean defaultEnabled;
private final Class<?> expectedClass;
public Forwarder(String attribute, boolean defaultEnabled,
Class<?> expectedClass) {
this.attribute = attribute;
this.defaultEnabled = defaultEnabled;
this.expectedClass = expectedClass;
}
}
private static enum Status {DISABLED, ENABLED, DEFAULT}
public static void main(String[] args) throws Exception {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
MBeanServerForwarder ecdFwd =
EventClientDelegate.newForwarder();
Forwarder ecd = new Forwarder(
JMXConnectorServer.EVENT_CLIENT_DELEGATE_FORWARDER, true,
ecdFwd.getClass());
Forwarder[] forwarders = {ecd};
// Now go through every combination of forwarders. Each forwarder
// may be explicitly enabled, explicitly disabled, or left to its
// default value.
int nStatus = Status.values().length;
int limit = (int) Math.pow(nStatus, forwarders.length);
for (int i = 0; i < limit; i++) {
Status[] status = new Status[forwarders.length];
int ii = i;
for (int j = 0; j < status.length; j++) {
status[j] = Status.values()[ii % nStatus];
ii /= nStatus;
}
Map<String, String> env = new HashMap<String, String>();
String test = "";
for (int j = 0; j < status.length; j++) {
if (!test.equals(""))
test += "; ";
test += forwarders[j].attribute;
switch (status[j]) {
case DISABLED:
test += "=false";
env.put(forwarders[j].attribute, "false");
break;
case ENABLED:
test += "=true";
env.put(forwarders[j].attribute, "true");
break;
case DEFAULT:
test += "=default(" + forwarders[j].defaultEnabled + ")";
break;
}
}
boolean consistent = isConsistent(env);
test += "; (" + (consistent ? "" : "in") + "consistent)";
System.out.println(test);
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///");
try {
JMXConnectorServer cs =
JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
if (!consistent) {
fail("Inconsistent attributes should have been rejected " +
"but were not");
}
checkForwarders(cs, forwarders, status);
} catch (IllegalArgumentException e) {
if (consistent) {
fail("Consistent attributes provoked IllegalArgumentException");
e.printStackTrace(System.out);
}
}
}
if (failure == null)
System.out.println("TEST PASSED");
else
throw new Exception(failure);
}
// Check that the classes of the forwarders in the system chain correspond
// to what we expect given the options we have passed. This check is a bit
// superficial in the sense that a forwarder might be for example a
// SingleMBeanForwarderHandler but that doesn't prove that it is the
// right Single MBean. Nevertheless the test should expose any severe
// wrongness.
//
// The check here makes some assumptions that could become untrue in the
// future. First, it assumes that the forwarders that are added have
// exactly the classes that are in the Forwarder[] array. So for example
// the forwarder for CONTEXT_FORWARDER must be of the same class as an
// explicit call to ClientContext.newContextForwarder. The spec doesn't
// require that - it only requires that the forwarder have the same
// behaviour. The second assumption is that the connector server doesn't
// add any forwarders of its own into the system chain, and again the spec
// doesn't disallow that.
private static void checkForwarders(
JMXConnectorServer cs, Forwarder[] forwarders, Status[] status) {
List<Class<?>> expectedClasses = new ArrayList<Class<?>>();
for (int i = 0; i < forwarders.length; i++) {
if (status[i] == Status.ENABLED ||
(status[i] == Status.DEFAULT && forwarders[i].defaultEnabled))
expectedClasses.add(forwarders[i].expectedClass);
}
MBeanServer stop = cs.getMBeanServer();
List<Class<?>> foundClasses = new ArrayList<Class<?>>();
for (MBeanServer mbs = cs.getSystemMBeanServer(); mbs != stop;
mbs = ((MBeanServerForwarder) mbs).getMBeanServer())
foundClasses.add(mbs.getClass());
if (!expectedClasses.equals(foundClasses)) {
fail("Incorrect forwarder chain: expected " + expectedClasses +
"; found " + foundClasses);
}
}
// env is consistent if either (a) localizer is not enabled or (b)
// localizer is enabled and context is enabled.
// Neither of those is present in this codebase so env is always consistent.
private static boolean isConsistent(Map<String, String> env) {
return true;
}
private static void fail(String why) {
System.out.println("FAILED: " + why);
failure = why;
}
}

View File

@ -44,13 +44,33 @@
We also test objects that are of known class but not serializable.
The test cases are similar.
*/
import java.io.*;
import java.net.*;
import java.util.*;
import javax.management.*;
import javax.management.loading.*;
import javax.management.remote.*;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectOutputStream;
import java.net.MalformedURLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import javax.management.Attribute;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerFactory;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectionNotification;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
import org.omg.CORBA.MARSHAL;
public class MissingClassTest {
private static final int NNOTIFS = 50;
@ -84,7 +104,6 @@ public class MissingClassTest {
serverLoader.loadClass("$ClientUnknown$").newInstance();
final String[] protos = {"rmi", /*"iiop",*/ "jmxmp"};
// iiop commented out until bug 4935098 is fixed
boolean ok = true;
for (int i = 0; i < protos.length; i++) {
try {
@ -105,7 +124,16 @@ public class MissingClassTest {
}
private static boolean test(String proto) throws Exception {
System.out.println("Testing for proto " + proto);
boolean ok = true;
for (boolean eventService : new boolean[] {false, true})
ok &= test(proto, eventService);
return ok;
}
private static boolean test(String proto, boolean eventService)
throws Exception {
System.out.println("Testing for proto " + proto + " with" +
(eventService ? "" : "out") + " Event Service");
boolean ok = true;
@ -117,6 +145,8 @@ public class MissingClassTest {
Map serverMap = new HashMap();
serverMap.put(JMXConnectorServerFactory.DEFAULT_CLASS_LOADER,
serverLoader);
serverMap.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
Boolean.toString(eventService));
// make sure no auto-close at server side
serverMap.put("jmx.remote.x.server.connection.timeout", "888888888");
@ -155,6 +185,8 @@ public class MissingClassTest {
ok = false;
} catch (IOException e) {
Throwable cause = e.getCause();
if (cause instanceof MARSHAL) // see CR 4935098
cause = cause.getCause();
if (cause instanceof ClassNotFoundException) {
System.out.println("Success: got an IOException wrapping " +
"a ClassNotFoundException");
@ -177,6 +209,8 @@ public class MissingClassTest {
ok = false;
} catch (IOException e) {
Throwable wrapped = e.getCause();
if (wrapped instanceof MARSHAL) // see CR 4935098
wrapped = wrapped.getCause();
if (wrapped instanceof ClassNotFoundException) {
System.out.println("Success: got an IOException wrapping " +
"a ClassNotFoundException: " +
@ -228,6 +262,8 @@ public class MissingClassTest {
ok = false;
} catch (IOException e) {
Throwable cause = e.getCause();
if (cause instanceof MARSHAL) // see CR 4935098
cause = cause.getCause();
if (cause instanceof ClassNotFoundException) {
System.out.println("Success: got an IOException " +
"wrapping a ClassNotFoundException");
@ -461,12 +497,13 @@ public class MissingClassTest {
while ((remain = deadline - System.currentTimeMillis()) >= 0) {
synchronized (result) {
if (result.failed
|| (result.knownCount == NNOTIFS
&& result.lostCount == NNOTIFS*2))
|| (result.knownCount >= NNOTIFS
&& result.lostCount >= NNOTIFS*2))
break;
result.wait(remain);
}
}
Thread.sleep(2); // allow any spurious extra notifs to arrive
if (result.failed) {
System.out.println("TEST FAILS: Notification strangeness");
return false;
@ -476,6 +513,11 @@ public class MissingClassTest {
"got NOTIFS_LOST for unknown and " +
"unserializable ones");
return true;
} else if (result.knownCount >= NNOTIFS
|| result.lostCount >= NNOTIFS*2) {
System.out.println("TEST FAILS: Received too many notifs: " +
"known=" + result.knownCount + "; lost=" + result.lostCount);
return false;
} else {
System.out.println("TEST FAILS: Timed out without receiving " +
"all notifs: known=" + result.knownCount +

View File

@ -33,10 +33,12 @@
*/
import java.net.MalformedURLException;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import javax.management.*;
import javax.management.remote.*;
import javax.management.remote.rmi.RMIConnectorServer;
public class AddRemoveTest {
private static final String[] protocols = {"rmi", "iiop", "jmxmp"};
@ -69,9 +71,16 @@ public class AddRemoveTest {
}
}
private static boolean test(String proto)
private static boolean test(String proto) throws Exception {
boolean ok = test(proto, false);
ok &= test(proto, true);
return ok;
}
private static boolean test(String proto, boolean eventService)
throws Exception {
System.out.println(">>> Test for protocol " + proto);
System.out.println(">>> Test for protocol " + proto + " with" +
(eventService ? "" : "out") + " event service");
JMXServiceURL u = new JMXServiceURL(proto, null, 0);
JMXConnectorServer server;
JMXServiceURL addr;
@ -89,7 +98,10 @@ public class AddRemoveTest {
try {
// with a client listener, but close the server first
server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
Map<String, String> env = Collections.singletonMap(
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
Boolean.toString(eventService));
server = JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
server.start();
addr = server.getAddress();

View File

@ -31,11 +31,12 @@
* @run main DiffHBTest
*/
import java.net.MalformedURLException;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import javax.management.*;
import javax.management.remote.*;
import javax.management.remote.rmi.RMIConnectorServer;
/**
* This test registeres an unique listener with two different handbacks,
@ -48,11 +49,6 @@ public class DiffHBTest {
private static ObjectName delegateName;
private static ObjectName timerName;
public static int received = 0;
public static final int[] receivedLock = new int[0];
public static Notification receivedNotif = null;
public static Object receivedHB = null;
public static final String[] hbs = new String[] {"0", "1"};
public static void main(String[] args) throws Exception {
@ -61,162 +57,174 @@ public class DiffHBTest {
delegateName = new ObjectName("JMImplementation:type=MBeanServerDelegate");
timerName = new ObjectName("MBean:name=Timer");
boolean ok = true;
String errors = "";
for (int i = 0; i < protocols.length; i++) {
try {
if (!test(protocols[i])) {
System.out.println(">>> Test failed for " + protocols[i]);
ok = false;
final String s = test(protocols[i]);
if (s != null) {
if ("".equals(errors)) {
errors = "Failed to " + protocols[i] + ": "+s;
} else {
System.out.println(">>> Test successed for " + protocols[i]);
errors = "\tFailed to " + protocols[i] + ": "+s;
}
} catch (Exception e) {
System.out.println(">>> Test failed for " + protocols[i]);
e.printStackTrace(System.out);
ok = false;
}
}
if (ok) {
System.out.println(">>> Test passed");
if ("".equals(errors)) {
System.out.println(">>> Passed!");
} else {
System.out.println(">>> TEST FAILED");
System.exit(1);
System.out.println(">>> Failed!");
throw new RuntimeException(errors);
}
}
private static boolean test(String proto) throws Exception {
System.out.println(">>> Test for protocol " + proto);
private static String test(String proto) throws Exception {
String ret = null;
for (boolean eventService : new boolean[] {false, true}) {
String s = test(proto, eventService);
if (s != null) {
if (ret == null)
ret = s;
else
ret = ret + "; " + s;
}
}
return ret;
}
private static String test(String proto, boolean eventService)
throws Exception {
System.out.println(">>> Test for protocol " + proto + " with" +
(eventService ? "" : "out") + " event service");
JMXServiceURL u = new JMXServiceURL(proto, null, 0);
JMXConnectorServer server;
JMXServiceURL addr;
JMXConnector client;
MBeanServerConnection mserver;
final NotificationListener dummyListener = new NotificationListener() {
public void handleNotification(Notification n, Object o) {
synchronized(receivedLock) {
if (n == null) {
System.out.println(">>> Got a null notification.");
System.exit(1);
}
// check number
if (received > 2) {
System.out.println(">>> Expect to receive 2 notifs, but get "+received);
System.exit(1);
}
if (received == 0) { // first time
receivedNotif = n;
receivedHB = o;
if (!hbs[0].equals(o) && !hbs[1].equals(o)) {
System.out.println(">>> Unkown handback: "+o);
System.exit(1);
}
} else { // second time
if (!receivedNotif.equals(n)) {
System.out.println(">>> Not get same notif twice.");
System.exit(1);
} else if (!hbs[0].equals(o) && !hbs[1].equals(o)) { // validate handback
System.out.println(">>> Unkown handback: "+o);
System.exit(1);
} else if (receivedHB.equals(o)) {
System.out.println(">>> Got same handback twice: "+o);
System.exit(1);
}
}
++received;
if (received == 2) {
receivedLock.notify();
}
}
}
};
try {
server = JMXConnectorServerFactory.newJMXConnectorServer(u, null, mbs);
Map<String, String> env = Collections.singletonMap(
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
Boolean.toString(eventService));
server =
JMXConnectorServerFactory.newJMXConnectorServer(u, env, mbs);
server.start();
addr = server.getAddress();
client = JMXConnectorFactory.newJMXConnector(addr, null);
client.connect(null);
mserver = client.getMBeanServerConnection();
mserver.addNotificationListener(delegateName, dummyListener, null, hbs[0]);
mserver.addNotificationListener(delegateName, dummyListener, null, hbs[1]);
for (int i=0; i<20; i++) {
synchronized(receivedLock) {
received = 0;
}
mserver.createMBean("javax.management.timer.Timer", timerName);
synchronized(receivedLock) {
if (received != 2) {
long remainingTime = waitingTime;
final long startTime = System.currentTimeMillis();
while (received != 2 && remainingTime > 0) {
receivedLock.wait(remainingTime);
remainingTime = waitingTime -
(System.currentTimeMillis() - startTime);
}
}
if (received != 2) {
System.out.println(">>> Expected 2 notifis, but received "+received);
return false;
}
}
synchronized(receivedLock) {
received = 0;
}
mserver.unregisterMBean(timerName);
synchronized(receivedLock) {
if (received != 2) {
long remainingTime = waitingTime;
final long startTime = System.currentTimeMillis();
while (received != 2 && remainingTime >0) {
receivedLock.wait(remainingTime);
remainingTime = waitingTime -
(System.currentTimeMillis() - startTime);
}
}
if (received != 2) {
System.out.println(">>> Expected 2 notifis, but received "+received);
return false;
}
}
}
mserver.removeNotificationListener(delegateName, dummyListener);
client.close();
server.stop();
} catch (MalformedURLException e) {
System.out.println(">>> Skipping unsupported URL " + u);
return true;
JMXServiceURL addr = server.getAddress();
client = JMXConnectorFactory.connect(addr, null);
} catch (Exception e) {
// not support
System.out.println(">>> not support: " + proto);
return null;
}
return true;
MBeanServerConnection mserver = client.getMBeanServerConnection();
System.out.print(">>>\t");
for (int i=0; i<5; i++) {
System.out.print(i + "\t");
final MyListener dummyListener = new MyListener();
mserver.addNotificationListener(
delegateName, dummyListener, null, hbs[0]);
mserver.addNotificationListener(
delegateName, dummyListener, null, hbs[1]);
mserver.createMBean("javax.management.timer.Timer", timerName);
long remainingTime = waitingTime;
final long startTime = System.currentTimeMillis();
try {
synchronized(dummyListener) {
while (!dummyListener.done && remainingTime > 0) {
dummyListener.wait(remainingTime);
remainingTime = waitingTime -
(System.currentTimeMillis() - startTime);
}
if (dummyListener.errorInfo != null) {
return dummyListener.errorInfo;
}
}
} finally {
//System.out.println("Unregister: "+i);
mserver.unregisterMBean(timerName);
mserver.removeNotificationListener(delegateName, dummyListener);
}
}
System.out.println("");
client.close();
server.stop();
return null;
}
private final static long waitingTime = 10000;
private static class MyListener implements NotificationListener {
public boolean done = false;
public String errorInfo = null;
private int received = 0;
private MBeanServerNotification receivedNotif = null;
private Object receivedHB = null;
public void handleNotification(Notification n, Object o) {
if (!(n instanceof MBeanServerNotification)) {
failed("Received an unexpected notification: "+n);
return;
}
if (!hbs[0].equals(o) && !hbs[1].equals(o)) {
failed("Unkown handback: "+o);
return;
}
// what we need
final MBeanServerNotification msn = (MBeanServerNotification)n;
if (!(MBeanServerNotification.REGISTRATION_NOTIFICATION.equals(
msn.getType())) ||
!msn.getMBeanName().equals(timerName)) {
return;
}
synchronized(this) {
received++;
if (received == 1) { // first time
receivedNotif = msn;
receivedHB = o;
return;
}
if (received > 2) {
failed("Expect to receive 2 notifs, but get "+received);
return;
}
// second time
if (receivedHB.equals(o)) {
failed("Got same handback twice: "+o);
} else if(!hbs[0].equals(o) && !hbs[1].equals(o)) {
failed("Unknown handback: "+o);
} else if (receivedNotif.getSequenceNumber() !=
msn.getSequenceNumber()) {
failed("expected to receive:\n"
+receivedNotif
+"\n but got\n"+msn);
}
// passed
done = true;
this.notify();
}
}
private void failed(String errorInfo) {
this.errorInfo = errorInfo;
done = true;
this.notify();
}
}
private final static long waitingTime = 2000;
}

View File

@ -29,11 +29,12 @@
* @author Shanliang JIANG
* @run clean EmptyDomainNotificationTest
* @run build EmptyDomainNotificationTest
* @run main EmptyDomainNotificationTest
* @run main EmptyDomainNotificationTest classic
* @run main EmptyDomainNotificationTest event
*/
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.Map;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerFactory;
@ -46,6 +47,7 @@ import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
public class EmptyDomainNotificationTest {
@ -80,11 +82,25 @@ public class EmptyDomainNotificationTest {
public static void main(String[] args) throws Exception {
String type = args[0];
boolean eventService;
if (type.equals("classic"))
eventService = false;
else if (type.equals("event"))
eventService = true;
else
throw new IllegalArgumentException(type);
final MBeanServer mbs = MBeanServerFactory.createMBeanServer();
final JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
JMXConnectorServer server = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
Map<String, String> env = Collections.singletonMap(
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
Boolean.toString(eventService));
JMXConnectorServer server =
JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
server.start();
JMXConnector client = JMXConnectorFactory.connect(server.getAddress(), null);

View File

@ -51,18 +51,29 @@
* been compiled by the second.
*/
import java.lang.management.ManagementFactory;
import javax.management.*;
import javax.management.remote.*;
import java.util.concurrent.*;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.Semaphore;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MBeanServerFactory;
import javax.management.MalformedObjectNameException;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
public class ListenerScaleTest {
private static final int WARMUP_WITH_ONE_MBEAN = 1000;
private static final int NOTIFS_TO_TIME = 100;
private static final int EXTRA_MBEANS = 20000;
private static final MBeanServer mbs =
ManagementFactory.getPlatformMBeanServer();
private static final ObjectName testObjectName;
static {
try {
@ -87,7 +98,7 @@ public class ListenerScaleTest {
}
};
private static final long timeNotif() {
private static final long timeNotif(MBeanServer mbs) {
try {
startTime = System.nanoTime();
nnotifs = 0;
@ -117,12 +128,20 @@ public class ListenerScaleTest {
};
public static void main(String[] args) throws Exception {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
test(false);
test(true);
}
private static void test(boolean eventService) throws Exception {
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
Sender sender = new Sender();
mbs.registerMBean(sender, testObjectName);
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
Map<String, String> env = Collections.singletonMap(
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE,
Boolean.toString(eventService));
JMXConnectorServer cs =
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
cs.start();
JMXServiceURL addr = cs.getAddress();
JMXConnector cc = JMXConnectorFactory.connect(addr);
@ -140,7 +159,7 @@ public class ListenerScaleTest {
mbsc.addNotificationListener(testObjectName, timingListener, null, null);
long singleMBeanTime = 0;
for (int i = 0; i < WARMUP_WITH_ONE_MBEAN; i++)
singleMBeanTime = timeNotif();
singleMBeanTime = timeNotif(mbs);
if (singleMBeanTime == 0)
singleMBeanTime = 1;
System.out.println("Time with a single MBean: " + singleMBeanTime + "ns");
@ -165,7 +184,7 @@ public class ListenerScaleTest {
}
System.out.println();
System.out.println("Timing a notification send now");
long manyMBeansTime = timeNotif();
long manyMBeansTime = timeNotif(mbs);
System.out.println("Time with many MBeans: " + manyMBeansTime + "ns");
double ratio = (double) manyMBeansTime / singleMBeanTime;
if (ratio > 100.0)

View File

@ -31,11 +31,11 @@
* @run main NotifBufferSizePropertyNameTest
*/
import java.io.IOException;
import java.util.*;
import javax.management.*;
import javax.management.remote.*;
import javax.management.remote.rmi.RMIConnectorServer;
/**
* This class tests also the size of a server notification buffer.
@ -88,6 +88,9 @@ public class NotifBufferSizePropertyNameTest {
private static void test(Map env) throws Exception {
final MBeanServer mbs = MBeanServerFactory.newMBeanServer();
env = new HashMap((env == null) ? Collections.emptyMap() : env);
env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
mbs.registerMBean(new NotificationEmitter(), oname);
JMXConnectorServer server = JMXConnectorServerFactory.newJMXConnectorServer(
url,

View File

@ -22,7 +22,7 @@
*/
/*
* @test NotifReconnectDeadlockTest
* @test
* @bug 6199899
* @summary Tests reconnection done by a fetching notif thread.
* @author Shanliang JIANG
@ -31,11 +31,21 @@
* @run main NotifReconnectDeadlockTest
*/
import java.io.IOException;
import java.util.*;
import javax.management.*;
import javax.management.remote.*;
import java.util.HashMap;
import java.util.Map;
import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.Notification;
import javax.management.NotificationBroadcasterSupport;
import javax.management.NotificationListener;
import javax.management.ObjectName;
import javax.management.remote.JMXConnectionNotification;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
/**
* "This test checks for a bug whereby reconnection did not work if (a) it was
@ -64,6 +74,7 @@ public class NotifReconnectDeadlockTest {
Map env = new HashMap(2);
env.put("jmx.remote.x.server.connection.timeout", new Long(serverTimeout));
env.put("jmx.remote.x.client.connection.check.period", new Long(Long.MAX_VALUE));
env.put(RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
final MBeanServer mbs = MBeanServerFactory.newMBeanServer();

View File

@ -156,7 +156,8 @@ public class NotificationAccessControllerTest {
List<Notification> received,
List<ObjectName> expected) {
if (received.size() != size) {
echo("Error: expecting " + size + " notifications");
echo("Error: expecting " + size + " notifications, got " +
received.size());
return 1;
} else {
for (Notification n : received) {

View File

@ -32,6 +32,8 @@
*/
import java.net.MalformedURLException;
import java.util.Collections;
import java.util.Map;
import javax.management.MBeanServerFactory;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
@ -44,6 +46,7 @@ import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnectorServer;
public class NotificationBufferCreationTest {
private static final MBeanServer mbs =
@ -86,6 +89,8 @@ public class NotificationBufferCreationTest {
JMXServiceURL u = null;
try {
u = new JMXServiceURL(protocol, null, 0);
Map<String, String> env = Collections.singletonMap(
RMIConnectorServer.DELEGATE_TO_EVENT_SERVICE, "false");
server =
JMXConnectorServerFactory.newJMXConnectorServer(u,
null,

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