提交 3be91f14 编写于 作者: S sjiang

6332907: Add ability for connector server to close individual connections

Reviewed-by: emcmanus
上级 17c6fc73
......@@ -812,7 +812,7 @@ public class EnvHelp {
* @param env
* @return
*/
public static boolean isServerDaemon(Map env) {
public static boolean isServerDaemon(Map<String, ?> env) {
return (env != null) &&
("true".equalsIgnoreCase((String)env.get(JMX_SERVER_DAEMON)));
}
......
......@@ -386,6 +386,34 @@ public abstract class JMXConnectorServer
return false;
}
/**
* Closes a client connection. If the connection is successfully closed,
* the method {@link #connectionClosed} is called to notify interested parties.
* <P>Not all connector servers support this method. For those that do, it
* should be possible to cause a new client connection to fail before it
* can be used, by calling this method from within a
* {@link javax.management.NotificationListener}
* when it receives a {@link JMXConnectionNotification#OPENED} notification.
* This allows the owner of a connector server to deny certain connections,
* typically based on the information in the connection id.
* <P>The implementation of this method in {@code JMXConnectorServer} throws
* {@code UnsupportedOperationException}. Subclasses can override this
* method to support closing a specified client connection.
*
* @param connectionId the id of the client connection to be closed.
* @throws IllegalStateException if the server is not started or is closed.
* @throws IllegalArgumentException if {@code connectionId} is null or is
* not the id of any open connection.
* @throws java.io.IOException if an I/O error appears when closing the
* connection.
*
* @since 1.7
*/
public void closeConnection(String connectionId)
throws IOException {
throw new UnsupportedOperationException();
}
/**
* <p>Install {@link MBeanServerForwarder}s in the system chain
* based on the attributes in the given {@code Map}. A connector
......
......@@ -602,6 +602,26 @@ public class RMIConnectorServer extends JMXConnectorServer {
return true;
}
/**
* {@inheritDoc}
* <P>The {@code RMIConnectorServer} class does support closing a specified
* client connection.
* @throws IllegalStateException if the server is not started or is closed.
* @throws IllegalArgumentException if {@code connectionId} is null or is
* not the id of any open connection.
* @since 1.7
*/
@Override
public void closeConnection(String connectionId)
throws IOException {
if (isActive()) {
rmiServerImpl.closeConnection(connectionId);
} else {
throw new IllegalStateException(
"The server is not started or is closed.");
}
}
/* We repeat the definitions of connection{Opened,Closed,Failed}
here so that they are accessible to other classes in this package
even though they have protected access. */
......
......@@ -249,20 +249,73 @@ public abstract class RMIServerImpl implements Closeable, RMIServer {
RMIConnection client = makeClient(connectionId, subject);
connServer.connectionOpened(connectionId, "Connection opened", null);
dropDeadReferences();
WeakReference<RMIConnection> wr = new WeakReference<RMIConnection>(client);
synchronized (clientList) {
clientList.add(wr);
}
connServer.connectionOpened(connectionId, "Connection opened", null);
synchronized (clientList) {
if (!clientList.contains(wr)) {
// can be removed only by a JMXConnectionNotification listener
throw new IOException("The connection is refused.");
}
}
if (tracing)
logger.trace("newClient","new connection done: " + connectionId );
return client;
}
/**
* Closes a client connection.
* @param connectionId the id of the client connection to be closed.
* @throws IllegalArgumentException if {@code connectionId} is null or is
* not the id of any open connection.
* @throws java.io.IOException if an I/O error appears when closing the
* connection.
*
* @since 1.7
*/
public void closeConnection(String connectionId)
throws IOException {
final boolean debug = logger.debugOn();
if (debug) logger.trace("closeConnection","cconnectionId="+connectionId);
if (connectionId == null)
throw new IllegalArgumentException("Null connectionId.");
RMIConnection client = null;
synchronized (clientList) {
dropDeadReferences();
for (Iterator<WeakReference<RMIConnection>> it = clientList.iterator();
it.hasNext(); ) {
client = it.next().get();
if (client != null && connectionId.equals(client.getConnectionId())) {
it.remove();
break;
}
}
}
if (client == null) {
throw new IllegalArgumentException("Unknown id: "+connectionId);
}
if (debug) logger.trace("closeConnection", "closing client connection.");
closeClient(client);
if (debug) logger.trace("closeConnection", "sending notif");
connServer.connectionClosed(connectionId,
"Client connection closed", null);
if (debug) logger.trace("closeConnection","done");
}
/**
* <p>Creates a new client connection. This method is called by
* the public method {@link #newClient(Object)}.</p>
......
/*
* Copyright 2003-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 6332907
* @summary test the ability for connector server to close individual connections
* @author Shanliang JIANG
* @run clean CloseConnectionTest
* @run build CloseConnectionTest
* @run main CloseConnectionTest
*/
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.management.MBeanServerFactory;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.remote.JMXConnectionNotification;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import javax.management.remote.JMXConnectorServer;
import javax.management.remote.JMXConnectorServerFactory;
public class CloseConnectionTest {
public static void main(String[] args) throws Exception {
System.out.println(">>> Test the ability for connector server to close " +
"individual connections.");
final String[] protos = new String[]{"rmi", "iiop", "jmxmp"};
for (String p : protos) {
System.out.println("\n>>> Testing the protocol " + p);
JMXServiceURL addr = new JMXServiceURL(p, null, 0);
System.out.println(">>> Creating a JMXConnectorServer on " + addr);
JMXConnectorServer server = null;
try {
server = JMXConnectorServerFactory.newJMXConnectorServer(addr,
null,
MBeanServerFactory.createMBeanServer());
} catch (Exception e) {
System.out.println(">>> Skip the protocol: " + p);
continue;
}
test1(server);
test2(server);
server.stop();
}
System.out.println(">>> Bye bye!");
}
private static void test1(JMXConnectorServer server) throws Exception {
try {
server.closeConnection("toto");
// not started, known id
throw new RuntimeException("An IllegalArgumentException is not thrown.");
} catch (IllegalStateException e) {
System.out.println(">>> Test1: Got expected IllegalStateException: " + e);
}
server.start();
System.out.println(">>>Test1 Started the server on " + server.getAddress());
try {
server.closeConnection("toto");
throw new RuntimeException("An IllegalArgumentException is not thrown.");
} catch (IllegalArgumentException e) {
System.out.println(">> Test1: Got expected IllegalArgumentException: " + e);
}
MyListener listener = new MyListener();
server.addNotificationListener(listener, null, null);
System.out.println(">>> Test1: Connecting a client to the server ...");
final JMXConnector conn = JMXConnectorFactory.connect(server.getAddress());
conn.getMBeanServerConnection().getDefaultDomain();
final String id1 = conn.getConnectionId();
listener.wait(JMXConnectionNotification.OPENED, timeout);
System.out.println(">>> Test1: Closing the connection: " + conn.getConnectionId());
server.closeConnection(id1);
listener.wait(JMXConnectionNotification.CLOSED, timeout);
System.out.println(">>> Test1: Using again the connector whose connection " +
"should be closed by the server, it should reconnect " +
"automatically to the server and get a new connection id.");
conn.getMBeanServerConnection().getDefaultDomain();
final String id2 = conn.getConnectionId();
listener.wait(JMXConnectionNotification.OPENED, timeout);
if (id1.equals(id2)) {
throw new RuntimeException("Failed, the first client connection is not closed.");
}
System.out.println(">>> Test1: Greate, we get a new connection id " + id2 +
", the first one is closed as expected.");
System.out.println(">>> Test1: Closing the client.");
conn.close();
System.out.println(">>> Test1: Stopping the server.");
server.removeNotificationListener(listener);
}
private static void test2(JMXConnectorServer server) throws Exception {
System.out.println(">>> Test2 close a connection before " +
"the client can use it...");
final Killer killer = new Killer(server);
server.addNotificationListener(killer, null, null);
System.out.println(">>> Test2 Connecting a client to the server ...");
final JMXConnector conn;
try {
conn = JMXConnectorFactory.connect(server.getAddress());
throw new RuntimeException(">>> Failed, do not receive an " +
"IOException telling the connection is refused.");
} catch (IOException ioe) {
System.out.println(">>> Test2 got expected IOException: "+ioe);
}
}
private static class MyListener implements NotificationListener {
public void handleNotification(Notification n, Object hb) {
if (n instanceof JMXConnectionNotification) {
synchronized (received) {
received.add((JMXConnectionNotification) n);
received.notify();
}
}
}
public JMXConnectionNotification wait(String type, long timeout)
throws Exception {
JMXConnectionNotification waited = null;
long toWait = timeout;
long deadline = System.currentTimeMillis() + timeout;
synchronized (received) {
while (waited == null && toWait > 0) {
received.wait(toWait);
for (JMXConnectionNotification n : received) {
if (type.equals(n.getType())) {
waited = n;
break;
}
}
received.clear();
toWait = deadline - System.currentTimeMillis();
}
}
if (waited == null) {
throw new RuntimeException("Do not receive expected notification " + type);
} else {
System.out.println(">>> Received expected notif: "+type+
" "+waited.getConnectionId());
}
return waited;
}
final List<JMXConnectionNotification> received =
new ArrayList<JMXConnectionNotification>();
}
private static class Killer implements NotificationListener {
public Killer(JMXConnectorServer server) {
this.server = server;
}
public void handleNotification(Notification n, Object hb) {
if (n instanceof JMXConnectionNotification) {
if (JMXConnectionNotification.OPENED.equals(n.getType())) {
final JMXConnectionNotification cn =
(JMXConnectionNotification)n;
try {
System.out.println(">>> Killer: close the connection "+
cn.getConnectionId());
server.closeConnection(cn.getConnectionId());
} catch (Exception e) {
// impossible?
e.printStackTrace();
System.exit(1);
}
}
}
}
private final JMXConnectorServer server;
}
private static final long timeout = 6000;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册