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