0N/A/*
2362N/A * Copyright (c) 2004, 2007, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A/*
0N/A * @test
0N/A * @bug 6190873
0N/A * @summary Tests that thread creation can use a user-supplied Executor
0N/A * @author Eamonn McManus
0N/A * @run clean ExecutorTest
0N/A * @run build ExecutorTest
0N/A * @run main ExecutorTest
0N/A */
0N/A
0N/Aimport java.lang.reflect.*;
0N/Aimport java.net.MalformedURLException;
0N/A
0N/Aimport java.util.*;
0N/Aimport java.util.concurrent.*;
0N/A
0N/Aimport javax.management.*;
0N/Aimport javax.management.remote.*;
0N/A
0N/A/*
0N/A When you create a JMXConnector client, you can supply a
0N/A "fetch-notifications Executor", which is a
0N/A java.util.concurrent.Executor that will be used each time the
0N/A connector client wants to call RMIConnection.fetchNotifications.
0N/A This is a hook that allows users to make that potentially-blocking
0N/A call from within a thread pool or the like. If you have very many
0N/A connections, you can potentially share the work of
0N/A fetchNotifications calls among a number of threads that is less than
0N/A the number of connections, decreasing thread usage at the expense of
0N/A increased latency.
0N/A
0N/A This test checks that the environment property does in fact work.
0N/A It creates a connection without that property and ensures that
0N/A notification sending does in fact work (with the default Executor).
0N/A Then it creates a connection with the property set to an Executor
0N/A that records how many times its execute method is invoked.
0N/A Notifications are sent one by one and each time the test waits for
0N/A the notification to be received. This implies that
0N/A fetchNotifications will be executed at least as many times as there
0N/A are notifications. Since each fetchNotifications call is supposed
0N/A to happen as an Executor.execute call, if Executor.execute has been
0N/A called fewer times then there were notifications, we know that the
0N/A Executor is not being used correctly.
0N/A */
0N/Apublic class ExecutorTest {
0N/A private static final String EXECUTOR_PROPERTY =
0N/A "jmx.remote.x.fetch.notifications.executor";
0N/A private static final String NOTIF_TYPE = "test.type";
0N/A
0N/A public static void main(String[] args) throws Exception {
0N/A String lastfail = null;
0N/A for (String proto : new String[] {"rmi", "iiop", "jmxmp"}) {
0N/A JMXServiceURL url = new JMXServiceURL(proto, null, 0);
0N/A JMXConnectorServer cs;
0N/A MBeanServer mbs = MBeanServerFactory.newMBeanServer();
0N/A try {
0N/A // Create server just to see if the protocol is supported
0N/A cs = JMXConnectorServerFactory.newJMXConnectorServer(url,
0N/A null,
0N/A mbs);
0N/A } catch (MalformedURLException e) {
0N/A System.out.println();
0N/A System.out.println("Ignoring protocol: " + proto);
0N/A continue;
0N/A }
0N/A String fail;
0N/A try {
0N/A fail = test(proto);
0N/A if (fail != null)
0N/A System.out.println("TEST FAILED: " + fail);
0N/A } catch (Exception e) {
0N/A e.printStackTrace(System.out);
0N/A fail = e.toString();
0N/A }
0N/A if (lastfail == null)
0N/A lastfail = fail;
0N/A }
0N/A if (lastfail == null)
0N/A return;
0N/A System.out.println();
0N/A System.out.println("TEST FAILED");
0N/A throw new Exception("Test failed: " + lastfail);
0N/A }
0N/A
0N/A private static enum TestType {NO_EXECUTOR, NULL_EXECUTOR, COUNT_EXECUTOR};
0N/A
0N/A private static String test(String proto) throws Exception {
0N/A System.out.println();
0N/A System.out.println("TEST: " + proto);
0N/A ClassLoader myClassLoader =
0N/A CountInvocationHandler.class.getClassLoader();
0N/A ExecutorService wrappedExecutor = Executors.newCachedThreadPool();
0N/A CountInvocationHandler executorHandler =
0N/A new CountInvocationHandler(wrappedExecutor);
0N/A Executor countExecutor = (Executor)
0N/A Proxy.newProxyInstance(myClassLoader,
0N/A new Class<?>[] {Executor.class},
0N/A executorHandler);
0N/A
0N/A JMXServiceURL url = new JMXServiceURL(proto, null, 0);
0N/A
0N/A for (TestType test : TestType.values()) {
0N/A Map<String, Executor> env = new HashMap<String, Executor>();
0N/A switch (test) {
0N/A case NO_EXECUTOR:
0N/A System.out.println("Test with no executor in env");
0N/A break;
0N/A case NULL_EXECUTOR:
0N/A System.out.println("Test with null executor in env");
0N/A env.put(EXECUTOR_PROPERTY, null);
0N/A break;
0N/A case COUNT_EXECUTOR:
0N/A System.out.println("Test with non-null executor in env");
0N/A env.put(EXECUTOR_PROPERTY, countExecutor);
0N/A break;
0N/A }
0N/A MBeanServer mbs = MBeanServerFactory.newMBeanServer();
0N/A ObjectName emitName = new ObjectName("blah:type=Emitter");
0N/A mbs.registerMBean(new Emitter(), emitName);
0N/A JMXConnectorServer cs =
0N/A JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
0N/A cs.start();
0N/A JMXServiceURL addr = cs.getAddress();
0N/A JMXConnector cc = JMXConnectorFactory.connect(addr, env);
0N/A MBeanServerConnection mbsc = cc.getMBeanServerConnection();
0N/A EmitterMBean emitter = (EmitterMBean)
0N/A MBeanServerInvocationHandler.newProxyInstance(mbsc,
0N/A emitName,
0N/A EmitterMBean.class,
0N/A false);
0N/A SemaphoreListener listener = new SemaphoreListener();
0N/A NotificationFilterSupport filter = new NotificationFilterSupport();
0N/A filter.enableType(NOTIF_TYPE);
0N/A mbsc.addNotificationListener(emitName, listener, filter, null);
0N/A final int NOTIF_COUNT = 10;
0N/A for (int i = 0; i < NOTIF_COUNT; i++) {
0N/A emitter.emit();
0N/A listener.await();
0N/A }
0N/A Thread.sleep(1);
0N/A listener.checkUnavailable();
0N/A System.out.println("Got notifications");
0N/A cc.close();
0N/A cs.stop();
0N/A if (test == TestType.COUNT_EXECUTOR) {
0N/A Method m = Executor.class.getMethod("execute", Runnable.class);
0N/A Integer count = executorHandler.methodCount.get(m);
0N/A if (count == null || count < NOTIF_COUNT)
0N/A return "Incorrect method count for execute: " + count;
0N/A System.out.println("Executor was called enough times");
0N/A }
0N/A }
0N/A
0N/A wrappedExecutor.shutdown();
0N/A return null;
0N/A }
0N/A
0N/A /* Simple MBean that sends a notification every time we ask it to. */
0N/A public static interface EmitterMBean {
0N/A public void emit();
0N/A }
0N/A
0N/A public static class Emitter
0N/A extends NotificationBroadcasterSupport implements EmitterMBean {
0N/A public void emit() {
0N/A sendNotification(new Notification(NOTIF_TYPE, this, seq++));
0N/A }
0N/A
0N/A private long seq = 1;
0N/A }
0N/A
0N/A /* Simple NotificationListener that allows you to wait until a
0N/A notification has been received. Since it uses a semaphore, you
0N/A can wait either before or after the notification has in fact
0N/A been received and it will work in either case. */
0N/A private static class SemaphoreListener implements NotificationListener {
0N/A void await() throws InterruptedException {
0N/A semaphore.acquire();
0N/A }
0N/A
0N/A /* Ensure no extra notifications were received. If we can acquire
0N/A the semaphore, that means its release() method was called more
0N/A times than its acquire() method, which means there were too
0N/A many notifications. */
0N/A void checkUnavailable() throws Exception {
0N/A if (semaphore.tryAcquire())
0N/A throw new Exception("Got extra notifications!");
0N/A }
0N/A
0N/A public void handleNotification(Notification n, Object h) {
0N/A semaphore.release();
0N/A }
0N/A
0N/A private final Semaphore semaphore = new Semaphore(0);
0N/A }
0N/A
0N/A /* Generic InvocationHandler that forwards all methods to a wrapped
0N/A object, but counts for each method how many times it was invoked. */
0N/A private static class CountInvocationHandler implements InvocationHandler {
0N/A final Map<Method, Integer> methodCount =
0N/A new HashMap<Method, Integer>();
0N/A private final Object wrapped;
0N/A
0N/A public CountInvocationHandler(Object wrapped) {
0N/A this.wrapped = wrapped;
0N/A }
0N/A
0N/A public Object invoke(Object proxy, Method method, Object[] args)
0N/A throws Throwable {
0N/A synchronized (methodCount) {
0N/A Integer count = methodCount.get(method);
0N/A if (count == null)
0N/A count = 0;
0N/A methodCount.put(method, count + 1);
0N/A }
0N/A return method.invoke(wrapped, (Object[]) args);
0N/A }
0N/A }
0N/A}