1 /* 2 * Copyright (c) 2003, 2008, 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 /* @test 25 * 26 * @summary The juicer is the classic RMI stress test. The juicer makes 27 * a large number of concurrent, long running, remote method invocations 28 * between many threads which have exported remote objects. These 29 * threads use remote objects that carry on deep "two party" 30 * recursion. The juicer relies on Distributed Garbage Collection to 31 * unexport these remote objects when no more references are held to them. 32 * The two parties in the recursion are OrangeImpl and 33 * OrangeEchoImpl. OrangeImpl checks the base case of the recursion 34 * so that the program will exit. 35 * 36 * When the AppleUserImpl.main() method is invoked, the class binds an 37 * instance of itself in a registry. A second server process, 38 * an ApplicationServer, is started which looks up the recently 39 * bound AppleUser object. This server is either started up in 40 * the same VM or can optionally be started in a separate VM on the 41 * same host or on a different host. When this test is run on the 42 * RMI profile, ApplicationServer must be started by AppleUserImpl 43 * and the complete juicer runs in a single process. 44 * 45 * The second server process instructs the AppleUserImpl to "use" some apples. 46 * AppleUserImpl creates a new thread for each apple. These threads 47 * initiate the two party recursion. 48 * 49 * Each recursive call nests to a depth determined by this 50 * expression: (2 + Math.abs(random.nextInt() % (maxLevel + 1)), 51 * where maxLevel is a command line parameter. Thus each recursive 52 * call nests a random number of levels between 2 and maxLevel. 53 * 54 * The test ends when an exception is encountered or the stop time 55 * has been reached. 56 * 57 * @library ../../testlibrary 58 * 59 * @build Apple AppleEvent AppleImpl AppleUserImpl 60 * @build Orange OrangeEcho OrangeEchoImpl OrangeImpl 61 * @build ApplicationServer 62 * 63 * @run main/othervm/policy=security.policy AppleUserImpl -seconds 30 64 * 65 * @author Peter Jones, Nigel Daley 66 */ 67 68 import java.rmi.RemoteException; 69 import java.rmi.NoSuchObjectException; 70 import java.rmi.server.UnicastRemoteObject; 71 import java.rmi.registry.LocateRegistry; 72 import java.util.Random; 73 import java.util.logging.Logger; 74 import java.util.logging.Level; 75 76 /** 77 * The AppleUserImpl class implements the behavior of the remote 78 * "apple user" objects exported by the server. The application server 79 * passes each of its remote "apple" objects to an apple user, and an 80 * AppleUserThread is created for each apple. 81 */ 82 public class AppleUserImpl extends UnicastRemoteObject implements AppleUser { 83 84 private static final Logger logger = 85 Logger.getLogger("reliability.appleuser"); 86 private static int threadNum = 0; 87 private static long testDuration = 0; 88 private static int maxLevel = 7; 89 private static Exception status = null; 90 private static boolean finished = false; 91 private static boolean startTestNotified = false; 92 private static final Random random = new Random(); 93 private static final Object lock = new Object(); 94 95 public AppleUserImpl() throws RemoteException { 96 } 97 98 /** 99 * Allows the other server process to indicate that it is ready 100 * to start "juicing". 101 */ 102 public synchronized void startTest() throws RemoteException { 103 startTestNotified = true; 104 this.notifyAll(); 105 } 106 107 /** 108 * Allows the other server process to report an exception to this 109 * process and thereby terminate the test. 110 */ 111 public void reportException(Exception status) throws RemoteException { 112 synchronized (lock) { 113 this.status = status; 114 lock.notifyAll(); 115 } 116 } 117 118 /** 119 * "Use" supplied apple object. Create an AppleUserThread to 120 * stress it out. 121 */ 122 public synchronized void useApple(Apple apple) throws RemoteException { 123 String threadName = Thread.currentThread().getName(); 124 logger.log(Level.FINEST, 125 threadName + ": AppleUserImpl.useApple(): BEGIN"); 126 127 AppleUserThread t = 128 new AppleUserThread("AppleUserThread-" + (++threadNum), apple); 129 t.start(); 130 131 logger.log(Level.FINEST, 132 threadName + ": AppleUserImpl.useApple(): END"); 133 } 134 135 /** 136 * The AppleUserThread class repeatedly invokes calls on its associated 137 * Apple object to stress the RMI system. 138 */ 139 class AppleUserThread extends Thread { 140 141 final Apple apple; 142 143 public AppleUserThread(String name, Apple apple) { 144 super(name); 145 this.apple = apple; 146 } 147 148 public void run() { 149 int orangeNum = 0; 150 long stopTime = System.currentTimeMillis() + testDuration; 151 Logger logger = Logger.getLogger("reliability.appleuserthread"); 152 153 try { 154 do { // loop until stopTime is reached 155 156 /* 157 * Notify apple with some apple events. This tests 158 * serialization of arrays. 159 */ 160 int numEvents = Math.abs(random.nextInt() % 5); 161 AppleEvent[] events = new AppleEvent[numEvents]; 162 for (int i = 0; i < events.length; i++) { 163 events[i] = new AppleEvent(orangeNum % 3); 164 } 165 apple.notify(events); 166 167 /* 168 * Request a new orange object be created in 169 * the application server. 170 */ 171 Orange orange = apple.newOrange( 172 "Orange(" + getName() + ")-" + (++orangeNum)); 173 174 /* 175 * Create a large message of random ints to pass to orange. 176 */ 177 int msgLength = 1000 + Math.abs(random.nextInt() % 3000); 178 int[] message = new int[msgLength]; 179 for (int i = 0; i < message.length; i++) { 180 message[i] = random.nextInt(); 181 } 182 183 /* 184 * Invoke recursive call on the orange. Base case 185 * of recursion inverts messgage. 186 */ 187 OrangeEchoImpl echo = new OrangeEchoImpl( 188 "OrangeEcho(" + getName() + ")-" + orangeNum); 189 int[] response = orange.recurse(echo, message, 190 2 + Math.abs(random.nextInt() % (maxLevel + 1))); 191 192 /* 193 * Verify message was properly inverted and not corrupted 194 * through all the recursive method invocations. 195 */ 196 if (response.length != message.length) { 197 throw new RuntimeException( 198 "ERROR: CORRUPTED RESPONSE: " + 199 "wrong length of returned array " + "(should be " + 200 message.length + ", is " + response.length + ")"); 201 } 202 for (int i = 0; i < message.length; i++) { 203 if (~message[i] != response[i]) { 204 throw new RuntimeException( 205 "ERROR: CORRUPTED RESPONSE: " + 206 "at element " + i + "/" + message.length + 207 " of returned array (should be " + 208 Integer.toHexString(~message[i]) + ", is " + 209 Integer.toHexString(response[i]) + ")"); 210 } 211 } 212 213 try { 214 Thread.sleep(Math.abs(random.nextInt() % 10) * 1000); 215 } catch (InterruptedException e) { 216 } 217 218 } while (System.currentTimeMillis() < stopTime); 219 220 } catch (Exception e) { 221 status = e; 222 } 223 finished = true; 224 synchronized (lock) { 225 lock.notifyAll(); 226 } 227 } 228 } 229 230 private static void usage() { 231 System.err.println("Usage: AppleUserImpl [-hours <hours> | " + 232 "-seconds <seconds>]"); 233 System.err.println(" [-maxLevel <maxLevel>]"); 234 System.err.println(" [-othervm]"); 235 System.err.println(" [-exit]"); 236 System.err.println(" hours The number of hours to run the juicer."); 237 System.err.println(" The default is 0 hours."); 238 System.err.println(" seconds The number of seconds to run the juicer."); 239 System.err.println(" The default is 0 seconds."); 240 System.err.println(" maxLevel The maximum number of levels to "); 241 System.err.println(" recurse on each call."); 242 System.err.println(" The default is 7 levels."); 243 System.err.println(" othervm If present, the VM will wait for the"); 244 System.err.println(" ApplicationServer to start in"); 245 System.err.println(" another process."); 246 System.err.println(" The default is to run everything in"); 247 System.err.println(" a single VM."); 248 System.err.println(" exit If present, the VM will call"); 249 System.err.println(" System.exit() when main() finishes."); 250 System.err.println(" The default is to not call"); 251 System.err.println(" System.exit()."); 252 System.err.println(); 253 } 254 255 /** 256 * Entry point for the "juicer" server process. Create and export 257 * an apple user implementation in an rmiregistry running on localhost. 258 */ 259 public static void main(String[] args) { 260 String durationString = null; 261 boolean othervm = false; 262 boolean exit = false; 263 try { 264 // parse command line args 265 for (int i = 0; i < args.length ; i++ ) { 266 String arg = args[i]; 267 if (arg.equals("-hours")) { 268 if (durationString != null) { 269 usage(); 270 } 271 i++; 272 int hours = Integer.parseInt(args[i]); 273 durationString = hours + " hours"; 274 testDuration = hours * 60 * 60 * 1000; 275 } else if (arg.equals("-seconds")) { 276 if (durationString != null) { 277 usage(); 278 } 279 i++; 280 long seconds = Long.parseLong(args[i]); 281 durationString = seconds + " seconds"; 282 testDuration = seconds * 1000; 283 } else if (arg.equals("-maxLevel")) { 284 i++; 285 maxLevel = Integer.parseInt(args[i]); 286 } else if (arg.equals("-othervm")) { 287 othervm = true; 288 } else if (arg.equals("-exit")) { 289 exit = true; 290 } else { 291 usage(); 292 } 293 } 294 if (durationString == null) { 295 durationString = testDuration + " milliseconds"; 296 } 297 } catch (Throwable t) { 298 usage(); 299 throw new RuntimeException("TEST FAILED: Bad argument"); 300 } 301 302 AppleUserImpl user = null; 303 long startTime = 0; 304 Thread server = null; 305 int exitValue = 0; 306 try { 307 user = new AppleUserImpl(); 308 309 synchronized (user) { 310 // create new registry and bind new AppleUserImpl in registry 311 LocateRegistry.createRegistry(2006); 312 LocateRegistry.getRegistry(2006).rebind("AppleUser",user); 313 314 // start the other server if applicable 315 if (othervm) { 316 // the other server must be running in a separate process 317 logger.log(Level.INFO, "Application server must be " + 318 "started in separate process"); 319 } else { 320 Class app = Class.forName("ApplicationServer"); 321 server = new Thread((Runnable) app.newInstance()); 322 logger.log(Level.INFO, "Starting application server " + 323 "in same process"); 324 server.start(); 325 } 326 327 // wait for other server to call startTest method 328 logger.log(Level.INFO, "Waiting for application server " + 329 "process to start"); 330 while (!startTestNotified) { 331 user.wait(); 332 } 333 } 334 335 startTime = System.currentTimeMillis(); 336 logger.log(Level.INFO, "Test starting"); 337 338 // wait for exception to be reported or first thread to complete 339 logger.log(Level.INFO, "Waiting " + durationString + " for " + 340 "test to complete or exception to be thrown"); 341 342 synchronized (lock) { 343 while (status == null && !finished) { 344 lock.wait(); 345 } 346 } 347 348 if (status != null) { 349 throw new RuntimeException("TEST FAILED: " 350 + "juicer server reported an exception", status); 351 } else { 352 logger.log(Level.INFO, "TEST PASSED"); 353 } 354 } catch (Exception e) { 355 logger.log(Level.INFO, "TEST FAILED"); 356 exitValue = 1; 357 if (exit) { 358 e.printStackTrace(); 359 } 360 throw new RuntimeException("TEST FAILED: " 361 + "unexpected exception", e); 362 } finally { 363 long actualDuration = System.currentTimeMillis() - startTime; 364 logger.log(Level.INFO, "Test finished"); 365 try { 366 UnicastRemoteObject.unexportObject(user, true); 367 } catch (NoSuchObjectException ignore) { 368 } 369 logger.log(Level.INFO, "Test duration was " + 370 (actualDuration/1000) + " seconds " + 371 "(" + (actualDuration/3600000) + " hours)"); 372 System.gc(); System.gc(); 373 if (exit) { 374 System.exit(exitValue); 375 } 376 } 377 } 378 }