6332907: Add ability for connector server to close individual connections
Reviewed-by: emcmanus
This commit is contained in:
parent
e7d171eac3
commit
6a4903d458
@ -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>
|
||||
|
@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user