gov.nist.javax.sip.stack.sctp.SCTPMessageProcessor Maven / Gradle / Ivy
package gov.nist.javax.sip.stack.sctp;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import com.sun.nio.sctp.SctpChannel;
import com.sun.nio.sctp.SctpServerChannel;
import gov.nist.core.HostPort;
import gov.nist.javax.sip.stack.MessageChannel;
import gov.nist.javax.sip.stack.MessageProcessor;
import gov.nist.javax.sip.stack.SIPTransactionStack;
/**
* SCTP Message Processor
*
* @author Jeroen van Bemmel
*/
public final class SCTPMessageProcessor extends MessageProcessor implements Runnable {
private SctpServerChannel sctpServerChannel;
private Selector selector;
private SelectionKey key;
private boolean isRunning, doClose;
private final Set channels
= new ConcurrentSkipListSet();
/**
* Constructor, called via Class.newInstance() by SIPTransactionStack
*/
public SCTPMessageProcessor() {
super( "sctp" );
}
Selector getSelector() { return selector; }
SelectionKey registerChannel( SCTPMessageChannel c, SctpChannel channel )
throws ClosedChannelException {
synchronized (this) {
selector.wakeup();
return channel.register( selector, SelectionKey.OP_READ, c );
}
}
@Override
public MessageChannel createMessageChannel(HostPort targetHostPort)
throws IOException {
return this.createMessageChannel( targetHostPort.getInetAddress(), targetHostPort.getPort() );
}
@Override
public MessageChannel createMessageChannel(InetAddress targetHost, int port)
throws IOException {
SCTPMessageChannel c = new SCTPMessageChannel( this,
new InetSocketAddress(targetHost,port) );
channels.add( c );
return c;
}
@Override
public int getDefaultTargetPort() {
return 5060; // same as UDP and TCP
}
@Override
public int getMaximumMessageSize() {
return Integer.MAX_VALUE;
}
@Override
public SIPTransactionStack getSIPStack() {
return sipStack;
}
@Override
public boolean inUse() {
return isRunning;
}
@Override
public boolean isSecure() {
return false;
}
public void run() {
try {
do {
int n = selector.select();
if (n>0) {
Iterator i = selector.selectedKeys().iterator();
while ( i.hasNext() ) {
SelectionKey key = i.next();
i.remove();
if ( key.isReadable() ) {
SCTPMessageChannel channel = (SCTPMessageChannel) key.attachment();
channel.readMessages();
} else if (key.isAcceptable()) {
SctpChannel ch = sctpServerChannel.accept();
SCTPMessageChannel c = new SCTPMessageChannel( this, ch );
channels.add( c );
}
}
}
synchronized (this) {
if (doClose) {
selector.close();
return;
}
}
} while ( selector.isOpen() );
} catch (IOException ioe) {
ioe.printStackTrace();
try {
selector.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
this.stop();
}
}
}
@Override
public void start() throws IOException {
this.sctpServerChannel = SctpServerChannel.open();
sctpServerChannel.bind( new InetSocketAddress(this.getIpAddress(),this.getPort()) );
sctpServerChannel.configureBlocking( false );
this.selector = Selector.open();
this.key = sctpServerChannel.register( selector, SelectionKey.OP_ACCEPT );
// Start a daemon thread to handle reception
this.isRunning = true;
Thread thread = new Thread(this);
thread.setDaemon(true);
thread.setName("SCTPMessageProcessorThread");
thread.setPriority(Thread.MAX_PRIORITY);
thread.start();
}
@Override
public void stop() {
this.isRunning = false;
this.doClose = true;
for ( SCTPMessageChannel c : channels ) {
c.closeNoRemove(); // avoids call to removeChannel -> ConcurrentModification
}
channels.clear();
try {
key.cancel();
sctpServerChannel.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
synchronized (this) {
selector.wakeup();
}
}
}
void removeChannel(SCTPMessageChannel messageChannel) {
channels.remove( messageChannel );
}
}