Return-Path: Delivered-To: apmail-incubator-harmony-commits-archive@www.apache.org Received: (qmail 90242 invoked from network); 8 Sep 2006 02:19:44 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 8 Sep 2006 02:19:44 -0000 Received: (qmail 30354 invoked by uid 500); 8 Sep 2006 02:19:43 -0000 Delivered-To: apmail-incubator-harmony-commits-archive@incubator.apache.org Received: (qmail 30254 invoked by uid 500); 8 Sep 2006 02:19:43 -0000 Mailing-List: contact harmony-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: harmony-dev@incubator.apache.org Delivered-To: mailing list harmony-commits@incubator.apache.org Received: (qmail 30226 invoked by uid 99); 8 Sep 2006 02:19:43 -0000 Received: from asf.osuosl.org (HELO asf.osuosl.org) (140.211.166.49) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 07 Sep 2006 19:19:43 -0700 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received-SPF: pass (asf.osuosl.org: local policy) Received: from [140.211.166.113] (HELO eris.apache.org) (140.211.166.113) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 07 Sep 2006 19:19:38 -0700 Received: by eris.apache.org (Postfix, from userid 65534) id 552F41A9822; Thu, 7 Sep 2006 19:19:18 -0700 (PDT) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r441341 [3/3] - /incubator/harmony/enhanced/classlib/trunk/modules/luni-kernel/src/main/java/java/lang/ Date: Fri, 08 Sep 2006 02:19:17 -0000 To: harmony-commits@incubator.apache.org From: ndbeyer@apache.org X-Mailer: svnmailer-1.1.0 Message-Id: <20060908021918.552F41A9822@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni-kernel/src/main/java/java/lang/ThreadGroup.java URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/luni-kernel/src/main/java/java/lang/ThreadGroup.java?view=diff&rev=441341&r1=441340&r2=441341 ============================================================================== --- incubator/harmony/enhanced/classlib/trunk/modules/luni-kernel/src/main/java/java/lang/ThreadGroup.java (original) +++ incubator/harmony/enhanced/classlib/trunk/modules/luni-kernel/src/main/java/java/lang/ThreadGroup.java Thu Sep 7 19:19:16 2006 @@ -30,822 +30,821 @@ * @see Thread * @see SecurityManager */ - public class ThreadGroup implements Thread.UncaughtExceptionHandler { - // Name of this ThreadGroup - private String name; + // Name of this ThreadGroup + private String name; - // Maximum priority for Threads inside this ThreadGroup - private int maxPriority = Thread.MAX_PRIORITY; + // Maximum priority for Threads inside this ThreadGroup + private int maxPriority = Thread.MAX_PRIORITY; - // The ThreadGroup to which this ThreadGroup belongs - ThreadGroup parent = null; + // The ThreadGroup to which this ThreadGroup belongs + ThreadGroup parent; - int numThreads = 0; - - // The Threads this ThreadGroup contains - private Thread[] childrenThreads = new Thread[5]; - - // The number of children groups - int numGroups = 0; - - // The ThreadGroups this ThreadGroup contains - private ThreadGroup[] childrenGroups = new ThreadGroup[3]; - - // Locked when using the childrenGroups field - private Object childrenGroupsLock = new Object(); - - // Locked when using the childrenThreads field - private Object childrenThreadsLock = new Object(); - - // Whether this ThreadGroup is a daemon ThreadGroup or not - private boolean isDaemon = false; - - // Whether this ThreadGroup has already been destroyed or not - private boolean isDestroyed = false; - - // Memory space to associate all new threads with - private long memorySpace; - - /** - * Used by the JVM to create the "system" ThreadGroup. Construct a - * ThreadGroup instance, and assign the name "system". - */ - private ThreadGroup() { - name = "system"; - } - - /** - * Constructs a new ThreadGroup with the name provided. The new ThreadGroup - * will be child of the ThreadGroup to which the - * Thread.currentThread() belongs. - * - * @param name - * Name for the ThreadGroup being created - * - * @throws SecurityException - * if checkAccess() for the parent group fails - * with a SecurityException - * - * @see java.lang.Thread#currentThread - */ - - public ThreadGroup(String name) { - this(Thread.currentThread().getThreadGroup(), name); - } - - /** - * Constructs a new ThreadGroup with the name provided, as child of the - * ThreadGroup parent - * - * @param parent - * Parent ThreadGroup - * @param name - * Name for the ThreadGroup being created - * - * @throws NullPointerException - * if parent is null - * @throws SecurityException if checkAccess() for the parent group fails with a SecurityException - * @throws IllegalThreadStateException if parent has been destroyed already - */ - public ThreadGroup(ThreadGroup parent, String name) { - super(); - if (Thread.currentThread() != null) { - // If parent is null we must throw NullPointerException, but that - // will be done "for free" with the message send below - parent.checkAccess(); - } - - this.name = name; - this.setParent(parent); - if (parent != null) { - this.setMaxPriority(parent.getMaxPriority()); - if (parent.isDaemon()) - this.setDaemon(true); - } - } - - /** - * Initialize the "main" ThreadGroup - */ - ThreadGroup(ThreadGroup parent) { - this.name = "main"; - this.setParent(parent); - } - - /** - * Returns the number of Threads which are children of the receiver, - * directly or indirectly. - * - * @return Number of children Threads - */ - - public int activeCount() { - int count = numThreads; - // Lock this subpart of the tree as we walk - synchronized (this.childrenGroupsLock) { - for (int i = 0; i < numGroups; i++) - count += this.childrenGroups[i].activeCount(); - } - return count; - } - - /** - * Returns the number of ThreadGroups which are children of the receiver, - * directly or indirectly. - * - * @return Number of children ThreadGroups - */ - public int activeGroupCount() { - int count = 0; - // Lock this subpart of the tree as we walk - synchronized (this.childrenGroupsLock) { - for (int i = 0; i < numGroups; i++) - // One for this group & the subgroups - count += 1 + this.childrenGroups[i].activeGroupCount(); - } - return count; - } - - /** - * Adds a Thread to the receiver. This should only be visible to class - * java.lang.Thread, and should only be called when a new Thread is created - * and initialized by the constructor. - * - * @param thread - * Thread to add to the receiver - * - * @throws IllegalThreadStateException - * if the receiver has been destroyed already - * - * @see #remove(java.lang.Thread) - */ - final void add(Thread thread) throws IllegalThreadStateException { - synchronized (this.childrenThreadsLock) { - if (!isDestroyed) { - if (childrenThreads.length == numThreads) { - Thread[] newThreads = new Thread[childrenThreads.length * 2]; - System.arraycopy(childrenThreads, 0, newThreads, 0, - numThreads); - newThreads[numThreads++] = thread; - childrenThreads = newThreads; - } else - childrenThreads[numThreads++] = thread; - } else - throw new IllegalThreadStateException(); - } - } - - /** - * Adds a ThreadGroup to the receiver. - * - * @param g - * ThreadGroup to add to the receiver - * - * @throws IllegalThreadStateException - * if the receiver has been destroyed already - */ - private void add(ThreadGroup g) throws IllegalThreadStateException { - synchronized (this.childrenGroupsLock) { - if (!isDestroyed) { - if (childrenGroups.length == numGroups) { - ThreadGroup[] newGroups = new ThreadGroup[childrenGroups.length * 2]; - System - .arraycopy(childrenGroups, 0, newGroups, 0, - numGroups); - newGroups[numGroups++] = g; - childrenGroups = newGroups; - } else - childrenGroups[numGroups++] = g; - } else - throw new IllegalThreadStateException(); - } - } - - /** - * The definition of this method depends on the deprecated method - * suspend(). The behavior of this call was never specified. - * - * @param b - * Used to control low memory implicit suspension - * - * @deprecated Required deprecated method suspend(). - */ - public boolean allowThreadSuspension(boolean b) { - // Does not apply to this VM, no-op - return true; - } - - /** - * If there is a SecurityManager installed, call checkAccess - * in it passing the receiver as parameter, otherwise do nothing. - */ - public final void checkAccess() { - // Forwards the message to the SecurityManager (if there's one) passing - // the receiver as parameter - SecurityManager currentManager = System.getSecurityManager(); - if (currentManager != null) - currentManager.checkAccess(this); - } - - /** - * Destroys the receiver and recursively all its subgroups. It is only legal - * to destroy a ThreadGroup that has no Threads. Any daemon ThreadGroup is - * destroyed automatically when it becomes empty (no Threads and no - * ThreadGroups in it). - * - * @throws IllegalThreadStateException - * if the receiver or any of its subgroups has been destroyed - * already - * @throws SecurityException - * if this.checkAccess() fails with a - * SecurityException - */ - - public final void destroy() { - checkAccess(); - - // Lock this subpart of the tree as we walk - synchronized (this.childrenThreadsLock) { - synchronized (this.childrenGroupsLock) { - int toDestroy = numGroups; - // Call recursively for subgroups - for (int i = 0; i < toDestroy; i++) { - // We always get the first element - remember, when the - // child dies it removes itself from our collection. See - // below. - this.childrenGroups[0].destroy(); - } - - if (parent != null) - parent.remove(this); - - // Now that the ThreadGroup is really destroyed it can be tagged - // as so - this.isDestroyed = true; - } - } - } - - /* - * Auxiliary method that destroys the receiver and recursively all its - * subgroups if the receiver is a daemon ThreadGroup. - * - * @see #destroy - * @see #setDaemon - * @see #isDaemon - */ - private void destroyIfEmptyDaemon() { - // Has to be non-destroyed daemon to make sense - synchronized (this.childrenThreadsLock) { - if (isDaemon && !isDestroyed && numThreads == 0) { - synchronized (this.childrenGroupsLock) { - if (numGroups == 0) - destroy(); - } - } - } - } - - /** - * Copies an array with all Threads which are children of the receiver - * (directly or indirectly) into the array threads passed as - * parameters. If the array passed as parameter is too small no exception is - * thrown - the extra elements are simply not copied. - * - * @param threads - * Thread array into which the Threads will be copied - * @return How many Threads were copied over - * - */ - public int enumerate(Thread[] threads) { - return enumerate(threads, true); - } - - /** - * Copies an array with all Threads which are children of the receiver into - * the array threads passed as parameter. Children Threads of - * subgroups are recursively copied as well if parameter - * recurse is true. - * - * If the array passed as parameter is too small no exception is thrown - - * the extra elements are simply not copied. - * - * @param threads - * array into which the Threads will be copied - * @param recurse - * Indicates whether Threads in subgroups should be recursively - * copied as well or not - * @return How many Threads were copied over - * - */ - public int enumerate(Thread[] threads, boolean recurse) { - return enumerateGeneric(threads, recurse, 0, true); - } - - /** - * Copies an array with all ThreadGroups which are children of the receiver - * (directly or indirectly) into the array groups passed as - * parameters. If the array passed as parameter is too small no exception is - * thrown - the extra elements are simply not copied. - * - * @param groups - * array into which the ThreadGroups will be copied - * @return How many ThreadGroups were copied over - * - */ - public int enumerate(ThreadGroup[] groups) { - return enumerate(groups, true); - } - - /** - * Copies an array with all ThreadGroups which are children of the receiver - * into the array groups passed as parameter. Children - * ThreadGroups of subgroups are recursively copied as well if parameter - * recurse is true. - * - * If the array passed as parameter is too small no exception is thrown - - * the extra elements are simply not copied. - * - * @param groups - * array into which the ThreadGroups will be copied - * @param recurse - * Indicates whether ThreadGroups in subgroups should be - * recursively copied as well or not - * @return How many ThreadGroups were copied over - * - */ - public int enumerate(ThreadGroup[] groups, boolean recurse) { - return enumerateGeneric(groups, recurse, 0, false); - } - - /** - * Copies into enumeration starting at - * enumerationIndex all Threads or ThreadGroups in the - * receiver. If recurse is true, recursively enumerate the - * elements in subgroups. - * - * If the array passed as parameter is too small no exception is thrown - - * the extra elements are simply not copied. - * - * @param enumeration - * array into which the elements will be copied - * @param recurse - * Indicates whether subgroups should be - * enumerated or not - * @param enumerationIndex - * Indicates in which position of the enumeration array we are - * @param enumeratingThreads - * Indicates whether we are enumerating Threads or ThreadGroups - * @return How many elements were enumerated/copied over - */ - private int enumerateGeneric(Object[] enumeration, boolean recurse, - int enumerationIndex, boolean enumeratingThreads) { - checkAccess(); - - Object[] immediateCollection = enumeratingThreads ? (Object[]) childrenThreads - : (Object[]) childrenGroups; - Object syncLock = enumeratingThreads ? childrenThreadsLock - : childrenGroupsLock; - - synchronized (syncLock) { // Lock this subpart of the tree as we walk - for (int i = enumeratingThreads ? numThreads : numGroups; --i >= 0;) { - if (!enumeratingThreads - || ((Thread) immediateCollection[i]).isAlive()) { - if (enumerationIndex >= enumeration.length) - return enumerationIndex; - enumeration[enumerationIndex++] = immediateCollection[i]; - } - } - } - - if (recurse) { // Lock this subpart of the tree as we walk - synchronized (this.childrenGroupsLock) { - for (int i = 0; i < numGroups; i++) { - if (enumerationIndex >= enumeration.length) - return enumerationIndex; - enumerationIndex = childrenGroups[i].enumerateGeneric( - enumeration, recurse, enumerationIndex, - enumeratingThreads); - } - } - } - return enumerationIndex; - } - - /** - * Answers the maximum allowed priority for a Thread in the receiver. - * - * @return the maximum priority (an int) - * - * @see #setMaxPriority - */ - public final int getMaxPriority() { - return maxPriority; - } - - /** - * Answers the name of the receiver. - * - * @return the receiver's name (a java.lang.String) - */ - public final String getName() { - return name; - } - - /** - * Answers the receiver's parent ThreadGroup. It can be null if the receiver - * is the the root ThreadGroup. - * - * @return the parent ThreadGroup - * - */ - public final ThreadGroup getParent() { - if (parent != null) - parent.checkAccess(); - return parent; - } - - /** - * Interrupts every Thread in the receiver and recursively in all its - * subgroups. - * - * @throws SecurityException - * if this.checkAccess() fails with a - * SecurityException - * - * @see Thread#interrupt - */ - public final void interrupt() { - checkAccess(); - // Lock this subpart of the tree as we walk - synchronized (this.childrenThreadsLock) { - for (int i = 0; i < numThreads; i++) - this.childrenThreads[i].interrupt(); - } - // Lock this subpart of the tree as we walk - synchronized (this.childrenGroupsLock) { - for (int i = 0; i < numGroups; i++) - this.childrenGroups[i].interrupt(); - } - } - - /** - * Answers true if the receiver is a daemon ThreadGroup, false otherwise. - * - * @return if the receiver is a daemon ThreadGroup - * - * @see #setDaemon - * @see #destroy - */ - public final boolean isDaemon() { - return isDaemon; - } - - /** - * Answers true if the receiver has been destroyed already, false otherwise. - * - * @return if the receiver has been destroyed already - * - * @see #destroy - */ - public boolean isDestroyed() { - return isDestroyed; - } - - /** - * Outputs to System.out a text representation of the - * hierarchy of Threads and ThreadGroups in the receiver (and recursively). - * Proper identation is done to suggest the nesting of groups inside groups - * and threads inside groups. - */ - public void list() { - // We start in a fresh line - System.out.println(); - list(0); - } - - /* - * Outputs to System.outa text representation of the - * hierarchy of Threads and ThreadGroups in the receiver (and recursively). - * The identation will be four spaces per level of nesting. - * - * @param levels How many levels of nesting, so that proper identetion can - * be output. - */ - private void list(int levels) { - for (int i = 0; i < levels; i++) - System.out.print(" "); // 4 spaces for each level - - // Print the receiver - System.out.println(this.toString()); - - // Print the children threads, with 1 extra identation - synchronized (this.childrenThreadsLock) { - for (int i = 0; i < numThreads; i++) { - // children get an extra identation, 4 spaces for each level - for (int j = 0; j <= levels; j++) - System.out.print(" "); - System.out.println(this.childrenThreads[i]); - } - } - synchronized (this.childrenGroupsLock) { - for (int i = 0; i < numGroups; i++) - this.childrenGroups[i].list(levels + 1); - } - } - - /** - * Answers true if the receiver is a direct or indirect parent group of - * ThreadGroup g, false otherwise. - * - * @param g - * ThreadGroup to test - * - * @return if the receiver is parent of the ThreadGroup passed as parameter - * - */ - public final boolean parentOf(ThreadGroup g) { - while (g != null) { - if (this == g) - return true; - g = g.parent; - } - return false; - } - - /** - * Removes a Thread from the receiver. This should only be visible to class - * java.lang.Thread, and should only be called when a Thread dies. - * - * @param thread - * Thread to remove from the receiver - * - * @see #add(Thread) - */ - final void remove(java.lang.Thread thread) { - synchronized (this.childrenThreadsLock) { - for (int i = 0; i < numThreads; i++) { - if (childrenThreads[i].equals(thread)) { - numThreads--; - System.arraycopy(childrenThreads, i + 1, childrenThreads, - i, numThreads - i); - childrenThreads[numThreads] = null; - break; - } - } - } - destroyIfEmptyDaemon(); - } - - /** - * Removes an immediate subgroup from the receiver. - * - * @param g - * Threadgroup to remove from the receiver - * - * @see #add(Thread) - * @see #add(ThreadGroup) - */ - private void remove(ThreadGroup g) { - synchronized (this.childrenGroupsLock) { - for (int i = 0; i < numGroups; i++) { - if (childrenGroups[i].equals(g)) { - numGroups--; - System.arraycopy(childrenGroups, i + 1, childrenGroups, i, - numGroups - i); - childrenGroups[numGroups] = null; - break; - } - } - } - destroyIfEmptyDaemon(); - } - - /** - * Resumes every Thread in the receiver and recursively in all its - * subgroups. - * - * @throws SecurityException - * if this.checkAccess() fails with a - * SecurityException - * - * @see Thread#resume - * @see #suspend - * - * @deprecated Requires deprecated method Thread.resume(). - */ - public final void resume() { - checkAccess(); - // Lock this subpart of the tree as we walk - synchronized (this.childrenThreadsLock) { - for (int i = 0; i < numThreads; i++) - this.childrenThreads[i].resume(); - } - // Lock this subpart of the tree as we walk - synchronized (this.childrenGroupsLock) { - for (int i = 0; i < numGroups; i++) - this.childrenGroups[i].resume(); - } - } - - /** - * Configures the receiver to be a daemon ThreadGroup or not. Daemon - * ThreadGroups are automatically destroyed when they become empty. - * - * @param isDaemon - * new value defining if receiver should be daemon or not - * - * @throws SecurityException - * if checkAccess() for the parent group fails - * with a SecurityException - * - * @see #isDaemon - * @see #destroy - */ - public final void setDaemon(boolean isDaemon) { - checkAccess(); - this.isDaemon = isDaemon; - } - - /** - * Configures the maximum allowed priority for a Thread in the receiver and - * recursively in all its subgroups. - * - * One can never change the maximum priority of a ThreadGroup to be higher - * than it was. Such an attempt will not result in an exception, it will - * simply leave the ThreadGroup with its current maximum priority. - * - * @param newMax - * the new maximum priority to be set - * - * @throws SecurityException - * if checkAccess() fails with a - * SecurityException - * @throws IllegalArgumentException - * if the new priority is greater than Thread.MAX_PRIORITY or - * less than Thread.MIN_PRIORITY - * - * @see #getMaxPriority - */ - public final void setMaxPriority(int newMax) { - checkAccess(); - - if (newMax <= this.maxPriority) { - if (newMax < Thread.MIN_PRIORITY) - newMax = Thread.MIN_PRIORITY; - - int parentPriority = parent == null ? newMax : parent - .getMaxPriority(); - this.maxPriority = parentPriority <= newMax ? parentPriority - : newMax; - // Lock this subpart of the tree as we walk - synchronized (this.childrenGroupsLock) { - // ??? why not maxPriority - for (int i = 0; i < numGroups; i++) - this.childrenGroups[i].setMaxPriority(newMax); - } - } - } - - /** - * Sets the parent ThreadGroup of the receiver, and adds the receiver to the - * parent's collection of immediate children (if parent is - * not null). - * - * @param parent - * The parent ThreadGroup, or null if the receiver is to be the - * root ThreadGroup - * - * @see #getParent - * @see #parentOf - */ - private void setParent(ThreadGroup parent) { - if (parent != null) - parent.add(this); - this.parent = parent; - } - - /** - * Stops every Thread in the receiver and recursively in all its subgroups. - * - * @throws SecurityException - * if this.checkAccess() fails with a - * SecurityException - * - * @see Thread#stop() - * @see Thread#stop(Throwable) - * @see ThreadDeath - * - * @deprecated Requires deprecated method Thread.stop(). - */ - public final void stop() { - if (stopHelper()) - Thread.currentThread().stop(); - } - - /** - * @deprecated Requires deprecated method Thread.suspend(). - */ - private final boolean stopHelper() { - checkAccess(); - - boolean stopCurrent = false; - // Lock this subpart of the tree as we walk - synchronized (this.childrenThreadsLock) { - Thread current = Thread.currentThread(); - for (int i = 0; i < numThreads; i++) - if (this.childrenThreads[i] == current) { - stopCurrent = true; - } else { - this.childrenThreads[i].stop(); - } - } - // Lock this subpart of the tree as we walk - synchronized (this.childrenGroupsLock) { - for (int i = 0; i < numGroups; i++) - stopCurrent |= this.childrenGroups[i].stopHelper(); - } - return stopCurrent; - } - - /** - * Suspends every Thread in the receiver and recursively in all its - * subgroups. - * - * @throws SecurityException - * if this.checkAccess() fails with a - * SecurityException - * - * @see Thread#suspend - * @see #resume - * - * @deprecated Requires deprecated method Thread.suspend(). - */ - public final void suspend() { - if (suspendHelper()) - Thread.currentThread().suspend(); - } - - /** - * @deprecated Requires deprecated method Thread.suspend(). - */ - private final boolean suspendHelper() { - checkAccess(); - - boolean suspendCurrent = false; - // Lock this subpart of the tree as we walk - synchronized (this.childrenThreadsLock) { - Thread current = Thread.currentThread(); - for (int i = 0; i < numThreads; i++) - if (this.childrenThreads[i] == current) { - suspendCurrent = true; - } else { - this.childrenThreads[i].suspend(); - } - } - // Lock this subpart of the tree as we walk - synchronized (this.childrenGroupsLock) { - for (int i = 0; i < numGroups; i++) - suspendCurrent |= this.childrenGroups[i].suspendHelper(); - } - return suspendCurrent; - } - - /** - * Answers a string containing a concise, human-readable description of the - * receiver. - * - * @return a printable representation for the receiver. - */ - public String toString() { - return getClass().getName() + "[name=" + this.getName() + ",maxpri=" - + this.getMaxPriority() + "]"; - } - - /** - * Any uncaught exception in any Thread has to be forwarded (by the VM) to - * the Thread's ThreadGroup by sending this message (uncaughtException). - * This allows users to define custom ThreadGroup classes and custom - * behavior for when a Thread has an uncaughtException or when it does - * (ThreadDeath). - * - * @param t - * Thread with an uncaught exception - * @param e - * The uncaught exception itself - * - * @see Thread#stop() - * @see Thread#stop(Throwable) - * @see ThreadDeath - */ - public void uncaughtException(Thread t, Throwable e) { - if (parent != null) { - parent.uncaughtException(t, e); - } else if (!(e instanceof ThreadDeath)) { - // No parent group, has to be 'system' Thread Group - e.printStackTrace(System.err); - } - } + int numThreads; + + // The Threads this ThreadGroup contains + private Thread[] childrenThreads = new Thread[5]; + + // The number of children groups + int numGroups; + + // The ThreadGroups this ThreadGroup contains + private ThreadGroup[] childrenGroups = new ThreadGroup[3]; + + // Locked when using the childrenGroups field + private Object childrenGroupsLock = new Object(); + + // Locked when using the childrenThreads field + private Object childrenThreadsLock = new Object(); + + // Whether this ThreadGroup is a daemon ThreadGroup or not + private boolean isDaemon; + + // Whether this ThreadGroup has already been destroyed or not + private boolean isDestroyed; + + /** + * Used by the JVM to create the "system" ThreadGroup. Construct a + * ThreadGroup instance, and assign the name "system". + */ + private ThreadGroup() { + name = "system"; + } + + /** + * Constructs a new ThreadGroup with the name provided. The new ThreadGroup + * will be child of the ThreadGroup to which the + * Thread.currentThread() belongs. + * + * @param name Name for the ThreadGroup being created + * + * @throws SecurityException if checkAccess() for the parent + * group fails with a SecurityException + * + * @see java.lang.Thread#currentThread + */ + + public ThreadGroup(String name) { + this(Thread.currentThread().getThreadGroup(), name); + } + + /** + * Constructs a new ThreadGroup with the name provided, as child of the + * ThreadGroup parent + * + * @param parent Parent ThreadGroup + * @param name Name for the ThreadGroup being created + * + * @throws NullPointerException if parent is + * null + * @throws SecurityException if checkAccess() for the parent + * group fails with a SecurityException + * @throws IllegalThreadStateException if parent has been + * destroyed already + */ + public ThreadGroup(ThreadGroup parent, String name) { + super(); + if (Thread.currentThread() != null) { + // If parent is null we must throw NullPointerException, but that + // will be done "for free" with the message send below + parent.checkAccess(); + } + + this.name = name; + this.setParent(parent); + if (parent != null) { + this.setMaxPriority(parent.getMaxPriority()); + if (parent.isDaemon()) { + this.setDaemon(true); + } + } + } + + /** + * Initialize the "main" ThreadGroup + */ + ThreadGroup(ThreadGroup parent) { + this.name = "main"; + this.setParent(parent); + } + + /** + * Returns the number of Threads which are children of the receiver, + * directly or indirectly. + * + * @return Number of children Threads + */ + + public int activeCount() { + int count = numThreads; + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + count += this.childrenGroups[i].activeCount(); + } + } + return count; + } + + /** + * Returns the number of ThreadGroups which are children of the receiver, + * directly or indirectly. + * + * @return Number of children ThreadGroups + */ + public int activeGroupCount() { + int count = 0; + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + // One for this group & the subgroups + count += 1 + this.childrenGroups[i].activeGroupCount(); + } + } + return count; + } + + /** + * Adds a Thread to the receiver. This should only be visible to class + * java.lang.Thread, and should only be called when a new Thread is created + * and initialized by the constructor. + * + * @param thread Thread to add to the receiver + * + * @throws IllegalThreadStateException if the receiver has been destroyed + * already + * + * @see #remove(java.lang.Thread) + */ + final void add(Thread thread) throws IllegalThreadStateException { + synchronized (this.childrenThreadsLock) { + if (!isDestroyed) { + if (childrenThreads.length == numThreads) { + Thread[] newThreads = new Thread[childrenThreads.length * 2]; + System.arraycopy(childrenThreads, 0, newThreads, 0, numThreads); + newThreads[numThreads++] = thread; + childrenThreads = newThreads; + } else { + childrenThreads[numThreads++] = thread; + } + } else { + throw new IllegalThreadStateException(); + } + } + } + + /** + * Adds a ThreadGroup to the receiver. + * + * @param g ThreadGroup to add to the receiver + * + * @throws IllegalThreadStateException if the receiver has been destroyed + * already + */ + private void add(ThreadGroup g) throws IllegalThreadStateException { + synchronized (this.childrenGroupsLock) { + if (!isDestroyed) { + if (childrenGroups.length == numGroups) { + ThreadGroup[] newGroups = new ThreadGroup[childrenGroups.length * 2]; + System.arraycopy(childrenGroups, 0, newGroups, 0, numGroups); + newGroups[numGroups++] = g; + childrenGroups = newGroups; + } else { + childrenGroups[numGroups++] = g; + } + } else { + throw new IllegalThreadStateException(); + } + } + } + + /** + * The definition of this method depends on the deprecated method + * suspend(). The behavior of this call was never specified. + * + * @param b Used to control low memory implicit suspension + * + * @deprecated Required deprecated method suspend(). + */ + @Deprecated + public boolean allowThreadSuspension(boolean b) { + // Does not apply to this VM, no-op + return true; + } + + /** + * If there is a SecurityManager installed, call checkAccess + * in it passing the receiver as parameter, otherwise do nothing. + */ + public final void checkAccess() { + // Forwards the message to the SecurityManager (if there's one) passing + // the receiver as parameter + SecurityManager currentManager = System.getSecurityManager(); + if (currentManager != null) { + currentManager.checkAccess(this); + } + } + + /** + * Destroys the receiver and recursively all its subgroups. It is only legal + * to destroy a ThreadGroup that has no Threads. Any daemon ThreadGroup is + * destroyed automatically when it becomes empty (no Threads and no + * ThreadGroups in it). + * + * @throws IllegalThreadStateException if the receiver or any of its + * subgroups has been destroyed already + * @throws SecurityException if this.checkAccess() fails with + * a SecurityException + */ + + public final void destroy() { + checkAccess(); + + // Lock this subpart of the tree as we walk + synchronized (this.childrenThreadsLock) { + synchronized (this.childrenGroupsLock) { + int toDestroy = numGroups; + // Call recursively for subgroups + for (int i = 0; i < toDestroy; i++) { + // We always get the first element - remember, when the + // child dies it removes itself from our collection. See + // below. + this.childrenGroups[0].destroy(); + } + + if (parent != null) { + parent.remove(this); + } + + // Now that the ThreadGroup is really destroyed it can be tagged + // as so + this.isDestroyed = true; + } + } + } + + /* + * Auxiliary method that destroys the receiver and recursively all its + * subgroups if the receiver is a daemon ThreadGroup. + * + * @see #destroy + * @see #setDaemon + * @see #isDaemon + */ + private void destroyIfEmptyDaemon() { + // Has to be non-destroyed daemon to make sense + synchronized (this.childrenThreadsLock) { + if (isDaemon && !isDestroyed && numThreads == 0) { + synchronized (this.childrenGroupsLock) { + if (numGroups == 0) { + destroy(); + } + } + } + } + } + + /** + * Copies an array with all Threads which are children of the receiver + * (directly or indirectly) into the array threads passed as + * parameters. If the array passed as parameter is too small no exception is + * thrown - the extra elements are simply not copied. + * + * @param threads Thread array into which the Threads will be copied + * @return How many Threads were copied over + * + */ + public int enumerate(Thread[] threads) { + return enumerate(threads, true); + } + + /** + * Copies an array with all Threads which are children of the receiver into + * the array threads passed as parameter. Children Threads of + * subgroups are recursively copied as well if parameter + * recurse is true. + * + * If the array passed as parameter is too small no exception is thrown - + * the extra elements are simply not copied. + * + * @param threads array into which the Threads will be copied + * @param recurse Indicates whether Threads in subgroups should be + * recursively copied as well or not + * @return How many Threads were copied over + * + */ + public int enumerate(Thread[] threads, boolean recurse) { + return enumerateGeneric(threads, recurse, 0, true); + } + + /** + * Copies an array with all ThreadGroups which are children of the receiver + * (directly or indirectly) into the array groups passed as + * parameters. If the array passed as parameter is too small no exception is + * thrown - the extra elements are simply not copied. + * + * @param groups array into which the ThreadGroups will be copied + * @return How many ThreadGroups were copied over + * + */ + public int enumerate(ThreadGroup[] groups) { + return enumerate(groups, true); + } + + /** + * Copies an array with all ThreadGroups which are children of the receiver + * into the array groups passed as parameter. Children + * ThreadGroups of subgroups are recursively copied as well if parameter + * recurse is true. + * + * If the array passed as parameter is too small no exception is thrown - + * the extra elements are simply not copied. + * + * @param groups array into which the ThreadGroups will be copied + * @param recurse Indicates whether ThreadGroups in subgroups should be + * recursively copied as well or not + * @return How many ThreadGroups were copied over + * + */ + public int enumerate(ThreadGroup[] groups, boolean recurse) { + return enumerateGeneric(groups, recurse, 0, false); + } + + /** + * Copies into enumeration starting at + * enumerationIndex all Threads or ThreadGroups in the + * receiver. If recurse is true, recursively enumerate the + * elements in subgroups. + * + * If the array passed as parameter is too small no exception is thrown - + * the extra elements are simply not copied. + * + * @param enumeration array into which the elements will be copied + * @param recurse Indicates whether subgroups should be enumerated or not + * @param enumerationIndex Indicates in which position of the enumeration + * array we are + * @param enumeratingThreads Indicates whether we are enumerating Threads or + * ThreadGroups + * @return How many elements were enumerated/copied over + */ + private int enumerateGeneric(Object[] enumeration, boolean recurse, int enumerationIndex, + boolean enumeratingThreads) { + checkAccess(); + + Object[] immediateCollection = enumeratingThreads ? (Object[]) childrenThreads + : (Object[]) childrenGroups; + Object syncLock = enumeratingThreads ? childrenThreadsLock : childrenGroupsLock; + + synchronized (syncLock) { // Lock this subpart of the tree as we walk + for (int i = enumeratingThreads ? numThreads : numGroups; --i >= 0;) { + if (!enumeratingThreads || ((Thread) immediateCollection[i]).isAlive()) { + if (enumerationIndex >= enumeration.length) { + return enumerationIndex; + } + enumeration[enumerationIndex++] = immediateCollection[i]; + } + } + } + + if (recurse) { // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + if (enumerationIndex >= enumeration.length) { + return enumerationIndex; + } + enumerationIndex = childrenGroups[i].enumerateGeneric(enumeration, recurse, + enumerationIndex, enumeratingThreads); + } + } + } + return enumerationIndex; + } + + /** + * Answers the maximum allowed priority for a Thread in the receiver. + * + * @return the maximum priority (an int) + * + * @see #setMaxPriority + */ + public final int getMaxPriority() { + return maxPriority; + } + + /** + * Answers the name of the receiver. + * + * @return the receiver's name (a java.lang.String) + */ + public final String getName() { + return name; + } + + /** + * Answers the receiver's parent ThreadGroup. It can be null if the receiver + * is the the root ThreadGroup. + * + * @return the parent ThreadGroup + * + */ + public final ThreadGroup getParent() { + if (parent != null) { + parent.checkAccess(); + } + return parent; + } + + /** + * Interrupts every Thread in the receiver and recursively in all its + * subgroups. + * + * @throws SecurityException if this.checkAccess() fails with + * a SecurityException + * + * @see Thread#interrupt + */ + public final void interrupt() { + checkAccess(); + // Lock this subpart of the tree as we walk + synchronized (this.childrenThreadsLock) { + for (int i = 0; i < numThreads; i++) { + this.childrenThreads[i].interrupt(); + } + } + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + this.childrenGroups[i].interrupt(); + } + } + } + + /** + * Answers true if the receiver is a daemon ThreadGroup, false otherwise. + * + * @return if the receiver is a daemon ThreadGroup + * + * @see #setDaemon + * @see #destroy + */ + public final boolean isDaemon() { + return isDaemon; + } + + /** + * Answers true if the receiver has been destroyed already, false otherwise. + * + * @return if the receiver has been destroyed already + * + * @see #destroy + */ + public boolean isDestroyed() { + return isDestroyed; + } + + /** + * Outputs to System.out a text representation of the + * hierarchy of Threads and ThreadGroups in the receiver (and recursively). + * Proper identation is done to suggest the nesting of groups inside groups + * and threads inside groups. + */ + public void list() { + // We start in a fresh line + System.out.println(); + list(0); + } + + /* + * Outputs to System.outa text representation of the + * hierarchy of Threads and ThreadGroups in the receiver (and recursively). + * The identation will be four spaces per level of nesting. + * + * @param levels How many levels of nesting, so that proper identetion can + * be output. + */ + private void list(int levels) { + for (int i = 0; i < levels; i++) { + System.out.print(" "); // 4 spaces for each level + } + + // Print the receiver + System.out.println(this.toString()); + + // Print the children threads, with 1 extra identation + synchronized (this.childrenThreadsLock) { + for (int i = 0; i < numThreads; i++) { + // children get an extra identation, 4 spaces for each level + for (int j = 0; j <= levels; j++) { + System.out.print(" "); + } + System.out.println(this.childrenThreads[i]); + } + } + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + this.childrenGroups[i].list(levels + 1); + } + } + } + + /** + * Answers true if the receiver is a direct or indirect parent group of + * ThreadGroup g, false otherwise. + * + * @param g ThreadGroup to test + * + * @return if the receiver is parent of the ThreadGroup passed as parameter + * + */ + public final boolean parentOf(ThreadGroup g) { + while (g != null) { + if (this == g) { + return true; + } + g = g.parent; + } + return false; + } + + /** + * Removes a Thread from the receiver. This should only be visible to class + * java.lang.Thread, and should only be called when a Thread dies. + * + * @param thread Thread to remove from the receiver + * + * @see #add(Thread) + */ + final void remove(java.lang.Thread thread) { + synchronized (this.childrenThreadsLock) { + for (int i = 0; i < numThreads; i++) { + if (childrenThreads[i].equals(thread)) { + numThreads--; + System + .arraycopy(childrenThreads, i + 1, childrenThreads, i, numThreads + - i); + childrenThreads[numThreads] = null; + break; + } + } + } + destroyIfEmptyDaemon(); + } + + /** + * Removes an immediate subgroup from the receiver. + * + * @param g Threadgroup to remove from the receiver + * + * @see #add(Thread) + * @see #add(ThreadGroup) + */ + private void remove(ThreadGroup g) { + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + if (childrenGroups[i].equals(g)) { + numGroups--; + System.arraycopy(childrenGroups, i + 1, childrenGroups, i, numGroups - i); + childrenGroups[numGroups] = null; + break; + } + } + } + destroyIfEmptyDaemon(); + } + + /** + * Resumes every Thread in the receiver and recursively in all its + * subgroups. + * + * @throws SecurityException if this.checkAccess() fails with + * a SecurityException + * + * @see Thread#resume + * @see #suspend + * + * @deprecated Requires deprecated method Thread.resume(). + */ + @SuppressWarnings("deprecation") + @Deprecated + public final void resume() { + checkAccess(); + // Lock this subpart of the tree as we walk + synchronized (this.childrenThreadsLock) { + for (int i = 0; i < numThreads; i++) { + this.childrenThreads[i].resume(); + } + } + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + this.childrenGroups[i].resume(); + } + } + } + + /** + * Configures the receiver to be a daemon ThreadGroup or not. Daemon + * ThreadGroups are automatically destroyed when they become empty. + * + * @param isDaemon new value defining if receiver should be daemon or not + * + * @throws SecurityException if checkAccess() for the parent + * group fails with a SecurityException + * + * @see #isDaemon + * @see #destroy + */ + public final void setDaemon(boolean isDaemon) { + checkAccess(); + this.isDaemon = isDaemon; + } + + /** + * Configures the maximum allowed priority for a Thread in the receiver and + * recursively in all its subgroups. + * + * One can never change the maximum priority of a ThreadGroup to be higher + * than it was. Such an attempt will not result in an exception, it will + * simply leave the ThreadGroup with its current maximum priority. + * + * @param newMax the new maximum priority to be set + * + * @throws SecurityException if checkAccess() fails with a + * SecurityException + * @throws IllegalArgumentException if the new priority is greater than + * Thread.MAX_PRIORITY or less than Thread.MIN_PRIORITY + * + * @see #getMaxPriority + */ + public final void setMaxPriority(int newMax) { + checkAccess(); + + if (newMax <= this.maxPriority) { + if (newMax < Thread.MIN_PRIORITY) { + newMax = Thread.MIN_PRIORITY; + } + + int parentPriority = parent == null ? newMax : parent.getMaxPriority(); + this.maxPriority = parentPriority <= newMax ? parentPriority : newMax; + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + // ??? why not maxPriority + for (int i = 0; i < numGroups; i++) { + this.childrenGroups[i].setMaxPriority(newMax); + } + } + } + } + + /** + * Sets the parent ThreadGroup of the receiver, and adds the receiver to the + * parent's collection of immediate children (if parent is + * not null). + * + * @param parent The parent ThreadGroup, or null if the receiver is to be + * the root ThreadGroup + * + * @see #getParent + * @see #parentOf + */ + private void setParent(ThreadGroup parent) { + if (parent != null) { + parent.add(this); + } + this.parent = parent; + } + + /** + * Stops every Thread in the receiver and recursively in all its subgroups. + * + * @throws SecurityException if this.checkAccess() fails with + * a SecurityException + * + * @see Thread#stop() + * @see Thread#stop(Throwable) + * @see ThreadDeath + * + * @deprecated Requires deprecated method Thread.stop(). + */ + @SuppressWarnings("deprecation") + @Deprecated + public final void stop() { + if (stopHelper()) { + Thread.currentThread().stop(); + } + } + + /** + * @deprecated Requires deprecated method Thread.suspend(). + */ + @SuppressWarnings("deprecation") + @Deprecated + private final boolean stopHelper() { + checkAccess(); + + boolean stopCurrent = false; + // Lock this subpart of the tree as we walk + synchronized (this.childrenThreadsLock) { + Thread current = Thread.currentThread(); + for (int i = 0; i < numThreads; i++) { + if (this.childrenThreads[i] == current) { + stopCurrent = true; + } else { + this.childrenThreads[i].stop(); + } + } + } + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + stopCurrent |= this.childrenGroups[i].stopHelper(); + } + } + return stopCurrent; + } + + /** + * Suspends every Thread in the receiver and recursively in all its + * subgroups. + * + * @throws SecurityException if this.checkAccess() fails with + * a SecurityException + * + * @see Thread#suspend + * @see #resume + * + * @deprecated Requires deprecated method Thread.suspend(). + */ + @SuppressWarnings("deprecation") + @Deprecated + public final void suspend() { + if (suspendHelper()) { + Thread.currentThread().suspend(); + } + } + + /** + * @deprecated Requires deprecated method Thread.suspend(). + */ + @SuppressWarnings("deprecation") + @Deprecated + private final boolean suspendHelper() { + checkAccess(); + + boolean suspendCurrent = false; + // Lock this subpart of the tree as we walk + synchronized (this.childrenThreadsLock) { + Thread current = Thread.currentThread(); + for (int i = 0; i < numThreads; i++) { + if (this.childrenThreads[i] == current) { + suspendCurrent = true; + } else { + this.childrenThreads[i].suspend(); + } + } + } + // Lock this subpart of the tree as we walk + synchronized (this.childrenGroupsLock) { + for (int i = 0; i < numGroups; i++) { + suspendCurrent |= this.childrenGroups[i].suspendHelper(); + } + } + return suspendCurrent; + } + + /** + * Answers a string containing a concise, human-readable description of the + * receiver. + * + * @return a printable representation for the receiver. + */ + @Override + public String toString() { + return getClass().getName() + "[name=" + this.getName() + ",maxpri=" + + this.getMaxPriority() + "]"; + } + + /** + * Any uncaught exception in any Thread has to be forwarded (by the VM) to + * the Thread's ThreadGroup by sending this message (uncaughtException). + * This allows users to define custom ThreadGroup classes and custom + * behavior for when a Thread has an uncaughtException or when it does + * (ThreadDeath). + * + * @param t Thread with an uncaught exception + * @param e The uncaught exception itself + * + * @see Thread#stop() + * @see Thread#stop(Throwable) + * @see ThreadDeath + */ + public void uncaughtException(Thread t, Throwable e) { + if (parent != null) { + parent.uncaughtException(t, e); + } else if (!(e instanceof ThreadDeath)) { + // No parent group, has to be 'system' Thread Group + e.printStackTrace(System.err); + } + } } Modified: incubator/harmony/enhanced/classlib/trunk/modules/luni-kernel/src/main/java/java/lang/Throwable.java URL: http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/modules/luni-kernel/src/main/java/java/lang/Throwable.java?view=diff&rev=441341&r1=441340&r2=441341 ============================================================================== --- incubator/harmony/enhanced/classlib/trunk/modules/luni-kernel/src/main/java/java/lang/Throwable.java (original) +++ incubator/harmony/enhanced/classlib/trunk/modules/luni-kernel/src/main/java/java/lang/Throwable.java Thu Sep 7 19:19:16 2006 @@ -20,9 +20,8 @@ import java.io.PrintStream; import java.io.PrintWriter; - /** - * This class must be implemented by the vm vendor, or the reference + * This class must be implemented by the VM vendor, or the reference * implementation can be used if the documented natives are implemented. * * This class is the superclass of all classes which can be thrown by the @@ -38,313 +37,305 @@ * @see RuntimeException */ public class Throwable implements java.io.Serializable { - private static final long serialVersionUID = -3042686055658047285L; + private static final long serialVersionUID = -3042686055658047285L; - /** - * The message provided when the exception was created. - */ - private String detailMessage; - - /** - * An object which describes the walkback. This field is stored by the - * fillInStackTrace() native, and used by the getStackTraceImpl() native. - */ - private transient Object walkback; - - /** - * The cause of this Throwable. Null when there is no cause. - */ - private Throwable cause = this; - - private StackTraceElement[] stackTrace = null; - - /** - * Constructs a new instance of this class with its walkback filled in. - */ - public Throwable() { - super(); - fillInStackTrace(); - } - - /** - * Constructs a new instance of this class with its walkback and message - * filled in. - * - * @param detailMessage - * String The detail message for the exception. - */ - public Throwable(String detailMessage) { - this(); - this.detailMessage = detailMessage; - } - - /** - * Constructs a new instance of this class with its walkback, message and - * cause filled in. - * - * @param detailMessage - * String The detail message for the exception. - * @param throwable - * The cause of this Throwable - */ - public Throwable(String detailMessage, Throwable throwable) { - this(); - this.detailMessage = detailMessage; - cause = throwable; - } - - /** - * Constructs a new instance of this class with its walkback and cause - * filled in. - * - * @param throwable - * The cause of this Throwable - */ - public Throwable(Throwable throwable) { - this(); - this.detailMessage = throwable == null ? null : throwable.toString(); - cause = throwable; - } - - /** - * This native must be implemented to use the reference implementation of - * this class. - * - * Record in the receiver a walkback from the point where this message was - * sent. The message is public so that code which catches a throwable and - * then re-throws it can adjust the walkback to represent the - * location where the exception was re-thrown. - * - * @return the receiver - */ - public native Throwable fillInStackTrace(); - - /** - * Answers the extra information message which was provided when the - * throwable was created. If no message was provided at creation time, then - * answer null. - * - * @return String The receiver's message. - */ - public String getMessage() { - return detailMessage; - } - - /** - * Answers the extra information message which was provided when the - * throwable was created. If no message was provided at creation time, then - * answer null. Subclasses may override this method to answer localized text - * for the message. - * - * @return String The receiver's message. - */ - public String getLocalizedMessage() { - return getMessage(); - } - - /** - * This native must be implemented to use the reference implementation of - * this class. The result of this native is cloned, and returned from the - * public API getStackTrace(). - * - * Answers an array of StackTraceElement. Each StackTraceElement represents - * a entry on the stack. - * - * @return an array of StackTraceElement representing the stack - */ - private native StackTraceElement[] getStackTraceImpl(); - - /** - * Answers an array of StackTraceElement. Each StackTraceElement represents - * a entry on the stack. - * - * @return an array of StackTraceElement representing the stack - */ - public StackTraceElement[] getStackTrace() { - return (StackTraceElement[]) getInternalStackTrace().clone(); - } - - /** - * Sets the array of StackTraceElements. Each StackTraceElement represents a - * entry on the stack. A copy of this array will be returned by - * getStackTrace() and printed by printStackTrace(). - * - * @param trace - * The array of StackTraceElement - */ - public void setStackTrace(StackTraceElement[] trace) { - StackTraceElement[] newTrace = (StackTraceElement[]) trace.clone(); - for (int i = 0; i < newTrace.length; i++) - if (newTrace[i] == null) - throw new NullPointerException(); - stackTrace = newTrace; - } - - /** - * Outputs a printable representation of the receiver's walkback on the - * System.err stream. - */ - public void printStackTrace() { - printStackTrace(System.err); - } - - /** - * Count the number of duplicate stack frames, starting from - * the end of the stack. - * - * @param currentStack a stack to compare - * @param parentStack a stack to compare - * - * @return the number of duplicate stack frames. - */ - private static int countDuplicates(StackTraceElement[] currentStack, - StackTraceElement[] parentStack) { - int duplicates = 0; - int parentIndex = parentStack.length; - for (int i = currentStack.length; --i >= 0 && --parentIndex >= 0;) { - StackTraceElement parentFrame = parentStack[parentIndex]; - if (parentFrame.equals(currentStack[i])) { - duplicates++; - } else { - break; - } - } - return duplicates; - } - - /** - * Answers an array of StackTraceElement. Each StackTraceElement represents - * a entry on the stack. Cache the stack trace in the stackTrace field, returning - * the cached field when it has already been initialized. - * - * @return an array of StackTraceElement representing the stack - */ - private StackTraceElement[] getInternalStackTrace() { - if (stackTrace == null) - stackTrace = getStackTraceImpl(); - return stackTrace; - } - - /** - * Outputs a printable representation of the receiver's walkback on the - * stream specified by the argument. - * - * @param err - * PrintStream The stream to write the walkback on. - */ - public void printStackTrace(PrintStream err) { - err.println(toString()); - // Don't use getStackTrace() as it calls clone() - // Get stackTrace, in case stackTrace is reassigned - StackTraceElement[] stack = getInternalStackTrace(); - for (int i = 0; i < stack.length; i++) - err.println("\tat " + stack[i]); - - StackTraceElement[] parentStack = stack; - Throwable throwable = getCause(); - while (throwable != null) { - err.print("Caused by: "); - err.println(throwable); - StackTraceElement[] currentStack = throwable - .getInternalStackTrace(); - int duplicates = countDuplicates(currentStack, parentStack); - for (int i = 0; i < currentStack.length - duplicates; i++) - err.println("\tat " + currentStack[i]); - if (duplicates > 0) { - err.println("\t... " + duplicates + " more"); - } - parentStack = currentStack; - throwable = throwable.getCause(); - } - } - - /** - * Outputs a printable representation of the receiver's walkback on the - * writer specified by the argument. - * - * @param err - * PrintWriter The writer to write the walkback on. - */ - public void printStackTrace(PrintWriter err) { - err.println(toString()); - // Don't use getStackTrace() as it calls clone() - // Get stackTrace, in case stackTrace is reassigned - StackTraceElement[] stack = getInternalStackTrace(); - for (int i = 0; i < stack.length; i++) - err.println("\tat " + stack[i]); - - StackTraceElement[] parentStack = stack; - Throwable throwable = getCause(); - while (throwable != null) { - err.print("Caused by: "); - err.println(throwable); - StackTraceElement[] currentStack = throwable - .getInternalStackTrace(); - int duplicates = countDuplicates(currentStack, parentStack); - for (int i = 0; i < currentStack.length - duplicates; i++) - err.println("\tat " + currentStack[i]); - if (duplicates > 0) { - err.println("\t... " + duplicates + " more"); - } - parentStack = currentStack; - throwable = throwable.getCause(); - } - } - - /** - * Answers a string containing a concise, human-readable description of the - * receiver. - * - * @return String a printable representation for the receiver. - */ - public String toString() { - String msg = getLocalizedMessage(); - String name = getClass().getName(); - if (msg == null) { - return name; - } - return new StringBuffer(name.length() + 2 + msg.length()).append(name) - .append(": ").append(msg).toString(); - } - - /** - * Initialize the cause of the receiver. The cause cannot be reassigned. - * - * @param throwable - * The cause of this Throwable - * - * @exception IllegalArgumentException - * when the cause is the receiver - * @exception IllegalStateException - * when the cause has already been initialized - * - * @return the receiver. - */ - public synchronized Throwable initCause(Throwable throwable) { - if (cause == this) { - if (throwable != this) { - cause = throwable; - return this; - } - throw new IllegalArgumentException("Cause cannot be the receiver"); - } - throw new IllegalStateException("Cause already initialized"); - } - - /** - * Answers the cause of this Throwable, or null if there is no cause. - * - * @return Throwable The receiver's cause. - */ - public Throwable getCause() { - if (cause == this) - return null; - return cause; - } - - private void writeObject(ObjectOutputStream s) throws IOException { - // ensure the stackTrace field is initialized - getInternalStackTrace(); - s.defaultWriteObject(); - } + /** + * The message provided when the exception was created. + */ + private String detailMessage; + + /** + * The cause of this Throwable. Null when there is no cause. + */ + private Throwable cause = this; + + private StackTraceElement[] stackTrace; + + /** + * Constructs a new instance of this class with its walkback filled in. + */ + public Throwable() { + super(); + fillInStackTrace(); + } + + /** + * Constructs a new instance of this class with its walkback and message + * filled in. + * + * @param detailMessage String The detail message for the exception. + */ + public Throwable(String detailMessage) { + this(); + this.detailMessage = detailMessage; + } + + /** + * Constructs a new instance of this class with its walkback, message and + * cause filled in. + * + * @param detailMessage String The detail message for the exception. + * @param throwable The cause of this Throwable + */ + public Throwable(String detailMessage, Throwable throwable) { + this(); + this.detailMessage = detailMessage; + cause = throwable; + } + + /** + * Constructs a new instance of this class with its walkback and cause + * filled in. + * + * @param throwable The cause of this Throwable + */ + public Throwable(Throwable throwable) { + this(); + this.detailMessage = throwable == null ? null : throwable.toString(); + cause = throwable; + } + + /** + * This native must be implemented to use the reference implementation of + * this class. + * + * Record in the receiver a walkback from the point where this message was + * sent. The message is public so that code which catches a throwable and + * then re-throws it can adjust the walkback to represent the + * location where the exception was re-thrown. + * + * @return the receiver + */ + public native Throwable fillInStackTrace(); + + /** + * Answers the extra information message which was provided when the + * throwable was created. If no message was provided at creation time, then + * answer null. + * + * @return String The receiver's message. + */ + public String getMessage() { + return detailMessage; + } + + /** + * Answers the extra information message which was provided when the + * throwable was created. If no message was provided at creation time, then + * answer null. Subclasses may override this method to answer localized text + * for the message. + * + * @return String The receiver's message. + */ + public String getLocalizedMessage() { + return getMessage(); + } + + /** + * This native must be implemented to use the reference implementation of + * this class. The result of this native is cloned, and returned from the + * public API getStackTrace(). + * + * Answers an array of StackTraceElement. Each StackTraceElement represents + * a entry on the stack. + * + * @return an array of StackTraceElement representing the stack + */ + private native StackTraceElement[] getStackTraceImpl(); + + /** + * Answers an array of StackTraceElement. Each StackTraceElement represents + * a entry on the stack. + * + * @return an array of StackTraceElement representing the stack + */ + public StackTraceElement[] getStackTrace() { + return getInternalStackTrace().clone(); + } + + /** + * Sets the array of StackTraceElements. Each StackTraceElement represents a + * entry on the stack. A copy of this array will be returned by + * getStackTrace() and printed by printStackTrace(). + * + * @param trace The array of StackTraceElement + */ + public void setStackTrace(StackTraceElement[] trace) { + StackTraceElement[] newTrace = trace.clone(); + for (java.lang.StackTraceElement element : newTrace) { + if (element == null) { + throw new NullPointerException(); + } + } + stackTrace = newTrace; + } + + /** + * Outputs a printable representation of the receiver's walkback on the + * System.err stream. + */ + public void printStackTrace() { + printStackTrace(System.err); + } + + /** + * Count the number of duplicate stack frames, starting from the end of the + * stack. + * + * @param currentStack a stack to compare + * @param parentStack a stack to compare + * + * @return the number of duplicate stack frames. + */ + private static int countDuplicates(StackTraceElement[] currentStack, + StackTraceElement[] parentStack) { + int duplicates = 0; + int parentIndex = parentStack.length; + for (int i = currentStack.length; --i >= 0 && --parentIndex >= 0;) { + StackTraceElement parentFrame = parentStack[parentIndex]; + if (parentFrame.equals(currentStack[i])) { + duplicates++; + } else { + break; + } + } + return duplicates; + } + + /** + * Answers an array of StackTraceElement. Each StackTraceElement represents + * a entry on the stack. Cache the stack trace in the stackTrace field, + * returning the cached field when it has already been initialized. + * + * @return an array of StackTraceElement representing the stack + */ + private StackTraceElement[] getInternalStackTrace() { + if (stackTrace == null) { + stackTrace = getStackTraceImpl(); + } + return stackTrace; + } + + /** + * Outputs a printable representation of the receiver's walkback on the + * stream specified by the argument. + * + * @param err PrintStream The stream to write the walkback on. + */ + public void printStackTrace(PrintStream err) { + err.println(toString()); + // Don't use getStackTrace() as it calls clone() + // Get stackTrace, in case stackTrace is reassigned + StackTraceElement[] stack = getInternalStackTrace(); + for (java.lang.StackTraceElement element : stack) { + err.println("\tat " + element); + } + + StackTraceElement[] parentStack = stack; + Throwable throwable = getCause(); + while (throwable != null) { + err.print("Caused by: "); + err.println(throwable); + StackTraceElement[] currentStack = throwable.getInternalStackTrace(); + int duplicates = countDuplicates(currentStack, parentStack); + for (int i = 0; i < currentStack.length - duplicates; i++) { + err.println("\tat " + currentStack[i]); + } + if (duplicates > 0) { + err.println("\t... " + duplicates + " more"); + } + parentStack = currentStack; + throwable = throwable.getCause(); + } + } + + /** + * Outputs a printable representation of the receiver's walkback on the + * writer specified by the argument. + * + * @param err PrintWriter The writer to write the walkback on. + */ + public void printStackTrace(PrintWriter err) { + err.println(toString()); + // Don't use getStackTrace() as it calls clone() + // Get stackTrace, in case stackTrace is reassigned + StackTraceElement[] stack = getInternalStackTrace(); + for (java.lang.StackTraceElement element : stack) { + err.println("\tat " + element); + } + + StackTraceElement[] parentStack = stack; + Throwable throwable = getCause(); + while (throwable != null) { + err.print("Caused by: "); + err.println(throwable); + StackTraceElement[] currentStack = throwable.getInternalStackTrace(); + int duplicates = countDuplicates(currentStack, parentStack); + for (int i = 0; i < currentStack.length - duplicates; i++) { + err.println("\tat " + currentStack[i]); + } + if (duplicates > 0) { + err.println("\t... " + duplicates + " more"); + } + parentStack = currentStack; + throwable = throwable.getCause(); + } + } + + /** + * Answers a string containing a concise, human-readable description of the + * receiver. + * + * @return String a printable representation for the receiver. + */ + @Override + public String toString() { + String msg = getLocalizedMessage(); + String name = getClass().getName(); + if (msg == null) { + return name; + } + return new StringBuffer(name.length() + 2 + msg.length()).append(name).append(": ") + .append(msg).toString(); + } + + /** + * Initialize the cause of the receiver. The cause cannot be reassigned. + * + * @param throwable The cause of this Throwable + * + * @exception IllegalArgumentException when the cause is the receiver + * @exception IllegalStateException when the cause has already been + * initialized + * + * @return the receiver. + */ + public synchronized Throwable initCause(Throwable throwable) { + if (cause == this) { + if (throwable != this) { + cause = throwable; + return this; + } + throw new IllegalArgumentException("Cause cannot be the receiver"); + } + throw new IllegalStateException("Cause already initialized"); + } + + /** + * Answers the cause of this Throwable, or null if there is no cause. + * + * @return Throwable The receiver's cause. + */ + public Throwable getCause() { + if (cause == this) { + return null; + } + return cause; + } + + private void writeObject(ObjectOutputStream s) throws IOException { + // ensure the stackTrace field is initialized + getInternalStackTrace(); + s.defaultWriteObject(); + } }