* input - dealing with things like line continuation
*/
public void run() {
Pipeline inputStream = this.rawInputStream;
final StackLogger stackLogger = sipStack.getStackLogger();
// inputStream = new MyFilterInputStream(this.rawInputStream);
// I cannot use buffered reader here because we may need to switch
// encodings to read the message body.
try {
while (true) {
this.sizeCounter = this.maxMessageSize;
// this.messageSize = 0;
StringBuilder inputBuffer = new StringBuilder();
if (Debug.parserDebug)
Debug.println("Starting parse!");
String line1;
String line2 = null;
while (true) {
try {
line1 = readLine(inputStream);
// ignore blank lines.
if (line1.equals("\n")) {
if (Debug.parserDebug) {
Debug.println("Discarding blank line. ");
}
continue;
} else
break;
} catch (IOException ex) {
if(postParseExecutor != null){
synchronized (messagesOrderingMap) {
try {
messagesOrderingMap.wait();
} catch (InterruptedException e) {}
}
}
Debug.printStackTrace(ex);
this.rawInputStream.stopTimer();
return;
}
}
inputBuffer.append(line1);
// Guard against bad guys.
this.rawInputStream.startTimer();
Debug.println("Reading Input Stream");
while (true) {
try {
line2 = readLine(inputStream);
inputBuffer.append(line2);
if (line2.trim().equals(""))
break;
} catch (IOException ex) {
if(postParseExecutor != null){
synchronized (messagesOrderingMap) {
try {
messagesOrderingMap.wait();
} catch (InterruptedException e) {}
}
}
this.rawInputStream.stopTimer();
Debug.printStackTrace(ex);
return;
}
}
// Stop the timer that will kill the read.
this.rawInputStream.stopTimer();
inputBuffer.append(line2);
// smp.setParseExceptionListener(sipMessageListener);
// smp.setReadBody(false);
SIPMessage sipMessage = null;
try {
if (stackLogger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
stackLogger.logDebug("About to parse : " + inputBuffer.toString());
}
sipMessage = smp.parseSIPMessage(inputBuffer.toString().getBytes(), false, false, sipMessageListener);
if (sipMessage == null) {
this.rawInputStream.stopTimer();
continue;
}
} catch (ParseException ex) {
// Just ignore the parse exception.
stackLogger.logError("Detected a parse error", ex);
continue;
}
if (Debug.debug) {
Debug.println("Completed parsing message");
}
ContentLength cl = (ContentLength) sipMessage
.getContentLength();
int contentLength = 0;
if (cl != null) {
contentLength = cl.getContentLength();
} else {
contentLength = 0;
}
if (Debug.debug) {
Debug.println("contentLength " + contentLength);
}
if (contentLength == 0) {
sipMessage.removeContent();
} else if (maxMessageSize == 0
|| contentLength < this.sizeCounter) {
byte[] message_body = new byte[contentLength];
int nread = 0;
while (nread < contentLength) {
// Start my starvation timer.
// This ensures that the other end
// writes at least some data in
// or we will close the pipe from
// him. This prevents DOS attack
// that takes up all our connections.
this.rawInputStream.startTimer();
try {
int readlength = inputStream.read(message_body,
nread, contentLength - nread);
if (readlength > 0) {
nread += readlength;
} else {
break;
}
} catch (IOException ex) {
stackLogger.logError("Exception Reading Content",ex);
break;
} finally {
// Stop my starvation timer.
this.rawInputStream.stopTimer();
}
}
sipMessage.setMessageContent(message_body);
}
// Content length too large - process the message and
// return error from there.
if (sipMessageListener != null) {
try {
if(postParseExecutor == null) {
/**
* If gov.nist.javax.sip.TCP_POST_PARSING_THREAD_POOL_SIZE is disabled
* we continue with the old logic here.
*/
if(sipStack.sipEventInterceptor != null) {
sipStack.sipEventInterceptor.beforeMessage(sipMessage);
}
sipMessageListener.processMessage(sipMessage);
if(sipStack.sipEventInterceptor != null) {
sipStack.sipEventInterceptor.afterMessage(sipMessage);
}
} else {
/**
* gov.nist.javax.sip.TCP_POST_PARSING_THREAD_POOL_SIZE is enabled so
* we use the threadpool to execute the task.
*/
// we need to guarantee message ordering on the same socket on TCP
// so we lock and queue of messages per Call Id
final String callId = sipMessage.getCallId().getCallId();
// http://dmy999.com/article/34/correct-use-of-concurrenthashmap
CallIDOrderingStructure orderingStructure = messagesOrderingMap.get(callId);
if(orderingStructure == null) {
CallIDOrderingStructure newCallIDOrderingStructure = new CallIDOrderingStructure();
orderingStructure = messagesOrderingMap.putIfAbsent(callId, newCallIDOrderingStructure);
if(orderingStructure == null) {
orderingStructure = newCallIDOrderingStructure;
if (stackLogger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
stackLogger.logDebug("new CallIDOrderingStructure added for message " + sipMessage);
}
}
}
final CallIDOrderingStructure callIDOrderingStructure = orderingStructure;
// we add the message to the pending queue of messages to be processed for that call id here
// to avoid blocking other messages with a different call id
// that could be processed in parallel
callIDOrderingStructure.getMessagesForCallID().offer(sipMessage);
Thread messageDispatchTask = new Thread() {
@Override
public void run() {
// we acquire it in the thread to avoid blocking other messages with a different call id
// that could be processed in parallel
Semaphore semaphore = callIDOrderingStructure.getSemaphore();
final Queue<SIPMessage> messagesForCallID = callIDOrderingStructure.getMessagesForCallID();
if(sipStack.sipEventInterceptor != null) {
sipStack.sipEventInterceptor.beforeMessage(messagesForCallID.peek());
}
try {
semaphore.acquire();
} catch (InterruptedException e) {
stackLogger.logError("Semaphore acquisition for callId " + callId + " interrupted", e);
}
// once acquired we get the first message to process
SIPMessage message = messagesForCallID.poll();
if (stackLogger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
stackLogger.logDebug("semaphore acquired for message " + message);
}
try {
sipMessageListener.processMessage(message);
} catch (Exception e) {
stackLogger.logError("Error occured processing message", e);
// We do not break the TCP connection because other calls use the same socket here
} finally {
if(callIDOrderingStructure.getMessagesForCallID().size() <= 0) {
messagesOrderingMap.remove(callId);
if (stackLogger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
stackLogger.logDebug("CallIDOrderingStructure removed for message " + callId);
}
}
if (stackLogger.isLoggingEnabled(StackLogger.TRACE_DEBUG)) {
stackLogger.logDebug("releasing semaphore for message " + message);
}
//release the semaphore so that another thread can process another message from the call id queue in the correct order
// or a new message from another call id queue
semaphore.release();
if(messagesOrderingMap.isEmpty()) {