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
|
* @param env
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static boolean isServerDaemon(Map env) {
|
public static boolean isServerDaemon(Map<String, ?> env) {
|
||||||
return (env != null) &&
|
return (env != null) &&
|
||||||
("true".equalsIgnoreCase((String)env.get(JMX_SERVER_DAEMON)));
|
("true".equalsIgnoreCase((String)env.get(JMX_SERVER_DAEMON)));
|
||||||
}
|
}
|
||||||
|
@ -386,6 +386,34 @@ public abstract class JMXConnectorServer
|
|||||||
return false;
|
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
|
* <p>Install {@link MBeanServerForwarder}s in the system chain
|
||||||
* based on the attributes in the given {@code Map}. A connector
|
* based on the attributes in the given {@code Map}. A connector
|
||||||
|
@ -602,6 +602,26 @@ public class RMIConnectorServer extends JMXConnectorServer {
|
|||||||
return true;
|
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}
|
/* We repeat the definitions of connection{Opened,Closed,Failed}
|
||||||
here so that they are accessible to other classes in this package
|
here so that they are accessible to other classes in this package
|
||||||
even though they have protected access. */
|
even though they have protected access. */
|
||||||
|
@ -249,20 +249,73 @@ public abstract class RMIServerImpl implements Closeable, RMIServer {
|
|||||||
|
|
||||||
RMIConnection client = makeClient(connectionId, subject);
|
RMIConnection client = makeClient(connectionId, subject);
|
||||||
|
|
||||||
connServer.connectionOpened(connectionId, "Connection opened", null);
|
|
||||||
|
|
||||||
dropDeadReferences();
|
dropDeadReferences();
|
||||||
WeakReference<RMIConnection> wr = new WeakReference<RMIConnection>(client);
|
WeakReference<RMIConnection> wr = new WeakReference<RMIConnection>(client);
|
||||||
synchronized (clientList) {
|
synchronized (clientList) {
|
||||||
clientList.add(wr);
|
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)
|
if (tracing)
|
||||||
logger.trace("newClient","new connection done: " + connectionId );
|
logger.trace("newClient","new connection done: " + connectionId );
|
||||||
|
|
||||||
return client;
|
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
|
* <p>Creates a new client connection. This method is called by
|
||||||
* the public method {@link #newClient(Object)}.</p>
|
* 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