1 /*
   2  * Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 4940957 8025205
  27  * @summary Tests behaviour when connections break
  28  * @author Eamonn McManus
  29  * @key intermittent
  30  * @modules java.management
  31  * @run clean BrokenConnectionTest
  32  * @run build BrokenConnectionTest
  33  * @run main BrokenConnectionTest
  34  */
  35 
  36 import java.io.*;
  37 import java.lang.reflect.*;
  38 import java.nio.channels.ServerSocketChannel;
  39 import java.net.*;
  40 import java.rmi.server.*;
  41 import java.util.*;
  42 
  43 import java.rmi.UnmarshalException;
  44 
  45 import javax.management.*;
  46 import javax.management.remote.*;
  47 import javax.management.remote.rmi.*;
  48 
  49 // resolve ambiguity
  50 import java.lang.reflect.Proxy;
  51 
  52 public class BrokenConnectionTest {
  53     private static ObjectName DELEGATE_NAME;
  54     private static ObjectName BREAK_NAME;
  55     private static ObjectName LISTENER_NAME;
  56     public static void main(String[] args) throws Exception {
  57         DELEGATE_NAME =
  58             new ObjectName("JMImplementation:type=MBeanServerDelegate");
  59         BREAK_NAME = new ObjectName("test:type=Break");
  60         LISTENER_NAME = new ObjectName("test:type=Listener");
  61 
  62         String failed = "";
  63 
  64         final String[] protos = {"rmi", "jmxmp"};
  65 
  66         for (int i = 0; i < protos.length; i++) {
  67             final String proto = protos[i];
  68             System.out.println();
  69             System.out.println("------- Testing for " + proto + " -------");
  70             try {
  71                 if (!test(proto))
  72                     failed += " " + proto;
  73             } catch (Exception e) {
  74                 System.out.println("FAILED WITH EXCEPTION:");
  75                 e.printStackTrace(System.out);
  76                 failed += " " + proto;
  77             }
  78         }
  79 
  80         System.out.println();
  81 
  82         if (failed.length() > 0) {
  83             System.out.println("TEST FAILED FOR:" + failed);
  84             System.exit(1);
  85         }
  86 
  87         System.out.println("Test passed");
  88     }
  89 
  90     private static boolean test(String proto) throws Exception {
  91         if (proto.equals("rmi"))
  92             return rmiTest();
  93         else if (proto.equals("jmxmp"))
  94             return jmxmpTest();
  95         else
  96             throw new AssertionError(proto);
  97     }
  98 
  99     private static interface Breakable {
 100         public JMXConnectorServer createConnectorServer(MBeanServer mbs)
 101                 throws IOException;
 102         public void setBroken(boolean broken);
 103     }
 104 
 105     private static interface TestAction {
 106         public String toString();
 107         public boolean test(MBeanServerConnection mbsc, Breakable breakable)
 108                 throws Exception;
 109     }
 110 
 111     private static abstract class Operation implements TestAction {
 112         public String toString() {
 113             return opName() + ", break, " + opName();
 114         }
 115         void init(MBeanServerConnection mbsc) throws Exception {}
 116         abstract String opName();
 117         public boolean test(MBeanServerConnection mbsc, Breakable breakable)
 118                 throws Exception {
 119             init(mbsc);
 120             operation(mbsc);
 121             System.out.println("Client ran " + opName() + " OK");
 122             breakable.setBroken(true);
 123             System.out.println("Broke connection, run " + opName() + " again");
 124             try {
 125                 operation(mbsc);
 126                 System.out.println("TEST FAILED: " + opName() +
 127                                    " should fail!");
 128                 return false;
 129             } catch (IOException e) {
 130                 System.out.println("Got IOException as expected (" + e + ")");
 131             }
 132             return true;
 133         }
 134         abstract void operation(MBeanServerConnection mbsc) throws Exception;
 135     }
 136 
 137     private static TestAction[] tests = {
 138         new Operation() {
 139             String opName() {
 140                 return "getDefaultDomain";
 141             }
 142             void operation(MBeanServerConnection mbsc) throws Exception {
 143                 mbsc.getDefaultDomain();
 144             }
 145         },
 146         new Operation() {
 147             String opName() {
 148                 return "addNotificationListener(NL)";
 149             }
 150             void operation(MBeanServerConnection mbsc) throws Exception {
 151                 mbsc.addNotificationListener(DELEGATE_NAME,
 152                                              new CountListener(), null, null);
 153             }
 154         },
 155         new Operation() {
 156             String opName() {
 157                 return "addNotificationListener(MB)";
 158             }
 159             void init(MBeanServerConnection mbsc) throws Exception {
 160                 mbsc.createMBean(CountListener.class.getName(),
 161                                  LISTENER_NAME);
 162             }
 163             void operation(MBeanServerConnection mbsc) throws Exception {
 164                 mbsc.addNotificationListener(DELEGATE_NAME, LISTENER_NAME,
 165                                              null, null);
 166             }
 167         },
 168         new Operation() {
 169             String opName() {
 170                 return "removeNotificationListener(NL)";
 171             }
 172             void init(MBeanServerConnection mbsc) throws Exception {
 173                 for (int i = 0; i < NLISTENERS; i++) {
 174                     NotificationListener l = new CountListener();
 175                     mbsc.addNotificationListener(DELEGATE_NAME, l, null, null);
 176                     listeners.add(l);
 177                 }
 178             }
 179             void operation(MBeanServerConnection mbsc) throws Exception {
 180                 NotificationListener l = (NotificationListener)
 181                     listeners.remove(0);
 182                 mbsc.removeNotificationListener(DELEGATE_NAME, l, null, null);
 183             }
 184             static final int NLISTENERS = 2;
 185             List/*<NotificationListener>*/ listeners = new ArrayList();
 186         },
 187         new Operation() {
 188             String opName() {
 189                 return "removeNotificationListener(MB)";
 190             }
 191             void init(MBeanServerConnection mbsc) throws Exception {
 192                 mbsc.createMBean(CountListener.class.getName(),
 193                                  LISTENER_NAME);
 194             }
 195             void operation(MBeanServerConnection mbsc) throws Exception {
 196                 try {
 197                     mbsc.removeNotificationListener(DELEGATE_NAME,
 198                                                     LISTENER_NAME,
 199                                                     null, null);
 200                     throw new IllegalArgumentException("removeNL should not " +
 201                                                        "have worked!");
 202                 } catch (ListenerNotFoundException e) {
 203                     // normal - there isn't one!
 204                 }
 205             }
 206         },
 207         new Operation() {
 208             String opName() {
 209                 return "createMBean(className, objectName)";
 210             }
 211             void operation(MBeanServerConnection mbsc) throws Exception {
 212                 ObjectName name =
 213                     new ObjectName("test:instance=" + nextInstance());
 214                 mbsc.createMBean(CountListener.class.getName(), name);
 215             }
 216             private synchronized int nextInstance() {
 217                 return ++instance;
 218             }
 219             private int instance;
 220         },
 221         new Operation() {
 222             String opName() {
 223                 return "getAttribute";
 224             }
 225             void operation(MBeanServerConnection mbsc) throws Exception {
 226                 mbsc.getAttribute(DELEGATE_NAME, "ImplementationName");
 227             }
 228         },
 229         new Operation() {
 230             String opName() {
 231                 return "getAttributes";
 232             }
 233             void operation(MBeanServerConnection mbsc) throws Exception {
 234                 mbsc.getAttribute(DELEGATE_NAME, "ImplementationName");
 235             }
 236         },
 237         new Operation() {
 238             String opName() {
 239                 return "getDomains";
 240             }
 241             void operation(MBeanServerConnection mbsc) throws Exception {
 242                 mbsc.getDomains();
 243             }
 244         },
 245         new Operation() {
 246             String opName() {
 247                 return "getMBeanCount";
 248             }
 249             void operation(MBeanServerConnection mbsc) throws Exception {
 250                 mbsc.getMBeanCount();
 251             }
 252         },
 253         new Operation() {
 254             String opName() {
 255                 return "getMBeanInfo";
 256             }
 257             void operation(MBeanServerConnection mbsc) throws Exception {
 258                 mbsc.getMBeanInfo(DELEGATE_NAME);
 259             }
 260         },
 261         new Operation() {
 262             String opName() {
 263                 return "getObjectInstance";
 264             }
 265             void operation(MBeanServerConnection mbsc) throws Exception {
 266                 mbsc.getObjectInstance(DELEGATE_NAME);
 267             }
 268         },
 269         new Operation() {
 270             String opName() {
 271                 return "invoke";
 272             }
 273             void operation(MBeanServerConnection mbsc) throws Exception {
 274                 mbsc.invoke(BREAK_NAME, "doNothing", new Object[0],
 275                             new String[0]);
 276             }
 277         },
 278         new Operation() {
 279             String opName() {
 280                 return "isInstanceOf";
 281             }
 282             void operation(MBeanServerConnection mbsc) throws Exception {
 283                 mbsc.isInstanceOf(DELEGATE_NAME, "whatsit");
 284             }
 285         },
 286         new Operation() {
 287             String opName() {
 288                 return "isRegistered";
 289             }
 290             void operation(MBeanServerConnection mbsc) throws Exception {
 291                 mbsc.isRegistered(DELEGATE_NAME);
 292             }
 293         },
 294         new Operation() {
 295             String opName() {
 296                 return "queryMBeans";
 297             }
 298             void operation(MBeanServerConnection mbsc) throws Exception {
 299                 mbsc.queryMBeans(new ObjectName("*:*"), null);
 300             }
 301         },
 302         new Operation() {
 303             String opName() {
 304                 return "queryNames";
 305             }
 306             void operation(MBeanServerConnection mbsc) throws Exception {
 307                 mbsc.queryNames(new ObjectName("*:*"), null);
 308             }
 309         },
 310         new Operation() {
 311             String opName() {
 312                 return "setAttribute";
 313             }
 314             void operation(MBeanServerConnection mbsc) throws Exception {
 315                 mbsc.setAttribute(BREAK_NAME,
 316                                   new Attribute("Nothing", null));
 317             }
 318         },
 319         new Operation() {
 320             String opName() {
 321                 return "setAttributes";
 322             }
 323             void operation(MBeanServerConnection mbsc) throws Exception {
 324                 AttributeList attrs = new AttributeList();
 325                 attrs.add(new Attribute("Nothing", null));
 326                 mbsc.setAttributes(BREAK_NAME, attrs);
 327             }
 328         },
 329         new Operation() {
 330             String opName() {
 331                 return "unregisterMBean";
 332             }
 333             void init(MBeanServerConnection mbsc) throws Exception {
 334                 for (int i = 0; i < NBEANS; i++) {
 335                     ObjectName name = new ObjectName("test:instance=" + i);
 336                     mbsc.createMBean(CountListener.class.getName(), name);
 337                     names.add(name);
 338                 }
 339             }
 340             void operation(MBeanServerConnection mbsc) throws Exception {
 341                 ObjectName name = (ObjectName) names.remove(0);
 342                 mbsc.unregisterMBean(name);
 343             }
 344             private static final int NBEANS = 2;
 345             private List/*<ObjectName>*/ names = new ArrayList();
 346         },
 347         new TestAction() {
 348             public String toString() {
 349                 return "break during send for setAttribute";
 350             }
 351             public boolean test(MBeanServerConnection mbsc,
 352                                 Breakable breakable) throws Exception {
 353                 Attribute attr =
 354                     new Attribute("Break", new BreakWhenSerialized(breakable));
 355                 try {
 356                     mbsc.setAttribute(BREAK_NAME, attr);
 357                     System.out.println("TEST FAILED: setAttribute with " +
 358                                        "BreakWhenSerializable did not fail!");
 359                     return false;
 360                 } catch (IOException e) {
 361                     System.out.println("Got IOException as expected: " + e);
 362 
 363                     return true;
 364                 }
 365             }
 366         },
 367         new TestAction() {
 368             public String toString() {
 369                 return "break during receive for getAttribute";
 370             }
 371             public boolean test(MBeanServerConnection mbsc,
 372                                 Breakable breakable) throws Exception {
 373                 try {
 374                     mbsc.getAttribute(BREAK_NAME, "Break");
 375                     System.out.println("TEST FAILED: getAttribute of " +
 376                                        "BreakWhenSerializable did not fail!");
 377                     return false;
 378                 } catch (IOException e) {
 379                     System.out.println("Got IOException as expected: " + e);
 380 
 381                     return true;
 382                 }
 383             }
 384         },
 385     };
 386 
 387     public static interface BreakMBean {
 388         public BreakWhenSerialized getBreak();
 389         public void setBreak(BreakWhenSerialized x);
 390 //      public void breakOnNotify();
 391         public void doNothing();
 392         public void setNothing(Object x);
 393     }
 394 
 395     public static class Break
 396             extends NotificationBroadcasterSupport implements BreakMBean {
 397         public Break(Breakable breakable) {
 398             this.breakable = breakable;
 399         }
 400 
 401         public BreakWhenSerialized getBreak() {
 402             return new BreakWhenSerialized(breakable);
 403         }
 404 
 405         public void setBreak(BreakWhenSerialized x) {
 406             throw new IllegalArgumentException("setBreak worked but " +
 407                                                "should not!");
 408         }
 409 
 410 //      public void breakOnNotify() {
 411 //          Notification broken = new Notification("type", "source", 0L);
 412 //          broken.setUserData(new BreakWhenSerialized(breakable));
 413 //          sendNotification(broken);
 414 //      }
 415 
 416         public void doNothing() {}
 417 
 418         public void setNothing(Object x) {}
 419 
 420         private final Breakable breakable;
 421     }
 422 
 423     private static class BreakWhenSerialized implements Serializable {
 424         BreakWhenSerialized(Breakable breakable) {
 425             this.breakable = breakable;
 426         }
 427 
 428         private void writeObject(ObjectOutputStream out) throws IOException {
 429             breakable.setBroken(true);
 430         }
 431 
 432         private final transient Breakable breakable;
 433     }
 434 
 435     private static class FailureNotificationFilter
 436             implements NotificationFilter {
 437         public boolean isNotificationEnabled(Notification n) {
 438             System.out.println("Filter: " + n + " (" + n.getType() + ")");
 439 
 440             final String failed =
 441                 JMXConnectionNotification.FAILED;
 442             return (n instanceof JMXConnectionNotification
 443                     && n.getType().equals(JMXConnectionNotification.FAILED));
 444         }
 445     }
 446 
 447     public static interface CountListenerMBean {}
 448 
 449     public static class CountListener
 450             implements CountListenerMBean, NotificationListener {
 451         public synchronized void handleNotification(Notification n, Object h) {
 452             count++;
 453         }
 454 
 455         int count;
 456     }
 457 
 458     private static boolean test(Breakable breakable)
 459             throws Exception {
 460         boolean alreadyMissedFailureNotif = false;
 461         String failed = "";
 462         for (int i = 1; i <= tests.length; i++) {
 463             TestAction ta = tests[i - 1];
 464             System.out.println();
 465             System.out.println("Test " + i + ": " + ta);
 466             MBeanServer mbs = MBeanServerFactory.newMBeanServer();
 467             Break breakMBean = new Break(breakable);
 468             mbs.registerMBean(breakMBean, BREAK_NAME);
 469             JMXConnectorServer cs = breakable.createConnectorServer(mbs);
 470             System.out.println("Created and started connector server");
 471             JMXServiceURL addr = cs.getAddress();
 472             JMXConnector cc = JMXConnectorFactory.connect(addr);
 473             CountListener failureListener = new CountListener();
 474             NotificationFilter failureFilter = new FailureNotificationFilter();
 475             cc.addConnectionNotificationListener(failureListener,
 476                                                  failureFilter,
 477                                                  null);
 478             MBeanServerConnection mbsc = cc.getMBeanServerConnection();
 479             System.out.println("Client connected OK");
 480             boolean thisok = ta.test(mbsc, breakable);
 481 
 482             try {
 483                 System.out.println("Stopping server");
 484                 cs.stop();
 485             } catch (IOException e) {
 486                 System.out.println("Ignoring exception on stop: " + e);
 487             }
 488             if (thisok) {
 489                 System.out.println("Waiting for failure notif");
 490                 // pass or test timeout. see 8025205
 491                 do {
 492                     Thread.sleep(100);
 493                 } while (failureListener.count < 1);
 494 
 495                 Thread.sleep(1000); // if more notif coming ...
 496                 if (failureListener.count > 1) {
 497                     System.out.println("Got too many failure notifs: " +
 498                                        failureListener.count);
 499                     thisok = false;
 500                 }
 501             }
 502             if (!thisok)
 503                 failed = failed + " " + i;
 504             System.out.println("Test " + i + (thisok ? " passed" : " FAILED"));
 505             breakable.setBroken(false);
 506         }
 507         if (failed.equals(""))
 508             return true;
 509         else {
 510             System.out.println("FAILING CASES:" + failed);
 511             return false;
 512         }
 513     }
 514 
 515     private static class BreakableRMI implements Breakable {
 516         public JMXConnectorServer createConnectorServer(MBeanServer mbs)
 517                 throws IOException {
 518             JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
 519             Map env = new HashMap();
 520             env.put(RMIConnectorServer.RMI_SERVER_SOCKET_FACTORY_ATTRIBUTE,
 521                     brssf);
 522             JMXConnectorServer cs =
 523                 JMXConnectorServerFactory.newJMXConnectorServer(url, env, mbs);
 524             cs.start();
 525             return cs;
 526         }
 527 
 528         public void setBroken(boolean broken) {
 529             brssf.setBroken(broken);
 530         }
 531 
 532         private final BreakableRMIServerSocketFactory brssf =
 533             new BreakableRMIServerSocketFactory();
 534     }
 535 
 536     private static boolean rmiTest() throws Exception {
 537         System.out.println("RMI broken connection test");
 538         Breakable breakable = new BreakableRMI();
 539         return test(breakable);
 540     }
 541 
 542     private static class BreakableRMIServerSocketFactory
 543             implements RMIServerSocketFactory {
 544 
 545         public synchronized ServerSocket createServerSocket(int port)
 546                 throws IOException {
 547             if (broken)
 548                 throw new IOException("ServerSocket has been broken");
 549             BreakableServerSocket bss = new BreakableServerSocket(port);
 550             bssList.add(bss);
 551             return bss;
 552         }
 553 
 554         synchronized void setBroken(boolean broken) {
 555             this.broken = broken;
 556 //          System.out.println("BRSSF.setBroken(" + broken + ")");
 557             for (Iterator it = bssList.iterator(); it.hasNext(); ) {
 558                 BreakableServerSocket bss = (BreakableServerSocket) it.next();
 559 //              System.out.println((broken ? "" : "un") + "break " + bss);
 560                 bss.setBroken(broken);
 561             }
 562         }
 563 
 564         private final List/*<BreakableServerSocket>*/ bssList =
 565             new ArrayList();
 566         private boolean broken = false;
 567     }
 568 
 569     private static class BreakableJMXMP implements Breakable {
 570         BreakableJMXMP() throws IOException {
 571             bss = new BreakableServerSocket(0);
 572         }
 573 
 574         public JMXConnectorServer createConnectorServer(MBeanServer mbs)
 575                 throws IOException {
 576             try {
 577                 InvocationHandler scsih =
 578                     new SocketConnectionServerInvocationHandler(bss);
 579                 final String mcs =
 580                     "javax.management.remote.generic.MessageConnectionServer";
 581                 final Class messageConnectionServerClass = Class.forName(mcs);
 582                 final Class[] proxyInterfaces = {messageConnectionServerClass};
 583                 Object socketConnectionServer =
 584                     Proxy.newProxyInstance(this.getClass().getClassLoader(),
 585                                            proxyInterfaces,
 586                                            scsih);
 587                 Map env = new HashMap();
 588                 env.put("jmx.remote.message.connection.server",
 589                         socketConnectionServer);
 590                 final String gcs =
 591                     "javax.management.remote.generic.GenericConnectorServer";
 592                 final Class genericConnectorServerClass = Class.forName(gcs);
 593                 final Class[] constrTypes = {Map.class, MBeanServer.class};
 594                 final Constructor constr =
 595                     genericConnectorServerClass.getConstructor(constrTypes);
 596                 JMXConnectorServer cs = (JMXConnectorServer)
 597                     constr.newInstance(new Object[] {env, mbs});
 598                 cs.start();
 599                 return cs;
 600             } catch (Exception e) {
 601                 e.printStackTrace(System.out);
 602                 throw new AssertionError(e);
 603             }
 604         }
 605 
 606         public void setBroken(boolean broken) {
 607             bss.setBroken(broken);
 608         }
 609 
 610         private final BreakableServerSocket bss;
 611     }
 612 
 613     private static boolean jmxmpTest() throws Exception {
 614         System.out.println("JMXMP broken connection test");
 615         try {
 616             Class.forName("javax.management.remote.generic.GenericConnector");
 617         } catch (ClassNotFoundException e) {
 618             System.out.println("Optional classes not present, skipping test");
 619             return true;
 620         }
 621         Breakable breakable = new BreakableJMXMP();
 622         return test(breakable);
 623     }
 624 
 625     private static class BreakableServerSocket extends ServerSocket {
 626         BreakableServerSocket(int port) throws IOException {
 627             super();
 628             ss = new ServerSocket(port);
 629         }
 630 
 631         synchronized void setBroken(boolean broken) {
 632             this.broken = broken;
 633 //          System.out.println("BSS.setBroken(" + broken + ")");
 634             if (!broken)
 635                 return;
 636             for (Iterator it = sList.iterator(); it.hasNext(); ) {
 637                 Socket s = (Socket) it.next();
 638                 try {
 639 //                  System.out.println("Break: " + s);
 640                     s.close();
 641                 } catch (IOException e) {
 642                     System.out.println("Unable to close socket: " + s +
 643                                        ", ignoring (" + e + ")");
 644                 }
 645                 it.remove();
 646             }
 647         }
 648 
 649         public void bind(SocketAddress endpoint) throws IOException {
 650             ss.bind(endpoint);
 651         }
 652 
 653         public void bind(SocketAddress endpoint, int backlog)
 654                 throws IOException {
 655             ss.bind(endpoint, backlog);
 656         }
 657 
 658         public InetAddress getInetAddress() {
 659             return ss.getInetAddress();
 660         }
 661 
 662         public int getLocalPort() {
 663             return ss.getLocalPort();
 664         }
 665 
 666         public SocketAddress getLocalSocketAddress() {
 667             return ss.getLocalSocketAddress();
 668         }
 669 
 670         public Socket accept() throws IOException {
 671 //          System.out.println("BSS.accept");
 672             Socket s = ss.accept();
 673 //          System.out.println("BSS.accept returned: " + s);
 674             if (broken)
 675                 s.close();
 676             else
 677                 sList.add(s);
 678             return s;
 679         }
 680 
 681         public void close() throws IOException {
 682             ss.close();
 683         }
 684 
 685         public ServerSocketChannel getChannel() {
 686             return ss.getChannel();
 687         }
 688 
 689         public boolean isBound() {
 690             return ss.isBound();
 691         }
 692 
 693         public boolean isClosed() {
 694             return ss.isClosed();
 695         }
 696 
 697         public void setSoTimeout(int timeout) throws SocketException {
 698             ss.setSoTimeout(timeout);
 699         }
 700 
 701         public int getSoTimeout() throws IOException {
 702             return ss.getSoTimeout();
 703         }
 704 
 705         public void setReuseAddress(boolean on) throws SocketException {
 706             ss.setReuseAddress(on);
 707         }
 708 
 709         public boolean getReuseAddress() throws SocketException {
 710             return ss.getReuseAddress();
 711         }
 712 
 713         public String toString() {
 714             return "BreakableServerSocket wrapping " + ss.toString();
 715         }
 716 
 717         public void setReceiveBufferSize (int size) throws SocketException {
 718             ss.setReceiveBufferSize(size);
 719         }
 720 
 721         public int getReceiveBufferSize() throws SocketException {
 722             return ss.getReceiveBufferSize();
 723         }
 724 
 725         private final ServerSocket ss;
 726         private final List/*<Socket>*/ sList = new ArrayList();
 727         private boolean broken = false;
 728     }
 729 
 730     /* We do a lot of messy reflection stuff here because we don't
 731        want to reference the optional parts of the JMX Remote API in
 732        an environment (J2SE) where they won't be present.  */
 733 
 734     /* This class implements the logic that allows us to pretend that
 735        we have a class that looks like this:
 736        class SocketConnectionServer implements MessageConnectionServer {
 737            public MessageConnection accept() throws IOException {...}
 738            public JMXServiceURL getAddress() {...}
 739            public void start(Map env) throws IOException {...}
 740            public void stop() throws IOException {...}
 741        }
 742      */
 743     private static class SocketConnectionServerInvocationHandler
 744             implements InvocationHandler {
 745         SocketConnectionServerInvocationHandler(ServerSocket ss) {
 746             this.ss = ss;
 747         }
 748 
 749         public Object invoke(Object proxy, Method method, Object[] args)
 750                 throws Exception {
 751             final String mname = method.getName();
 752             try {
 753                 if (mname.equals("accept"))
 754                     return accept();
 755                 else if (mname.equals("getAddress"))
 756                     return getAddress();
 757                 else if (mname.equals("start"))
 758                     start((Map) args[0]);
 759                 else if (mname.equals("stop"))
 760                     stop();
 761                 else // probably a method inherited from Object
 762                     return method.invoke(this, args);
 763             } catch (InvocationTargetException ite) {
 764                 Throwable t = ite.getCause();
 765                 if (t instanceof IOException) {
 766                     throw (IOException)t;
 767                 } else if (t instanceof RuntimeException) {
 768                     throw (RuntimeException)t;
 769                 } else {
 770                     throw ite;
 771                 }
 772             }
 773 
 774             return null;
 775         }
 776 
 777         private Object/*MessageConnection*/ accept() throws Exception {
 778             System.out.println("SCSIH.accept()");
 779             Socket s = ss.accept();
 780             Class socketConnectionClass =
 781                 Class.forName("com.sun.jmx.remote.socket.SocketConnection");
 782             Constructor constr =
 783                 socketConnectionClass.getConstructor(new Class[] {Socket.class});
 784             return constr.newInstance(new Object[] {s});
 785 //          InvocationHandler scih = new SocketConnectionInvocationHandler(s);
 786 //          Class messageConnectionClass =
 787 //              Class.forName("javax.management.generic.MessageConnection");
 788 //          return Proxy.newProxyInstance(this.getClass().getClassLoader(),
 789 //                                        new Class[] {messageConnectionClass},
 790 //                                        scih);
 791         }
 792 
 793         private JMXServiceURL getAddress() throws Exception {
 794             System.out.println("SCSIH.getAddress()");
 795             return new JMXServiceURL("jmxmp", null, ss.getLocalPort());
 796         }
 797 
 798         private void start(Map env) throws IOException {
 799             System.out.println("SCSIH.start(" + env + ")");
 800         }
 801 
 802         private void stop() throws IOException {
 803             System.out.println("SCSIH.stop()");
 804         }
 805 
 806         private final ServerSocket ss;
 807     }
 808 }