Path: blob/master/test/jdk/java/rmi/reliability/juicer/AppleUserImpl.java
41153 views
/*1* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/* @test24*25* @summary The juicer is the classic RMI stress test. The juicer makes26* a large number of concurrent, long running, remote method invocations27* between many threads which have exported remote objects. These28* threads use remote objects that carry on deep "two party"29* recursion. The juicer relies on Distributed Garbage Collection to30* unexport these remote objects when no more references are held to them.31* The two parties in the recursion are OrangeImpl and32* OrangeEchoImpl. OrangeImpl checks the base case of the recursion33* so that the program will exit.34*35* When the AppleUserImpl.main() method is invoked, the class binds an36* instance of itself in a registry. A second server process,37* an ApplicationServer, is started which looks up the recently38* bound AppleUser object. This server is either started up in39* the same VM or can optionally be started in a separate VM on the40* same host or on a different host. When this test is run on the41* RMI profile, ApplicationServer must be started by AppleUserImpl42* and the complete juicer runs in a single process.43*44* The second server process instructs the AppleUserImpl to "use" some apples.45* AppleUserImpl creates a new thread for each apple. These threads46* initiate the two party recursion.47*48* Each recursive call nests to a depth determined by this49* expression: (2 + Math.abs(random.nextInt() % (maxLevel + 1)),50* where maxLevel is a command line parameter. Thus each recursive51* call nests a random number of levels between 2 and maxLevel.52*53* The test ends when an exception is encountered or the stop time54* has been reached.55*56* @library ../../testlibrary57* @modules java.rmi/sun.rmi.registry58* java.rmi/sun.rmi.server59* java.rmi/sun.rmi.transport60* java.rmi/sun.rmi.transport.tcp61* @build TestLibrary62* Apple AppleEvent AppleImpl63* Orange OrangeEcho OrangeEchoImpl OrangeImpl64* ApplicationServer65*66* @run main/othervm/policy=security.policy AppleUserImpl -seconds 3067*68* @author Peter Jones, Nigel Daley69*/7071import java.rmi.NoSuchObjectException;72import java.rmi.RemoteException;73import java.rmi.registry.LocateRegistry;74import java.rmi.registry.Registry;75import java.rmi.server.UnicastRemoteObject;76import java.util.Random;77import java.util.logging.Level;78import java.util.logging.Logger;7980/**81* The AppleUserImpl class implements the behavior of the remote82* "apple user" objects exported by the server. The application server83* passes each of its remote "apple" objects to an apple user, and an84* AppleUserThread is created for each apple.85*/86public class AppleUserImpl extends UnicastRemoteObject implements AppleUser {87private static int registryPort = -1;88private static final Logger logger =89Logger.getLogger("reliability.appleuser");90private static int threadNum = 0;91private static long testDuration = 0;92private static int maxLevel = 7;93private static Exception status = null;94private static boolean finished = false;95private static boolean startTestNotified = false;96private static final Random random = new Random();97private static final Object lock = new Object();9899public AppleUserImpl() throws RemoteException {100}101102/**103* Allows the other server process to indicate that it is ready104* to start "juicing".105*/106public synchronized void startTest() throws RemoteException {107startTestNotified = true;108this.notifyAll();109}110111/**112* Allows the other server process to report an exception to this113* process and thereby terminate the test.114*/115public void reportException(Exception status) throws RemoteException {116synchronized (lock) {117this.status = status;118lock.notifyAll();119}120}121122/**123* "Use" supplied apple object. Create an AppleUserThread to124* stress it out.125*/126public synchronized void useApple(Apple apple) throws RemoteException {127String threadName = Thread.currentThread().getName();128logger.log(Level.FINEST,129threadName + ": AppleUserImpl.useApple(): BEGIN");130131AppleUserThread t =132new AppleUserThread("AppleUserThread-" + (++threadNum), apple);133t.start();134135logger.log(Level.FINEST,136threadName + ": AppleUserImpl.useApple(): END");137}138139/**140* The AppleUserThread class repeatedly invokes calls on its associated141* Apple object to stress the RMI system.142*/143class AppleUserThread extends Thread {144145final Apple apple;146147public AppleUserThread(String name, Apple apple) {148super(name);149this.apple = apple;150}151152public void run() {153int orangeNum = 0;154long stopTime = System.currentTimeMillis() + testDuration;155Logger logger = Logger.getLogger("reliability.appleuserthread");156157try {158do { // loop until stopTime is reached159160/*161* Notify apple with some apple events. This tests162* serialization of arrays.163*/164int numEvents = Math.abs(random.nextInt() % 5);165AppleEvent[] events = new AppleEvent[numEvents];166for (int i = 0; i < events.length; i++) {167events[i] = new AppleEvent(orangeNum % 3);168}169apple.notify(events);170171/*172* Request a new orange object be created in173* the application server.174*/175Orange orange = apple.newOrange(176"Orange(" + getName() + ")-" + (++orangeNum));177178/*179* Create a large message of random ints to pass to orange.180*/181int msgLength = 1000 + Math.abs(random.nextInt() % 3000);182int[] message = new int[msgLength];183for (int i = 0; i < message.length; i++) {184message[i] = random.nextInt();185}186187/*188* Invoke recursive call on the orange. Base case189* of recursion inverts messgage.190*/191OrangeEchoImpl echo = new OrangeEchoImpl(192"OrangeEcho(" + getName() + ")-" + orangeNum);193int[] response = orange.recurse(echo, message,1942 + Math.abs(random.nextInt() % (maxLevel + 1)));195196/*197* Verify message was properly inverted and not corrupted198* through all the recursive method invocations.199*/200if (response.length != message.length) {201throw new RuntimeException(202"ERROR: CORRUPTED RESPONSE: " +203"wrong length of returned array " + "(should be " +204message.length + ", is " + response.length + ")");205}206for (int i = 0; i < message.length; i++) {207if (~message[i] != response[i]) {208throw new RuntimeException(209"ERROR: CORRUPTED RESPONSE: " +210"at element " + i + "/" + message.length +211" of returned array (should be " +212Integer.toHexString(~message[i]) + ", is " +213Integer.toHexString(response[i]) + ")");214}215}216217try {218Thread.sleep(Math.abs(random.nextInt() % 10) * 1000);219} catch (InterruptedException e) {220}221222} while (System.currentTimeMillis() < stopTime);223224} catch (Exception e) {225status = e;226}227finished = true;228synchronized (lock) {229lock.notifyAll();230}231}232}233234private static void usage() {235System.err.println("Usage: AppleUserImpl [-hours <hours> | " +236"-seconds <seconds>]");237System.err.println(" [-maxLevel <maxLevel>]");238System.err.println(" [-othervm]");239System.err.println(" [-exit]");240System.err.println(" hours The number of hours to run the juicer.");241System.err.println(" The default is 0 hours.");242System.err.println(" seconds The number of seconds to run the juicer.");243System.err.println(" The default is 0 seconds.");244System.err.println(" maxLevel The maximum number of levels to ");245System.err.println(" recurse on each call.");246System.err.println(" The default is 7 levels.");247System.err.println(" othervm If present, the VM will wait for the");248System.err.println(" ApplicationServer to start in");249System.err.println(" another process.");250System.err.println(" The default is to run everything in");251System.err.println(" a single VM.");252System.err.println(" exit If present, the VM will call");253System.err.println(" System.exit() when main() finishes.");254System.err.println(" The default is to not call");255System.err.println(" System.exit().");256System.err.println();257}258259/**260* Entry point for the "juicer" server process. Create and export261* an apple user implementation in an rmiregistry running on localhost.262*/263public static void main(String[] args) {264String durationString = null;265boolean othervm = false;266boolean exit = false;267try {268// parse command line args269for (int i = 0; i < args.length ; i++ ) {270String arg = args[i];271if (arg.equals("-hours")) {272if (durationString != null) {273usage();274}275i++;276int hours = Integer.parseInt(args[i]);277durationString = hours + " hours";278testDuration = hours * 60 * 60 * 1000;279} else if (arg.equals("-seconds")) {280if (durationString != null) {281usage();282}283i++;284long seconds = Long.parseLong(args[i]);285durationString = seconds + " seconds";286testDuration = seconds * 1000;287} else if (arg.equals("-maxLevel")) {288i++;289maxLevel = Integer.parseInt(args[i]);290} else if (arg.equals("-othervm")) {291othervm = true;292} else if (arg.equals("-exit")) {293exit = true;294} else {295usage();296}297}298if (durationString == null) {299durationString = testDuration + " milliseconds";300}301} catch (Throwable t) {302usage();303throw new RuntimeException("TEST FAILED: Bad argument");304}305306AppleUserImpl user = null;307long startTime = 0;308Thread server = null;309int exitValue = 0;310try {311user = new AppleUserImpl();312313synchronized (user) {314// create new registry and bind new AppleUserImpl in registry315Registry registry = TestLibrary.createRegistryOnEphemeralPort();316registryPort = TestLibrary.getRegistryPort(registry);317LocateRegistry.getRegistry(registryPort).rebind("AppleUser",318user);319320// start the other server if applicable321if (othervm) {322// the other server must be running in a separate process323logger.log(Level.INFO, "Application server must be " +324"started in separate process");325} else {326Class app = Class.forName("ApplicationServer");327java.lang.reflect.Constructor appConstructor =328app.getDeclaredConstructor(new Class[] {Integer.TYPE});329server = new Thread((Runnable) appConstructor.newInstance(registryPort));330logger.log(Level.INFO, "Starting application server " +331"in same process");332server.start();333}334335// wait for other server to call startTest method336logger.log(Level.INFO, "Waiting for application server " +337"process to start");338while (!startTestNotified) {339user.wait();340}341}342343startTime = System.currentTimeMillis();344logger.log(Level.INFO, "Test starting");345346// wait for exception to be reported or first thread to complete347logger.log(Level.INFO, "Waiting " + durationString + " for " +348"test to complete or exception to be thrown");349350synchronized (lock) {351while (status == null && !finished) {352lock.wait();353}354}355356if (status != null) {357throw new RuntimeException("TEST FAILED: "358+ "juicer server reported an exception", status);359} else {360logger.log(Level.INFO, "TEST PASSED");361}362} catch (Exception e) {363logger.log(Level.INFO, "TEST FAILED");364exitValue = 1;365if (exit) {366e.printStackTrace();367}368throw new RuntimeException("TEST FAILED: "369+ "unexpected exception", e);370} finally {371long actualDuration = System.currentTimeMillis() - startTime;372logger.log(Level.INFO, "Test finished");373try {374UnicastRemoteObject.unexportObject(user, true);375} catch (NoSuchObjectException ignore) {376}377logger.log(Level.INFO, "Test duration was " +378(actualDuration/1000) + " seconds " +379"(" + (actualDuration/3600000) + " hours)");380System.gc(); System.gc();381if (exit) {382System.exit(exitValue);383}384}385}386}387388389