1 /*
   2  * Copyright (c) 2016, 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 8141591
  27  * @summary Tests if notifications are received after executor is shutdown
  28  * @author Harsha Wardhana B
  29  *
  30  * @run clean ExecutorShutdownTest
  31  * @run build ExecutorShutdownTest
  32  * @run main ExecutorShutdownTest
  33  */
  34 import java.util.*;
  35 import java.util.concurrent.*;
  36 import javax.management.*;
  37 import javax.management.remote.*;
  38 
  39 /*
  40   When you create a JMXConnector client, you can supply a
  41   "fetch-notifications Executor", which is a
  42   java.util.concurrent.Executor that will be used each time the
  43   connector client wants to call RMIConnection.fetchNotifications.
  44   If such executor is not supplies, the connector client will fallback
  45   on default LinearExecutor. This test checks if user supplied executor
  46   is shutdown abruptly, LinearExecutor is used to handle notifications.
  47  */
  48 public class ExecutorShutdownTest {
  49 
  50     private static final String EXECUTOR_PROPERTY
  51             = "jmx.remote.x.fetch.notifications.executor";
  52     private static final String NOTIF_TYPE = "test.type";
  53 
  54     public static void main(String[] args) throws Exception {
  55 
  56         // Start JMXConnector Server
  57         JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
  58         MBeanServer mbs = MBeanServerFactory.newMBeanServer();
  59         ObjectName emitName = new ObjectName("blah:type=Emitter");
  60         mbs.registerMBean(new Emitter(), emitName);
  61         JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url,
  62                 null,
  63                 mbs);
  64         cs.start();
  65 
  66         // Create executor to provide to JMXConnector client
  67         ExecutorService executor = Executors.newCachedThreadPool();
  68         Map<String, Executor> env = new HashMap<>();
  69         env.put(EXECUTOR_PROPERTY, executor);
  70         JMXServiceURL addr = cs.getAddress();
  71 
  72         try (JMXConnector cc = JMXConnectorFactory.connect(addr, env)) {
  73             MBeanServerConnection mbsc = cc.getMBeanServerConnection();
  74             EmitterMBean emitter = (EmitterMBean) MBeanServerInvocationHandler.newProxyInstance(mbsc,
  75                     emitName,
  76                     EmitterMBean.class,
  77                     false);
  78             SemaphoreListener listener = new SemaphoreListener();
  79             NotificationFilterSupport filter = new NotificationFilterSupport();
  80             filter.enableType(NOTIF_TYPE);
  81             mbsc.addNotificationListener(emitName, listener, filter, null);
  82 
  83             final int NOTIF_COUNT = 3;
  84             for (int i = 0; i < NOTIF_COUNT; i++) {
  85                 emitter.emit();
  86                 listener.await();
  87             }
  88             Thread.sleep(1);
  89             listener.checkUnavailable();
  90             System.out.println("Got notifications with client provided executor");
  91 
  92             // After shutting down executor, notifications are handled by linear executor
  93             executor.shutdown();
  94             for (int i = 0; i < NOTIF_COUNT; i++) {
  95                 emitter.emit();
  96                 listener.await();
  97             }
  98             Thread.sleep(1);
  99             listener.checkUnavailable();
 100             System.out.println("Got notifications with linear executor");
 101         }
 102         cs.stop();
 103         System.out.println("TEST PASSED !!!");
 104     }
 105 
 106     /* Simple MBean that sends a notification every time we ask it to.  */
 107     public static interface EmitterMBean {
 108 
 109         public void emit();
 110     }
 111 
 112     public static class Emitter
 113             extends NotificationBroadcasterSupport implements EmitterMBean {
 114 
 115         public void emit() {
 116             sendNotification(new Notification(NOTIF_TYPE, this, seq++));
 117         }
 118 
 119         private long seq = 1;
 120     }
 121 
 122     /* Simple NotificationListener that allows you to wait until a
 123        notification has been received.  Since it uses a semaphore, you
 124        can wait either before or after the notification has in fact
 125        been received and it will work in either case.  */
 126     private static class SemaphoreListener implements NotificationListener {
 127 
 128         void await() throws InterruptedException {
 129             semaphore.acquire();
 130         }
 131 
 132         /* Ensure no extra notifications were received.  If we can acquire
 133            the semaphore, that means its release() method was called more
 134            times than its acquire() method, which means there were too
 135            many notifications.  */
 136         void checkUnavailable() throws Exception {
 137             if (semaphore.tryAcquire()) {
 138                 throw new Exception("Got extra notifications!");
 139             }
 140         }
 141 
 142         public void handleNotification(Notification n, Object h) {
 143             semaphore.release();
 144         }
 145 
 146         private final Semaphore semaphore = new Semaphore(0);
 147     }
 148 }