/* * JBoss, Home of Professional Open Source * Copyright 2008-10, Red Hat and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * * @authors Andrew Dinn */ package org.jboss.byteman.rule.helper; import org.jboss.byteman.rule.Rule; import org.jboss.byteman.rule.exception.ExecuteException; import org.jboss.byteman.synchronization.*; import org.jboss.byteman.synchronization.Timer; import org.jboss.byteman.agent.Transformer; import java.io.*; import java.util.*; /** * This is the default helper class which is used to define builtin operations for rules. * Methods provided on this class are automatically made available as builtin operations in * expressions appearing in rule event bindings, conditions and actions. Although Helper * methods are all instance methods the message recipient for the method call is implicit * and does not appear in the builtin call. It does, however, appear in the runtime * invocation, giving the builtin operation access to the helper and thence the rule being * fired. */ public class Helper { protected Rule rule; protected Helper(Rule rule) { this.rule = rule; } // rule debug tracing support /** * builtin to print a message during rule execution. n.b. this always returns true which * means it can be invoked during condition execution. * punts to a static call to Helper.dotraceln("dbg", debugPrefix + text) to * do the actual printing to the "dbg" trace stream where debugPrefix is a prefix * including the keyword debug and a key based on the rule name which is specific * to the injection point for the rule. * @param text the message to be printed as trace output * @return true */ public boolean debug(String text) { if (Transformer.isDebug()) { dotraceln("dbg", "rule.debug{" + rule.getKey() + "} : " + text); } return true; } // file + System.out/err based trace support /** * builtin to open a trace output stream identified by identifier to a file located in the * current working directory using a unique generated name. equivalent to calling * traceOpen(identifier, null). * @param identifier an identifier used subsequently to identify the trace output stream * @return true if new file and stream was created, false if a stream identified by identifier * already existed or the identifier is null, "out" or "err" */ public boolean traceOpen(Object identifier) { return traceOpen(identifier, null); } /** * builtin to open a trace output stream which is redirected to a file in the current directory * if specified. Punts to static call Helper.doTraceOpen(identifier, fileName) * @param identifier an identifier used subsequently to identify the trace output stream * @param fileName the name of the trace file or null if a name should be generated * @return true if the output stream was successfully created. false if a stream identified by * identifier already existed or if a file of the same name already exists or the identifier is * null, "out" or "err" */ public boolean traceOpen(Object identifier, String fileName) { return doTraceOpen(identifier, fileName); } /** * builtin to close the trace output stream identified by identifier flushing any pending * output. Punts to static call Helper.doTraceClose(identifier) * @param identifier an identifier used subsequently to identify the trace output stream * @return true if the stream was flushed and closed, false if no stream is identified by * identifier or identifer is null, "out" or "err" */ public boolean traceClose(Object identifier) { return doTraceClose(identifier); } /** * equivalent to calling trace("out", message). * @param message the message to be printed in the output stream * @return true */ public boolean trace(String message) { return dotrace("out", message); } /** * punts to static call Helper.dotrace(identifier, message). * @param identifier an identifier used subsequently to identify the trace output stream * @param message the message to be printed * @return true */ public boolean trace(Object identifier, String message) { return dotrace(identifier, message); } /** * equivalent to calling traceln("out", message). * @param message the message to be traced * @return true */ public boolean traceln(String message) { return dotraceln("out", message); } /** * punts to static call dotraceln(identifier, message). * @param identifier an identifier used subsequently to identify the trace output stream * @param message the message to be traced * @return true */ public boolean traceln(Object identifier, String message) { return dotraceln(identifier, message); } /** * version for backwards compatibility -- docs and original code were mismatched * @param identifier an identifier used subsequently to identify the trace output stream * @return true if the open succeeds false if it fails */ public boolean openTrace(Object identifier) { return traceOpen(identifier); } /** * version for backwards compatibility -- docs and original code were mismatched * @param identifier an identifier used subsequently to identify the trace output stream * @param fileName the name of the trace file or null if a name should be generated * @return true if the open succeeds false if it fails */ public boolean openTrace(Object identifier, String fileName) { return traceOpen(identifier, fileName); } /** * version for backwards compatibility -- docs and original code were mismatched * @param identifier an identifier used subsequently to identify the trace output stream * @return true if the close succeeds false if it fails */ public boolean closeTrace(Object identifier) { return traceClose(identifier); } // public static methods (i.e. non-builtins) allowing Byteman agent to access trace capability /** * punts to static call dotraceln("out", msg) to print msg to the "out" * trace stream * @param msg the message to be traced * @return true */ public static boolean out(String msg) { dotraceln("out", msg); return true; } /** * punts to static call dotraceln("err", msg) to print msg to the "err" * trace stream * @param msg the message to be traced * @return true */ public static boolean err(String msg) { dotraceln("err", msg); return true; } /** * punts to static call dotraceln("vrb", msg) to print msg to the "vrb" * trace stream when the verbose log level is enabled * @param msg the message to be traced * @return true */ public static boolean verbose(String msg) { if (Transformer.isVerbose()) { dotraceln("vrb", msg); } return true; } /** * punts to static call dotraceln("nzy", msg) to print msg to the "nzy" * trace stream when the noisy log level is enabled * * @param msg the message to be traced * @return true */ public static boolean noisy(String msg) { // cannot implement this properly until noisy logging can be configured if (false) { dotraceln("nzy", msg); } return true; } /** * Print the stack trace for th to the "out" trace stream * @param th the throwable stack trace */ public static void outTraceException(Throwable th) { doTraceException("out", th); } /** * Print the stack trace for th to the "err" trace stream * @param th the throwable stack trace */ public static void errTraceException(Throwable th) { doTraceException("err", th); } /** * Print the stack trace for th to System.out when the verbose log level is enabled * @param th the throwable stack trace */ public static void verboseTraceException(Throwable th) { if (Transformer.isVerbose()) { doTraceException("vrb", th); } } /** * Print the stack trace to System.out when the noisy log level is enabled * @param th the throwable stack trace */ public static void noisyTraceException(Throwable th) { // cannot implement this properly until noisy logging can be configured if(false) { doTraceException("nzy", th); } } // private static implementation of trace functionality /** * open a trace output stream identified by identifier to a file located in the current working * directory using the given file name or a generated name if the supplied name is null * @param identifier an identifier used subsequently to identify the trace output stream * @param fileName the name of the trace file or null if a name should be generated * @return true if new file and stream was created, false if a stream identified by identifier * already existed or if a file of the same name already exists or the identifier is null, "out" * or "err" */ private static boolean doTraceOpen(Object identifier, String fileName) { if (identifier == null) { return false; } synchronized(traceMap) { PrintStream stream = traceMap.get(identifier); String name = fileName; if (stream != null) { return false; } if (fileName == null) { name = nextFileName(identifier); } File file = new File(name); if (file.exists() && !file.canWrite()) { if (fileName == null) { // keep trying new names until we hit an unused one do { name = nextFileName(identifier); file = new File(name); } while (file.exists() && !file.canWrite()); } else { // can't open file as requested return false; } } FileOutputStream fos; try { if (file.exists()) { fos = new FileOutputStream(file, true); } else { fos = new FileOutputStream(file, true); } } catch (FileNotFoundException e) { // oops, just return false return false; } PrintStream ps = new PrintStream(fos, true); traceMap.put(identifier, ps); return true; } } /** * close the trace output stream identified by identifier flushing any pending output. * @param identifier an identifier used subsequently to identify the trace output stream * @return true if the stream was flushed and closed, false if no stream is identified by * identifier or identifer is null, "out" or "err" */ private static boolean doTraceClose(Object identifier) { if (identifier == null || identifier.equals("out") || identifier.equals("err")) { return false; } synchronized(traceMap) { // need to do the close while synchronized so we ensure an open cannot // proceed until we have flushed all changes to disk PrintStream ps = traceMap.get(identifier); if (ps != null) { // make sure not to close System.out which we may see as the // trace stream bound to "dbg", "vrb" or "nzy" if (ps != System.out && ps != System.err) { ps.close(); } traceMap.remove(identifier); return true; } } return false; } /** * write the supplied message to the trace stream identified by identifier, creating a new stream * if none exists * @param identifier an identifier used subsequently to identify the trace output stream * @param message the message to be traced * @return true * * caveat: if identifier is the string "out" or null the message will be written to System.out. * if identifier is the string "err" the message will be written to System.err. */ private static boolean dotrace(Object identifier, String message) { synchronized(traceMap) { PrintStream ps = traceMap.get(identifier); if (ps == null) { if (doTraceOpen(identifier, null)) { ps = traceMap.get(identifier); } else { ps = System.out; } } ps.print(message); ps.flush(); } return true; } /** * write the supplied message to the trace stream identified by identifier, creating a new stream * if none exists, and append a new line * @param identifier an identifier used subsequently to identify the trace output stream * @param message the message to be traced * @return true * * caveat: if identifier is the string "out" or null the message will be written to System.out. * if identifier is the string "err" the message will be written to System.err. */ private static boolean dotraceln(Object identifier, String message) { synchronized(traceMap) { PrintStream ps = traceMap.get(identifier); if (ps == null) { if (doTraceOpen(identifier, null)) { ps = traceMap.get(identifier); } else { ps = System.out; } } ps.println(message); ps.flush(); } return true; } /** * * @param id the tracestream to write to * @param th the throwable to dump a stacktrace for */ private static void doTraceException(Object id, Throwable th) { PrintStream ps; synchronized (traceMap) { ps = traceMap.get(id); if (ps == null) { ps = System.out; } } th.printStackTrace(ps); } // flag support /** * set a flag keyed by the supplied object if it is not already set * @param identifier the object identifying the relevant flag * @return true if the flag was clear before this call otherwise false */ public boolean flag(Object identifier) { synchronized (flagSet) { return flagSet.add(identifier); } } /** * test the state of the flag keyed by the supplied object * @param identifier the object identifying the relevant flag * @return true if the flag is set otherwise false */ public boolean flagged(Object identifier) { synchronized (flagSet) { return flagSet.contains(identifier); } } /** * clear the flag keyed by the supplied object if it is not already clear * @param identifier the object identifying the relevant flag * @return true if the flag was clear before this call otherwise false */ public boolean clear(Object identifier) { synchronized (flagSet) { return flagSet.remove(identifier); } } // countdown support /** * for backwards compatibility * @param identifier an object which uniquely identifies the countdown in question * @return true if the countdown is currently installed */ public boolean getCountDown(Object identifier) { return isCountDown(identifier); } /** * builtin to test test if a countdown has been installed * @param identifier an object which uniquely identifies the countdown in question * @return true if the countdown is currently installed */ public boolean isCountDown(Object identifier) { synchronized (countDownMap) { return (countDownMap.get(identifier) != null); } } /** * alias for createCountDown provided for backwards compatibility * @param identifier an object which uniquely identifies the countdown in question * @param count the number of times the countdown needs to be counted down before the * countdown operation returns true. e.g. if count is supplied as 2 then the first two * calls to {@link #countDown(Object)} will return false and the third call will return true. * @return true if a new countdown is installed, false if one already exists. */ public boolean addCountDown(Object identifier, int count) { return createCountDown(identifier, count); } /** * builtin to test create a countdown identified by a specific object and with the specified * count. n.b. this builtin checks if a countdown identified by the supplied object is * currently installed, returning false if so, otherwise atomically adds the countdown * and returns true. This allows the builtin to be used safely in conditions where concurrent * rule firings (including firings of multiple rules) might otherwise lead to a race condition. * @param identifier an object which uniquely identifies the countdown in question * @param count the number of times the countdown needs to be counted down before the * countdown operation returns true. e.g. if count is supplied as 2 then the first two * calls to {@link #countDown(Object)} will return false and the third call will return true. * @return true if a new countdown is installed, false if one already exists. */ public boolean createCountDown(Object identifier, int count) { synchronized (countDownMap) { if (countDownMap.get(identifier) == null) { countDownMap.put(identifier, new CountDown(count)); return true; } } return false; } /** * builtin to decrement the countdown identified by a specific object, uninstalling it and * returning true only when the count is zero. * @param identifier an object which uniquely identifies the countdown in question * @return true if the countdown is installed and its count is zero, otherwise false */ public boolean countDown(Object identifier) { synchronized (countDownMap) { CountDown countDown = countDownMap.get(identifier); if (countDown != null) { boolean result = countDown.decrement(); if (result) { countDownMap.remove(identifier); } return result; } } // we must only fire a decrement event once for a given counter return false; } // wait/notify support /** * test if there are threads waiting for an event identified by the supplied object to * be signalled * @param identifier an object identifying the event to be signalled * @return true if threads are waiting for the associated event to be signalled */ public boolean waiting(Object identifier) { return (getWaiter(identifier, false) != null); } /** * wait for another thread to signal an event with no timeout. see * {@link #waitFor(Object, long)} for details and caveats regarding calling this builtin. * @param identifier an object used to identify the signal that is to be waited on. */ public void waitFor(Object identifier) { waitFor(identifier, 0); } /** * wait for another thread to signal an event with a specific * timeout or no timeout if zero is supplied as the second * argument. this may be called in a rule event, condition or * action. it will suspend the current thread pending signalling * of the event at which point rule processing will either * continue or abort depending upon the type of signal. if an * exception is thrown it will be an instance of runtime exception * which, in normal circumstances, will cause the thread to * exit. The exception may not kill the thread f the trigger * method or calling code contains a catch-all handler so care * must be used to ensure that an abort of waiting threads has the * desired effect. n.b. care must also be employed if the current * thread is inside a synchronized block since there is a * potential for the waitFor call to cause deadlock. * @param identifier an object used to identify the signal that is * to be waited on. n.b. the wait operation is not performed using * synchronization on the supplied object as the rule system * cannot safely release and reobtain locks on application * data. this argument is used as a key to identify a * synchronization object private to the rule system. * @param millisecs hwo long to wait */ public void waitFor(Object identifier, long millisecs) { Waiter waiter = getWaiter(identifier, true); waiter.waitFor(millisecs); } /** * call signalWake(Object, boolean) defaulting the second argument to * false * @param identifier an object used to identify the signal that is * to be waited on. n.b. the wait operation is not performed using * synchronization on the supplied object as the rule system * cannot safely release and reobtain locks on application * data. this argument is used * @return true if a waiting thread was woken false if no thread * was waiting */ public boolean signalWake(Object identifier) { return signalWake(identifier, false); } /** * signal an event identified by the supplied object, causing all * waiting threads to resume rule processing and clearing the * event. if there are no threads waiting either because there has * been no call to {@link #waitFor} or because some other thread * has sent the signal then this call returns false, otherwise it * returns true. This operation is atomic, allowing the builtin to * be used in rule conditions. * @param identifier an object used to identify the which waiting * threads the signal should be delivered to. n.b. the operation * is not performed using a notify on the supplied object. this * argument is used as a key to identify a synchronization object * private to the rule system. * @param mustMeet if true then the signal operation must not be * delivered until some other thread is actually waiting on a * waiter identified by identifier. if there is no such waiter * when this method is called then the calling thread will suspend * until one arrives. * @return true if a waiting thread was woken false if no thread was waiting */ public boolean signalWake(Object identifier, boolean mustMeet) { if (mustMeet == false) { Waiter waiter = removeWaiter(identifier); if (waiter != null) { return waiter.signalWake(); } return false; } else { Waiter waiter; // may need to do test and insert atomically synchronized (waitMap) { // see if we have a waiter waiter = removeWaiter(identifier); if (waiter != null) { return waiter.signalWake(); } else { // insert a pre-signalled waiter waiter = new Waiter(identifier, true, false); waitMap.put(identifier, waiter); } } // ok, so we need to wait until a wait has happened synchronized (waiter) { while (!waiter.waiting()) { try { waiter.wait(); } catch (InterruptedException e) { // do nothing } } } // remove the association between the waiter and the wait map synchronized (waitMap) { removeWaiter(identifier); } return true; } } /** * for backwards compatibility * @param identifier an object used to identify the which waiting * threads the signal should be delivered to. n.b. the operation * is not performed using a notify on the supplied object. this * argument is used as a key to identify a synchronization object * private to the rule system. * @return true if a waiting thread was killed false if no thread * was waiting */ public boolean signalKill(Object identifier) { return signalThrow(identifier); } /** * for backwards compatibility * @param identifier an object used to identify the which waiting * threads the signal should be delivered to. n.b. the operation * is not performed using a notify on the supplied object. this * argument is used as a key to identify a synchronization object * private to the rule system. * @param mustMeet if true then the signal operation must not be * delivered until some other thread is actually waiting on a * waiter identified by identifier. if there is no such waiter * when this method is called then the calling thread will suspend * until one arrives. * @return true if a waiting thread was killed false if no thread * was waiting */ public boolean signalKill(Object identifier, boolean mustMeet) { return signalThrow(identifier, mustMeet); } /** * call signalThrow(Object, boolean) defaulting the second argument to * false * @param identifier an object used to identify the which waiting * threads the signal should be delivered to. n.b. the operation * is not performed using a notify on the supplied object. this * argument is used as a key to identify a synchronization object * private to the rule system. * @return true if a throw occured in a waiting thread false if no * thread was waiting */ public boolean signalThrow(Object identifier) { return signalThrow(identifier, false); } /** * signal an event identified by the suppied object, causing all * waiting threads to throw an exception and clearing the * event. if there are no objects waiting, either because there * has been no call to {@link #waitFor} or because some other * thread has already sent the signal, then this call returns * false, otherwise it returns true. This operation is atomic, * allowing the builtin to be used safely in rule conditions. * @param identifier an object used to identify the which waiting * threads the signal should be delivered to. n.b. the operation * is not performed using a notify on the supplied object. this * argument is used as a key to identify a synchronization object * private to the rule system. * @param mustMeet if true then the signal operation must not be * delivered until some other thread is actually waiting on a * waiter identified by identifier. if there is no such waiter * when this method is called then the calling thread will suspend * until one arrives. * @return true if a throw occured in a waiting thread false if no * thread was waiting */ public boolean signalThrow(Object identifier, boolean mustMeet) { if (mustMeet == false) { Waiter waiter = removeWaiter(identifier); if (waiter != null) { return waiter.signalThrow(); } return false; } else { Waiter waiter; // may need to do test and insert atomically synchronized (waitMap) { // see if we have a waiter waiter = removeWaiter(identifier); if (waiter != null) { return waiter.signalThrow(); } else { // insert a pre-signalled waiter waiter = new Waiter(identifier, true, false); waitMap.put(identifier, waiter); } } // ok, so we need to wait until a wait has happened synchronized (waiter) { while (!waiter.waiting()) { try { waiter.wait(); } catch (InterruptedException e) { // do nothing } } } // remove the association between the waiter and the wait map synchronized (waitMap) { removeWaiter(identifier); } return true; } } /** * delay execution of the current thread for a specified number of * milliseconds * @param millisecs how many milliseconds to delay for */ public void delay(long millisecs) { try { Thread.sleep(millisecs); } catch (InterruptedException e) { // ignore this } } // rendezvous support /** * call createRendezvous(Object, int, boolean) supplying false for the last parameter * @param identifier an identifier for the rendezvous * @param expected the number of threads expected to meet at the rendezvous * @return true if the rendezvous is created or false if a rendezvous identified by identifier already exists */ public boolean createRendezvous(Object identifier, int expected) { return createRendezvous(identifier, expected, false); } /** * create a rendezvous for a given number of threads to join * @param identifier an identifier for the rendezvious in * subsequent rendezvous operations * @param expected the number of threads expected to meet at the * rendezvous * @param restartable true if the rendezvous is can be repeatedly * entered false if it is deleted after the first time all * expected threads arrive * @return true if the rendezvous is created or false if a * rendezvous identified by identifier already exists */ public boolean createRendezvous(Object identifier, int expected, boolean restartable) { // need to do this atomically synchronized (rendezvousMap) { Rendezvous rendezvous = rendezvousMap.get(identifier); if (rendezvous != null) { return false; } rendezvous = new Rendezvous(expected, restartable); rendezvousMap.put(identifier, rendezvous); } return true; } /** * test whether a rendezvous with a specific expected count is associated with identifier * @param identifier the identifier for the rendezvous * @param expected the number of threads expected to meet at the rendezvous * @return true if the endezvous exists and is active otherwise false */ public boolean isRendezvous(Object identifier, int expected) { int arrived = getRendezvous(identifier, expected); return (arrived >= 0 && arrived < expected); } /** * test whether a rendezvous with a specific expected count is * associated with identifier * @param identifier the identifier for the rendezvous * @param expected the number of threads expected to meet at the rendezvous * @return the numer of threads currently arrived at the rendezvous */ public int getRendezvous(Object identifier, int expected) { Rendezvous rendezvous = rendezvousMap.get(identifier); if (rendezvous == null || rendezvous.getExpected() != expected) { return -1; } synchronized (rendezvous) { return rendezvous.getArrived(); } } /** * meet other threads at a given rendezvous returning only when * the expected number have arrived * @param identifier the identifier for the rendezvous * @return an ordinal which sorts all parties to the rendezvous in * order of arrival from 0 to (expected-1) or -1 if the rendezvous * does not exist */ public int rendezvous(Object identifier) { return rendezvous(identifier, 0); } /** * meet other threads at a given rendezvous returning either when * the expected number have arrived or if a timeout is exceeded * @param identifier the identifier for the rendezvous * @param millis the timeout after which the caller may return * @return an ordinal which sorts all parties to the rendezvous in * order of arrival from 0 to (expected-1) or -1 if the rendezvous * does not exist or the wait times out */ public int rendezvous(Object identifier, long millis) { Rendezvous rendezvous = rendezvousMap.get(identifier); if (rendezvous != null) { synchronized(rendezvous) { int result = rendezvous.rendezvous(millis); // make sure the rendezvous is removed from the map if required // n.b. this implementation makes sure the remove happens before any thread // successfully passes the rendezvous call if (rendezvous.needsRemove()) { rendezvousMap.remove(identifier); rendezvous.setRemoved(); } return result; } } return -1; } /** * delete a rendezvous. All threads waiting inside a call to rendezvous return result -1; * @param identifier the identifier for the rendezvous * @param expected the number of threads expected to meet at the * rendezvous * @return true if the rendezvous was active and deleted and false if it had already been deleted */ public boolean deleteRendezvous(Object identifier, int expected) { Rendezvous rendezvous = rendezvousMap.get(identifier); if (rendezvous == null || rendezvous.getExpected() != expected) { return false; } synchronized (rendezvous) { if (rendezvous.delete()) { if (rendezvous.needsRemove()) { rendezvousMap.remove(identifier); } return true; } } // hmm, completed before we got there return false; } public boolean createJoin(Object key, int max) { if (max <= 0) { return false; } synchronized(joinerMap) { if (joinerMap.get(key) != null) { return false; } joinerMap.put(key, new Joiner(max)); } return true; } public boolean isJoin(Object key, int max) { synchronized(joinerMap) { Joiner joiner = joinerMap.get(key); if (joiner == null || joiner.getMax() != max) { return false; } } return true; } public boolean joinEnlist(Object key) { Joiner joiner; synchronized (joinerMap) { joiner = joinerMap.get(key); } if (joiner == null) { return false; } Thread current = Thread.currentThread(); switch (joiner.addChild(current)) { case DUPLICATE: case EXCESS: { // failed to add child return false; } case ADDED: case FILLED: { // added child but parent was not waiting so leave joiner in the map for parent to find return true; } case DONE: default: { // added child and parent was waiting so remove joiner from map now synchronized (joinerMap) { joinerMap.remove(key); } return true; } } } public boolean joinWait(Object key, int count) { return joinWait(key, count, 0); } public boolean joinWait(Object key, int count, long millis) { Joiner joiner; synchronized (joinerMap) { joiner = joinerMap.get(key); } if (joiner == null || joiner.getMax() != count) { return false; } Thread current = Thread.currentThread(); if (joiner.joinChildren(current, millis)) { // successfully joined all child threads so remove joiner form map synchronized (joinerMap) { joinerMap.remove(key); } return true; } else { // hmm, another thread must have done the join so leave it do the remove return true; } } private static HashMap<Object, Joiner> joinerMap = new HashMap<Object, Joiner>(); // counter support /** * create a counter identified by the given object with count 0 as its initial count * @param o an identifier used to refer to the counter in future * @return true if a new counter was created and false if one already existed under the given identifier */ public boolean createCounter(Object o) { return createCounter(o, 0); } /** * create a counter identified by the given object with the supplied value as its iniital count * @param o an identifier used to refer to the counter in future * @param value the initial value for the counter * @return true if a new counter was created and false if one already existed under the given identifier */ public boolean createCounter(Object o, int value) { synchronized (counterMap) { Counter counter = counterMap.get(o); if (counter != null) { return false; } else { counterMap.put(o, new Counter(value)); return true; } } } /** * delete a counter identified by the given object with count 0 as its initial count * @param o the identifier for the coounter * @return true if a counter was deleted and false if no counter existed under the given identifier */ public boolean deleteCounter(Object o) { synchronized (counterMap) { Counter counter = counterMap.get(o); if (counter != null) { counterMap.remove(o); return true; } else { return false; } } } /** * read the value of the counter associated with given identifier, creating a new one with count zero * if none exists * @param o the identifier for the counter * @return the value of the counter */ public int readCounter(Object o) { return readCounter(o, false); } /** * read and optionally reset to zero the value of the counter associated with given identifier, creating * a new one with count zero if none exists * @param o the identifier for the counter * @param zero if true then zero the counter * @return the value of the counter */ public int readCounter(Object o, boolean zero) { synchronized (counterMap) { Counter counter = counterMap.get(o); if (counter == null) { counter = new Counter(); counterMap.put(o, counter); } return counter.count(zero); } } /** * increment the value of the counter associated with given identifier, creating a new one with count zero * if none exists * @param o the identifier for the counter * @return the value of the counter after the increment */ public int incrementCounter(Object o) { return incrementCounter(o, 1); } /** * decrement the value of the counter associated with given identifier, creating a new one with count zero * if none exists * @param o the identifier for the counter * @return the value of the counter after the decrement */ public int decrementCounter(Object o) { return incrementCounter(o, -1); } /** * increment the value of the counter associated with given identifier by the given amount, creating a new one * with count zero if none exists * @param o the identifier for the counter * @param amount the amount to add to the counter * @return the value of the counter after the increment */ public int incrementCounter(Object o, int amount) { synchronized (counterMap) { Counter counter = counterMap.get(o); if (counter == null) { counter = new Counter(); counterMap.put(o, counter); } return counter.increment(amount); } } // timer support /** * create a timer identified by the given object * @param o an identifier used to refer to the timer in future * @return true if a new timer was created and false if one already existed under the given identifier */ public boolean createTimer(Object o) { synchronized (timerMap) { Timer timer = timerMap.get(o); if (timer != null) { return false; } else { timerMap.put(o, new Timer()); return true; } } } /** * delete a timer identified by the given object * @param o the identifier for the timer * @return true if a timer was deleted and false if no timer existed under the given identifier */ public boolean deleteTimer(Object o) { synchronized (timerMap) { Timer timer = timerMap.get(o); if (timer != null) { timerMap.remove(o); return true; } else { return false; } } } /** * get the elapsed time from the start (or last reset) of timer associated with given identifier, * creating a new one if none exists * @param o the identifier for the timer * @return the elapsed time since the start (or reset) of the timer */ public long getElapsedTimeFromTimer(Object o) { synchronized (timerMap) { Timer timer = timerMap.get(o); if (timer == null) { timer = new Timer(); timerMap.put(o, timer); } return timer.getElapsedTime(); } } /** * reset the timer associated with given identifier, creating a new one * if none exists * @param o the identifier for the timer * @return the current elapsed value of the timer before the reset */ public long resetTimer(Object o) { synchronized (timerMap) { Timer timer = timerMap.get(o); if (timer == null) { timer = new Timer(); timerMap.put(o, timer); } return timer.reset(); } } // link support /** * create a LinkMap used to store links between names and values * @param mapName the identifier for the map * @return true if a new map was created and false if one already existed under the given identifier */ public boolean createLinkMap(Object mapName) { synchronized(linkMaps) { if (linkMaps.get(mapName) == null) { linkMaps.put(mapName, new HashMap<Object, Object>()); return true; } } return false; } /** * delete a LinkMap used to store links between names and values * @param mapName the identifier for the map * @return true if the map was deleted and false if no map existed under the given identifier */ public boolean deleteLinkMap(Object mapName) { synchronized(linkMaps) { HashMap<Object, Object> map = linkMaps.get(mapName); if (map != null) { linkMaps.remove(mapName); return true; } else { return false; } } } /** * atomically add a link from name to value to the LinkMap * identified by mapName returning any previously linked * Object or null if no link currently exists in the map * @param mapName the identifier for the map * @param name the name of the key * @param value the value to be stored in the map * @return the previous value stored under name, if any, or null */ public Object link(Object mapName, Object name, Object value) { synchronized(linkMaps) { HashMap<Object, Object> map = linkMaps.get(mapName); if (map == null) { linkMaps.put(mapName, new HashMap<Object, Object>()); map = linkMaps.get(mapName); } return map.put(name, value); } } /** * retrieve the Object name currently is linked to from * the LinkMap named by mapName or null if no link currently * exists in the map * @param mapName the identifier for the map * @param name the name of the key * @return the value stored in the map under the given key */ public Object linked(Object mapName, Object name) { synchronized(linkMaps) { HashMap<Object, Object> map = linkMaps.get(mapName); if (map != null) { return map.get(name); } } return null; } /** * atomically remove any link from name returning the * Object it is currently linked to or null if no link * currently exists in the map * @param mapName the identifier for the map * @param name the name of the key * @return the previous value stored under name, if any, or null */ public Object unlink(Object mapName, Object name) { synchronized(linkMaps) { HashMap<Object, Object> map = linkMaps.get(mapName); if (map != null) { return map.remove(name); } } return null; } // default link support /** * add a link to the default LinkMap by calling link("default", name, value) * @param name the name of the key * @param value the value to be stored under key * @return the previous value stored under name, if any, or null */ public Object link(Object name, Object value) { return link("default", name, value); } /** * retrieve a link from the default LinkMap by calling linked("default", name) * @param name the name of the key * @return the value stored in the map under the given key */ public Object linked(Object name) { return linked("default", name); } /** * delete a link from the default LinkMap by calling unlink("default", name) * @param name the name of the key * @return the previous value stored under name, if any, or null */ public Object unlink(Object name) { return unlink("default", name); } /** * cause the current thread to throw a runtime exception which will normally cause it to exit. * The exception may not kill the thread if the trigger method or calling code contains a * catch-all handler so care must be employed to ensure that a call to this builtin has the * desired effect. */ public void killThread() { throw new ExecuteException("rule " + rule.getName() + " : killing thread " + Thread.currentThread().getName()); } /** * cause the current JVM to halt immediately, simulating a crash as near as possible. exit code -1 * is returned */ public void killJVM() { killJVM(-1); } /** * cause the current JVM to halt immediately, simulating a crash * as near as possible. * @param exitCode the code to be passed to the runtime halt call */ public void killJVM(int exitCode) { java.lang.Runtime.getRuntime().halt(exitCode); } // call stack management support // // matching caller frames against exact name /** * test whether the name of the method which called the the trigger method matches the supplied name * by calling callerEquals(name, false) * @param name the name to match * @return true if the name of the method which called the the trigger method matches the supplied name * otherwise false */ public boolean callerEquals(String name) { return callerEquals(name, false); } /** * test whether the name of any of the selected methods in the stack which called the trigger method * matches the supplied name by calling callerEquals(name, 1, frameCount) * @param name the name to match * @param frameCount the number of frames to check * @return true if the name of the method which called the the trigger method matches the supplied name * otherwise false */ public boolean callerEquals(String name, int frameCount) { return callerEquals(name, 1, frameCount); } /** * test whether the name of any of the selected methods in the stack which called the trigger method * matches the supplied name by calling callerEquals(name, false, startFrame, frameCount) * @param name the name to match * @param startFrame the frame index to start checking from with 0 * identifing the trigger method frame * @param frameCount the number of frames to check * @return true if the name of the method which called the the trigger method matches the supplied name * otherwise false */ public boolean callerEquals(String name, int startFrame, int frameCount) { return callerEquals(name, false, startFrame, frameCount); } /** * test whether the name of method which called the the trigger method matches the supplied name * by calling callerEquals(name, includeClass, false) * @param name the name to match * @param includeClass true if the check shoudl include class name false if not * @return true if the name of the method which called the the trigger method matches the supplied name * otherwise false */ public boolean callerEquals(String name, boolean includeClass) { return callerEquals(name, includeClass, false); } /** * test whether the name of method which called the the trigger method matches the supplied name * by calling callerEquals(name, includeClass, false, frameCount) * @param name the name to match * @param includeClass true if the check should include the class * name false if not * @param frameCount the number of frames to check * @return true if the name of the method which called the the trigger method matches the supplied name * otherwise false */ public boolean callerEquals(String name, boolean includeClass, int frameCount) { return callerEquals(name, includeClass, false, frameCount); } /** * test whether the name of method which called the the trigger method matches the supplied name * by calling callerEquals(name, includeClass, false, startFrame, frameCount) * @param name the name to match * @param includeClass true if the check should include the class name false if not * @param startFrame the frame index to start checking from with 0 * identifing the trigger method frame * @param frameCount the number of frames to check * @return true if the name of the method which called the the trigger method matches the supplied name * otherwise false */ public boolean callerEquals(String name, boolean includeClass, int startFrame, int frameCount) { return callerEquals(name, includeClass, false, startFrame, frameCount); } /** * test whether the name of method which called the the trigger method matches the supplied name * by calling callerEquals(name, includeClass, includePackage, 1) * @param name the name to match * @param includeClass true if the check should include the class name false if not * @param includePackage true if the check should include the package name false if not * @return true if the name of the method which called the the trigger method matches the supplied name * otherwise false */ public boolean callerEquals(String name, boolean includeClass, boolean includePackage) { return callerEquals(name, includeClass, includePackage, 1); } /** * test whether the name of any of the selected methods in the stack which called the trigger method * matches the supplied name by calling * callerCheck(name, false, includeClass, includePackage, 1, frameCount) * @param name the name to match * @param includeClass true if the check should include the class name false if not * @param includePackage true if the check should include the package name false if not * @param frameCount the number of frames to check * @return true if the name of the method which called the the trigger method matches the supplied name * otherwise false */ public boolean callerEquals(String name, boolean includeClass, boolean includePackage, int frameCount) { return callerCheck(name, false, includeClass, includePackage, 1, frameCount); } /** * test whether the name of any of the selected methods in the stack which called the trigger method * matches the supplied name by calling * callerCheck(name, false, includeClass, false, startFrame, frameCount) * @param name the name to match * @param includeClass true if the check should include the class name false if not * @param includePackage true if the check should include the package name false if not * @param startFrame the frame index to start checking from with 0 * identifing the trigger method frame * @param frameCount the number of frames to check * @return true if the name of the method which called the the trigger method matches the supplied name * otherwise false */ public boolean callerEquals(String name, boolean includeClass, boolean includePackage, int startFrame, int frameCount) { return callerCheck(name, false, includeClass, includePackage, startFrame, frameCount); } // matching caller frames against reg exp /** * test whether the name of the method which called the the trigger method matches the supplied regular * by calling callerMatches(regExp, false) * @param regExp the pattern to match * @return true if the name of the method which called the the trigger method matches the supplied * regular expression otherwise false */ public boolean callerMatches(String regExp) { return callerMatches(regExp, false); } /** * test whether the name of any of the selected methods in the stack which called the trigger method * matches the supplied regular expression by calling callerMatches(regExp, 1, frameCount) * @param regExp the pattern to match * @param frameCount the number of frames to check * @return true if the name of the method which called the the trigger method matches the supplied * regular expression otherwise false */ public boolean callerMatches(String regExp, int frameCount) { return callerMatches(regExp, 1, frameCount); } /** * test whether the name of any of the selected methods in the stack which called the trigger method * matches the supplied regular expression by calling callerMatches(regExp, false, startFrame, frameCount) * @param regExp the pattern to match * @param startFrame the frame index to start checking from with 0 * identifing the trigger method frame * @param frameCount the number of frames to check * @return true if the name of the method which called the the trigger method matches the supplied * regular expression otherwise false */ public boolean callerMatches(String regExp, int startFrame, int frameCount) { return callerMatches(regExp, false, startFrame, frameCount); } /** * test whether the name of method which called the the trigger method matches the supplied regular * expression by calling callerMatches(regExp, includeClass, false) * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not * @return true if the name of the method which called the the trigger method matches the supplied regular * expression otherwise false */ public boolean callerMatches(String regExp, boolean includeClass) { return callerMatches(regExp, includeClass, false); } /** * test whether the name of method which called the the trigger method matches the supplied regular * expression by calling callerMatches(regExp, includeClass, false, frameCount) * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not * @param frameCount the number of frames to check * @return true if the name of the method which called the the trigger method matches the supplied regular * expression otherwise false */ public boolean callerMatches(String regExp, boolean includeClass, int frameCount) { return callerMatches(regExp, includeClass, false, frameCount); } /** * test whether the name of method which called the the trigger method matches the supplied regular * expression by calling callerMatches(regExp, includeClass, false, startFrame, frameCount) * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not * @param startFrame the frame index to start checking from with 0 * identifing the trigger method frame * @param frameCount the number of frames to check * @return true if the name of the method which called the the trigger method matches the supplied regular * expression otherwise false */ public boolean callerMatches(String regExp, boolean includeClass, int startFrame, int frameCount) { return callerMatches(regExp, includeClass, false, startFrame, frameCount); } /** * test whether the name of method which called the the trigger method matches the supplied regular * expression by calling callerMatches(regExp, includeClass, includePackage, 1) * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not * @return true if the name of the method which called the the trigger method matches the supplied regular * expression otherwise false */ public boolean callerMatches(String regExp, boolean includeClass, boolean includePackage) { return callerMatches(regExp, includeClass, includePackage, 1); } /** * test whether the name of method which called the the trigger method matches the supplied regular * expression by calling callerMatches(regExp, includeClass, includePackage, 1, frameCount) * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not * @param frameCount the number of frames to check * @return true if the name of the method which called the the trigger method matches the supplied regular * expression otherwise false */ public boolean callerMatches(String regExp, boolean includeClass, boolean includePackage, int frameCount) { return callerMatches(regExp, includeClass, includePackage, 1, frameCount); } /** * test whether the name of any of the selected methods in the stack which called the trigger method * matches the supplied regular expression by calling * callerCheck(regExp, true, includeClass, includePackage, 1, frameCount) * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not * @param startFrame the frame index to start checking from with 0 * identifing the trigger method frame * @param frameCount the number of frames to check * @return true if the name of the method which called the the trigger method matches the supplied * regular expression otherwise false */ public boolean callerMatches(String regExp, boolean includeClass, boolean includePackage, int startFrame, int frameCount) { return callerCheck(regExp, true, includeClass, includePackage, startFrame, frameCount); } /** * test whether the name of any of the selected methods in the stack which called the trigger method * matches the supplied regular expression. * @param match an expression which will be matched against the name of the method which called * the trigger method * @param isRegExp true if match should be matched as a regular expression and false if it should be matched * using a String equals comparison. * @param includeClass true if the match should be against the class qualified method name * @param includePackage true if the match should be against the package and class qualified method name. * ignored if includeClass is not also true. * @param startFrame identifies the first frame which frame which should be considered. 0 identifies * the trigger frame, 1 the frame for the caller of the trigger method etc. If startFrame is negative * false is returned. * @param frameCount counts the frames which should be checked starting from the first caller. if * this is non-positive or exceeds the actual number of callers above the start frame then all frames in * the stack are tested. * @return true if the name of one of the selected methods in the call stack starting from the trigger * method matches the supplied match value otherwise false */ public boolean callerCheck(String match, boolean isRegExp, boolean includeClass, boolean includePackage, int startFrame, int frameCount) { StackTraceElement[] stack = getStack(); int triggerIndex = triggerIndex(stack); if (startFrame < 0) { return false; } int lastIndex; if (frameCount <= 0) { lastIndex = Integer.MAX_VALUE; } else { lastIndex = startFrame + frameCount; } int matched = matchIndex(stack, match, isRegExp, includeClass, includePackage, triggerIndex + startFrame, triggerIndex + lastIndex); return (matched >= 0); } // call stack management support // // tracing caller frames /** * print a stack trace to System.out by calling traceStack(null) */ public void traceStack() { traceStack(null); } /** * print a stack trace to System.out by calling traceStack(prefix, "out") * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used */ public void traceStack(String prefix) { traceStack(prefix, "out"); } /** * print a stack trace to the trace stream identified by key by calling traceStack(prefix, key, 0) * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param key an object identifying the trace stream to which * output should be generated */ public void traceStack(String prefix, Object key) { traceStack(prefix, key, 0); } /** * print a stack trace to System.out by calling traceStack(null, maxFrames) * @param maxFrames the maximum number of frames to print or 0 if * no limit should apply */ public void traceStack(int maxFrames) { traceStack(null, maxFrames); } /** * print a stack trace to System.out by calling traceStack(prefix, "out", maxFrames) * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param maxFrames the maximum number of frames to print or 0 if * no limit should apply */ public void traceStack(String prefix, int maxFrames) { traceStack(prefix, "out", maxFrames); } /** * print a stack trace to the trace stream identified by key * * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param key an object identifying the trace stream to which * output should be generated * @param maxFrames the maximum number of frames to print or 0 if * no limit should apply */ public void traceStack(String prefix, Object key, int maxFrames) { String stackTrace = formatStack(prefix, maxFrames); trace(key, stackTrace); } // tracing frames of all stacks /** * print trace of all threads' stacks to System.out by calling traceAllStacks(null) */ public void traceAllStacks() { traceAllStacks(null); } /** * print trace of all threads' stacks to System.out by calling traceAllStacks(prefix, "out") * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used */ public void traceAllStacks(String prefix) { traceAllStacks(prefix, "out"); } /** * print trace of all threads' stacks to the trace stream identified by key by calling traceAllStacks(prefix, key, 0) * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param key an object identifying the trace stream to which * output should be generated */ public void traceAllStacks(String prefix, Object key) { traceAllStacks(prefix, key, 0); } /** * print trace of all threads' stacks to System.out by calling traceAllStacks(null, maxFrames) * @param maxFrames the maximum number of frames to print or 0 if * no limit should apply */ public void traceAllStacks(int maxFrames) { traceAllStacks(null, maxFrames); } /** * print trace of all threads' stacks to System.out by calling traceAllStacks(prefix, "out", maxFrames) * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param maxFrames the maximum number of frames to print or 0 if * no limit should apply */ public void traceAllStacks(String prefix, int maxFrames) { traceAllStacks(prefix, "out", maxFrames); } /** * print trace of all threads' stacks to the trace stream identified by key * * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param key an object identifying the trace stream to which * output should be generated * @param maxFrames the maximum number of frames to print or 0 if * no limit should apply */ public void traceAllStacks(String prefix, Object key, int maxFrames) { trace(key, formatAllStacks(prefix, maxFrames)); } // trace stack of a specific thread /** * print a stack trace of a specific thread to System.out by calling traceThreadStack(threadName, null) * @param threadName a string identifying the thread */ public void traceThreadStack(String threadName) { traceThreadStack(threadName, null); } /** * print a stack trace of a specific thread to System.out by calling traceThreadStack(threadName, prefix, "out") * @param threadName a string identifying the thread * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + threadName + "\n" is used */ public void traceThreadStack(String threadName, String prefix) { traceThreadStack(threadName, prefix, "out"); } /** * print a stack trace of a specific thread to the trace stream identified by key by calling traceThreadStack(threadName, prefix, key, 0) * @param threadName a string identifying the thread * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + threadName + "\n" is used * @param key an object identifying the trace stream to which * output should be generated */ public void traceThreadStack(String threadName, String prefix, Object key) { traceThreadStack(threadName, prefix, key, 0); } /** * print a stack trace of a specific thread to System.out by calling traceThreadStack(threadName, null, maxFrames) * @param threadName a string identifying the thread * @param maxFrames the maximum number of frames to print or 0 if * no limit should apply */ public void traceThreadStack(String threadName, int maxFrames) { traceThreadStack(threadName, null, maxFrames); } /** * print a stack trace of a specific thread of a specific thread to System.out by calling traceThreadStack(threadName, prefix, "out", maxFrames) * @param threadName a string identifying the thread * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + threadName + "\n" is used * @param maxFrames the maximum number of frames to print or 0 if * no limit should apply */ public void traceThreadStack(String threadName, String prefix, int maxFrames) { traceThreadStack(threadName, prefix, "out", maxFrames); } /** * print a stack trace to the trace stream identified by key * * @param threadName a string identifying the thread * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + threadName + "\n" is used * @param key an object identifying the trace stream to which * output should be generated * @param maxFrames the maximum number of frames to print or 0 if * no limit should apply */ public void traceThreadStack(String threadName, String prefix, Object key, int maxFrames) { String stackTrace = formatThreadStack(threadName, prefix, maxFrames); trace(key, stackTrace); } /** * print all stack frames which match pattern to System.out by calling traceStackMatching(pattern, null) * @param regExp the pattern to match */ public void traceStackMatching(String regExp) { traceStackMatching(regExp, null); } /** * print all stack frames which match pattern to System.out preceded by prefix by calling * traceStackMatching(pattern, prefix, "out") * @param regExp the pattern to match * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used */ public void traceStackMatching(String regExp, String prefix) { traceStackMatching(regExp, prefix, "out"); } /** * print all stack frames which match pattern to System.out preceded by prefix by calling * @param regExp the pattern to match * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param key an object identifying the trace stream to which * output should be generated * traceStackMatching(pattern, false, prefix, key) */ public void traceStackMatching(String regExp, String prefix, Object key) { traceStackMatching(regExp, false, prefix, key); } /** * print all stack frames which match pattern to System.out by calling * traceStackMatching(pattern, includeClass, false) * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not */ public void traceStackMatching(String regExp, boolean includeClass) { traceStackMatching(regExp, includeClass, false); } /** * print all stack frames which match pattern to System.out preceded by prefix by calling * traceStackMatching(pattern, includeClass, false, prefix) * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used */ public void traceStackMatching(String regExp, boolean includeClass, String prefix) { traceStackMatching(regExp, includeClass, false, prefix); } /** * print all stack frames which match pattern to System.out preceded by prefix by calling * traceStackMatching(pattern, includeClass, false, prefix, key) * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param key an object identifying the trace stream to which * output should be generated */ public void traceStackMatching(String regExp, boolean includeClass, String prefix, Object key) { traceStackMatching(regExp, includeClass, false, prefix, key); } /** * print all stack frames which match pattern to System.out by calling * traceStackMatching(pattern, includeClass, includePackage, null) * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not */ public void traceStackMatching(String regExp, boolean includeClass, boolean includePackage) { traceStackMatching(regExp, includeClass, includePackage, null); } /** * print all stack frames which match pattern to System.out preceded by prefix by calling * traceStackMatching(pattern, includeClass, , includePackage, prefix, "out") * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used */ public void traceStackMatching(String regExp, boolean includeClass, boolean includePackage, String prefix) { traceStackMatching(regExp, includeClass, includePackage, prefix, "out"); } /** * print all stack frames which match pattern to the trace stream identified by key preceded by prefix. * * @param regExp a pattern which will be matched against the method name of the stack frame as a * regular expression by calling String.matches() * @param includeClass true if the match should be against the package and class qualified method name * @param includePackage true if the match should be against the package and class qualified method name. * ignored if includeClass is not also true. * @param prefix a String to be printed once before printing each line of stack trace. if supplied as null * then the prefix "Stack trace for thread " + Thread.currentThread().getName() + " matching " + pattern + "\n" is used * @param key an object identifying the trace stream to which output should be generated */ public void traceStackMatching(String regExp, boolean includeClass, boolean includePackage, String prefix, Object key) { String stackTrace = formatStackMatching(regExp, includeClass, includePackage, prefix); trace(key, stackTrace); } // tracing stack range by exact match /** * print all stack frames between the frames which match start and end to System.out by calling * traceStackBetween(from, to, null) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. */ public void traceStackBetween(String from, String to) { traceStackBetween(from, to, null); } /** * print all stack frames between the frames which match start and end to System.out preceded by prefix * by calling traceStackBetween(from, to, prefix, "out") * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used */ public void traceStackBetween(String from, String to, String prefix) { traceStackBetween(from, to, prefix, "out"); } /** * print all stack frames between the frames which match start and end preceded by prefix * by calling traceStackBetween(from, to, false, prefix, key) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param key an object identifying the trace stream to which * output should be generated */ public void traceStackBetween(String from, String to, String prefix, Object key) { traceStackBetween(from, to, false, prefix, key); } /** * print all stack frames between the frames which match start and end to System.out by calling * traceStackBetween(from, to, includeClass, false) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not */ public void traceStackBetween(String from, String to, boolean includeClass) { traceStackBetween(from, to, includeClass, false); } /** * print all stack frames between the frames which match start and end to System.out by calling * traceStackBetween(from, to, includeClass, false, prefix) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used */ public void traceStackBetween(String from, String to, boolean includeClass, String prefix) { traceStackBetween(from, to, includeClass, false, prefix); } /** * print all stack frames between the frames which match start and end preceded by prefix * by calling traceStackBetween(from, to, includeClass, false, prefix, key) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param key an object identifying the trace stream to which * output should be generated */ public void traceStackBetween(String from, String to, boolean includeClass, String prefix, Object key) { traceStackBetween(from, to, includeClass, false, prefix, key); } /** * print all stack frames between the frames which match start and end to System.out by calling * traceStackBetween(from, to, includeClass, includePackage, null) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not */ public void traceStackBetween(String from, String to, boolean includeClass, boolean includePackage) { traceStackBetween(from, to, includeClass, includePackage, null); } /** * print all stack frames between the frames which match start and end to System.out preceded by prefix * by calling traceStackBetween(from, to, includeClass, includePackage, prefix, "out") * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used */ public void traceStackBetween(String from, String to, boolean includeClass, boolean includePackage, String prefix) { traceStackBetween(from, to, includeClass, includePackage, prefix, "out"); } /** * print all stack frames between the frames which match start and end preceded by prefix * by calling traceStackBetween(from, to, false, includeClass, includePackage, prefix, key) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param key an object identifying the trace stream to which * output should be generated */ public void traceStackBetween(String from, String to, boolean includeClass, boolean includePackage, String prefix, Object key) { traceStackRange(from, to, false, includeClass, includePackage, prefix, key); } // tracing stack range by regular expression match /** * print all stack frames between the frames which match start and end to System.out by calling * traceStackBetweenMatches(from, to, null) * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. */ public void traceStackBetweenMatches(String from, String to) { traceStackBetweenMatches(from, to, null); } /** * print all stack frames between the frames which match start and end to System.out preceded by prefix * by calling traceStackBetweenMatches(from, to, prefix, "out") * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is */ public void traceStackBetweenMatches(String from, String to, String prefix) { traceStackBetweenMatches(from, to, prefix, "out"); } /** * print all stack frames between the frames which match start and end to System.out preceded by prefix * by calling traceStackBetweenMatches(from, to, false, prefix, key) * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param key an object identifying the trace stream to which * output should be generated */ public void traceStackBetweenMatches(String from, String to, String prefix, Object key) { traceStackBetweenMatches(from, to, false, prefix, key); } /** * print all stack frames between the frames which match start and end to System.out by calling * traceStackBetweenMatches(from, to, includeClass, false) * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not */ public void traceStackBetweenMatches(String from, String to, boolean includeClass) { traceStackBetweenMatches(from, to, includeClass, false); } /** * print all stack frames between the frames which match start and end to System.out by calling * traceStackBetweenMatches(from, to, includeClass, false, prefix) * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used */ public void traceStackBetweenMatches(String from, String to, boolean includeClass, String prefix) { traceStackBetweenMatches(from, to, includeClass, false, prefix); } /** * print all stack frames between the frames which match start and end preceded by prefix * by calling traceStackBetween(from, to, includeClass, false, prefix, key) * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param key an object identifying the trace stream to which * output should be generated */ public void traceStackBetweenMatches(String from, String to, boolean includeClass, String prefix, Object key) { traceStackBetweenMatches(from, to, includeClass, false, prefix, key); } /** * print all stack frames between the frames which match start and end to System.out by calling * traceStackBetweenMatches(from, to, includeClass, includePackage, null) * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not */ public void traceStackBetweenMatches(String from, String to, boolean includeClass, boolean includePackage) { traceStackBetweenMatches(from, to, includeClass, includePackage, null); } /** * print all stack frames between the frames which match start and end to System.out preceded by prefix * by calling traceStackBetweenMatches(from, to, true, includeClass, includePackage, prefix, "out"); * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used */ public void traceStackBetweenMatches(String from, String to, boolean includeClass, boolean includePackage, String prefix) { traceStackBetweenMatches(from, to, includeClass, includePackage, prefix, "out"); } /** * print all stack frames between the frames which match start and end preceded by prefix * by calling traceStackRange(from, to, true, includeClass, includePackage, prefix, key) * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @param key an object identifying the trace stream to which * output should be generated */ public void traceStackBetweenMatches(String from, String to, boolean includeClass, boolean includePackage, String prefix, Object key) { traceStackRange(from, to, true, includeClass, includePackage, prefix, key); } /** * print all stack frames between the frames which match start and end to the trace stream identified by key * preceded by prefix. * * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param isRegExp true if from and true should be matched as * regular expressions or false if they should be matched using a * String equals comparison. * @param includeClass true if the match should be against the * package and class qualified method name * @param includePackage true if the match should be against the * package and class qualified method name. ignored if * includeClass is not also true. * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace (restricted) for " + Thread.currentThread().getName() + * "\n" is used * @param key an object identifying the trace stream to which * output should be generated */ public void traceStackRange(String from, String to, boolean isRegExp, boolean includeClass, boolean includePackage, String prefix, Object key) { String stackTrace = formatStackRange(from, to, isRegExp, includeClass, includePackage, prefix); trace(key, stackTrace); } // call stack management support // // retrieving caller frames /** * return a stack trace by calling formatStack(null) * @return a stack trace formatted as a String */ public String formatStack() { return formatStack(null); } /** * return a stack trace by calling formatStack(prefix, 0) * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @return a stack trace formatted as a String */ public String formatStack(String prefix) { return formatStack(prefix, 0); } /** * return a stack trace by calling formatStack(null, maxFrames) * @param maxFrames the maximum number of frames to print or 0 if * no limit should apply * @return a stack trace formatted as a String */ public String formatStack(int maxFrames) { return formatStack(null, maxFrames); } /** * print a stack trace to the trace stream identified by key * * @param prefix a String to be printed once before printing each line of stack trace. if supplied as null * then the prefix "Stack trace for thread " + Thread.currentThread().getName() + "\n" is used * @param maxFrames the maximum number of frames to print or 0 if no limit should apply * @return a stack trace formatted as a String */ public String formatStack(String prefix, int maxFrames) { StringBuffer buffer = new StringBuffer(); appendStack(buffer, prefix, maxFrames, Thread.currentThread(), getStack()); return buffer.toString(); } // // retrieving frames for all threads /** * return all stack traces by calling formatAllStacks(null) * @return all thread stack traces formatted as a String */ public String formatAllStacks() { return formatAllStacks(null); } /** * return all stack traces by calling formatAllStacks(prefix, 0) * @param prefix a String to be printed once before printing each line of stack trace. if supplied as null * then the prefix "Stack trace for thread " + Thread.currentThread().getName() + "\n" is used * @return all thread stack traces formatted as a String */ public String formatAllStacks(String prefix) { return formatAllStacks(prefix, 0); } /** * return all stack traces by calling formatAllStacks(null, maxFrames) * @param maxFrames the maximum number of frames to print or 0 if * no limit should apply * @return all thread stack traces formatted as a String */ public String formatAllStacks(int maxFrames) { return formatAllStacks(null, maxFrames); } /** * return all stack traces * * @param prefix a String to be printed once before printing each line of stack trace. if supplied as null * then the prefix "Stack trace for thread " + Thread.currentThread().getName() + "\n" is used * @param maxFrames the maximum number of frames to print or 0 if no limit should apply * @return all thread stack traces formatted as a String */ public String formatAllStacks(String prefix, int maxFrames) { StringBuffer buffer = new StringBuffer(); Map<Thread, StackTraceElement[]> stacks = Thread.getAllStackTraces(); for (Map.Entry<Thread, StackTraceElement[]> entry : stacks.entrySet()) { appendStack(buffer, prefix, maxFrames, entry.getKey(), entry.getValue()); buffer.append('\n'); } return buffer.toString(); } // // retrieving frames for all threads /** * return stack traces of a specific thread by calling formatThreadStack(threadName, null) * @param threadName a string identifying the thread * @return a stack trace formatted as a String */ public String formatThreadStack(String threadName) { return formatThreadStack(threadName, null); } /** * return all stack traces by calling formatThreadStack(threadName, prefix, 0) * @param threadName a string identifying the thread * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + threadName + "\n" is used * @return a stack trace formatted as a String */ public String formatThreadStack(String threadName, String prefix) { return formatThreadStack(threadName, prefix, 0); } /** * return all stack traces by calling formatThreadStack(threadName, null, maxFrames) * @param threadName a string identifying the thread * @param maxFrames the maximum number of frames to print or 0 if no limit should apply * @return a stack trace formatted as a String */ public String formatThreadStack(String threadName, int maxFrames) { return formatThreadStack(threadName, null, maxFrames); } /** * return all stack traces * * @param threadName a string identifying the thread * @param prefix a String to be printed once before printing each line of stack trace. if supplied as null * then the prefix "Stack trace for thread " + threadName + "\n" is used * @param maxFrames the maximum number of frames to print or 0 if no limit should apply * @return a stack trace formatted as a String */ public String formatThreadStack(String threadName, String prefix, int maxFrames) { StringBuffer buffer = new StringBuffer(); Map<Thread, StackTraceElement[]> stacks = Thread.getAllStackTraces(); boolean found = false; for (Map.Entry<Thread, StackTraceElement[]> entry : stacks.entrySet()) { Thread thread = entry.getKey(); if (thread.getName().equals(threadName)) { appendStack(buffer, prefix, maxFrames, thread, entry.getValue()); found = true; } } if (!found) { buffer.append("Thread "); buffer.append(threadName); buffer.append(" not found\n"); } return buffer.toString(); } private void appendStack(StringBuffer buffer, String prefix, int maxFrames, Thread thread, StackTraceElement[] stack) { int l = stack.length; int i; if (thread == Thread.currentThread()) { // trim off the byteman trigger parts i = triggerIndex(stack); if (i < 0) { return; } } else { i = 0; } if (prefix != null) { buffer.append(prefix); } else { buffer.append("Stack trace for thread "); buffer.append(thread.getName()); buffer.append('\n'); } boolean dotdotdot = false; if (maxFrames > 0 && (i + maxFrames) < l) { l = i + maxFrames; dotdotdot = true; } for (; i < l; i++) { printlnFrame(buffer, stack[i]); } if (dotdotdot) { buffer.append(" . . .\n"); } } // retrieving caller frames which match a regular expression /** * return a String tracing all stack frames which match pattern by calling formatStackMatching(pattern, null) * @param regExp the pattern to match * @return a stack trace formatted as a String */ public String formatStackMatching(String regExp) { return formatStackMatching(regExp, null); } /** * return a String tracing all stack frames which match pattern by calling * formatStackMatching(pattern, false, prefix) * @param regExp the pattern to match * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @return a stack trace formatted as a String */ public String formatStackMatching(String regExp, String prefix) { return formatStackMatching(regExp, false, prefix); } /** * return a String tracing all stack frames which match pattern by calling * formatStackMatching(pattern, includeClass, false) * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not * @return a stack trace formatted as a String */ public String formatStackMatching(String regExp, boolean includeClass) { return formatStackMatching(regExp, includeClass, false); } /** * return a String tracing all stack frames which match pattern by calling * formatStackMatching(pattern, includeClass, false, prefix) * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @return a stack trace formatted as a String */ public String formatStackMatching(String regExp, boolean includeClass, String prefix) { return formatStackMatching(regExp, includeClass, false, prefix); } /** * return a String tracing all stack frames which match pattern by calling * formatStackMatching(pattern, includeClass, includePackage, null) * @param regExp the pattern to match * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not * @return a stack trace formatted as a String */ public String formatStackMatching(String regExp, boolean includeClass, boolean includePackage) { return formatStackMatching(regExp, includeClass, includePackage, null); } /** * return a String tracing all stack frames which match pattern. * * @param regExp a pattern which will be matched against the method name of the stack frame as a * regular expression by calling String.matches() * @param includeClass true if the match should be against the package and class qualified method name * @param includePackage true if the match should be against the package and class qualified method name. * ignored if includeClass is not also true. * @param prefix a String to be printed once before printing each line of stack trace. if supplied as null * then the prefix "Stack trace for thread " + Thread.currentThread().getName() + " matching " + pattern + "\n" is used * @return a stack trace formatted as a String */ public String formatStackMatching(String regExp, boolean includeClass, boolean includePackage, String prefix) { StringBuffer buffer = new StringBuffer(); StackTraceElement[] stack = getStack(); int l = stack.length; int i = triggerIndex(stack); if (i < 0) { return ""; } if (prefix != null) { buffer.append(prefix); } else { buffer.append("Stack trace for thread "); buffer.append(Thread.currentThread().getName()); buffer.append(" matching "); buffer.append(regExp); buffer.append('\n'); } for (; i < l; i++) { String fullName; if (includeClass) { String className = stack[i].getClassName(); if (!includePackage) { int dotIdx = className.lastIndexOf('.'); if (dotIdx >= 0) { className = className.substring(dotIdx + 1); } } fullName = className + "." + stack[i].getMethodName(); } else { fullName = stack[i].getMethodName(); } if (fullName.matches(regExp)) { printlnFrame(buffer, stack[i]); } } return buffer.toString(); } // retrieving caller frames between exact matched start and end frame /** * return a String tracing the stack between the frames which match start and end by calling * formatStackBetween(from, to, null) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @return a stack trace formatted as a String */ public String formatStackBetween(String from, String to) { return formatStackBetween(from, to, null); } /** * return a String tracing the stack between the frames which match start and end * by calling formatStackBetween(from, to, false, false, false, prefix) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @return a stack trace formatted as a String */ public String formatStackBetween(String from, String to, String prefix) { return formatStackBetween(from, to, false, false, prefix); } /** * return a String tracing the stack between the frames which match start and end by calling * formatStackBetween(from, to, includeClass, false) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @return a stack trace formatted as a String */ public String formatStackBetween(String from, String to, boolean includeClass) { return formatStackBetween(from, to, includeClass, false); } /** * return a String tracing the stack between the frames which match start and end by calling * formatStackBetween(from, to, includeClass, false, prefix) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @return a stack trace formatted as a String */ public String formatStackBetween(String from, String to, boolean includeClass, String prefix) { return formatStackBetween(from, to, includeClass, false, prefix); } /** * return a String tracing the stack between the frames which match start and end by calling * formatStackBetween(from, to, includeClass, includePackage, null) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not * @return a stack trace formatted as a String */ public String formatStackBetween(String from, String to, boolean includeClass, boolean includePackage) { return formatStackBetween(from, to, includeClass, includePackage, null); } /** * return a String tracing the stack between the frames which match start and end by calling * formatStackRange(from, to, false, includeClass, includePackage, prefix) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @return a stack trace formatted as a String */ public String formatStackBetween(String from, String to, boolean includeClass, boolean includePackage, String prefix) { return formatStackRange(from, to, false, includeClass, includePackage, prefix); } // retrieving caller frames between regular expression matched start and end frame /** * return a String tracing the stack between the frames which match start and end by calling * formatStackBetweenMatches(from, to, null) * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @return a stack trace formatted as a String */ public String formatStackBetweenMatches(String from, String to) { return formatStackBetweenMatches(from, to, null); } /** * return a String tracing the stack between the frames which match start and end * by calling formatStackBetweenMatches(from, to, false, false, false, prefix) * @param from a string which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a string which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @return a stack trace formatted as a String */ public String formatStackBetweenMatches(String from, String to, String prefix) { return formatStackBetweenMatches(from, to, false, false, prefix); } /** * return a String tracing the stack between the frames which match start and end by calling * formatStackBetweenMatches(from, to, includeClass, false) * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @return a stack trace formatted as a String */ public String formatStackBetweenMatches(String from, String to, boolean includeClass) { return formatStackBetweenMatches(from, to, includeClass, false); } /** * return a String tracing the stack between the frames which match start and end by calling * formatStackBetweenMatches(from, to, includeClass, false, prefix) * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @return a stack trace formatted as a String */ public String formatStackBetweenMatches(String from, String to, boolean includeClass, String prefix) { return formatStackBetweenMatches(from, to, includeClass, false, prefix); } /** * return a String tracing the stack between the frames which match start and end by calling * formatStackBetweenMatches(from, to, includeClass, includePackage, null) * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not * @return a stack trace formatted as a String */ public String formatStackBetweenMatches(String from, String to, boolean includeClass, boolean includePackage) { return formatStackBetweenMatches(from, to, includeClass, includePackage, null); } /** * return a String tracing the stack between the frames which match start and end by calling * formatStackRange(from, to, true, includeClass, includePackage, prefix) * @param from a pattern which identifies the first frame which * should be printed. from will be matched against the name of * each successive stack frame from the trigger methdo frame until * a matching frame is found. If null is supplied then the trigger * frame will be used as the first frame to print. If a non-null * value is supplied and no match is found then no frames will be * printed. * @param to a pattern which identifies the last frame which * should be printed. to will be matched against the name of each * successive stack frame following the first matched frame until * a matching frame is found. If null is supplied or no match is * found then the bottom frame will be used as the last frame to * print. * @param includeClass true if the check should include the * class name false if not * @param includePackage true if the check should include the * package name false if not * @param prefix a String to be printed once before printing each * line of stack trace. if supplied as null then the prefix "Stack * trace for thread " + Thread.currentThread().getName() + "\n" is * used * @return a stack trace formatted as a String */ public String formatStackBetweenMatches(String from, String to, boolean includeClass, boolean includePackage, String prefix) { return formatStackRange(from, to, true, includeClass, includePackage, prefix); } /** * return a String tracing the stack between the frames which match start and end. * * @param from a pattern which identifies the first frame which should be printed. from will be matched against * the name of each successive stack frame from the trigger methdo frame until a matching frame is found. If null * is supplied then the trigger frame will be used as the first frame to print. If a non-null value is supplied * and no match is found then no frames will be printed. * @param to a pattern which identifies the last frame which should be printed. to will be matched against * the name of each successive stack frame following the first matched frame until a matching frame is found. * If null is supplied or no match is found then the bottom frame will be used as the last frame to print. * @param isRegExp true if from and true should be matched as regular expressions or false if they should be * matched using a String equals comparison. * @param includeClass true if the match should be against the package and class qualified method name * @param includePackage true if the match should be against the package and class qualified method name. * ignored if includeClass is not also true. * @param prefix a String to be printed once before printing each line of stack trace. if supplied as null * then the prefix "Stack trace (restricted) for " + Thread.currentThread().getName() + "\n" is used * @return a stack trace formatted as a String */ public String formatStackRange(String from, String to, boolean isRegExp, boolean includeClass, boolean includePackage, String prefix) { StringBuffer buffer = new StringBuffer(); StackTraceElement[] stack = getStack(); int l = stack.length; int i = triggerIndex(stack); if (i < 0) { return ""; } int first; if (from != null) { first = matchIndex(stack, from, isRegExp, includeClass, includePackage, i, l); if (first < 0) { return ""; } } else { first = i; } int last; if (to != null) { last = matchIndex(stack, to, isRegExp, includeClass, includePackage, first + 1, l); if (last < 0) { last = l - 1; } } else { last = l - 1; } if (prefix != null) { buffer.append(prefix); } else { buffer.append("Stack trace (restricted) for "); buffer.append(Thread.currentThread().getName()); buffer.append('\n'); } // n.b. the range includes the last matched frame for (i = first; i <= last; i++) { printlnFrame(buffer, stack[i]); } return buffer.toString(); } // trigger management /** * enable or disable recursive triggering of rules by subsequent operations performed during binding, * testing or firing of the current rule in the current thread. * @param enabled true if triggering should be enabled or false if it should be disabled * @return always returns true so it can be called in a rule condition */ public boolean setTriggering(boolean enabled) { if (enabled) { Rule.enableTriggers(); } else { Rule.disableTriggers(); } return true; } // exposed functionality of the instrumentation instance /** * provide an estimate of an object's size * * return -1 if not running in a real agent * @param o the object whose size is sought * @return the size or -1 */ public long getObjectSize(Object o) { return rule.getObjectSize(o); } /** * return a unique name for the trigger point associated with this * rule. n.b. a single rule may give rise to more than one trigger * point if the rule applies to several methods with the same name * or to several classes with the same (package unqualified) name, * or even to several versions of the same compiled class loaded * into distinct class loaders. * * @return a unique name for the trigger point from which this * rule was invoked */ public String toString() { return rule.getName(); } // lifecycle management public static void activated() { if (Transformer.isDebug()) { System.out.println("Default helper activated"); } } public static void deactivated() { if (Transformer.isDebug()) { System.out.println("Default helper deactivated"); } } public static void installed(Rule rule) { if (Transformer.isDebug()) { System.out.println("Installed rule using default helper : " + rule.getName()); } } public static void uninstalled(Rule rule) { if (Transformer.isDebug()) { System.out.println("Uninstalled rule using default helper : " + rule.getName()); } } // private and protected implementation private StackTraceElement[] stack = null; /** * access to the current stack frames * * @return an array of stack trace elements for the current stack */ protected StackTraceElement[] getStack() { if (stack == null) { synchronized (this) { stack = Thread.currentThread().getStackTrace(); } } return stack; } private static String RULE_CLASS_NAME = Rule.class.getCanonicalName(); private static String RULE_EXECUTE_METHOD_NAME = "execute"; /** * return the index of the frame in stack for the trigger method * below which the rule system was entered or -1 if it cannot be * found * @param stack an array of stack trace elements for the current * stack * @return the index of the frame for the trigger method or -1 if * it cannot be found */ protected int triggerIndex(StackTraceElement[] stack) { int l= stack.length; int i; // find the trigger method frame above the rule engine entry point // we should see two calls to rule.execute() for (i = 0; i < l; i++) { if (RULE_CLASS_NAME.equals(stack[i].getClassName()) && RULE_EXECUTE_METHOD_NAME.equals(stack[i].getMethodName())) { break; } } if (i >= l - 1 || !RULE_CLASS_NAME.equals(stack[i].getClassName()) || !RULE_EXECUTE_METHOD_NAME.equals(stack[i].getMethodName())) { // illegal usage new ExecuteException("Helper.formatStack : can only be called below Rule.execute()").printStackTrace(); return -1; } return i + 2; } /** * return the index of the first frame at or below index start * which matches pattern * @param stack array of stack trace elements * @param pattern a pattern to be matched against the concatenated * frame method name using String.matches() * @param isRegExp true if the pattern should be matched as a * regular expression or false if it should be matched using a * String equals comparison * @param includeClass true if the method name should be qualified * with the package and class name * @param includePackage true if the check should include the * package name false if not * @param start the index of the first frame which should be * tested for a match. this must be greater than or equal to the * trigger index. * @param limit the index of the first frame which should not be * tested for a match. this must be less than or equal to the * stack length * @return the index of the matching frame between start and limit * - 1 or -1 if it no match found */ protected int matchIndex(StackTraceElement[] stack, String pattern, boolean isRegExp, boolean includeClass, boolean includePackage, int start, int limit) { int l= stack.length; int i = start; if (limit > l) { limit = l; } // find the trigger method frame above the rule engine entry point // we should see two calls to rule.execute() for (; i < limit; i++) { String fullName; if (includeClass) { String className = stack[i].getClassName(); if (!includePackage) { int dotIdx = className.lastIndexOf('.'); if (dotIdx >= 0) { className = className.substring(dotIdx + 1); } } fullName = className + "." + stack[i].getMethodName(); } else { fullName = stack[i].getMethodName(); } if (isRegExp) { if (fullName.matches(pattern)) { return i; } } else { if (fullName.equals(pattern)) { return i; } } } return -1; } /** * print the details of stack frame followed by a newline to * buffer by calling printlnFrame(buffer, frame) then * buffer.append('\n') * @param buffer the buffer to write to * @param frame the stack frame to print */ protected void printlnFrame(StringBuffer buffer, StackTraceElement frame) { printFrame(buffer, frame); buffer.append('\n'); } /** * print the details of stack frame to buffer * @param buffer the buffer to write to * @param frame the stack frame to print */ protected void printFrame(StringBuffer buffer, StackTraceElement frame) { buffer.append(frame.getClassName()); buffer.append("."); buffer.append(frame.getMethodName()); String fileName = frame.getFileName(); if (fileName != null) { buffer.append("("); buffer.append(fileName); buffer.append(":"); buffer.append(frame.getLineNumber()); buffer.append(")"); } else { buffer.append(" (Unknown Source)"); } } /** * lookup the waiter object used to target wait and signal * requests associated with a specific identifying object * @param object the identifer for the waiter * @param createIfAbsent true if the waiter should be (atomically) * inserted if it is not present * @return the waiter if it was found or inserted or null if it * was not found and createIfAbsent was false */ private Waiter getWaiter(Object object, boolean createIfAbsent) { Waiter waiter; synchronized(waitMap) { waiter = waitMap.get(object); if (waiter == null && createIfAbsent) { waiter = new Waiter(object); waitMap.put(object, waiter); } } return waiter; } /** * remove the waiter object used to target wait and signal * requests associated with a specific identifying object * @param object the identifer for the waiter * @return the waiter if it was found or inserted or null if it * was not found and createIfAbsent was false */ private Waiter removeWaiter(Object object) { return waitMap.remove(object); } private static int nextFileIndex = 0; private static synchronized int nextFileIndex() { return nextFileIndex++; } /** * generate a name for an output file to be used to sink the trace stream * named by identifier. the name will normally start with the prefix * "trace" followed by a 9 digit number followed by a ".log" suffix. * In the special case that identifier is one of the special String values * "dbg", "vrb" or "nzy" idenitfying the 3 special Byteman trace streams * used to log rule debug messages or verbose/noisy level Byteman agent trace * messages the returned file name will start with the respective prefixes * "debug", "verbose" and "noisy". * @param identifier the identifier of the trace stream for which a file * is being opened * @return a name to be used for the trace file. */ private static String nextFileName(Object identifier) { // if we are writing to the debug, verbose or noisy trace streams // then start the file name with the that name as prefix otherwise // juts start it with the prefix "trace" String prefix; if ("dbg".equals(identifier)) { prefix = "debug"; } else if ("vrb".equals(identifier)) { prefix = "verbose"; } else if ("nzy".equals(identifier)) { prefix = "noisy"; } else { prefix = "trace"; } StringWriter writer = new StringWriter(); String digits = Integer.toString(nextFileIndex()); int numDigits = digits.length(); int idx; writer.write(prefix); // this pads up to 9 digits but we may get more if we open enough files! for (idx = 9; idx > numDigits; idx--) { writer.write('0'); } writer.write(digits); writer.write(".log"); return writer.toString(); } /** * a hash map used to identify trace streams from their * identifying objects */ private static HashMap<Object, PrintStream> traceMap = new HashMap<Object, PrintStream>(); /** * a set used to identify settings for boolean flags associated * with arbitrary objects. if an object is in the set then the * flag associated with the object is set (true) otherwise it is * clear (false). */ private static Set<Object> flagSet = new HashSet<Object>(); /** * a hash map used to identify countdowns from their identifying * objects */ private static HashMap<Object, CountDown> countDownMap = new HashMap<Object, CountDown>(); /** * a hash map used to identify counters from their identifying * objects */ private static HashMap<Object, Counter> counterMap = new HashMap<Object, Counter>(); /** * a hash map used to identify waiters from their identifying * objects */ private static HashMap<Object, Waiter> waitMap = new HashMap<Object, Waiter>(); /** * a hash map used to identify rendezvous from their identifying * objects */ private static HashMap<Object, Rendezvous> rendezvousMap = new HashMap<Object, Rendezvous>(); /** * a hash map used to identify timer from their identifying * objects */ private static HashMap<Object, Timer> timerMap = new HashMap<Object, Timer>(); /** * a hash map used to identify maps from their identifying * objects */ private static HashMap<Object, HashMap<Object, Object>> linkMaps = new HashMap<Object, HashMap<Object, Object>>(); // initialise the trace map so it contains the system output and // error keyed under "out" and "err" static { // set up out and err trace streams to System.out and System.err // traceClose ensures they cannot be closed and hence re-opened traceMap.put("out", System.out); traceMap.put("err", System.err); // default dbg, vrb and nzy trace streams to System.out // they can be closed and re-opened traceMap.put("dbg", System.out); traceMap.put("vrb", System.out); traceMap.put("nzy", System.out); } }