1 /*
   2  * Copyright (c) 2016, 2018, 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 8159377
  27  * @library /test/lib
  28  * @summary Tests ObjectFilter on default agent
  29  * @author Harsha Wardhana B
  30  * @modules java.management
  31  * @build DefaultAgentFilterTest
  32  * @run main/othervm/timeout=600 -XX:+UsePerfData DefaultAgentFilterTest
  33  */
  34 import java.io.EOFException;
  35 import java.io.File;
  36 import java.io.IOException;
  37 import java.io.InvalidClassException;
  38 import java.io.Serializable;
  39 import java.lang.reflect.InvocationTargetException;
  40 import java.net.BindException;
  41 import java.rmi.UnmarshalException;
  42 import java.rmi.registry.LocateRegistry;
  43 import java.rmi.registry.Registry;
  44 import java.util.ArrayList;
  45 import java.util.Arrays;
  46 import java.util.HashMap;
  47 import java.util.HashSet;
  48 import java.util.List;
  49 import java.util.Map;
  50 import java.util.concurrent.atomic.AtomicBoolean;
  51 import javax.management.MBeanServerConnection;
  52 import javax.management.ObjectName;
  53 import javax.management.remote.JMXConnector;
  54 import javax.management.remote.JMXConnectorFactory;
  55 import javax.management.remote.JMXServiceURL;
  56 
  57 import jdk.test.lib.process.ProcessTools;
  58 import jdk.test.lib.Utils;
  59 
  60 public class DefaultAgentFilterTest {
  61 
  62     public static class MyTestObject implements Serializable {
  63 
  64         String a;
  65         int id;
  66     }
  67 
  68     public interface TestMBean {
  69 
  70         public void op1(HashSet<Object> params);
  71 
  72         public void op2(String s, HashSet<String> params);
  73 
  74         public void op3(MyTestObject obj, String s, HashMap<String, String> param);
  75     }
  76 
  77     public static class Test implements TestMBean {
  78 
  79         @Override
  80         public void op1(HashSet<Object> params) {
  81             System.out.println("Invoked op1");
  82         }
  83 
  84         @Override
  85         public void op2(String s, HashSet<String> params) {
  86             System.out.println("Invoked op2");
  87         }
  88 
  89         @Override
  90         public void op3(MyTestObject obj, String s, HashMap<String, String> param) {
  91             System.out.println("Invoked op3");
  92         }
  93     }
  94 
  95     private static class TestAppRun implements AutoCloseable {
  96 
  97         private Process p;
  98         private final ProcessBuilder pb;
  99         private final String name;
 100         private final AtomicBoolean started = new AtomicBoolean(false);
 101         private volatile long pid = -1;
 102 
 103         public TestAppRun(ProcessBuilder pb, String name) {
 104             this.pb = pb;
 105             this.name = name;
 106         }
 107 
 108         public synchronized void start() throws Exception {
 109             if (started.compareAndSet(false, true)) {
 110                 try {
 111                     AtomicBoolean error = new AtomicBoolean(false);
 112                     AtomicBoolean bindError = new AtomicBoolean(false);
 113                     p = ProcessTools.startProcess(
 114                             TEST_APP_NAME + "{" + name + "}",
 115                             pb,
 116                             (line) -> {
 117                                 if (line.toLowerCase().contains("exception")
 118                                 || line.toLowerCase().contains("error")) {
 119                                     error.set(true);
 120                                 }
 121                                 bindError.set(line.toLowerCase().contains("bindexception"));
 122                                 return true;
 123                             });
 124                     if (bindError.get()) {
 125                         throw new BindException("Process could not be started");
 126                     } else if (error.get()) {
 127                         throw new RuntimeException();
 128                     }
 129                     pid = p.pid();
 130                 } catch (Exception ex) {
 131                     if (p != null) {
 132                         p.destroy();
 133                         p.waitFor();
 134                     }
 135                     throw ex;
 136                 }
 137             }
 138         }
 139 
 140         public long getPid() {
 141             return pid;
 142         }
 143 
 144         public synchronized void stop()
 145                 throws IOException, InterruptedException {
 146             if (started.compareAndSet(true, false)) {
 147                 p.getOutputStream().write(0);
 148                 p.getOutputStream().flush();
 149                 int ec = p.waitFor();
 150                 if (ec != 0) {
 151                     StringBuilder msg = new StringBuilder();
 152                     msg.append("Test application '").append(name);
 153                     msg.append("' failed with exit code: ");
 154                     msg.append(ec);
 155                     System.err.println(msg);
 156                 }
 157             }
 158         }
 159 
 160         @Override
 161         public void close() throws Exception {
 162             stop();
 163         }
 164     }
 165 
 166     private static final String TEST_APP_NAME = "TestApp";
 167 
 168     private static void testDefaultAgent(String propertyFile) throws Exception {
 169         int port = Utils.getFreePort();
 170         String propFile = System.getProperty("test.src") + File.separator + propertyFile;
 171         List<String> pbArgs = new ArrayList<>(Arrays.asList(
 172                 "-cp",
 173                 System.getProperty("test.class.path"),
 174                 "-XX:+UsePerfData"
 175         ));
 176         String[] args = new String[]{
 177             "-Dcom.sun.management.jmxremote.port=" + port,
 178             "-Dcom.sun.management.jmxremote.authenticate=false",
 179             "-Dcom.sun.management.jmxremote.ssl=false",
 180             "-Dcom.sun.management.config.file=" + propFile
 181         };
 182         pbArgs.addAll(Arrays.asList(args));
 183         pbArgs.add(TEST_APP_NAME);
 184 
 185         ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
 186                 pbArgs.toArray(new String[pbArgs.size()])
 187         );
 188 
 189         try (TestAppRun s = new TestAppRun(pb, DefaultAgentFilterTest.class.getSimpleName())) {
 190             s.start();
 191             JMXServiceURL url = testConnect(port);
 192             testMBeanOperations(url);
 193         }
 194     }
 195 
 196     private static JMXServiceURL testConnect(int port) throws Exception {
 197         EOFException lastException = null;
 198         JMXServiceURL url = null;
 199         // factor adjusted timeout (5 seconds) for the RMI to become available
 200         long timeout = System.currentTimeMillis() + Utils.adjustTimeout(5000);
 201         do {
 202             lastException = null;
 203             try {
 204                 Registry registry = LocateRegistry.getRegistry(port);
 205                 String[] relist = registry.list();
 206                 for (int i = 0; i < relist.length; ++i) {
 207                     System.out.println("Got registry: " + relist[i]);
 208                 }
 209                 String jmxUrlStr = String.format(
 210                         "service:jmx:rmi:///jndi/rmi://localhost:%d/jmxrmi",
 211                         port);
 212                 url = new JMXServiceURL(jmxUrlStr);
 213 
 214                 try (JMXConnector c = JMXConnectorFactory.connect(url, null)) {
 215                     MBeanServerConnection conn = c.getMBeanServerConnection();
 216                     ObjectName name = new ObjectName("jtreg:type=Test");
 217                     conn.createMBean(Test.class.getName(), name);
 218                 }
 219             } catch (Exception ex) {
 220                 if (ex instanceof EOFException) {
 221                     lastException = (EOFException) ex;
 222                     System.out.println("Error establishing RMI connection. Retrying in 500ms.");
 223                     Thread.sleep(500);
 224                 } else {
 225                     throw ex;
 226                 }
 227             }
 228         } while (lastException != null && System.currentTimeMillis() < timeout);
 229         if (lastException != null) {
 230             throw lastException;
 231         }
 232         return url;
 233     }
 234 
 235     public static void main(String[] args) throws Exception {
 236         System.out.println("---" + DefaultAgentFilterTest.class.getName() + "-main: starting ...");
 237 
 238         boolean retry = false;
 239         do {
 240             try {
 241                 // blacklist String
 242                 testDefaultAgent("mgmt1.properties");
 243                 System.out.println("----\tTest FAILED !!");
 244                 throw new RuntimeException("---" + DefaultAgentFilterTest.class.getName() + " - No exception reported");
 245             } catch (Exception ex) {
 246                 if (ex instanceof InvocationTargetException) {
 247                     if (ex.getCause() instanceof BindException
 248                             || ex.getCause() instanceof java.rmi.ConnectException) {
 249                         System.out.println("Failed to allocate ports. Retrying ...");
 250                         retry = true;
 251                     }
 252                 } else if (ex instanceof InvalidClassException) {
 253                     System.out.println("----\tTest PASSED !!");
 254                 } else if (ex instanceof UnmarshalException
 255                         && ((UnmarshalException) ex).getCause() instanceof InvalidClassException) {
 256                     System.out.println("----\tTest PASSED !!");
 257                 } else {
 258                     System.out.println(ex);
 259                     System.out.println("----\tTest FAILED !!");
 260                     throw ex;
 261                 }
 262             }
 263         } while (retry);
 264         retry = false;
 265         do {
 266             try {
 267                 // blacklist non-existent class
 268                 testDefaultAgent("mgmt2.properties");
 269                 System.out.println("----\tTest PASSED !!");
 270             } catch (Exception ex) {
 271                 if (ex instanceof InvocationTargetException) {
 272                     if (ex.getCause() instanceof BindException
 273                             || ex.getCause() instanceof java.rmi.ConnectException) {
 274                         System.out.println("Failed to allocate ports. Retrying ...");
 275                         retry = true;
 276                     }
 277                 } else {
 278                     System.out.println(ex);
 279                     System.out.println("----\tTest FAILED !!");
 280                     throw ex;
 281                 }
 282             }
 283         } while (retry);
 284 
 285         System.out.println("---" + DefaultAgentFilterTest.class.getName() + "-main: finished ...");
 286     }
 287 
 288     private static void testMBeanOperations(JMXServiceURL serverUrl) throws Exception {
 289         Map<String, Object> clientEnv = new HashMap<>(1);
 290         ObjectName name = new ObjectName("jtreg:type=Test");
 291         try (JMXConnector client = JMXConnectorFactory.connect(serverUrl, clientEnv)) {
 292             MBeanServerConnection conn = client.getMBeanServerConnection();
 293 
 294             HashSet<String> set = new HashSet<>();
 295             set.add("test1");
 296             set.add("test2");
 297 
 298             String a = "A";
 299 
 300             Object[] params1 = {set};
 301             String[] sig1 = {HashSet.class.getName()};
 302             conn.invoke(name, "op1", params1, sig1);
 303 
 304             Object[] params2 = {a, set};
 305             String[] sig2 = {String.class.getName(), HashSet.class.getName()};
 306             conn.invoke(name, "op2", params2, sig2);
 307 
 308             HashMap<String, String> map = new HashMap<>();
 309             map.put("a", "A");
 310             map.put("b", "B");
 311 
 312             Object[] params3 = {new MyTestObject(), a, map};
 313             String[] sig3 = {MyTestObject.class.getName(), String.class.getName(),
 314                 HashMap.class.getName()};
 315             conn.invoke(name, "op3", params3, sig3);
 316         }
 317     }
 318 }
 319 
 320 class TestApp {
 321 
 322     private static void doSomething() throws IOException {
 323         int r = System.in.read();
 324         System.out.println("read: " + r);
 325     }
 326 
 327     public static void main(String args[]) throws Exception {
 328         System.out.println("main enter");
 329         System.out.flush();
 330         doSomething();
 331         System.out.println("main exit");
 332     }
 333 }