Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Class Members | File Members | Related Pages

src/net/threebit/jvr/util/GCOutbound.java

Go to the documentation of this file.
00001 package net.threebit.jvr.util;
00002 
00003 /*
00004  * $Id: GCOutbound.java,v 1.21 2005/04/28 04:05:51 kevino Exp $
00005  *
00006  * Copyright (c) 2003,2004 Kevin O'Donnell
00007  * 
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public
00010  * License as published by the Free Software Foundation; either
00011  * version 2.1 of the License, or (at your option) any later version.
00012  *
00013  * This library is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016  * Lesser General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU Lesser General Public
00019  * License along with this library; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307,  USA.
00021  */
00022 
00023 import java.util.*;
00024 import java.util.logging.Logger;
00025 import net.threebit.jvr.*;
00026 
00027 /**
00028  * When using GCOutbound with pdk_us_mf_io be sure to
00029  * turn on call progress and media detection in pdk_us_mf_io.cdp
00030  * <pre>
00031 R4 INTEGER_t PSL_MakeCall_CallProgress = 1
00032 R4 INTEGER_t PSL_MakeCall_MediaDetect = 1 
00033 All INTEGER_t CDP_OUT_ConnectType = 3
00034 
00035  * </pre>
00036  */
00037 
00038 public class GCOutbound extends JVRMetaEventListener {
00039 
00040   /**
00041    *
00042    */
00043   GC_PARM_BLK setConfigParmBlk = new GC_PARM_BLK();
00044 
00045   /** CAP processing. */
00046   public ImmediateCAP ic = null;
00047 
00048   /** Global Call protocol that the device was opened with. */
00049   public String protocol = null;
00050 
00051   /** Voice device handle used for dx.dial() calls. */
00052   public int dxdev;
00053 
00054   /** Line device used for dialing. */
00055   public long linedev;
00056 
00057   /** Called party number. */
00058   public String destination;
00059 
00060   /** Calling party number. */
00061   public String origin;
00062 
00063   /** Call Reference Number if available. */
00064   public long crn = -1;
00065 
00066   /** Set to true when this class is finished. */
00067   public boolean finished = false;
00068 
00069   /** Saved exception if one happens during dialing. */
00070   public Throwable error = null;
00071 
00072   /** True if the call becomes connected. */
00073   public boolean connected = false;
00074 
00075   /** The type of connection that was made (if any). */
00076   public int connectType = -1;
00077 
00078   /** 
00079    * If the call is not connected, this is the value of event.gcInfo.gcValue
00080    * that was captured at the time of the GCEV_DISCONNECTED event.
00081    */
00082   public int disconnectCause = -1;
00083 
00084   /**
00085    *
00086    */
00087   public int pamdDelay = 1500;
00088 
00089   /**
00090    *
00091    */
00092   public DX_CAP cap = new DX_CAP();
00093 
00094   /**
00095    *
00096    */
00097   public int makeCallTimeout = 60;
00098 
00099   /**
00100    * Constructs a GCOutbound object that will drive a call to the specified number.
00101    * @param dxdev A voice device handle to use.
00102    * @param linedev A valid Global Call line device handle.
00103    * @param destination The phone number to connect to.
00104    * @param origin The phone number that is provided for caller ID (the number *doing* the calling).
00105    */
00106   public GCOutbound (int dxdev, long linedev, String destination, String origin) throws JVRException {
00107     this.dxdev = dxdev;
00108     this.linedev = linedev;
00109     this.destination = destination;
00110     this.origin = origin;
00111 
00112     cap.ca_nbrdna = 8;
00113     cap.ca_intflg = dx.DX_PAMDOPTEN;
00114     cap.ca_pamd_spdval = (byte) dx.PAMD_ACCU;
00115     cap.ca_hedge = 1;
00116     cap.ca_dtn_pres = 0;
00117   }
00118 
00119   /**
00120    * Sends stop signals to any in-progress operations then
00121    * generates the GCOutbound finished event with "stopped"
00122    * as an argument.
00123    */
00124   public void stop() throws JVRException {
00125     finished(true);
00126   }
00127 
00128   /**
00129    * After initialization, call this method to start dialing.  This method
00130    * calls {@link gc#ResetLineDev} to make certain that the line device
00131    * is NULL/IDLE.  It also primes the event handling mechanisms.
00132    */
00133   public void start() throws JVRException {
00134     /*
00135     if (protocol != null && (!protocol.equals("ISDN"))) {
00136       gc.SetParm(linedev,gc.GCPR_MEDIADETECT,new GC_PARM(gc.GCPV_ENABLE));
00137     }
00138     */
00139     if (protocol != null && (!protocol.equals("ISDN"))) {
00140       // gc.AttachResource(linedev,dxdev,null,null,gc.GC_VOICEDEVICE,gc.EV_SYNC);
00141       gc.SetCallProgressParm(linedev,cap);
00142     }
00143     gc.ResetLineDev(linedev,gc.EV_ASYNC);
00144   }
00145 
00146   public boolean handleEvent (JVREvent e) throws JVRException {
00147     if (finished) { return true; }
00148     if (e.target == ic) {
00149       jvr.removeEventListener(ic);
00150       if (ic.connected) {
00151         finished(); 
00152       }
00153       else {
00154         // IC is done, but the line is not connected - we are responsible for hanging up.
00155         gc.DropCall(crn,gc.GC_NORMAL_CLEARING,gc.EV_ASYNC);
00156       }
00157     }
00158     return true;
00159   }
00160 
00161   public boolean handleEvent (MetaEvent e) throws JVRException {
00162     if (finished) { return true; }
00163     if (! e.isGC()) { return true; }
00164     if (e.linedev != linedev) { return true; }
00165     try {
00166       if (e.evttype == gc.GCEV_RESETLINEDEV) { 
00167         if (protocol != null && protocol.equals("ISDN")) {
00168           // Configure the linedev for G.711 Mu-law Layer 1 Protocol.  This may
00169           // not apply in all cases, but I don't feel like taking the time to
00170           // *not* hardcode it at the moment.
00171           gc.util_insert_parm_val(
00172             setConfigParmBlk,
00173             gc.GCSET_CHAN_CAPABILITY,
00174             gc.GCPARM_CAPABILITY,
00175             4, // size of an integer; needs work here - Java programmers should not need to know data sizes.
00176             gc.GCCAP_AUDIO_g711Ulaw56k
00177           );
00178           gc.SetConfigData(gc.GCTGT_CCLIB_CHAN,e.linedev,setConfigParmBlk,gc.EV_ASYNC,gc.GCUPDATE_IMMEDIATE,gc.EV_ASYNC);
00179         }
00180         else {
00181           // Go ahead and start the call.
00182           // TODO: make 30 second timeout configurable.
00183           crn = gc.MakeCall(e.linedev, destination, null, makeCallTimeout, gc.EV_ASYNC);
00184         }
00185       }
00186       else if (e.evttype == gc.GCEV_SETCONFIGDATA) {
00187         // Only set the calling party number when protocol is "ISDN".
00188         if (protocol != null && protocol.equals("ISDN")) {
00189           gc.SetCallingNum(e.linedev, origin);
00190         }
00191         // Timeout value has no meaning for ISDN calls.
00192         crn = gc.MakeCall(e.linedev, destination, null, 0, gc.EV_ASYNC);
00193       }
00194       else if (e.evttype == gc.GCEV_PROCEEDING) {
00195         if (protocol != null && protocol.equals("ISDN")) {
00196           // TODO: make configurable.
00197           // Only to CAP when running as ISDN.
00198           dx.listen(dxdev, dt.getxmitslot(gc.GetNetworkH(e.linedev)));
00199           dt.listen(gc.GetNetworkH(e.linedev), dx.getxmitslot(dxdev));
00200           ic = new ImmediateCAP(dxdev,cap,pamdDelay);
00201           jvr.addEventListener(ic);
00202           ic.start();
00203         }
00204       }
00205       else if (e.evttype == gc.GCEV_ALERTING) {
00206         // Nothing to do here.
00207       }
00208       else if (e.evttype == gc.GCEV_PROGRESSING) {
00209         // Nothing to do here.
00210       }
00211       else if (e.evttype == gc.GCEV_CALLSTATUS) {
00212         // Timeout or no answer
00213         // if (ic != null) { dx.stopch(dxdev,dx.EV_ASYNC); }
00214         connected = false;
00215         gc.DropCall(crn,gc.GC_NORMAL_CLEARING,gc.EV_ASYNC);
00216       }
00217       else if (e.evttype == gc.GCEV_CONNECTED) {
00218 
00219         // For FXS and Analog protocols, check for "CONNECTED" but not really connected conditions.
00220         // If they are met, then drop the call.
00221         if (protocol == null || (!protocol.equals("ISDN"))) {
00222           if (e.gcInfo.gcValue == gc.GCRV_NORB || e.gcInfo.gcValue == gc.GCRV_DIALTONE) {
00223             // logger.info("Connected, but not really connected: " + jvr.symbolName("GCRV_",e.gcInfo.gcValue));
00224             disconnectCause = e.gcInfo.gcValue;
00225             gc.DropCall(crn,gc.GC_NORMAL_CLEARING,gc.EV_ASYNC);
00226             return true;
00227           }
00228         }
00229 
00230         // The call is "really" connected.
00231         connected = true;
00232         connectType = ((Integer)gc.GetCallInfo(crn,gc.CONNECT_TYPE)).intValue();
00233 
00234         if (connectType == 0) {
00235           // fall back to the original gcValue in the event; sigh.
00236           connectType = e.gcInfo.gcValue;
00237         }
00238 
00239         // When not running as ISDN, start ImmediateCAP to exhaust the pamdDelay
00240         // if this is a PAMD media detection.
00241         if (protocol == null || (!protocol.equals("ISDN"))) {
00242           if (connectType == gc.GCCT_PAMD) {
00243             // logger.info("Other");
00244             ic = new ImmediateCAP(dxdev,cap,pamdDelay);
00245             ic.jumpToPamdDelay = true;
00246             jvr.addEventListener(ic);
00247             ic.start();
00248           }
00249           else {
00250             // Not PAMD, so we are finished.
00251             finished();
00252           }
00253         }
00254       }
00255       else if (e.evttype == gc.GCEV_DISCONNECTED) {
00256         if (connected) { connected = false; /*logger.info("changing connect status to false");*/ }
00257         if (ic != null) { dx.stopch(dxdev,dx.EV_ASYNC); }
00258         // When the remote side hangs up (if at all), then a disconnect event will occur.
00259         // When that happens, we need to drop the call.  This is here because a caller
00260         // may drop while we are doing Call Analysis.
00261         //
00262         // OR
00263         //
00264         // Call was never completed in the first place (USER_BUSY, etc).
00265         //
00266         // Remember the gcValue.
00267         disconnectCause = e.gcInfo.gcValue;
00268         gc.DropCall(crn,gc.GC_NORMAL_CLEARING,gc.EV_ASYNC);
00269       }
00270       else if (e.evttype == gc.GCEV_DROPCALL) {
00271         // Call is dropped; now release all of the call resources.
00272         connected = false;
00273         gc.ReleaseCallEx(crn,gc.EV_ASYNC);
00274       }
00275       else if (e.evttype == gc.GCEV_RELEASECALL) {
00276         // Use case is finished.  Mark as done (so satisfy assertTrue() below) and exit.
00277         connected = false;
00278         finished();
00279       }
00280       /////////////////////////////
00281       // Errors, Errors, Errors. //
00282       /////////////////////////////
00283       else if (e.evttype == gc.GCEV_SETCONFIGDATA_FAIL) { 
00284         failed(e); 
00285       }
00286       else if (e.evttype == gc.GCEV_RESTARTFAIL) { 
00287         failed(e); 
00288       }
00289       else if (e.evttype == gc.GCEV_RELEASECALL_FAIL) { 
00290         failed(e); 
00291       }
00292       else if (e.evttype == gc.GCEV_TASKFAIL) { 
00293         failed(e); 
00294       }
00295       // else { logger.info("!!!!! NOT HANDLED !!!!!"); } // just for testing.
00296       return true;
00297     }
00298     catch (JVRException ex) {
00299       // We should not receive exceptions while handling events.
00300       // Default policy is to hang up any calls that are present.
00301       // Since we are inside the event handler there is no reason
00302       // to rethrow the exception - just mark ourselves as finished.
00303       logger.info("Exception while handling event: " + e);
00304       logger.throwing(getClass().getName(),"handleEvent",ex);
00305       if (crn != -1) {
00306         try {
00307           // TODO: don't use 'NORMAL_CLEARING'
00308           gc.DropCall(crn,gc.GC_NORMAL_CLEARING,gc.EV_SYNC);
00309         }
00310         catch (JVRException ignore) { logger.throwing(getClass().getName(),"handleEvent",ignore); }
00311         try {
00312           gc.ReleaseCallEx(crn,gc.EV_SYNC);
00313         }
00314         catch (JVRException ignore) { logger.throwing(getClass().getName(),"handleEvent",ignore); }
00315       }
00316       if (linedev != -1) {
00317         try {
00318           gc.ResetLineDev(linedev,gc.EV_SYNC);
00319         }
00320         catch (JVRException ignore) { logger.throwing(getClass().getName(),"handleEvent",ignore); }
00321         try {
00322           gc.WaitCall(linedev,0,gc.EV_ASYNC);
00323         }
00324         catch (JVRException ignore) { logger.throwing(getClass().getName(),"handleEvent",ignore); }
00325       }
00326       if (error == null) { error = ex; }
00327       else {
00328         logger.throwing(getClass().getName(),"handleEvent",ex); 
00329       }
00330       finished();
00331     }
00332     return true;
00333   }
00334 
00335   /**
00336    *
00337    */
00338   public void failed (Object o) throws JVRException {
00339     Throwable newError = null;
00340     if (o instanceof Throwable) {
00341       newError = new JVRException("Error during processing.",(Throwable)o);
00342     }
00343     else {
00344       newError = new JVRException("Error during processing: " + o);
00345     }
00346     if (error == null) { error = newError; }
00347     else {
00348       // Log the latest error but keep the original one.
00349       logger.throwing(getClass().getName(),"failed",newError);
00350     }
00351     finished();
00352   }
00353 
00354   public void finished() throws JVRException {
00355     finished(false);
00356   }
00357 
00358   /**
00359    * Called when the class decides that it is no longer responsible
00360    * for the line device and/or call reference number.
00361    */
00362   public void finished (boolean stopped) throws JVRException {
00363     // Mark ourselves as finished.
00364     if (finished) { return; }
00365     finished = true;
00366     if (ic != null) {
00367       jvr.removeEventListener(ic);
00368       try {
00369         dx.stopch(dxdev,dx.EV_ASYNC);
00370       }
00371       catch (JVRException e) { logger.throwing(getClass().getName(),"finished",e); }
00372     }
00373     /*
00374     if (protocol != null && (!protocol.equals("ISDN"))) {
00375       gc.Detach(linedev,dxdev,gc.EV_SYNC);
00376     }
00377     */
00378     // Notify any "waiters" that we are done.
00379     synchronized (this) { notifyAll(); }
00380     // Fire an event for asynchronous JVR event listeners.
00381     if (stopped) {
00382       new JVREvent(this,"stopped").fire();
00383     }
00384     else {
00385       new JVREvent(this,"finished").fire();
00386     }
00387   }
00388 
00389   /**
00390    * Synchronous function.
00391    * 
00392    * @param dxdev Voice device handle to use when doing CAP.
00393    * @param linedev Global call line device handle to use to make the call.
00394    * @param destination Destination phone number.
00395    * @param origin Origin (calling party number).
00396    * @param timeout Time in milliseconds before the call is aborted.
00397    * @param protocol The Global Call protocol that was used to open the line device.
00398    * @return The instance of GCOutbound that was used to make the call.
00399    */
00400   public static GCOutbound makeCall (final int dxdev, final long linedev, 
00401     final String destination, final String origin, final long timeout, final DX_CAP cap, final String protocol) throws JVRException {
00402 
00403     //
00404     // Class that listens for events on an instance of GCOutbound.
00405     // ASYNC/SYNC bridge.
00406     //
00407     class MyEventListener extends JVRMetaEventListener {
00408       /** Instance of GCOutbound that does that actual calling. */
00409       public GCOutbound gcob = null;
00410       /** True if the listener finishes normally. */
00411       public boolean finished = false;
00412       /** Handle events on "gcob". */
00413       public boolean handleEvent (JVREvent event) {
00414         try {
00415           if (event.target == this) {
00416             // This is the signal to begin the call.
00417             gcob = new GCOutbound(dxdev,linedev,destination,origin);
00418             gcob.protocol = protocol;
00419             if (cap != null) { gcob.cap = cap; }
00420             jvr.addEventListener(gcob);
00421             gcob.start();
00422           }
00423           else if (event.target == gcob) {
00424             // Is this the "finished" event?
00425             if ("finished".equals(event.data)) {
00426               // logger.info("Connect Type: " + jvr.symbolName("GCCT_",gcob.connectType));
00427               finished = true;
00428               jvr.removeEventListener(gcob);
00429               synchronized (this) { notifyAll(); }
00430             }
00431           }
00432         }
00433         catch (Throwable t) {
00434           jvr.removeEventListener(gcob);
00435           logger.throwing(getClass().getName(),"handleEvent",t);
00436         }
00437         return true;
00438       }
00439     }
00440 
00441     MyEventListener my = new MyEventListener();
00442     jvr.addEventListener(my);
00443     synchronized (my) {
00444       try {
00445         new JVREvent(my).fire();
00446         if (timeout <= 0) {
00447           my.wait(); 
00448         }
00449         else {
00450           my.wait(timeout); 
00451         }
00452       }
00453       catch (InterruptedException ignore) { }
00454     }
00455     jvr.removeEventListener(my);
00456 
00457     if (! my.finished) {
00458       // TODO: The GCOutbound instance may still be running. Perhaps the call exists
00459       // now.  Unfortunately the CRN is lost and there is no way to hang up the line!
00460       throw new JVRException("Timeout expired waiting for GCOutbound to finish.");
00461     }
00462 
00463     GCOutbound gcob = my.gcob;
00464     if (gcob.error != null) { throw new JVRException("GCOutbound raised an exception",gcob.error); }
00465     if (! gcob.connected) {
00466       // Globalcall never connected.  If CAP finished then use it as the exception detail.
00467       if (gcob.ic != null && gcob.ic.finished) {
00468         throw new JVRException("GCOutbound did not connect.  CAP resulted in " + jvr.symbolName("CR_",my.gcob.ic.cpTerm));
00469       }
00470       // CAP did not finish, so use the Globalcall disconnect cause.
00471       throw new JVRException("Not connected: " +jvr.symbolName("GCRV_",my.gcob.disconnectCause));
00472     }
00473 
00474     return gcob;
00475   }
00476 
00477   /**
00478    *
00479    */
00480   public String toString() {
00481     return 
00482       "GCOutbound{"+
00483       "linedev="+linedev+";"+
00484       "dxdev="+dxdev+";"+
00485       "crn="+crn+";"+
00486       "connected="+connected+";"+
00487       "destination="+destination+";"+
00488       "origin="+origin+";"+
00489       "protocol="+protocol+";"+
00490       "finished="+finished+";"+
00491       "connectType="+connectType+";"+
00492       "disconnectCause="+jvr.symbolName("GCRV_",disconnectCause)+";"+
00493       "ic="+ic+""+
00494       "}";
00495   }
00496 
00497 }

Generated on Sat Jul 16 03:47:35 2005 for JVR by doxygen 1.3.5