Re: [Asterisk-java-users] Problems with concurrent java-threads answering incoming calls
Brought to you by:
srt
From: Johan S. <js...@sv...> - 2008-02-20 13:22:55
|
Hi again, hi Stefan, ! Sorry for the delay but I've been strangled by deadlines and boring stuff. Now posting my code, so our guru Stefan? might give some input on the problems I'm having. The Answer-class below.service will get called for 7-8 simultanious calls. So 7-8 threads working at the same time if I've understood this. In my code, each thread is doing AgiChannel-work, AsteriskManagerInterface-work, mysql-db-work, and playing/receiving dmtf-tones, and logging to one common logfile using log4J. All threads use the same logfile simultaneously too. Recap of my problem. When checking my outputlog (not included), I see, sometimes one thread have log entries of playing dtmf-tones, and starting the getDTMF-function. Then other threads are executing in between, maybe one more call is answered or hungup. Then the end of getDTMF-logentry is present (and no tones could be found on the channel). It works perfectly when only one or two threads are working. I suspected my cpu was overloaded with DTMF-detection, so I invested in the VPMADTO32 echo-cancelling/hw-dtmf-supported dsp-card for my analog 8ch TDM800P. But still the same problem. So dtmf-detection was not the issue I think. :( I'm posting extracts of my Agi-code in java, and hopefully it will show fatal errors stopping performance to you, dear gurus. I apologize for stripping things, so it will not compile, but I must remove company-private stuff before posting in public. Sincerely, Johan >From Sweden :) My code ============ ========================= ANSWER CLASS ========================= package com.test.johan.callManager; import javax.jms.*; import java.io.FileInputStream; import java.io.IOException; import java.sql.SQLException; import java.text.DateFormat; import java.util.Date; import java.util.InvalidPropertiesFormatException; import java.util.Iterator; import java.util.List; import java.util.Properties; import javax.jms.JMSException; import org.apache.log4j.Logger; import org.asteriskjava.fastagi.AgiChannel; import org.asteriskjava.fastagi.AgiException; import org.asteriskjava.fastagi.AgiRequest; import org.asteriskjava.fastagi.BaseAgiScript; import org.asteriskjava.live.AsteriskChannel; import org.asteriskjava.live.ChannelState; import org.asteriskjava.live.DefaultAsteriskServer; import org.asteriskjava.live.HangupCause; import org.asteriskjava.live.ManagerCommunicationException; import org.asteriskjava.live.NoSuchChannelException; public class Answer extends BaseAgiScript { private static enum AnswerState {ALARM_INIT, ALARM_MSG_REQUEST, ALARM_GET_DTMFTONES, ALARM_CHECK_IF_LASTMESSAGE, ALARM_VERIFYCHECKSUM_AND_RESPOND, ALARM_HANDLE_MESSAGETYPE, ALARM_RETRY_BROKEN_MESSAGE, ALARM_PAUS_BEFORE_NEXTMESSAGE, ALARM_ENDOFREQUESTS, ALARM_NODTMF_ERROR}; private DefaultAsteriskServer asteriskServer; private DbApi dbApi; private static String propertyFile; private static Properties prop = new Properties(); private static final Logger logger = Logger.getLogger(Answer.class); private static String answerInQueName; private static String answerOutQueName; private static String jmsUrl; private static String dbUrl; private static String dbUser; private static String dbPasswd; private static String asteriskUrl; private static String asteriskUser; private static String asteriskPasswd; private static int timeOutInSeconds; private static int PAUS_BETWEEN_DTMF_MESS = 500; public Answer() throws InvalidPropertiesFormatException, IOException { propertyFile = System.getProperty("com.johan.test.callManager.configFile"); if (propertyFile == null) { propertyFile = "callManager.xml"; } FileInputStream fis; fis = new FileInputStream(propertyFile); prop.loadFromXML(fis); //Read in all the properties from the file: answerInQueName = prop.getProperty("Answer.AnswerInQueName"); answerOutQueName = prop.getProperty("Answer.AnswerOutQueName"); jmsUrl = prop.getProperty("Answer.jmsUrl"); dbUrl = prop.getProperty("General.dbUrl"); dbUser = prop.getProperty("General.dbUser"); dbPasswd = prop.getProperty("General.dbPasswd"); asteriskUrl = prop.getProperty("General.asteriskUrl"); asteriskUser = prop.getProperty("General.asteriskUser"); asteriskPasswd = prop.getProperty("General.asteriskPasswd"); timeOutInSeconds = Integer.parseInt(prop.getProperty("Answer.timeOutInSeconds")); numberOfRequestPerProtocol = Integer.parseInt(prop.getProperty("Answer.numberOfRequestPerProtocol")); dbApi = new DbApi(dbUrl, dbUser, dbPasswd); asteriskServer = new DefaultAsteriskServer(asteriskUrl, asteriskUser, asteriskPasswd); fis.close(); } public void service(AgiRequest request, AgiChannel agiChannel){ // Thread.currentThread().setPriority(1); JmsHandler jmsHandler = null; AsteriskChannel incomingChannel = null; Date timeOfAlarmCall = new Date(); GenericMessage incomingMessage = null; int lineNumber = -1; boolean keepRequesting = true; String dtmfIncomingMessage=""; AnswerState state = AnswerState.ALARM_INIT; AlarmSwitchMessageType messageType = null; // ######################################################################## // MAIN WHILE while (keepRequesting && channelIsUp) { switch(state) { case ALARM_INIT: { logger.debug("ALARM_INIT"); try { logger.debug("Creating JmsHandler"); jmsHandler = new JmsHandler(jmsUrl, answerInQueName, answerOutQueName); agiChannel.answer(); String incomingChannelName = agiChannel.getName(); timeOfAlarmCall = new Date(); logger.debug("Answered incoming call on channel: " + incomingChannelName + " on date: " + DateFormat.getDateInstance().format(timeOfAlarmCall)); // Wait a little, so channel is setup, before we send DTMF-tones. Thread.sleep(500); //We use incomingChannel instead of the incoming agiChannel because it supports redirect and playDtmf. //In theory this could have been done by passing AgiActions to the channel. incomingChannel = CallManager.getChannelFromAsteriskServer(incomingChannelName, asteriskServer); state = AnswerState.ALARM_MSG_REQUEST; } catch (SQLException se) { state = AnswerState.ALARM_ENDOFREQUESTS; // eller init-error... } catch(TestException se) { state =AnswerState.ALARM_ENDOFREQUESTS; // eller init-error... } catch (InterruptedException ie) { state =AnswerState.ALARM_ENDOFREQUESTS; // eller init-error... }catch(ManagerCommunicationException mce) { state =AnswerState.ALARM_ENDOFREQUESTS; // eller init-error... } catch(AgiException ae) { state = AnswerState.ALARM_ENDOFREQUESTS; // eller init-error... } catch( JMSException je) { state =AnswerState.ALARM_ENDOFREQUESTS; // eller init-error... } }break; case ALARM_MSG_REQUEST: { logger.debug("ALARM_MSG_REQUEST"); // send dtmf-tones and wait for the response dtmf-tones try { // Thread.currentThread().setPriority(10); playDtmf(TestProtocol.MESSAGE_REQUEST, incomingChannel); state = AnswerState.ALARM_GET_DTMFTONES; } catch (ManagerCommunicationException mce) { logger.fatal("ERROR "+mce.getMessage()); state = AnswerState.ALARM_ENDOFREQUESTS; } catch(TestException se) { logger.fatal("ERROR "+se.getMessage()); state = AnswerState.ALARM_ENDOFREQUESTS; } } break; case ALARM_GET_DTMFTONES: { logger.debug("ALARM_GET_DTMFTONES"); dtmfIncomingMessage = ""; try { dtmfIncomingMessage = CallManager.getDtmfMessage(agiChannel); logger.debug("DTMF->PC: " + dtmfIncomingMessage); if(dtmfIncomingMessage.length()>0) state = AnswerState.ALARM_ENDOFREQUESTS; else { // Nothing received statusMessageRetries++; logger.fatal("ERROR No DTMF-received! "); state = AnswerState.ALARM_ENDOFREQUESTS; } } catch(AgiException ae) { logger.fatal("ERROR "+ae.getMessage()); state = AnswerState.ALARM_ENDOFREQUESTS; } catch(TestException se) { logger.fatal("ERROR "+se.getMessage()); state = AnswerState.ALARM_ENDOFREQUESTS; } } break; case ALARM_ENDOFREQUESTS: { // Thread.currentThread().setPriority(1); logger.debug("ALARM_ENDOFREQUESTS"); agiChannel = null; incomingChannel = null; // Close jms, release resources. This is not happening if we get an agiexception due to code right now jmsHandler.cleanUpConnection(); }break; } // end switch(state) } // end while } } ========================= ========================= ========================= CLASS CALLED CALLMANAGER ========================= ========================= ========================= package com.johan.test.callManager; import org.apache.log4j.Logger; import org.asteriskjava.fastagi.AgiChannel; import org.asteriskjava.fastagi.AgiException; import org.asteriskjava.live.AsteriskChannel; import org.asteriskjava.live.AsteriskServer; import org.asteriskjava.live.HangupCause; import org.asteriskjava.live.ManagerCommunicationException; import org.asteriskjava.live.NoSuchChannelException; public class CallManager { private static final Logger logger = Logger.getLogger(CallManager.class); private static final int requestTimeOutInSeconds = 5; private static final int timeOutForEachDigitInMilliSeconds = 500; private static final int timeBetweenTonesInMs = 140; private static final int lengthOfDTMFTone = 140; private static enum dtmfState {DTMF_INIT, DTMF_WAIT_CHAR, DTMF_STORE_CHAR, DTMF_FINISHED }; public CallManager () { } protected static AsteriskChannel getChannelFromAsteriskServer (String channelName, AsteriskServer asteriskServer) throws ManagerCommunicationException { AsteriskChannel channel = null; for (AsteriskChannel asteriskChannel : asteriskServer.getChannels()) { // Checks if the name matches the incoming string: if ((asteriskChannel.getName().compareTo(channelName) == 0)) { channel = asteriskChannel; } } return channel; } protected static void playDtmf(String dtmfString, AsteriskChannel channel) throws ManagerCommunicationException, IllegalArgumentException, TestException { String sentString[] = new String[50]; logger.debug("PC->DTMF:" + dtmfString); int length = dtmfString.length(); for (int j = 0; j < length; j++) { try { channel.playDtmf(dtmfString.substring(j, j + 1)); sentString[timerIndex] = dtmfString.substring(j, j + 1); } catch (NoSuchChannelException e) { // throw new TestException("Channel disconnected while playing Dtmf!"); } try { Thread.sleep(timeBetweenTonesInMs); } catch (InterruptedException e) { // logger.fatal(e.getMessage()); } } } protected static String getDtmfMessage(AgiChannel agiChannel) throws AgiException, TestException { // logger.debug("Starting getDtmfMessage()"); long timerValue[] = new long[50]; long timerValueBefore[] = new long[50]; char receivedValue[] = new char[50]; int timerIndex = 0; int cyclesBeforeTimeout = (1000 * requestTimeOutInSeconds) / timeOutForEachDigitInMilliSeconds; int i = 0; String message = ""; char[] charBuf = new char[150]; int charPos=0; char newChar = 0; long startTime = 0; boolean running = true; dtmfState state = dtmfState.DTMF_INIT; int waitDelay = 1000; while(running) { switch(state) { case DTMF_INIT: { startTime = System.currentTimeMillis(); waitDelay = 10000; state = dtmfState.DTMF_WAIT_CHAR; logger.debug("Get-DTMF start"); } break; case DTMF_WAIT_CHAR: { if(charPos>64) state = dtmfState.DTMF_FINISHED; try{ if (agiChannel.getChannelStatus() == AlarmSwitch.AGI_CHANNEL_STATE_UP) { newChar = agiChannel.waitForDigit(waitDelay); if(newChar>0) state = dtmfState.DTMF_STORE_CHAR; else { // logger.debug("No more DTMF-tones, timeout!"); state = dtmfState.DTMF_FINISHED; } } else { throw new TestException("Channel disconnected while waiting for digits!"); } } catch(AgiException ae) { // logger.fatal("ERROR " + ae.getMessage()); state = dtmfState.DTMF_FINISHED; } } break; case DTMF_STORE_CHAR: { message = message + String.valueOf(newChar); //charBuf[charPos] = newChar; //charPos++; waitDelay = 400; state = dtmfState.DTMF_WAIT_CHAR; }break; case DTMF_FINISHED: { logger.debug("Get-DTMF done"); running = false; }break; } } // message = new String(charBuf); return message; } } -----Ursprungligt meddelande----- Från: ast...@li... [mailto:ast...@li...] För Stefan Reuter Skickat: den 14 februari 2008 16:32 Till: ast...@li... Ämne: [SPAM] - Re: [Asterisk-java-users] Problems with concurrent java-threads answering incoming calls - Email found in subject Johan Sandgren wrote: > What about my problem, any clever ideas? would you mind to post your code? =Stefan -- reuter network consulting Neusser Str. 110 50760 Koeln Germany Telefon: +49 221 1305699-0 Telefax: +49 221 1305699-90 E-Mail: ste...@re... Jabber: ste...@re... WWW: http://www.reucon.com Steuernummern 215/5140/1791 USt-IdNr. DE220701760 |