You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by ke...@apache.org on 2006/03/29 03:59:52 UTC

svn commit: r389653 - in /geronimo/trunk/modules/commonj: ./ src/ src/java/ src/java/org/ src/java/org/apache/ src/java/org/apache/geronimo/ src/java/org/apache/geronimo/commonj/ src/java/org/apache/geronimo/commonj/timers/ src/test/ src/test/org/ src/...

Author: kevan
Date: Tue Mar 28 17:59:49 2006
New Revision: 389653

URL: http://svn.apache.org/viewcvs?rev=389653&view=rev
Log:
Commit initial implementation of Commonj Timer API from GERONIMO-1553

Added:
    geronimo/trunk/modules/commonj/LICENSE.txt
    geronimo/trunk/modules/commonj/NOTICE.txt
    geronimo/trunk/modules/commonj/pom.xml
    geronimo/trunk/modules/commonj/project.xml
    geronimo/trunk/modules/commonj/src/
    geronimo/trunk/modules/commonj/src/java/
    geronimo/trunk/modules/commonj/src/java/org/
    geronimo/trunk/modules/commonj/src/java/org/apache/
    geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/
    geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/
    geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/
    geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/ScheduledRunnable.java
    geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/ScheduledTimerExecutor.java
    geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/State.java
    geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/TimerImpl.java
    geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/TimerManagerImpl.java
    geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/WorkRunnable.java
    geronimo/trunk/modules/commonj/src/test/
    geronimo/trunk/modules/commonj/src/test/org/
    geronimo/trunk/modules/commonj/src/test/org/apache/
    geronimo/trunk/modules/commonj/src/test/org/apache/geronimo/
    geronimo/trunk/modules/commonj/src/test/org/apache/geronimo/commonj/
    geronimo/trunk/modules/commonj/src/test/org/apache/geronimo/commonj/timers/
    geronimo/trunk/modules/commonj/src/test/org/apache/geronimo/commonj/timers/TimerManagerTest.java

Added: geronimo/trunk/modules/commonj/LICENSE.txt
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/commonj/LICENSE.txt?rev=389653&view=auto
==============================================================================
--- geronimo/trunk/modules/commonj/LICENSE.txt (added)
+++ geronimo/trunk/modules/commonj/LICENSE.txt Tue Mar 28 17:59:49 2006
@@ -0,0 +1,203 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+

Added: geronimo/trunk/modules/commonj/NOTICE.txt
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/commonj/NOTICE.txt?rev=389653&view=auto
==============================================================================
--- geronimo/trunk/modules/commonj/NOTICE.txt (added)
+++ geronimo/trunk/modules/commonj/NOTICE.txt Tue Mar 28 17:59:49 2006
@@ -0,0 +1,3 @@
+This product includes software developed by
+The Apache Software Foundation (http://www.apache.org/).
+

Added: geronimo/trunk/modules/commonj/pom.xml
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/commonj/pom.xml?rev=389653&view=auto
==============================================================================
--- geronimo/trunk/modules/commonj/pom.xml (added)
+++ geronimo/trunk/modules/commonj/pom.xml Tue Mar 28 17:59:49 2006
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+
+    Copyright 2006 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!-- $Rev: 384585 $ $Date: 2006-03-09 14:21:14 -0500 (Thu, 09 Mar 2006) $ -->
+
+<project>
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.geronimo</groupId>
+    <artifactId>geronimo</artifactId>
+    <version>1.2-SNAPSHOT</version>
+  </parent>
+  <groupId>org.apache.geronimo</groupId>
+  <artifactId>geronimo-commonj</artifactId>
+  <version>${geronimoVersion}</version>
+  <name>Geronimo :: Commonj</name>  
+  <description>Geronimo Commonj</description>
+  <build>
+    <plugins>
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <inherited>true</inherited>
+        <configuration>
+          <excludes>
+            <exclude>**/TestStore.class</exclude>
+            <exclude>**/TestTransport.class</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <dependency>        
+      <groupId>org.apache.geronimo.specs</groupId>
+      <artifactId>geronimo-commonj_1.1_spec</artifactId>
+      <version>${geronimo_spec_commonj_version}</version>
+    </dependency>
+    <dependency>
+      <groupId>backport-util-concurrent</groupId>
+      <artifactId>backport-util-concurrent</artifactId>
+      <version>2.0_01_pd</version>
+    </dependency>
+  </dependencies>
+</project>

Added: geronimo/trunk/modules/commonj/project.xml
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/commonj/project.xml?rev=389653&view=auto
==============================================================================
--- geronimo/trunk/modules/commonj/project.xml (added)
+++ geronimo/trunk/modules/commonj/project.xml Tue Mar 28 17:59:49 2006
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+
+    Copyright 2006 The Apache Software Foundation
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!-- $Rev: 356097 $ $Date: 2005-12-11 17:29:03 -0800 (Sun, 11 Dec 2005) $ -->
+
+<project>
+    <pomVersion>3</pomVersion>
+    <extend>../../etc/project.xml</extend>
+
+    <!-- ===================== -->
+    <!-- Module Identification -->
+    <!-- ===================== -->
+
+    <name>Geronimo :: Commonj</name>
+    <id>geronimo-commonj</id>
+    <shortDescription>Geronimo Commonj</shortDescription>
+    <description>Geronimo Commonj</description>
+    <url>http://geronimo.apache.org/modules/commonj/</url>
+    <siteDirectory>/www/geronimo.apache.org/modules/commonj</siteDirectory>
+    <distributionDirectory>/www/incubator.apache.org/projects/geronimo/builds/commonj</distributionDirectory>
+
+    <package>org.apache.geronimo.commonj</package>
+
+    <!-- ============ -->
+    <!-- Dependencies -->
+    <!-- ============ -->
+
+    <dependencies>
+
+        <dependency>        
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-commonj_1.1_spec</artifactId>
+            <version>${geronimo_spec_commonj_version}</version>
+        </dependency>
+        
+        <dependency>
+          <groupId>backport-util-concurrent</groupId>
+          <artifactId>backport-util-concurrent</artifactId>
+          <version>2.0_01_pd</version>
+        </dependency>
+    </dependencies>
+</project>

Added: geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/ScheduledRunnable.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/ScheduledRunnable.java?rev=389653&view=auto
==============================================================================
--- geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/ScheduledRunnable.java (added)
+++ geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/ScheduledRunnable.java Tue Mar 28 17:59:49 2006
@@ -0,0 +1,45 @@
+/**
+ *
+ * Copyright 2006 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.commonj.timers;
+
+import edu.emory.mathcs.backport.java.util.concurrent.ExecutorService;
+import edu.emory.mathcs.backport.java.util.concurrent.RunnableScheduledFuture;
+
+final class ScheduledRunnable implements Runnable {
+    private TimerImpl timer;
+
+    private TimerManagerImpl timerManager;
+
+    private ExecutorService workExecutor;
+
+    public ScheduledRunnable(TimerImpl timer, ExecutorService workExecutor) {
+        this.timer = timer;
+        this.workExecutor = workExecutor;
+
+        this.timerManager = timer.getTimerManager();
+    }
+
+    public void run() {
+        if (timerManager.preInvoke(timer)) {
+            workExecutor.submit(new WorkRunnable(timer));
+        }
+    }
+    
+    public void setFuture(RunnableScheduledFuture task){
+        timer.setFuture(task);
+    }
+}

Added: geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/ScheduledTimerExecutor.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/ScheduledTimerExecutor.java?rev=389653&view=auto
==============================================================================
--- geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/ScheduledTimerExecutor.java (added)
+++ geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/ScheduledTimerExecutor.java Tue Mar 28 17:59:49 2006
@@ -0,0 +1,45 @@
+/**
+ *
+ * Copyright 2006 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.commonj.timers;
+
+import edu.emory.mathcs.backport.java.util.concurrent.Callable;
+import edu.emory.mathcs.backport.java.util.concurrent.RunnableScheduledFuture;
+import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor;
+import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory;
+
+final class ScheduledTimerExecutor extends ScheduledThreadPoolExecutor {
+
+    public ScheduledTimerExecutor(int corePoolSize) {
+        super(corePoolSize);
+    }
+    
+    public ScheduledTimerExecutor(int corePoolSize, ThreadFactory threadFactory) {
+        super(corePoolSize, threadFactory);
+    }
+
+    protected RunnableScheduledFuture decorateTask(java.lang.Runnable runnable, RunnableScheduledFuture task) {
+        
+        // This is the timing thing.  Need to have this set before the timer can expire, so do it here.
+        ScheduledRunnable scheduledRunnable = (ScheduledRunnable) runnable;
+        scheduledRunnable.setFuture(task);
+        return task;
+    }
+
+    protected RunnableScheduledFuture decorateTask(Callable callable, RunnableScheduledFuture task) {
+        throw new UnsupportedOperationException();
+    }
+}

Added: geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/State.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/State.java?rev=389653&view=auto
==============================================================================
--- geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/State.java (added)
+++ geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/State.java Tue Mar 28 17:59:49 2006
@@ -0,0 +1,40 @@
+/**
+ *
+ * Copyright 2006 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.commonj.timers;
+
+class State {
+    private final String name;
+
+    private State(String name) {
+        this.name = name;
+    }
+
+    public String toString() {
+        return name;
+    }
+
+    public static final State RUNNING = new State("running");
+
+    public static final State SUSPENDING = new State("suspending");
+
+    public static final State SUSPENDED = new State("suspended");
+
+    public static final State STOPPED = new State("stopped");
+
+    public static final State STOPPING = new State("stopping");
+
+}

Added: geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/TimerImpl.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/TimerImpl.java?rev=389653&view=auto
==============================================================================
--- geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/TimerImpl.java (added)
+++ geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/TimerImpl.java Tue Mar 28 17:59:49 2006
@@ -0,0 +1,119 @@
+/**
+ *
+ * Copyright 2006 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.commonj.timers;
+
+import commonj.timers.Timer;
+import commonj.timers.TimerListener;
+import commonj.timers.CancelTimerListener;
+
+import edu.emory.mathcs.backport.java.util.concurrent.Future;
+
+public class TimerImpl implements Timer {
+
+    private TimerListener timerListener = null;
+
+    private TimerManagerImpl timerManager = null;
+
+    private Future future = null;
+
+    private long scheduledExecutionTime = 0L;
+
+    private long period = 0L;
+
+    private boolean fixedRate = false;
+
+    private boolean cancelled = false;
+    
+    private boolean stopped = false;
+
+    public TimerImpl(long scheduledExecutionTime, long period, boolean fixedRate,
+                     TimerListener timerListener, TimerManagerImpl manager) {
+        this.scheduledExecutionTime = scheduledExecutionTime;
+        this.period = period;
+        this.fixedRate = fixedRate;
+        
+        this.timerListener = timerListener;
+        this.timerManager = manager;
+    }
+
+    synchronized public boolean cancel() {
+        if (cancelled)
+            return false;
+        
+        cancelled = true;
+        
+        if (future != null) {
+            future.cancel(false);
+        }
+        
+        timerManager.cancel(this);
+
+        // TODO: probably shouldn't hold lock while calling timerCancel... 
+        if (timerListener instanceof CancelTimerListener) {
+            try {
+                ((CancelTimerListener)timerListener).timerCancel(this);
+            } catch (RuntimeException re) { } // ignore
+        }
+        
+        // TODO: not quite according to spec, i think. doesn't handle non-repeating timer properly
+        return true;
+    }
+
+    public TimerListener getTimerListener() {
+        return timerListener;
+    }
+
+    public long getScheduledExecutionTime() throws IllegalStateException {
+        return scheduledExecutionTime;
+    }
+
+    public long getPeriod() {
+        return period;
+    }
+
+    Future getFuture() {
+        return future;
+    }
+
+    void setFuture(Future future) {
+        this.future = future;
+    }
+
+    TimerManagerImpl getTimerManager() {
+        return timerManager;
+    }
+
+    boolean isFixedRate() {
+        return fixedRate;
+    }
+
+    void setStopped() {
+        stopped = true;
+    }
+
+    boolean isStopped() {
+        return stopped;
+    }
+
+    synchronized void updateScheduledExecutionTime() {
+        if (fixedRate) {
+            scheduledExecutionTime += period;
+        } else {
+            scheduledExecutionTime = System.currentTimeMillis() + period;
+        }
+    }
+}

Added: geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/TimerManagerImpl.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/TimerManagerImpl.java?rev=389653&view=auto
==============================================================================
--- geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/TimerManagerImpl.java (added)
+++ geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/TimerManagerImpl.java Tue Mar 28 17:59:49 2006
@@ -0,0 +1,431 @@
+/**
+ *
+ * Copyright 2006 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.commonj.timers;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import commonj.timers.StopTimerListener;
+import commonj.timers.Timer;
+import commonj.timers.TimerListener;
+import commonj.timers.TimerManager;
+
+import edu.emory.mathcs.backport.java.util.Queue;
+import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
+import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentLinkedQueue;
+import edu.emory.mathcs.backport.java.util.concurrent.ExecutorService;
+import edu.emory.mathcs.backport.java.util.concurrent.Executors;
+import edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService;
+import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
+import edu.emory.mathcs.backport.java.util.concurrent.locks.Condition;
+import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantLock;
+
+public class TimerManagerImpl implements TimerManager {
+
+    private static final ScheduledExecutorService scheduledExecutor = new ScheduledTimerExecutor(1);
+
+    private static final ExecutorService workExecutor = Executors.newCachedThreadPool();
+
+    private State state = State.RUNNING;
+
+    private ReentrantLock stateLock = new ReentrantLock();
+
+    private Condition stateChange = stateLock.newCondition();
+
+    private Map managedTimers = new ConcurrentHashMap();
+
+    private Map inflight = new ConcurrentHashMap();
+
+    // doesn't need synchronization, update when move to 5.0
+    private Queue suspendedWork = new ConcurrentLinkedQueue();
+
+    public TimerManagerImpl() {
+    }
+
+    public void suspend() {
+        doSuspend();
+    }
+
+    public boolean isSuspending() throws IllegalStateException {
+        State currentState = getState();
+        return (currentState == State.SUSPENDED || currentState == State.SUSPENDING);
+    }
+
+    public boolean isSuspended() throws IllegalStateException {
+        return getState() == State.SUSPENDED;
+    }
+
+    public boolean waitForSuspend(long timeout) 
+    throws InterruptedException, IllegalStateException, IllegalArgumentException {
+        if (timeout < 0) {
+            throw new IllegalArgumentException("Negative timeout is not permitted");
+        }
+
+        doSuspend();
+
+        stateLock.lock();
+        try {
+
+            long startTime = System.currentTimeMillis();
+            long currentTimeout = timeout;
+
+            while (currentTimeout > 0 && state == State.SUSPENDING) {
+                stateChange.await(currentTimeout, TimeUnit.MILLISECONDS);
+
+                long currentTime = System.currentTimeMillis();  
+                long duration = currentTime - startTime;
+                if (duration > currentTimeout) {
+                    currentTimeout -= duration;
+                    startTime = currentTime;
+                }
+            }
+            
+            if (state == State.SUSPENDED) {
+                return true;
+            }
+            return false;
+        } finally {
+            stateLock.unlock();
+        }
+    }
+
+    void doSuspend() {
+        stateLock.lock();
+        try {
+            if (state == State.SUSPENDING || state == State.SUSPENDED ) {
+                return; // nothing to do
+            }
+            
+            if (inflightWork()) {
+                setState(State.SUSPENDING);
+            } else { 
+                setState(State.SUSPENDED);
+            }
+
+        } finally {
+            stateLock.unlock();
+        }
+    }
+    
+    public void resume() throws IllegalStateException {
+        Queue resumeWork = null;
+
+        stateLock.lock();
+        try {
+            setState(State.RUNNING);
+            resumeWork = suspendedWork;
+            suspendedWork = new ConcurrentLinkedQueue();
+        } finally {
+            stateLock.unlock();
+        }
+
+        while (!resumeWork.isEmpty()) {
+            TimerImpl timer = (TimerImpl) resumeWork.remove();
+            Runnable runnable = new ScheduledRunnable(timer, workExecutor);
+
+            if (timer.getPeriod() == 0) {
+                scheduledExecutor.schedule(runnable, timer.getScheduledExecutionTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
+            } else if (timer.isFixedRate()) {
+                scheduledExecutor.scheduleAtFixedRate(runnable, timer.getScheduledExecutionTime() - System.currentTimeMillis(), timer.getPeriod(),
+                        TimeUnit.MILLISECONDS);
+            } else {
+                scheduledExecutor.scheduleWithFixedDelay(runnable, timer.getScheduledExecutionTime() - System.currentTimeMillis(), timer.getPeriod(),
+                        TimeUnit.MILLISECONDS);
+            }
+        }
+    }
+
+    public void stop() throws IllegalStateException {
+        doStop();
+    }
+
+    public boolean isStopped() {
+        return getState() == State.STOPPED;
+    }
+
+    public boolean isStopping() {
+        State currentState = getState();
+        return (currentState == State.STOPPED || currentState == State.STOPPING);
+    }
+
+    public boolean waitForStop(long timeout) throws InterruptedException, IllegalArgumentException {
+        if (timeout < 0) {
+            throw new IllegalArgumentException("Negative timeout is not permitted");
+        }
+
+        doStop();
+
+        stateLock.lock();
+        try {
+            long startTime = System.currentTimeMillis();
+            long currentTimeout = timeout;
+
+            while (currentTimeout > 0 && state == State.STOPPING) {
+                stateChange.await(currentTimeout, TimeUnit.MILLISECONDS);
+
+                long currentTime = System.currentTimeMillis();  
+                long duration = currentTime - startTime;
+                if (duration > currentTimeout) {
+                    currentTimeout -= duration;
+                    startTime = currentTime;
+                }
+            }
+            
+            if (state == State.STOPPED) {
+                return true;
+            }
+            return false;
+        } finally {
+            stateLock.unlock();
+        }
+    }
+
+    private void doStop() {
+        Set stopTimers = null;
+
+        stateLock.lock();
+        try {
+            if (getState() == State.STOPPING || getState() == State.STOPPED) {
+                return; // nothing to do
+            }
+            
+            if (inflightWork()) {
+                setState(State.STOPPING);
+            } else {
+                setState(State.STOPPED);
+            }
+
+            stopTimers = new HashSet(managedTimers.keySet());
+            stopTimers.removeAll(inflight.keySet());
+        } finally {
+            stateLock.unlock();
+        }
+
+        Iterator iterator = stopTimers.iterator();
+        while (iterator.hasNext()) {
+            TimerImpl timer = (TimerImpl) iterator.next();
+
+            // it's OK if the timer fires before we call cancel, pre-invoke will
+            // catch that case
+            timer.getFuture().cancel(false);
+            timer.setStopped();
+
+            TimerListener listener = timer.getTimerListener();
+            if (listener instanceof StopTimerListener) {
+                ((StopTimerListener) timer.getTimerListener()).timerStop(timer);
+            }
+            
+            managedTimers.remove (timer); // prevent garbage
+
+            workExecutor.submit(new WorkRunnable(timer));
+        }
+    }
+
+    public void checkState(String method) {
+        State currentState = getState();
+        if (currentState == State.STOPPING || currentState == State.STOPPED) {
+            throw new IllegalStateException("Cannot call method " + method + " when TimerManager is in state " + currentState + ".");
+        }
+    }
+
+    public Timer schedule(TimerListener listener, Date firstTime) throws IllegalArgumentException, IllegalStateException {
+        stateLock.lock();
+        try {
+            checkState("schedule()");
+
+            TimerImpl timer = new TimerImpl(firstTime.getTime(), 0L, false, listener, this);
+            managedTimers.put(timer, timer);
+            
+            Runnable runnable = new ScheduledRunnable(timer, workExecutor);
+            scheduledExecutor.schedule(runnable, firstTime.getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
+            
+            return timer;
+        } finally {
+            stateLock.unlock();
+        }
+    }
+
+    public Timer schedule(TimerListener listener, long delay) throws IllegalArgumentException, IllegalStateException {
+        stateLock.lock();
+        try {
+            checkState("schedule()");
+            
+            TimerImpl timer = new TimerImpl(System.currentTimeMillis() + delay, 0L, false, listener, this);
+            managedTimers.put(timer, timer);
+            
+            Runnable runnable = new ScheduledRunnable(timer, workExecutor);
+            scheduledExecutor.schedule(runnable, delay, TimeUnit.MILLISECONDS);
+            
+            return timer;
+        } finally {
+            stateLock.unlock();
+        }
+    }
+
+    public Timer schedule(TimerListener listener, Date firstTime, long period) throws IllegalArgumentException, IllegalStateException {
+        stateLock.lock();
+        try {
+            checkState("schedule()");
+            
+            TimerImpl timer = new TimerImpl(firstTime.getTime(), period, false, listener, this);
+            managedTimers.put(timer, timer);
+            
+            Runnable runnable = new ScheduledRunnable(timer, workExecutor);
+            scheduledExecutor.scheduleWithFixedDelay(runnable, firstTime.getTime() - System.currentTimeMillis(), period, TimeUnit.MILLISECONDS);
+            
+            return timer;
+        } finally {
+            stateLock.unlock();
+        }
+    }
+
+    public Timer schedule(TimerListener listener, long delay, long period) throws IllegalArgumentException, IllegalStateException {
+        stateLock.lock();
+        try {
+            checkState("schedule()");
+
+            TimerImpl timer = new TimerImpl(System.currentTimeMillis() + delay, period, false, listener, this);
+            managedTimers.put(timer, timer);
+
+            Runnable runnable = new ScheduledRunnable(timer, workExecutor);
+            scheduledExecutor.scheduleWithFixedDelay(runnable, delay, period, TimeUnit.MILLISECONDS);
+            return timer;
+        } finally {
+            stateLock.unlock();
+        }
+    }
+
+    public Timer scheduleAtFixedRate(TimerListener listener, Date firstTime, long period) throws IllegalArgumentException, IllegalStateException {
+        stateLock.lock();
+        try {
+            checkState("schedule()");
+            
+            TimerImpl timer = new TimerImpl(firstTime.getTime(), period, true, listener, this);
+            managedTimers.put(timer, timer);
+
+            Runnable runnable = new ScheduledRunnable(timer, workExecutor);
+            scheduledExecutor.scheduleAtFixedRate(runnable, firstTime.getTime() - System.currentTimeMillis(), period, TimeUnit.MILLISECONDS);
+
+            return timer;
+        } finally {
+            stateLock.unlock();
+        }
+    }
+
+    public Timer scheduleAtFixedRate(TimerListener listener, long delay, long period) throws IllegalArgumentException, IllegalStateException {
+        stateLock.lock();
+        try {
+            checkState("schedule()");
+
+            TimerImpl timer = new TimerImpl(System.currentTimeMillis() + delay, period, false, listener, this);
+            managedTimers.put(timer, timer);
+
+            Runnable runnable = new ScheduledRunnable(timer, workExecutor);
+            scheduledExecutor.scheduleAtFixedRate(runnable, delay, period, TimeUnit.MILLISECONDS);
+
+            return timer;
+        } finally {
+            stateLock.unlock();
+        }
+    }
+
+    State getState() {
+        stateLock.lock();
+        try {
+            return state;
+        } finally {
+            stateLock.unlock();
+        }
+    }
+
+    boolean preInvoke(TimerImpl timer) {
+        boolean continueInvoke = true;
+
+        stateLock.lock();
+        try {
+            if (getState() == State.RUNNING) {
+                inflight.put(timer, timer);
+            } else {
+                continueInvoke = false;
+                timer.getFuture().cancel(false);
+                if (state == State.SUSPENDING || state == State.SUSPENDED) {
+                    suspendedWork.add(timer);
+                }
+            }
+        } finally {
+            stateLock.unlock();
+        }
+
+        return continueInvoke;
+    }
+
+    void postInvoke(TimerImpl timer) {
+        stateLock.lock();
+        try {
+            inflight.remove(timer);
+            if (state == State.SUSPENDING && !inflightWork()) {
+                setState(State.SUSPENDED);
+            } else if (state == State.STOPPING && !inflightWork()) {
+                setState(State.STOPPED);
+            }
+
+            if (timer.getPeriod() == 0 || (state == State.STOPPING || state == State.STOPPED)) {
+                managedTimers.remove(timer);
+                if (state == State.STOPPING || state == State.STOPPED) {
+                    timer.getFuture().cancel(false);
+                }
+            } else {
+                timer.updateScheduledExecutionTime();
+            }
+
+        } finally {
+            stateLock.unlock();
+        }
+    }
+
+    private boolean inflightWork() {
+        return inflight.size() > 0;
+    }
+
+    private void setState(State newState) {
+        stateLock.lock();
+        try {
+            if (newState == State.SUSPENDING || newState == State.SUSPENDED) {
+                if (state == State.STOPPED || state == State.STOPPING) {
+                    throw new IllegalStateException("Cannot suspend a TimerManager that is in the " + state + " state.");
+                }
+            } else if (newState == State.RUNNING) {
+                if (state == State.STOPPED || state == State.STOPPING) {
+                    throw new IllegalStateException("Cannot transition a TimerManager that is in the " + state + " to state " + newState + ".");
+                }
+            }
+
+            state = newState;
+            stateChange.signalAll();
+        } finally {
+            stateLock.unlock();
+        }
+    }
+
+    void cancel(TimerImpl timer) {
+        managedTimers.remove(timer);
+        inflight.remove(timer);
+    }
+}

Added: geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/WorkRunnable.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/WorkRunnable.java?rev=389653&view=auto
==============================================================================
--- geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/WorkRunnable.java (added)
+++ geronimo/trunk/modules/commonj/src/java/org/apache/geronimo/commonj/timers/WorkRunnable.java Tue Mar 28 17:59:49 2006
@@ -0,0 +1,73 @@
+/**
+ *
+ * Copyright 2006 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.commonj.timers;
+
+import commonj.timers.StopTimerListener;
+import commonj.timers.TimerListener;
+
+final class WorkRunnable implements Runnable {
+    private TimerImpl timer = null;
+
+    private TimerListener timerListener;
+
+    private TimerManagerImpl timerManager = null;
+
+    public WorkRunnable(TimerImpl timer) {
+        this.timer = timer;
+        this.timerListener = timer.getTimerListener();
+        this.timerManager = timer.getTimerManager();
+    }
+
+    public void run() {
+        RuntimeException runtimeException = null;
+
+        try {
+            // here we use the state set during preinvoke, to avoid getting the lock again
+            if (!timer.isStopped()) {
+                timerListener.timerExpired(timer);
+            }
+        } catch (RuntimeException re) {
+            runtimeException = re;
+        }
+
+        try {
+            // here we get the lock, and see if things have changed since preinvoke
+            State state = timerManager.getState();
+            if ((state == State.STOPPING || state == State.STOPPED) && timerListener instanceof StopTimerListener) {
+                ((StopTimerListener) timerListener).timerStop(timer);
+            }
+        } catch (RuntimeException re) {
+            if (runtimeException == null) {
+                runtimeException = re;
+            }
+            // TODO log exception
+        }
+
+        try {
+            timerManager.postInvoke(timer);
+        } catch (RuntimeException re) {
+            if (runtimeException == null) {
+                runtimeException = re;
+            }
+            // TODO log exception
+        }
+
+        if (runtimeException != null) {
+            throw runtimeException;
+        }
+    }
+}

Added: geronimo/trunk/modules/commonj/src/test/org/apache/geronimo/commonj/timers/TimerManagerTest.java
URL: http://svn.apache.org/viewcvs/geronimo/trunk/modules/commonj/src/test/org/apache/geronimo/commonj/timers/TimerManagerTest.java?rev=389653&view=auto
==============================================================================
--- geronimo/trunk/modules/commonj/src/test/org/apache/geronimo/commonj/timers/TimerManagerTest.java (added)
+++ geronimo/trunk/modules/commonj/src/test/org/apache/geronimo/commonj/timers/TimerManagerTest.java Tue Mar 28 17:59:49 2006
@@ -0,0 +1,599 @@
+/**
+ *
+ * Copyright 2006 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+package org.apache.geronimo.commonj.timers;
+
+import java.util.Date;
+
+import commonj.timers.Timer;
+import commonj.timers.TimerListener;
+import commonj.timers.TimerManager;
+
+import junit.framework.TestCase;
+
+public class TimerManagerTest extends TestCase {
+
+    private static final long HEDGE = 100L;
+
+    private long delay = 1000L;
+
+    private int counter;
+
+    private long scheduleTime;
+
+    private long expirationTime;
+
+    private TimerManager timerManager = null;
+
+    private long period = 500L;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        timerManager = new TimerManagerImpl();
+    }
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        timerManager.stop();
+    }
+
+    /*
+     * Test method for
+     * 'org.apache.geronimo.commonj.timers.TimerManagerImpl.TimerManagerImpl()'
+     */
+    public void testTimerManagerImpl() {
+        assertFalse(timerManager.isSuspending());
+        assertFalse(timerManager.isSuspended());
+        assertFalse(timerManager.isStopping());
+        assertFalse(timerManager.isStopped());
+    }
+
+    /*
+     * Test method for
+     * 'org.apache.geronimo.commonj.timers.TimerManagerImpl.suspend()'
+     */
+    public void testSuspend() throws Exception {
+        suspendTest(1);
+    }
+
+    public void testSuspend_N() throws Exception {
+        suspendTest(10);
+    }
+
+    private void suspendTest(int timerCount) throws Exception {
+        counter = 0;
+        scheduleTime = System.currentTimeMillis();
+        expirationTime = 0;
+
+        for (int i = 0; i < timerCount; i++) {
+            timerManager.schedule(new TimerListener() {
+                public void timerExpired(Timer timer) {
+                    counter++;
+                    expirationTime = System.currentTimeMillis();
+                }
+            }, new Date(System.currentTimeMillis() + delay));
+        }
+
+        timerManager.suspend();
+
+        assertTrue(timerManager.isSuspending());
+        assertTrue(timerManager.isSuspended());
+
+        Thread.sleep(delay + HEDGE);
+
+        assertTrue(counter == 0);
+        timerManager.resume();
+        Thread.sleep(delay + HEDGE);
+
+        assertTrue(counter == timerCount);
+    }
+
+    /*
+     * Test method for
+     * 'org.apache.geronimo.commonj.timers.TimerManagerImpl.isSuspending()'
+     */
+    public void testIsSuspending() throws Exception {
+        counter = 0;
+        scheduleTime = System.currentTimeMillis();
+        expirationTime = 0;
+
+        timerManager.schedule(new TimerListener() {
+            public void timerExpired(Timer timer) {
+                counter++;
+                expirationTime = System.currentTimeMillis();
+                try {
+                    Thread.sleep(delay);
+                } catch (InterruptedException ie) {
+                    throw new RuntimeException(ie);
+                }
+                counter++;
+            }
+        }, new Date(System.currentTimeMillis()));
+
+        Thread.sleep(HEDGE);
+
+        timerManager.waitForSuspend(HEDGE);
+
+        assertTrue(timerManager.isSuspending());
+        assertTrue(counter == 1);
+        assertFalse(timerManager.isSuspended());
+
+        Thread.sleep(delay + HEDGE);
+
+        assertTrue(counter == 2);
+        assertTrue(timerManager.isSuspended());
+
+        timerManager.resume();
+
+        assertTrue(counter == 2);
+        long measuredDelay = expirationTime - scheduleTime;
+        assertTrue(measuredDelay < HEDGE);
+        assertFalse(timerManager.isSuspending());
+        assertFalse(timerManager.isSuspended());
+    }
+
+    public void testIsSuspending_N() throws Exception {
+        counter = 0;
+        scheduleTime = System.currentTimeMillis();
+        expirationTime = 0;
+
+        for (int i = 0; i < 10; i++) {
+            timerManager.schedule(new TimerListener() {
+                public void timerExpired(Timer timer) {
+                    counter++;
+                    expirationTime = System.currentTimeMillis();
+                    try {
+                        Thread.sleep(delay);
+                    } catch (InterruptedException ie) {
+                        throw new RuntimeException(ie);
+                    }
+                    counter++;
+                }
+            }, new Date(System.currentTimeMillis()));
+        }
+
+        Thread.sleep(HEDGE);
+
+        timerManager.waitForSuspend(HEDGE);
+
+        assertTrue(timerManager.isSuspending());
+        assertTrue(counter == 10);
+
+        Thread.sleep(delay + HEDGE);
+
+        assertTrue(counter == 20);
+        assertTrue(timerManager.isSuspended());
+
+        timerManager.resume();
+
+        assertTrue(counter == 20);
+        // long measuredDelay = expirationTime - scheduleTime;
+        // assertTrue(measuredDelay <HEDGE);
+        assertFalse(timerManager.isSuspending());
+        assertFalse(timerManager.isSuspended());
+    }
+
+    /*
+     * Test method for
+     * 'org.apache.geronimo.commonj.timers.TimerManagerImpl.isSuspended()'
+     */
+    public void testIsSuspended() {
+        assertFalse(timerManager.isSuspended());
+        timerManager.suspend();
+        assertTrue(timerManager.isSuspended());
+        timerManager.resume();
+        assertFalse(timerManager.isSuspended());
+    }
+
+    /*
+     * Test method for
+     * 'org.apache.geronimo.commonj.timers.TimerManagerImpl.stop()'
+     */
+    public void testStop() throws Exception {
+        stopTest(1);
+    }
+
+    public void testStop_N() throws Exception {
+        stopTest(10);
+    }
+
+    private void stopTest(int timerCount) throws Exception {
+        counter = 0;
+        scheduleTime = System.currentTimeMillis();
+        expirationTime = 0;
+
+        for (int i = 0; i < timerCount; i++) {
+            timerManager.schedule(new TimerListener() {
+                public void timerExpired(Timer timer) {
+                    counter++;
+                    expirationTime = System.currentTimeMillis();
+                }
+            }, new Date(System.currentTimeMillis() + delay));
+        }
+
+        timerManager.stop();
+
+        assertTrue(timerManager.isStopped());
+        assertTrue(timerManager.isStopping());
+        assertFalse(timerManager.isSuspending());
+        assertFalse(timerManager.isSuspended());
+
+        Thread.sleep(delay + HEDGE);
+
+        assertTrue(counter == 0);
+
+        boolean passed = false;
+        try {
+            timerManager.resume();
+        } catch (IllegalStateException ise) {
+            passed = true;
+        }
+        assertTrue(passed);
+
+        passed = false;
+        try {
+            timerManager.schedule(new TimerListener() {
+                public void timerExpired(Timer timer) {
+                }
+            }, new Date());
+        } catch (IllegalStateException ise) {
+            passed = true;
+        }
+        assertTrue(passed);
+
+        passed = false;
+        try {
+            timerManager.schedule(new TimerListener() {
+                public void timerExpired(Timer timer) {
+                }
+            }, HEDGE);
+        } catch (IllegalStateException ise) {
+            passed = true;
+        }
+        assertTrue(passed);
+
+        passed = false;
+        try {
+            timerManager.schedule(new TimerListener() {
+                public void timerExpired(Timer timer) {
+                }
+            }, new Date(), delay);
+        } catch (IllegalStateException ise) {
+            passed = true;
+        }
+        assertTrue(passed);
+
+        passed = false;
+        try {
+            timerManager.schedule(new TimerListener() {
+                public void timerExpired(Timer timer) {
+                }
+            }, HEDGE, delay);
+        } catch (IllegalStateException ise) {
+            passed = true;
+        }
+        assertTrue(passed);
+
+        passed = false;
+        try {
+            timerManager.scheduleAtFixedRate(new TimerListener() {
+                public void timerExpired(Timer timer) {
+                }
+            }, new Date(), delay);
+        } catch (IllegalStateException ise) {
+            passed = true;
+        }
+        assertTrue(passed);
+
+        passed = false;
+        try {
+            timerManager.scheduleAtFixedRate(new TimerListener() {
+                public void timerExpired(Timer timer) {
+                }
+            }, HEDGE, delay);
+        } catch (IllegalStateException ise) {
+            passed = true;
+        }
+        assertTrue(passed);
+
+        passed = false;
+        try {
+            timerManager.suspend();
+        } catch (IllegalStateException ise) {
+            passed = true;
+        }
+        assertTrue(passed);
+
+        passed = false;
+        try {
+            timerManager.waitForSuspend(0);
+        } catch (IllegalStateException ise) {
+            passed = true;
+        }
+        assertTrue(passed);
+
+        passed = true;
+        try {
+            timerManager.stop();
+        } catch (IllegalStateException ise) {
+            passed = false;
+        }
+        assertTrue(passed);
+
+        passed = true;
+        try {
+            timerManager.waitForStop(0);
+        } catch (IllegalStateException ise) {
+            passed = false;
+        }
+        assertTrue(passed);
+        timerManager = new TimerManagerImpl();
+    }
+
+    /*
+     * Test method for
+     * 'org.apache.geronimo.commonj.timers.TimerManagerImpl.waitForStop(long)'
+     */
+    public void testWaitForStop() throws Exception {
+        waitForStopTest(1);
+    }
+
+    public void waitForStopTest(int timerCount) throws Exception {
+        initializeTest();
+
+        for (int i = 0; i < timerCount; i++) {
+            timerManager.schedule(new TimerListener() {
+                public void timerExpired(Timer timer) {
+                    counter++;
+                    expirationTime = System.currentTimeMillis();
+                    try {
+                        Thread.sleep(delay);
+                    } catch (InterruptedException ie) {
+                        throw new RuntimeException(ie);
+                    }
+                    counter++;
+                }
+            }, new Date(System.currentTimeMillis()));
+        }
+
+        Thread.sleep(HEDGE);
+
+        timerManager.waitForStop(HEDGE);
+
+        assertTrue(timerManager.isStopping());
+        assertTrue(counter == timerCount);
+
+        Thread.sleep(delay + HEDGE);
+
+        assertTrue(counter == 2*timerCount);
+        assertTrue(timerManager.isStopped());
+
+        timerManager = new TimerManagerImpl();
+    }
+
+    /*
+     * Test method for
+     * 'org.apache.geronimo.commonj.timers.TimerManagerImpl.schedule(TimerListener,
+     * Date)'
+     */
+    public void testScheduleTimerListenerDate() throws Exception {
+        initializeTest();
+
+        timerManager.schedule(new TimerListener() {
+            public void timerExpired(Timer timer) {
+                counter++;
+                expirationTime = System.currentTimeMillis();
+            }
+        }, new Date(System.currentTimeMillis() + delay));
+
+        Thread.sleep(HEDGE);
+        assertTrue(counter == 0);
+
+        Thread.sleep(delay);
+        assertTrue(counter == 1);
+
+        long measuredDelay = expirationTime - scheduleTime;
+        assertTrue(measuredDelay > 0L);
+        assertTrue(Math.abs(measuredDelay - delay) < HEDGE);
+    }
+
+    /*
+     * Test method for
+     * 'org.apache.geronimo.commonj.timers.TimerManagerImpl.schedule(TimerListener,
+     * long)'
+     */
+    public void testScheduleTimerListenerLong() throws Exception {
+        initializeTest();
+
+        timerManager.schedule(new TimerListener() {
+            public void timerExpired(Timer timer) {
+                counter++;
+                expirationTime = System.currentTimeMillis();
+            }
+        }, delay);
+
+        Thread.sleep(HEDGE);
+        assertTrue(counter == 0);
+
+        Thread.sleep(delay);
+        assertTrue(counter == 1);
+
+        long measuredDelay = expirationTime - scheduleTime;
+        assertTrue(measuredDelay > 0L);
+        assertTrue(Math.abs(measuredDelay - delay) < HEDGE);
+    }
+
+    public void testScheduleTimerListenerLong_N() throws Exception {
+        initializeTest();
+
+        for (int i = 0; i < 10; i++)
+            timerManager.schedule(new TimerListener() {
+                public void timerExpired(Timer timer) {
+                    counter++;
+                }
+            }, delay);
+
+        Thread.sleep(HEDGE);
+        assertTrue(counter == 0);
+
+        Thread.sleep(delay + HEDGE);
+        assertTrue(counter == 10);
+    }
+
+    /*
+     * Test method for
+     * 
+     * 'org.apache.geronimo.commonj.timers.TimerManagerImpl.schedule(TimerListener,
+     * Date, long)'
+     */
+    public void testScheduleTimerListenerDateLong() throws Exception {
+        initializeTest();
+
+        Timer timer = timerManager.schedule(new TimerListener() {
+            public void timerExpired(Timer timer) {
+                counter++;
+                expirationTime = System.currentTimeMillis();
+            }
+        }, new Date(System.currentTimeMillis() + delay), period);
+
+        Thread.sleep(HEDGE);
+        assertTrue(counter == 0);
+
+        Thread.sleep(delay);
+        assertTrue(counter == 1);
+
+        long measuredDelay = expirationTime - scheduleTime;
+        assertTrue(measuredDelay > 0L);
+        assertTrue(Math.abs(measuredDelay - delay) < HEDGE);
+
+        Thread.sleep(period + HEDGE);
+        assertTrue(counter == 2);
+
+        timer.cancel();
+        Thread.sleep(period + HEDGE);
+        assertTrue(counter == 2);
+    }
+
+    /*
+     * Test method for
+     * 
+     * 'org.apache.geronimo.commonj.timers.TimerManagerImpl.schedule(TimerListener,
+     * long, long)'
+     */
+    public void testScheduleTimerListenerLongLong() throws Exception {
+        initializeTest();
+
+        Timer timer = timerManager.schedule(new TimerListener() {
+            public void timerExpired(Timer timer) {
+                counter++;
+                expirationTime = System.currentTimeMillis();
+            }
+        }, delay, period);
+
+        Thread.sleep(HEDGE);
+        assertTrue(counter == 0);
+
+        Thread.sleep(delay);
+        assertTrue(counter == 1);
+
+        long measuredDelay = expirationTime - scheduleTime;
+        assertTrue(measuredDelay > 0L);
+        assertTrue(Math.abs(measuredDelay - delay) < HEDGE);
+
+        Thread.sleep(period + HEDGE);
+        assertTrue(counter == 2);
+
+        timer.cancel();
+        Thread.sleep(period + HEDGE);
+        assertTrue(counter == 2);
+    }
+
+    /*
+     * Test method for
+     * 
+     * 'org.apache.geronimo.commonj.timers.TimerManagerImpl.scheduleAtFixedRate(TimerListener,
+     * Date, long)'
+     */
+    public void testScheduleAtFixedRateTimerListenerDateLong() throws Exception {
+        initializeTest();
+
+        Timer timer = timerManager.scheduleAtFixedRate(new TimerListener() {
+            public void timerExpired(Timer timer) {
+                counter++;
+                expirationTime = System.currentTimeMillis();
+            }
+        }, new Date(System.currentTimeMillis() + delay), period);
+
+        Thread.sleep(HEDGE);
+        assertTrue(counter == 0);
+
+        Thread.sleep(delay);
+        assertTrue(counter == 1);
+
+        long measuredDelay = expirationTime - scheduleTime;
+        assertTrue(measuredDelay > 0L);
+        assertTrue(Math.abs(measuredDelay - delay) < HEDGE);
+
+        Thread.sleep(period + HEDGE);
+        assertTrue(counter == 2);
+
+        timer.cancel();
+        Thread.sleep(period + HEDGE);
+        assertTrue(counter == 2);
+    }
+
+    /*
+     * Test method for
+     * 
+     * 'org.apache.geronimo.commonj.timers.TimerManagerImpl.scheduleAtFixedRate(TimerListener,
+     * long, long)'
+     */
+    public void testScheduleAtFixedRateTimerListenerLongLong() throws Exception {
+        initializeTest();
+
+        Timer timer = timerManager.scheduleAtFixedRate(new TimerListener() {
+            public void timerExpired(Timer timer) {
+                counter++;
+                expirationTime = System.currentTimeMillis();
+            }
+        }, delay, period);
+
+        Thread.sleep(HEDGE);
+        assertTrue(counter == 0);
+
+        Thread.sleep(delay);
+        assertTrue(counter == 1);
+
+        long measuredDelay = expirationTime - scheduleTime;
+        assertTrue(measuredDelay > 0L);
+        assertTrue(Math.abs(measuredDelay - delay) < HEDGE);
+
+        Thread.sleep(period + HEDGE);
+        assertTrue(counter == 2);
+
+        timer.cancel();
+        Thread.sleep(period + HEDGE);
+        assertTrue(counter == 2);
+    }
+
+    private void initializeTest() {
+        counter = 0;
+        scheduleTime = System.currentTimeMillis();
+        expirationTime = 0;
+    }
+}