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);
}
}