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

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

Go to the documentation of this file.
00001 package net.threebit.jvr.util;
00002 
00003 /*
00004  * $Id: ImmediateCAP.java,v 1.10 2004/07/02 18:11:38 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  * Use this class to begin immediate Call Progress and Analysis
00029  * on a voice resource by dialing an empty string with CAP enabled.
00030  * Usefull as a followup to ISDNOutbound to determine what type of
00031  * connection was made.
00032  */
00033 
00034 public class ImmediateCAP extends JVRMetaEventListener {
00035 
00036   public boolean connected = false;
00037 
00038   /**
00039    * When true at start time, we have been asked to just 
00040    * wait for the pamd silence to elapse. Hack!
00041    */
00042   public boolean jumpToPamdDelay = false;
00043 
00044   /** Voice resource. */
00045   private int dxDev = -1;
00046 
00047   /** DX_CAP settings used during dialing. */
00048   private DX_CAP cap = null;
00049 
00050   /** Set to true until this class is ready to start processing. */
00051   private boolean notReady = true;
00052 
00053   /** 
00054    * Set to true when this class is finished handling requests.
00055    * Necessary because the class may still be registered as an
00056    * event listener.
00057    */
00058   public boolean finished = false;
00059 
00060   /**
00061    *
00062    */
00063   public boolean stopped = false;
00064 
00065   /** Saved exception if one happens during dialing. */
00066   private Throwable failureException = null;
00067 
00068   /** dx.dial termination cause. */
00069   public long cpTerm = -1;
00070 
00071   /** type of connection. */
00072   public long connType = -1;
00073 
00074   /** Milliseconds of silence after dx.dial() returns CON_PAMD. */
00075   private int pamdDelay = 0;
00076   private long pamdDelayStarted = 0;
00077 
00078   /** True when silence is on. */
00079   private boolean silon = false;
00080 
00081   /** Time when silence started. */
00082   private long siltime = -1;
00083 
00084   /**
00085    * @param dxDev The voice resource to use for call analysis.
00086    * @param cap Call Analysis parameters to pass to {@link dx#dial}.
00087    * @param pamdDelay If PAMD is detected, this class will wait until
00088    *  this many milliseconds of silence has happenen before reporting
00089    *  PAMD.  Ie: wait for the answering machine message to finish
00090    *  playing.
00091    */
00092   public ImmediateCAP (int dxDev, DX_CAP cap, int pamdDelay) {
00093     this.dxDev = dxDev;
00094     this.cap = cap;
00095     this.pamdDelay = pamdDelay;
00096   }
00097 
00098   /**
00099    * After initialization, call this method to start dialing/cap.
00100    */
00101   public void start() throws JVRException {
00102     notReady = false;
00103     if (jumpToPamdDelay) {
00104       // implied state.
00105       cpTerm = dx.CR_CNCT;
00106       connType = dx.CON_PAMD;
00107       connected = true;
00108       startPamdDelayThread();
00109     }
00110     else {
00111       dx.dial(dxDev,"",cap,dx.EV_ASYNC|dx.DX_CALLP);
00112     }
00113   }
00114 
00115   /**
00116    *
00117    */
00118   private void startPamdDelayThread () throws JVRException {
00119     // start the silence detection.
00120     dx.setevtmsk(dxDev,dx.DM_SILOF|dx.DM_SILON);
00121     pamdDelayStarted = System.currentTimeMillis();
00122     Thread t = new Thread() {
00123       public void run() {
00124         try {
00125           while (true) {
00126             synchronized (ImmediateCAP.this) {
00127               if (finished) { return; }
00128               // TODO: make the maximum amount of time configurable.
00129               if ((System.currentTimeMillis()-pamdDelayStarted) >= (120*1000)) {
00130                 logger.info("WARNING: running for too long during PAMD silence delay.");
00131                 finished();
00132                 return;
00133               }
00134               if (silon) {
00135                 if ((System.currentTimeMillis()-siltime) >= pamdDelay) {
00136                   // The required amount of silence has been satisfied.
00137                   finished();
00138                   return;
00139                 }
00140                 logger.info("pamdDelay: " + pamdDelay + "; need another " + (System.currentTimeMillis()-siltime));
00141               }
00142             }
00143             try {
00144               sleep(200);
00145             } catch (Exception ignore) { }
00146           }
00147         }
00148         catch (Exception e) {
00149           logger.throwing(getClass().getName(),"run",e);
00150         }
00151       }
00152     };
00153     t.start();
00154   }
00155 
00156   /**
00157    * Handler for events on the linedev.  All other events are ignored.  All events
00158    * are ignored until this components {@link #start} method is called.
00159    */
00160   public boolean handleEvent (MetaEvent e) throws JVRException {
00161     synchronized (this) {
00162       if (notReady) { return true; }
00163       if (finished) { return true; }
00164       if (e.isGC()) { return true; }
00165       if (e.evtdev != dxDev) { return true; }
00166       try {
00167         if (e.evttype == dx.TDX_CST) {
00168           DX_CST cst = (DX_CST) e.evtdata;
00169           if (cst.cst_event == dx.DE_SILOFF) {
00170             silon = false;
00171           }
00172           else if (cst.cst_event == dx.DE_SILON) {
00173             silon = true;
00174             siltime = System.currentTimeMillis();
00175           }
00176           return true;
00177         }
00178         if (e.evttype == dx.TDX_CALLP) {
00179           // Call analysis has finished.
00180           cpTerm = dx.ATDX_CPTERM((int)e.evtdev);
00181           if (cpTerm == dx.CR_CNCT) {
00182             connected = true;
00183             connType = dx.ATDX_CONNTYPE((int)e.evtdev);
00184             logger.info("Connected due to: " + jvr.symbolName("CON_",connType));
00185             if (connType == dx.CON_PAMD) {
00186               // Started the thread that will wait for the configured amount
00187               // of silence to elapse before returning CON_PAMD.  This is required
00188               // because CON_PAMD can be reported before the end of the answering
00189               // machine greeting.
00190               logger.info("CON_PAMD");
00191               startPamdDelayThread();
00192               return true;
00193             }
00194           }
00195           else {
00196             logger.info("Not connected due to: " + jvr.symbolName("CR_",cpTerm));
00197             if (cpTerm == dx.CR_STOPD) {
00198               // Remember that call analysis did not completed.
00199               stopped = true;
00200             }
00201           }
00202           finished();
00203           return true;
00204         }
00205       }
00206       catch (JVRException ex) {
00207         failureException = ex;
00208         logger.throwing(getClass().getName(),"handleEvent",ex);
00209         finished();
00210       }
00211     }
00212     return true;
00213   }
00214 
00215   /**
00216    * Called when the class decides that it is no longer responsible
00217    * for the line device and/or call reference number.
00218    */
00219   public void finished() throws JVRException {
00220     // Mark ourselves as finished.
00221     if (finished) { logger.info("Called finished() twice.  Ignoring second request."); return; }
00222     finished = true;
00223     // SILON / SILOFF seems to have the side effect of
00224     // cancelling the dx_playiottdata(ASYNC) function.
00225     // Ergo, "unset" all events.
00226     dx.setevtmsk(dxDev,dx.DM_DIGITS);
00227     dx.setevtmsk(dxDev,dx.DM_DIGOFF);
00228     // dx.setevtmsk(dxDev,dx.DM_SILOF|dx.DM_SILON);
00229     // Notify any "waiters" that we are done.
00230     synchronized (this) { notifyAll(); }
00231     // Fire an event for asynchronous JVR event listeners.
00232     new JVREvent(this).fire();
00233     // For debugging only.
00234     logger.info("(FINISHED)\n" + this);
00235   }
00236 
00237   /**
00238    *
00239    */
00240   public String toString() {
00241     return "ImmediateCAP{"+
00242       "finished="+finished+";"+
00243       "connected="+connected+";"+
00244       "stopped="+stopped+";"+
00245       "cpTerm="+cpTerm+";"+
00246       "connType="+connType+";"+
00247       "jumpToPamdDelay="+jumpToPamdDelay+";"+
00248       "dxDev="+dxDev+";"+
00249       "notReady="+notReady+";"+
00250       "pamdDelay="+pamdDelay+";"+
00251       "pamdDelayStarted="+pamdDelayStarted+";"+
00252       "silon="+silon+";"+
00253       "siltime="+siltime+""+
00254       "}";
00255   }
00256 
00257 }

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