www.pudn.com > d4j.zip > Channel.java


// local.dialogic.Channel abstract class 
// $Id: Channel.java,v 1.9 2003/11/13 11:45:04 cgm8 Exp $ 
/*  
 * Copyright (c) 1999 Carlos G Mendioroz. 
 * 
 *  This file is part of D4J. 
 * 
 *  D4J is free software; you can redistribute it and/or 
 *  modify it under the terms of the GNU Lesser General Public 
 *  License as published by the Free Software Foundation; either 
 *  version 2 of the License, or (at your option) any later version. 
 *   
 *  D4J is distributed in the hope that it will be useful, 
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of 
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 *  Lesser General Public License for more details. 
 *   
 *  You should have received a copy of the GNU Lesser General Public 
 *  License along with this library; if not, write to the 
 *  Free Software Foundation, Inc., 59 Temple Place - Suite 330, 
 *  Boston, MA  02111-1307, USA. 
 * 
 * Report problems and direct all questions to: 
 * 
 *	tron@acm.org 
 */ 
package local.dialogic; 
import java.util.*; 
 
public abstract class Channel extends Observable { 
    // Class variables 
    static private java.util.Vector channelAtDev;   // Class instances 
    static private java.util.Vector resourceAtDev;  // registered resources 
    static private PollThread pollThread; 
    static private Object eventLock = new Object(); // thing to lock on. 
    static private LinkedList holdEvents = new LinkedList();; // in the mean time... 
    static private boolean hold = false;            // executing atomic 
 
    static protected boolean GC = false;    // use Global Call event fn 
    static { 
        channelAtDev = new java.util.Vector(5,4); 
        resourceAtDev = new java.util.Vector(5,4); 
        pollThread = new PollThread(); 
        pollThread.setDaemon(true); 
        pollThread.setName("Dialogic polling thread"); 
        pollThread.start(); 
    } 
 
    /** 
     * Used to carry out an action which will _never_ have an event fire during it. 
     * stops the event queue - so r should be quick and deterministic. 
     * In future it could be run on the event thread - a'la invokeLater() in swing 
     */ 
    public static void atomicAction(Runnable r){ 
        synchronized(eventLock) { 
            hold = true; 
        } 
        r.run(); 
        synchronized(eventLock) { 
            EVT evt; 
            hold = false; 
            while(holdEvents.size() > 0) { 
                evt = (EVT)holdEvents.removeFirst(); 
                handleEvent(evt); 
            } 
        } 
    } 
 
    /* 
     * waitevtex is no good because there is no efficient 
     * way of knowing of new devices I care about. 
     * We need a dynamic way of asking for events, i.e. PollThread at Channel 
     * 
     * This is done via signal handling in Linux. To have the same class, 
     * we just throw an exception if we call waitevt() in Linux, and let  
     * the poller silently die... 
     */ 
 
    private static class PollThread extends Thread { 
        // Run: poll events and hold last 
        public void run() 
        { 
            EVT evt; 
            if ((Dialogic.debug & Dialogic.DEBUG_EVSRC) != 0) { 
              System.out.println("Channel Poll thread started"); 
            } 
	    try { 
		while (true) { 
		    evt = new EVT(); 
		    Dialogic.sr_waitevt(evt); 
		    if (evt.dev == 0) { 
			if ((Dialogic.debug & Dialogic.DEBUG_EVSRC) != 0) { 
			    System.out.println("Channel Poll thread stopped"); 
			} 
			return; 
		    } 
		    handleEvent(evt); 
		} 
	    } catch (RuntimeException re) { 
		if ((Dialogic.debug & Dialogic.DEBUG_EVSRC) != 0) { 
		    System.out.println("Channel Poll thread stopped"); 
		} 
	    } 
	} 
    } 
 
    protected static void handleEvent(EVT evt) { 
	// Guard from dispatching events generated by 
        // atomicAction until completion 
        Channel ch = null; 
        ResourceServer res = null; 
 
        // Support for atomicAction: action not interrupted by events. 
        // It's only important, AFAIK, at open time when we want events to be dispatched 
        // only after device had an oportunity to register. 
        // Old implementation used to just hold, but this is a bad idea in the linux 
        // implementation that is monothreaded as far as events go, and blocks all driver 
        // comunication, i.e., big possibility for deadlock. 
        if (hold) { 
            synchronized(eventLock){ 
                // still holding... 
                if(hold) { 
                    holdEvents.add(evt); 
                    return; 
                } 
            } 
        } 
         
        if (GC) 
            GCChannel.gc_getMeta(evt); // No Dialogic indirection here... 
        try { 
            res = (ResourceServer)resourceAtDev.elementAt(evt.dev); 
        } catch (ArrayIndexOutOfBoundsException x ) { 
            if((Dialogic.debug & Dialogic.DEBUG_EVSRC) != 0){ 
                System.err.println("Got event before handler registered: "+evt); 
            } 
        } 
        if (res != null) { 
            if ((Dialogic.debug & Dialogic.DEBUG_EVSRC) != 0) 
                System.out.println(new java.util.Date().toString().substring(11,20) + 
                            res + ": " + evt); 
            res.service(evt); 
        } else { 
            dispatch(evt); 
        } 
    } 
 
    public static void dispatch(EVT evt) { 
        Channel ch = null; 
        try { 
          ch = (Channel)channelAtDev.elementAt(evt.dev); 
        } catch (ArrayIndexOutOfBoundsException x ) { 
          if((Dialogic.debug & Dialogic.DEBUG_EVSRC) != 0){ 
            System.err.println("Got event before handler registered: "+evt); 
          } 
        } 
        if (ch != null) { 
            if ((Dialogic.debug & Dialogic.DEBUG_EVSRC) != 0) 
                System.out.println(new java.util.Date().toString().substring(11,20) + 
                            Device.atDev(evt.dev) + ": " + evt); 
            ch.newEvent(evt); 
        } else { 
            if ((Dialogic.debug & Dialogic.DEBUG_EVSRC) != 0){ 
                System.out.println("unclaimed event: " + evt 
                    +"("+ evt.dev + "/" + evt.line + ")"); 
            } 
        } 
    } 
 
    // Utility fn to interrupt (and join) all channel group threads 
    // waiting for ThreadGroup.interrupt()/join() to be implemented. 
    // We leave serviceThread alone...and myself!  
    /* 5/3/02 it seems that sometimes we kill the thread that is clearing 
     * the channel and then let the channel in an unusable state :-( */ 
	public static void stopGroup(Channel ch) { 
		if ((Dialogic.debug & Dialogic.DEBUG_CHANNEL) != 0 && ch.group != null) 
			System.out.println("StopGroup("+ ch.toString()+ ")"); 
        if (ch.group == null) 
            return; 
         int n = ch.group.activeCount(); 
         if (n == 0) 
            return; 
         Thread list[] = new Thread[n]; 
         int count = ch.group.enumerate(list); 
         Thread me = Thread.currentThread(); 
          
         // Interrupt all of them, then join ! 
         for (int i = 0; i < count; i++) { 
            if (list[i] != ch.serviceThread && list[i] != me) 
                list[i].interrupt(); 
         } 
         for (int i = 0; i < count; i++) { 
            if (list[i] != ch.serviceThread && list[i] != me) { 
                try { 
                    list[i].join(); 
                } 
                catch(InterruptedException ie) { 
					if ((Dialogic.debug & Dialogic.DEBUG_CHANNEL) != 0 && ch.group != null) 
						System.out.println("StopGroup("+ ch.toString()+ ") interrupted"); 
                    Thread.currentThread().interrupt(); 
                } 
            } 
        } 
        if (me != ch.serviceThread) 
			ch.serviceThread.interrupt(); // He has to know! 
		if ((Dialogic.debug & Dialogic.DEBUG_CHANNEL) != 0 && ch.group != null) 
			System.out.println("StopGroup("+ ch.toString()+ ") exit"); 
    } 
 
    /** 
     * setGroup: recreate the service thread association  
     *  making a new group for this channel... 
     */ 
    public void setGroup() { 
        serviceThread = Thread.currentThread(); 
        group = serviceThread.getThreadGroup(); 
    } 
 
    // Variables 
    CallHandler handler = null; 
    // resource used to be a Resource but now we support a set of them... 
    java.util.Vector resource; 
    // events is logically a queue. It used to be a com.objectspace.Queue 
    // but now we use JDK2's LinkedList in favour of easier deployment. 
    private LinkedList events; 
    private boolean serviceActive = false; 
    protected EVT lastEvent = null; 
    protected Call call = null; 
 
    /** 
     * Visible state of channel (via getState() and observer interface). 
     * The actual Channel implementation is responsible 
     * for updating it using setState({OOS|FREE|INCOMING|OUTGOING}). 
     */ 
    protected Integer state = OOS; 
    ThreadGroup group = null; 
    Thread serviceThread = null; 
 
    // 
    public Channel() { 
        events = new LinkedList(); 
        resource = new java.util.Vector(2,1); 
    } 
 
    protected void register(Device dev) { 
        if (channelAtDev.size() <= dev.device) { 
            channelAtDev.setSize(dev.device + 1); 
            resourceAtDev.setSize(dev.device + 1); 
        } 
        channelAtDev.setElementAt(this, dev.device); 
        dev.channel = this; 
        if ((Dialogic.debug & Dialogic.DEBUG_EVDST) != 0) 
            System.out.println("Registered: " + dev); 
    } 
 
    protected void unregister(Device dev) { 
        channelAtDev.setElementAt(null, dev.device); 
    } 
 
    protected static void register(ResourceServer resourceServer, int device) { 
        if (resourceAtDev.size() <= device) { 
            resourceAtDev.setSize(device + 1); 
            channelAtDev.setSize(device + 1); 
        } 
 
 
        resourceAtDev.setElementAt(resourceServer, device); 
        if ((Dialogic.debug & Dialogic.DEBUG_EVDST) != 0) 
            System.out.println("Registered: " + device); 
    } 
 
    protected static void unregister(ResourceServer resourceServer, int device) { 
        if (resourceAtDev.size() <= device) 
            return; 
        resourceAtDev.setElementAt(null, device); 
    } 
 
    /** 
     * attach/dettach from resources 
     * just track them to free them at clear time 
     */ 
    public void attach(Resource aResource) { 
        if (!resource.contains(aResource)) 
            resource.add(aResource); 
    } 
 
    public void dettach(Resource aResource) { 
        if (!resource.contains(aResource)) { 
            aResource.free(this); 
            resource.removeElement(aResource); 
        } 
    } 
 
    protected abstract EVT service(EVT event); // Do line level service of new event 
    public abstract String toString();         // return a name 
    public abstract void close();		       // close a channel 
	public void finalize() throws Throwable  
	{ 
		close(); 
		super.finalize(); 
	} 
     
    /** 
     * Accept a call, i.e., start ringback for caller 
     */ 
    abstract void accept(); 
    /** 
     * @deprecated:  Old alias of answer(rings) 
     */ 
    void accept(int rings) { 
        accept(); 
        answer(rings); 
    } 
    /** 
     * Answer a call now. 
     */ 
    void answer() { 
        answer(0); 
    } 
    /** 
     * Answer a call after some ring cycles. Accepts it too if needed. 
     */ 
    abstract void answer(int rings); 
    /** 
     * Reject a call with given reason 
     */ 
    abstract void reject(int reason); 
    /** 
     * Start dialing - make a call 
     */ 
    abstract void dial(Call call, String dest); 
    /** 
     * Request caller's ANI 
     */ 
    abstract String ani(); 
    /** 
     * Request call's billing info in pulses 
     */ 
    abstract int pulses(); 
     
    public abstract Voice getVoice(); 
    public abstract Device getNetwork(); 
 
    public Integer getState() { 
        return state; 
    } 
 
    public Call getCall() { 
        return call; 
    } 
 
    protected void setState(Integer newState) { 
        if (state != newState) { 
            state = newState; 
            setChanged(); 
            notifyObservers(state); 
        } 
    } 
 
    public final static Integer FREE = new Integer(0);      // idle 
    public final static Integer INCOMING = new Integer(1);  // incoming call seizure 
    public final static Integer OUTGOING = new Integer(2);  // outgoing call seizure 
    public final static Integer OOS = new Integer(3);       // out of service 
 
    // Define a call handler 
    public void setCallHandler(CallHandler handler) { 
        this.handler = handler; 
    } 
 
    /** 
     * newEvent: send a channel a user event 
     */ 
    public void newEvent(int data) { 
        EVT evt = new EVT(); 
        evt.type = EVT.DT_USER; 
        evt.data = data; 
        this.newEvent(evt); 
    } 
         
    protected void newEvent(EVT evt) { 
        synchronized(events) { 
            lastEvent = evt; 
            events.add(evt); 
            events.notifyAll(); 
        } 
    } 
 
    public EVT lastEvent() { 
        return lastEvent; 
    } 
 
    public void flush() { 
        EVT evt; 
        synchronized(events) { 
            while(events.size() > 0) { 
                evt = (EVT)events.removeFirst(); 
                if ((Dialogic.debug & Dialogic.DEBUG_EVDST) != 0) 
                    System.out.println(new java.util.Date().toString().substring(11,20) + 
                            this + ": flushed " + evt); 
                service(evt); 
            } 
        } 
    } 
 
    /**  
     * Public access to channel event stream 
     */ 
     
    public EVT waitEvent() { 
        return waitEvent(0, false); 
    } 
 
    public EVT waitEvent(long milis) { 
        return waitEvent(milis, false); 
    } 
 
    /** 
     * Internal (D4J functions) access to event stream 
     * with precedence from public accessor.  
     * Uses boolean serviceActive to block public access 
     */ 
      
    void beginService() { 
        serviceActive = true; 
    } 
     
    void endService() { 
        serviceActive = false; 
        synchronized(events) { 
            events.notifyAll(); 
        } 
    } 
     
    boolean inService() { 
        return serviceActive; 
    } 
      
    EVT serviceWaitEvent() { 
        return waitEvent(0, true); 
    } 
 
    EVT serviceWaitEvent(long milis) { 
        return waitEvent(milis, true); 
    } 
 
    private EVT waitEvent(long milis, boolean service) { 
        EVT evt = null; 
 
        if (Thread.interrupted()) { 
            if ((Dialogic.debug & Dialogic.DEBUG_EVDST) != 0) 
                System.out.println(new java.util.Date().toString().substring(11,20) + 
                    this + ": interrupted");         
            throw new ChannelException("Interrupted"); 
        } 
 
        long end = 1, now = 0; 
        if (milis != 0)  
            end = new Date().getTime() + milis; 
        while (evt == null) { 
            if (milis != 0) { 
                now = new Date().getTime(); 
                if (now >= end) break; 
            } 
            synchronized(events) { 
                if((events.size() == 0) || (serviceActive && !service)) 
                    try {  
                        if (milis != 0) 
                            events.wait(end - now); 
                        else 
                            events.wait(); 
                    } catch (InterruptedException ie) { 
                        if ((Dialogic.debug & Dialogic.DEBUG_EVDST) != 0) 
                            System.out.println(new java.util.Date().toString().substring(11,20) + 
                            this + ": interrupted");         
                        throw new ChannelException("Interrupted"); 
                    } 
                if ((events.size() > 0) && (!serviceActive || service)) 
                    evt = (EVT)events.removeFirst(); 
            } 
            if (evt != null) { 
                if ((Dialogic.debug & Dialogic.DEBUG_EVDST) != 0) 
                   System.out.println(new java.util.Date().toString().substring(11,20) + 
                        this + ": " + evt); 
                evt = service(evt); 
                if ((Dialogic.debug & Dialogic.DEBUG_EVDST) != 0 && evt == null) 
                    System.out.println("...serviced"); 
            } else { 
                // Give chance to service ? 
                Thread.yield(); 
            } 
        } 
        return evt; 
    } 
 
    void clear() { 
        // Drop call first, to let threads know game is over... 28/1/2002 
        if (call != null) { 
            call.drop(); 
            call = null; 
        } 
        stopGroup(this); 
        while (!resource.isEmpty()) { 
            Resource aResource = (Resource)resource.firstElement(); 
            aResource.free(this); 
            resource.removeElement(aResource); 
        } 
        // Just in case, free lock 
        serviceActive = false; 
    } 
     
    public void listen(Resource res) { 
        this.getNetwork().listen(this, res); 
    } 
}