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