View Javadoc

1   /*
2    *  Copyright 2004-2006 Stefan Reuter
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *
16   */
17  package org.asteriskjava.fastagi;
18  
19  import java.lang.Thread.UncaughtExceptionHandler;
20  import java.util.concurrent.atomic.AtomicLong;
21  
22  import org.asteriskjava.util.Log;
23  import org.asteriskjava.util.LogFactory;
24  
25  /**
26   * Runs an AgiServer in a separate Thread.
27   * <p>
28   * You can use this class to run an AgiServer in the background of your
29   * application or run it in your servlet container or application server.
30   * <p>
31   * By default the thread used by this class is marked as daemon thread, that
32   * means it will be destroyed when the last user thread has finished.
33   * 
34   * @author srt
35   * @version $Id$
36   * @since 0.2
37   */
38  public class AgiServerThread
39  {
40      private final Log logger = LogFactory.getLog(getClass());
41      private static AtomicLong idCounter = new AtomicLong();
42      private AgiServer agiServer;
43      private Thread thread;
44      private boolean daemon = true;
45  
46      /**
47       * Creates a new AgiServerThread.
48       * <p>
49       * Before you can run this thread you must set an {@link AgiServer} using
50       * {@link #setAgiServer(AgiServer)}.
51       * <p>
52       * This constructor is mainly intended for use with setter based dependency
53       * injection.
54       */
55      public AgiServerThread()
56      {
57          super();
58      }
59  
60      /**
61       * Creates a new AgiServerThread that runs the given {@link AgiServer}.
62       * 
63       * @param agiServer the AgiServer to run.
64       */
65      public AgiServerThread(AgiServer agiServer)
66      {
67          super();
68          this.agiServer = agiServer;
69      }
70  
71      /**
72       * Sets the AgiServer to run.
73       * <p>
74       * This property must be set before starting the AgiServerThread by calling
75       * startup.
76       * 
77       * @param agiServer the AgiServer to run.
78       */
79      public void setAgiServer(AgiServer agiServer)
80      {
81          this.agiServer = agiServer;
82      }
83  
84      /**
85       * Marks the thread as either a daemon thread or a user thread.
86       * <p>
87       * Default is <code>true</code>.
88       * 
89       * @param daemon if <code>false</code>, marks the thread as a user
90       *            thread.
91       * @see Thread#setDaemon(boolean)
92       * @since 0.3
93       */
94      public void setDaemon(boolean daemon)
95      {
96          this.daemon = daemon;
97      }
98  
99      /**
100      * Starts the AgiServer in its own thread.
101      * <p>
102      * Note: The AgiServerThread is designed to handle one AgiServer instance at
103      * a time so calling this method twice without stopping the AgiServer in
104      * between will result in a RuntimeException.
105      * 
106      * @throws IllegalStateException if the mandatory property agiServer has not
107      *             been set or the AgiServer had already been started.
108      * @throws RuntimeException if the AgiServer can't be started due to IO
109      *             problems, for example because the socket has already been
110      *             bound by another process.
111      */
112     public synchronized void startup() throws IllegalStateException, RuntimeException
113     {
114         if (agiServer == null)
115         {
116             throw new IllegalStateException("Mandatory property agiServer is not set.");
117         }
118 
119         if (thread != null)
120         {
121             throw new IllegalStateException("AgiServer is already started");
122         }
123 
124         thread = createThread();
125         thread.start();
126     }
127 
128     protected Thread createThread()
129     {
130         Thread t;
131 
132         t = new Thread(new Runnable()
133         {
134             public void run()
135             {
136                 try
137                 {
138                     agiServer.startup();
139                 }
140                 catch (Throwable e)
141                 {
142                     throw new RuntimeException("Exception running AgiServer.", e);
143                 }
144             }
145         });
146         t.setName("Asterisk-Java AgiServer-" + idCounter.getAndIncrement());
147         t.setDaemon(daemon);
148         t.setUncaughtExceptionHandler(new AgiThreadUncaughtExceptionHanlder());
149 
150         return t;
151     }
152 
153     /**
154      * Stops the {@link AgiServer}.
155      * <p>
156      * The AgiServer must have been started by calling {@link #startup()} before
157      * you can stop it.
158      * 
159      * @see AgiServer#shutdown()
160      * @throws IllegalStateException if the mandatory property agiServer has not
161      *             been set or the AgiServer had already been shut down.
162      */
163     public synchronized void shutdown() throws IllegalStateException
164     {
165         if (agiServer == null)
166         {
167             throw new IllegalStateException("Mandatory property agiServer is not set.");
168         }
169 
170         agiServer.shutdown();
171 
172         if (thread != null)
173         {
174             try
175             {
176                 thread.join();
177             }
178             catch (InterruptedException e)
179             {
180                 logger.warn("Interrupted while waiting for AgiServer to shutdown.");
181                 Thread.currentThread().interrupt();
182             }
183             thread = null; // NOPMD by srt on 7/5/06 11:23 PM
184         }
185     }
186 
187     class AgiThreadUncaughtExceptionHanlder implements UncaughtExceptionHandler
188     {
189         public void uncaughtException(Thread t, Throwable e)
190         {
191             logger.error("Uncaught exception in AgiServerThread", e);
192         }
193     }
194 }