alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (TestScaffold.java)

This example Java source code file (TestScaffold.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

arginfo, breakpointevent, eventhandler, illegalargumentexception, interruptedexception, iterator, jdi, list, referencetype, stepevent, string, targetadapter, util, vmdisconnectevent

The TestScaffold.java Java example source code

/*
 * Copyright (c) 2002, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

import com.sun.jdi.*;
import com.sun.jdi.request.*;
import com.sun.jdi.event.*;
import java.util.*;
import java.io.*;

/**
 * Framework used by all JDI regression tests
 */
abstract public class TestScaffold extends TargetAdapter {
    private boolean shouldTrace = false;
    private VMConnection connection;
    private VirtualMachine vm;
    private EventRequestManager requestManager;
    private List listeners = Collections.synchronizedList(new LinkedList());

    /**
     * We create a VMDeathRequest, SUSPEND_ALL, to sync the BE and FE.
     */
    //private VMDeathRequest ourVMDeathRequest = null;
    Object ourVMDeathRequest = null;

    /**
     * We create an ExceptionRequest, SUSPEND_NONE so that we can
     * catch it and output a msg if an exception occurs in the
     * debuggee.
     */
    private ExceptionRequest ourExceptionRequest = null;

    /**
     * If we do catch an uncaught exception, we set this true
     * so the testcase can find out if it wants to.
     */
    private boolean exceptionCaught = false;
    ThreadReference vmStartThread = null;
    boolean vmDied = false;
    boolean vmDisconnected = false;
    final String[] args;
    protected boolean testFailed = false;

    static private class ArgInfo {
        String targetVMArgs = "";
        String targetAppCommandLine = "";
        String connectorSpec = "com.sun.jdi.CommandLineLaunch:";
        int traceFlags = 0;
    }

    /**
     * An easy way to sleep for awhile
     */
    public void mySleep(int millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException ee) {
        }
    }

    boolean getExceptionCaught() {
        return exceptionCaught;
    }

    void setExceptionCaught(boolean value) {
        exceptionCaught = value;
    }

    /**
     * Return true if eventSet contains the VMDeathEvent for the request in
     * the ourVMDeathRequest ivar.
     */
    private boolean containsOurVMDeathRequest(EventSet eventSet) {
        if (ourVMDeathRequest != null) {
            Iterator myIter = eventSet.iterator();
            while (myIter.hasNext()) {
                Event myEvent = (Event)myIter.next();
                if (!(myEvent instanceof VMDeathEvent)) {
                    // We assume that an EventSet contains only VMDeathEvents
                    // or no VMDeathEvents.
                    break;
                }
                if (ourVMDeathRequest.equals(myEvent.request())) {
                    return true;
                }
            }
        }
        return false;
    }

    /************************************************************************
     * The following methods override those in our base class, TargetAdapter.
     *************************************************************************/

    /**
     * Events handled directly by scaffold always resume (well, almost always)
     */
    public void eventSetComplete(EventSet set) {
        // The listener in connect(..) resumes after receiving our
        // special VMDeathEvent.  We can't also do the resume
        // here or we will probably get a VMDisconnectedException
        if (!containsOurVMDeathRequest(set)) {
            traceln("TS: set.resume() called");
            set.resume();
        }
    }

    /**
     * This method sets up default requests.
     * Testcases can override this to change default behavior.
     */
    protected void createDefaultEventRequests() {
        createDefaultVMDeathRequest();
        createDefaultExceptionRequest();
    }

    /**
     * We want the BE to stop when it issues a VMDeathEvent in order to
     * give the FE time to complete handling events that occured before
     * the VMDeath.  When we get the VMDeathEvent for this request in
     * the listener in connect(), we will do a resume.
     * If a testcase wants to do something special with VMDeathEvent's,
     * then it should override this method with an empty method or
     * whatever in order to suppress the automatic resume.  The testcase
     * will then be responsible for the handling of VMDeathEvents.  It
     * has to be sure that it does a resume if it gets a VMDeathEvent
     * with SUSPEND_ALL, and it has to be sure that it doesn't do a
     * resume after getting a VMDeath with SUSPEND_NONE (the automatically
     * generated VMDeathEvent.)
     */
    protected void createDefaultVMDeathRequest() {
//         ourVMDeathRequest = requestManager.createVMDeathRequest();
//         ourVMDeathRequest.setSuspendPolicy(EventRequest.SUSPEND_ALL);
//         ourVMDeathRequest.enable();
    }

    /**
     * This will allow us to print a warning if a debuggee gets an
     * unexpected exception.  The unexpected exception will be handled in
     * the exceptionThrown method in the listener created in the connect()
     * method.
     * If a testcase does not want an uncaught exception to cause a
     * msg, it must override this method.
     */
    protected void createDefaultExceptionRequest() {
        ourExceptionRequest = requestManager.createExceptionRequest(null,
                                                                false, true);

        // We can't afford to make this be other than SUSPEND_NONE.  Otherwise,
        // it would have to be resumed.  If our connect() listener resumes it,
        // what about the case where the EventSet contains other events with
        // SUSPEND_ALL and there are other listeners who expect the BE to still
        // be suspended when their handlers get called?
        ourExceptionRequest.setSuspendPolicy(EventRequest.SUSPEND_NONE);
        ourExceptionRequest.enable();
    }

    private class EventHandler implements Runnable {
        EventHandler() {
            Thread thread = new Thread(this);
            thread.setDaemon(true);
            thread.start();
        }

        private void notifyEvent(TargetListener listener, Event event) {
            if (event instanceof BreakpointEvent) {
                listener.breakpointReached((BreakpointEvent)event);
            } else if (event instanceof ExceptionEvent) {
                listener.exceptionThrown((ExceptionEvent)event);
            } else if (event instanceof StepEvent) {
                listener.stepCompleted((StepEvent)event);
            } else if (event instanceof ClassPrepareEvent) {
                listener.classPrepared((ClassPrepareEvent)event);
            } else if (event instanceof ClassUnloadEvent) {
                listener.classUnloaded((ClassUnloadEvent)event);
            } else if (event instanceof MethodEntryEvent) {
                listener.methodEntered((MethodEntryEvent)event);
            } else if (event instanceof MethodExitEvent) {
                listener.methodExited((MethodExitEvent)event);
            } else if (event instanceof AccessWatchpointEvent) {
                listener.fieldAccessed((AccessWatchpointEvent)event);
            } else if (event instanceof ModificationWatchpointEvent) {
                listener.fieldModified((ModificationWatchpointEvent)event);
            } else if (event instanceof ThreadStartEvent) {
                listener.threadStarted((ThreadStartEvent)event);
            } else if (event instanceof ThreadDeathEvent) {
                listener.threadDied((ThreadDeathEvent)event);
            } else if (event instanceof VMStartEvent) {
                listener.vmStarted((VMStartEvent)event);
            } else if (event instanceof VMDeathEvent) {
                listener.vmDied((VMDeathEvent)event);
            } else if (event instanceof VMDisconnectEvent) {
                listener.vmDisconnected((VMDisconnectEvent)event);
            } else {
                throw new InternalError("Unknown event type: " + event.getClass());
            }
        }

        private void traceSuspendPolicy(int policy) {
            if (shouldTrace) {
                switch (policy) {
                case EventRequest.SUSPEND_NONE:
                    traceln("TS: eventHandler: suspend = SUSPEND_NONE");
                    break;
                case EventRequest.SUSPEND_ALL:
                    traceln("TS: eventHandler: suspend = SUSPEND_ALL");
                    break;
                case EventRequest.SUSPEND_EVENT_THREAD:
                    traceln("TS: eventHandler: suspend = SUSPEND_EVENT_THREAD");
                    break;
                }
            }
        }

        public void run() {
            boolean connected = true;
            do {
                try {
                    EventSet set = vm.eventQueue().remove();
                    traceSuspendPolicy(set.suspendPolicy());
                    synchronized (listeners) {
                        ListIterator iter = listeners.listIterator();
                        while (iter.hasNext()) {
                            TargetListener listener = (TargetListener)iter.next();
                            traceln("TS: eventHandler: listener = " + listener);
                            listener.eventSetReceived(set);
                            if (listener.shouldRemoveListener()) {
                                iter.remove();
                            } else {
                                Iterator jter = set.iterator();
                                while (jter.hasNext()) {
                                    Event event = (Event)jter.next();
                                    traceln("TS: eventHandler:    event = " + event.getClass());

                                    if (event instanceof VMDisconnectEvent) {
                                        connected = false;
                                    }
                                    listener.eventReceived(event);
                                    if (listener.shouldRemoveListener()) {
                                        iter.remove();
                                        break;
                                    }
                                    notifyEvent(listener, event);
                                    if (listener.shouldRemoveListener()) {
                                        iter.remove();
                                        break;
                                    }
                                }
                                traceln("TS: eventHandler:   end of events loop");
                                if (!listener.shouldRemoveListener()) {
                                    traceln("TS: eventHandler:   calling ESC");
                                    listener.eventSetComplete(set);
                                    if (listener.shouldRemoveListener()) {
                                        iter.remove();
                                    }
                                }
                            }
                            traceln("TS: eventHandler: end of listeners loop");
                        }
                    }
                } catch (InterruptedException e) {
                    traceln("TS: eventHandler: InterruptedException");
                } catch (Exception e) {
                    failure("FAILED: Exception occured in eventHandler: " + e);
                    e.printStackTrace();
                    connected = false;
                    synchronized(TestScaffold.this) {
                        // This will make the waiters such as waitForVMDisconnect
                        // exit their wait loops.
                        vmDisconnected = true;
                        TestScaffold.this.notifyAll();
                    }
                }
                traceln("TS: eventHandler: End of outerloop");
            } while (connected);
            traceln("TS: eventHandler: finished");
        }
    }

    /**
     * Constructor
     */
    public TestScaffold(String[] args) {
        this.args = args;
    }

    public void enableScaffoldTrace() {
        this.shouldTrace = true;
    }

    public void disableScaffoldTrace() {
        this.shouldTrace = false;
    }


    protected void startUp(String targetName) {
        List argList = new ArrayList(Arrays.asList(args));
        argList.add(targetName);
        println("run args: " + argList);
        connect((String[]) argList.toArray(args));
        waitForVMStart();
    }

    protected BreakpointEvent startToMain(String targetName) {
        startUp(targetName);
        traceln("TS: back from startUp");
        BreakpointEvent bpr = resumeTo(targetName, "main", "([Ljava/lang/String;)V");
        waitForInput();
        return bpr;
    }

    protected void waitForInput() {
        if (System.getProperty("jpda.wait") != null) {
            try {
                System.err.println("Press <enter> to continue");
                System.in.read();
                System.err.println("running...");

            } catch(Exception e) {
            }
        }
    }

    /*
     * Test cases should implement tests in runTests and should
     * initiate testing by calling run().
     */
    abstract protected void runTests() throws Exception;

    final public void startTests() throws Exception {
        try {
            runTests();
        } finally {
            shutdown();
        }
    }

    protected void println(String str) {
        System.err.println(str);
    }

    protected void print(String str) {
        System.err.print(str);
    }

    protected void traceln(String str) {
        if (shouldTrace) {
            println(str);
        }
    }

    protected void failure(String str) {
        println(str);
        testFailed = true;
    }

    private ArgInfo parseArgs(String args[]) {
        ArgInfo argInfo = new ArgInfo();
        for (int i = 0; i < args.length; i++) {
            if (args[i].equals("-connect")) {
                i++;
                argInfo.connectorSpec = args[i];
            } else if (args[i].equals("-trace")) {
                i++;
                argInfo.traceFlags = Integer.decode(args[i]).intValue();
            } else if (args[i].startsWith("-J")) {
                argInfo.targetVMArgs += (args[i].substring(2) + ' ');

                /*
                 * classpath can span two arguments so we need to handle
                 * it specially.
                 */
                if (args[i].equals("-J-classpath")) {
                    i++;
                    argInfo.targetVMArgs += (args[i] + ' ');
                }
            } else {
                argInfo.targetAppCommandLine += (args[i] + ' ');
            }
        }
        return argInfo;
    }

    /**
     * This is called to connect to a debuggee VM.  It starts the VM and
     * installs a listener to catch VMStartEvent, our default events, and
     * VMDisconnectedEvent.  When these events appear, that is remembered
     * and waiters are notified.
     * This is normally called in the main thread of the test case.
     * It starts up an EventHandler thread that gets events coming in
     * from the debuggee and distributes them to listeners.  That thread
     * keeps running until a VMDisconnectedEvent occurs or some exception
     * occurs during its processing.
     *
     * The 'listenUntilVMDisconnect' method adds 'this' as a listener.
     * This means that 'this's vmDied method will get called.  This has a
     * default impl in TargetAdapter.java which can be overridden in the
     * testcase.
     *
     * waitForRequestedEvent also adds an adaptor listener that listens
     * for the particular event it is supposed to wait for (and it also
     * catches VMDisconnectEvents.)  This listener is removed once
     * its eventReceived method is called.
     * waitForRequestedEvent is called by most of the methods to do bkpts,
     * etc.
     */
    public void connect(String args[]) {
        ArgInfo argInfo = parseArgs(args);

        argInfo.targetVMArgs += VMConnection.getDebuggeeVMOptions();
        connection = new VMConnection(argInfo.connectorSpec,
                                      argInfo.traceFlags);

        addListener(new TargetAdapter() {
                public void eventSetComplete(EventSet set) {
                    if (TestScaffold.this.containsOurVMDeathRequest(set)) {
                        traceln("TS: connect: set.resume() called");
                        set.resume();

                        // Note that we want to do the above resume before
                        // waking up any sleepers.
                        synchronized(TestScaffold.this) {
                            TestScaffold.this.notifyAll();
                        }
                    }
                }

                public void vmStarted(VMStartEvent event) {
                    synchronized(TestScaffold.this) {
                        vmStartThread = event.thread();
                        TestScaffold.this.notifyAll();
                    }
                }
                /**
                 * By default, we catch uncaught exceptions and print a msg.
                 * The testcase must override the createDefaultExceptionRequest
                 * method if it doesn't want this behavior.
                 */
                public void exceptionThrown(ExceptionEvent event) {
                    if (TestScaffold.this.ourExceptionRequest != null &&
                        TestScaffold.this.ourExceptionRequest.equals(
                                                        event.request())) {
                        println("Note: Unexpected Debuggee Exception: " +
                                event.exception().referenceType().name() +
                                " at line " + event.location().lineNumber());
                        TestScaffold.this.exceptionCaught = true;
                    }
                }

                public void vmDied(VMDeathEvent event) {
                    vmDied = true;
                    traceln("TS: vmDied called");
                }

                public void vmDisconnected(VMDisconnectEvent event) {
                    synchronized(TestScaffold.this) {
                        vmDisconnected = true;
                        TestScaffold.this.notifyAll();
                    }
                }
            });
        if (connection.connector().name().equals("com.sun.jdi.CommandLineLaunch")) {
            if (argInfo.targetVMArgs.length() > 0) {
                if (connection.connectorArg("options").length() > 0) {
                    throw new IllegalArgumentException("VM options in two places");
                }
                connection.setConnectorArg("options", argInfo.targetVMArgs);
            }
            if (argInfo.targetAppCommandLine.length() > 0) {
                if (connection.connectorArg("main").length() > 0) {
                    throw new IllegalArgumentException("Command line in two places");
                }
                connection.setConnectorArg("main", argInfo.targetAppCommandLine);
            }
        }

        vm = connection.open();
        requestManager = vm.eventRequestManager();
        createDefaultEventRequests();
        new EventHandler();
    }


    public VirtualMachine vm() {
        return vm;
    }

    public EventRequestManager eventRequestManager() {
        return requestManager;
    }

    public void addListener(TargetListener listener) {
        traceln("TS: Adding listener " + listener);
        listeners.add(listener);
    }

    public void removeListener(TargetListener listener) {
        traceln("TS: Removing listener " + listener);
        listeners.remove(listener);
    }


    protected void listenUntilVMDisconnect() {
        try {
            addListener (this);
        } catch (Exception ex){
            ex.printStackTrace();
            testFailed = true;
        } finally {
            // Allow application to complete and shut down
            resumeToVMDisconnect();
        }
    }

    public synchronized ThreadReference waitForVMStart() {
        while ((vmStartThread == null) && !vmDisconnected) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }

        if (vmStartThread == null) {
            throw new VMDisconnectedException();
        }

        return vmStartThread;
    }

    public synchronized void waitForVMDisconnect() {
        traceln("TS: waitForVMDisconnect");
        while (!vmDisconnected) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }
        traceln("TS: waitForVMDisconnect: done");
    }

    public Event waitForRequestedEvent(final EventRequest request) {
        class EventNotification {
            Event event;
            boolean disconnected = false;
        }
        final EventNotification en = new EventNotification();

        TargetAdapter adapter = new TargetAdapter() {
            public void eventReceived(Event event) {
                if (request.equals(event.request())) {
                    traceln("TS:Listener2: got requested event");
                    synchronized (en) {
                        en.event = event;
                        en.notifyAll();
                    }
                    removeThisListener();
                } else if (event instanceof VMDisconnectEvent) {
                    traceln("TS:Listener2: got VMDisconnectEvent");
                    synchronized (en) {
                        en.disconnected = true;
                        en.notifyAll();
                    }
                    removeThisListener();
                }
            }
        };

        addListener(adapter);

        try {
            synchronized (en) {
                traceln("TS: waitForRequestedEvent: vm.resume called");
                vm.resume();

                while (!en.disconnected && (en.event == null)) {
                    en.wait();
                }
            }
        } catch (InterruptedException e) {
            return null;
        }

        if (en.disconnected) {
            throw new RuntimeException("VM Disconnected before requested event occurred");
        }
        return en.event;
    }

    private StepEvent doStep(ThreadReference thread, int gran, int depth) {
        final StepRequest sr =
                  requestManager.createStepRequest(thread, gran, depth);

        sr.addClassExclusionFilter("java.*");
        sr.addClassExclusionFilter("sun.*");
        sr.addClassExclusionFilter("com.sun.*");
        sr.addCountFilter(1);
        sr.enable();
        StepEvent retEvent = (StepEvent)waitForRequestedEvent(sr);
        requestManager.deleteEventRequest(sr);
        return retEvent;
    }

    public StepEvent stepIntoInstruction(ThreadReference thread) {
        return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_INTO);
    }

    public StepEvent stepIntoLine(ThreadReference thread) {
        return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_INTO);
    }

    public StepEvent stepOverInstruction(ThreadReference thread) {
        return doStep(thread, StepRequest.STEP_MIN, StepRequest.STEP_OVER);
    }

    public StepEvent stepOverLine(ThreadReference thread) {
        return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OVER);
    }

    public StepEvent stepOut(ThreadReference thread) {
        return doStep(thread, StepRequest.STEP_LINE, StepRequest.STEP_OUT);
    }

    public BreakpointEvent resumeTo(Location loc) {
        final BreakpointRequest request =
            requestManager.createBreakpointRequest(loc);
        request.addCountFilter(1);
        request.enable();
        return (BreakpointEvent)waitForRequestedEvent(request);
    }

    public ReferenceType findReferenceType(String name) {
        List rts = vm.classesByName(name);
        Iterator iter = rts.iterator();
        while (iter.hasNext()) {
            ReferenceType rt = (ReferenceType)iter.next();
            if (rt.name().equals(name)) {
                return rt;
            }
        }
        return null;
    }

    public Method findMethod(ReferenceType rt, String name, String signature) {
        List methods = rt.methods();
        Iterator iter = methods.iterator();
        while (iter.hasNext()) {
            Method method = (Method)iter.next();
            if (method.name().equals(name) &&
                method.signature().equals(signature)) {
                return method;
            }
        }
        return null;
    }

    public Location findLocation(ReferenceType rt, int lineNumber)
                         throws AbsentInformationException {
        List locs = rt.locationsOfLine(lineNumber);
        if (locs.size() == 0) {
            throw new IllegalArgumentException("Bad line number");
        } else if (locs.size() > 1) {
            throw new IllegalArgumentException("Line number has multiple locations");
        }

        return (Location)locs.get(0);
    }

    public BreakpointEvent resumeTo(String clsName, String methodName,
                                         String methodSignature) {
        ReferenceType rt = findReferenceType(clsName);
        if (rt == null) {
            rt = resumeToPrepareOf(clsName).referenceType();
        }

        Method method = findMethod(rt, methodName, methodSignature);
        if (method == null) {
            throw new IllegalArgumentException("Bad method name/signature");
        }

        return resumeTo(method.location());
    }

    public BreakpointEvent resumeTo(String clsName, int lineNumber) throws AbsentInformationException {
        ReferenceType rt = findReferenceType(clsName);
        if (rt == null) {
            rt = resumeToPrepareOf(clsName).referenceType();
        }

        return resumeTo(findLocation(rt, lineNumber));
    }

    public ClassPrepareEvent resumeToPrepareOf(String className) {
        final ClassPrepareRequest request =
            requestManager.createClassPrepareRequest();
        request.addClassFilter(className);
        request.addCountFilter(1);
        request.enable();
        return (ClassPrepareEvent)waitForRequestedEvent(request);
    }

    public void resumeToVMDisconnect() {
        try {
            traceln("TS: resumeToVMDisconnect: vm.resume called");
            vm.resume();
        } catch (VMDisconnectedException e) {
            // clean up below
        }
        waitForVMDisconnect();
    }

    public void shutdown() {
        shutdown(null);
    }

    public void shutdown(String message) {
        traceln("TS: shutdown: vmDied= " + vmDied +
                 ", vmDisconnected= " + vmDisconnected +
                 ", connection = " + connection);

        if ((connection != null)) {
            try {
                connection.disposeVM();
             } catch (VMDisconnectedException e) {
                // Shutting down after the VM has gone away. This is
                // not an error, and we just ignore it.
            }
        } else {
            traceln("TS: shutdown: disposeVM not called");
        }
        if (message != null) {
            println(message);
        }

        vmDied = true;
        vmDisconnected = true;
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java TestScaffold.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.