/* * Copyright 2004-2006 Stefan Reuter * * 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.asteriskjava.fastagi; import java.lang.Thread.UncaughtExceptionHandler; import java.util.concurrent.atomic.AtomicLong; import org.asteriskjava.util.Log; import org.asteriskjava.util.LogFactory; /** * Runs an AgiServer in a separate Thread. * <p> * You can use this class to run an AgiServer in the background of your * application or run it in your servlet container or application server. * <p> * By default the thread used by this class is marked as daemon thread, that * means it will be destroyed when the last user thread has finished. * * @author srt * @version $Id: AgiServerThread.java 938 2007-12-31 03:23:38Z srt $ * @since 0.2 */ public class AgiServerThread { private final Log logger = LogFactory.getLog(getClass()); private static AtomicLong idCounter = new AtomicLong(); private AgiServer agiServer; private Thread thread; private boolean daemon = true; /** * Creates a new AgiServerThread. * <p> * Before you can run this thread you must set an {@link AgiServer} using * {@link #setAgiServer(AgiServer)}. * <p> * This constructor is mainly intended for use with setter based dependency * injection. */ public AgiServerThread() { super(); } /** * Creates a new AgiServerThread that runs the given {@link AgiServer}. * * @param agiServer the AgiServer to run. */ public AgiServerThread(AgiServer agiServer) { super(); this.agiServer = agiServer; } /** * Sets the AgiServer to run. * <p> * This property must be set before starting the AgiServerThread by calling * startup. * * @param agiServer the AgiServer to run. */ public void setAgiServer(AgiServer agiServer) { this.agiServer = agiServer; } /** * Marks the thread as either a daemon thread or a user thread. * <p> * Default is <code>true</code>. * * @param daemon if <code>false</code>, marks the thread as a user * thread. * @see Thread#setDaemon(boolean) * @since 0.3 */ public void setDaemon(boolean daemon) { this.daemon = daemon; } /** * Starts the AgiServer in its own thread. * <p> * Note: The AgiServerThread is designed to handle one AgiServer instance at * a time so calling this method twice without stopping the AgiServer in * between will result in a RuntimeException. * * @throws IllegalStateException if the mandatory property agiServer has not * been set or the AgiServer had already been started. * @throws RuntimeException if the AgiServer can't be started due to IO * problems, for example because the socket has already been * bound by another process. */ public synchronized void startup() throws IllegalStateException, RuntimeException { if (agiServer == null) { throw new IllegalStateException("Mandatory property agiServer is not set."); } if (thread != null) { throw new IllegalStateException("AgiServer is already started"); } thread = createThread(); thread.start(); } protected Thread createThread() { Thread t; t = new Thread(new Runnable() { public void run() { try { agiServer.startup(); } catch (Throwable e) { throw new RuntimeException("Exception running AgiServer.", e); } } }); t.setName("Asterisk-Java AgiServer-" + idCounter.getAndIncrement()); t.setDaemon(daemon); t.setUncaughtExceptionHandler(new AgiThreadUncaughtExceptionHanlder()); return t; } /** * Stops the {@link AgiServer}. * <p> * The AgiServer must have been started by calling {@link #startup()} before * you can stop it. * * @see AgiServer#shutdown() * @throws IllegalStateException if the mandatory property agiServer has not * been set or the AgiServer had already been shut down. */ public synchronized void shutdown() throws IllegalStateException { if (agiServer == null) { throw new IllegalStateException("Mandatory property agiServer is not set."); } agiServer.shutdown(); if (thread != null) { try { thread.join(); } catch (InterruptedException e) { logger.warn("Interrupted while waiting for AgiServer to shutdown."); } thread = null; // NOPMD by srt on 7/5/06 11:23 PM } } class AgiThreadUncaughtExceptionHanlder implements UncaughtExceptionHandler { public void uncaughtException(Thread t, Throwable e) { logger.error("Uncaught exception in AgiServerThread", e); } } }