/*
* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the license at
* https://glassfish.dev.java.net/public/CDDLv1.0.html or
* glassfish/bootstrap/legal/CDDLv1.0.txt.
* See the License for the specific language governing
* permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at glassfish/bootstrap/legal/CDDLv1.0.txt.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* you own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Copyright (c) Ericsson AB, 2004-2007. All rights reserved.
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*/
package org.jvnet.glassfish.comms.sipagent.backend;
import org.jvnet.glassfish.comms.sipagent.model.MySipParser;
import org.jvnet.glassfish.comms.sipagent.model.ParserException;
import org.jvnet.glassfish.comms.sipagent.model.SipMessage;
import org.jvnet.glassfish.comms.sipagent.model.SipRequest;
import org.jvnet.glassfish.comms.sipagent.model.SipResponse;
import org.jvnet.glassfish.comms.sipagent.support.LogSupport;
import org.jvnet.glassfish.comms.sipagent.support.MessageTracer;
import org.jvnet.glassfish.comms.sipagent.model.ModelListener;
import org.jvnet.glassfish.comms.sipagent.support.Constants;
import org.jvnet.glassfish.comms.sipagent.transport.SocketHandler;
import org.jvnet.glassfish.comms.sipagent.transport.TCPSocketHandler;
import org.jvnet.glassfish.comms.sipagent.transport.TransportListener;
import org.jvnet.glassfish.comms.sipagent.transport.UDPSocketHandler;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author elnyvbo
*/
public class SocketController implements Controller, TransportListener {
private static Logger theirLogger =
Logger.getLogger("org.jvnet.glassfish.comms.sipagent.backend.SocketController");
private SocketHandler itsSocketHandler = null;
private List<ModelListener> itsListeners = null;
private Constants itsConstants = null;
/**
* Creates a new instance of SocketController
*/
public SocketController() {
}
public void init(Constants aConstants){
itsConstants = aConstants;
String host = itsConstants.getProperty(Constants.HOST);
String port = itsConstants.getProperty(Constants.PORT);
String localPort = itsConstants.getProperty(Constants.LOCAL_PORT);
int portNr = 5060;
int localPortNr = 6060;
try {
portNr = Integer.parseInt(port);
} catch (NumberFormatException nfe) {
theirLogger.log(Level.FINER,
"Illegal SIP server port specified in config, will use 5060",
nfe);
}
try {
localPortNr = Integer.parseInt(localPort);
} catch (NumberFormatException nfe) {
theirLogger.log(Level.FINER,
"Illegal local SIP port specified in config, will use 6060",
nfe);
}
theirLogger.fine(
"Connecting to host " + host + " on port " +port+ "...");
try {
InetAddress localHost = InetAddress.getLocalHost();
InetAddress remoteHost = InetAddress.getByName(host);
itsSocketHandler =
Constants.TCP.equals(itsConstants.getProperty(
Constants.TRANSPORT))
? new TCPSocketHandler(
localHost, localPortNr, remoteHost, portNr)
: new UDPSocketHandler(
localHost, localPortNr, remoteHost, portNr);
} catch (UnknownHostException e) {
theirLogger.severe(
"Unable to connect to server: " + LogSupport.stringify(e));
}
MessageTracer tracer = new MessageTracer();
itsSocketHandler.setTransportListenerList(
new Vector<TransportListener>());
itsSocketHandler.addListener(tracer);
itsSocketHandler.addListener(this);
// could add other listeners here e.g. for database storage
// logging to a file, or whatever.
// Prepare list of consumers
itsListeners = new Vector<ModelListener>();
new Thread(itsSocketHandler).start();
}
public SipRequest createRequest(){
SipRequest result = null;
try{
result = this.createRequest(itsConstants.dummyRequest());
}
catch (BackendException e){
// This would be a programmatic error as Constants should return
// a valid String! result will be null, and may result in a Null
// PointerException further down the chain. Log the error obviously...
theirLogger.log(Level.SEVERE,"PROGRAM ERROR ! ",e);
}
return result;
}
public SipRequest createRequest(String aBunchOfText)
throws BackendException {
SipRequest result;
try {
result = (SipRequest)(this.doParse(aBunchOfText));
}
catch (Throwable t){
throw new BackendException(
"Failed to create a SIP request based on the input text. ", t);
}
return result;
}
public SipResponse createResponse() {
throw new RuntimeException("NYI");
}
public SipResponse createResponse(String aBunchOfText)
throws BackendException {
SipResponse result;
try {
result = (SipResponse)(this.doParse(aBunchOfText));
}
catch (Throwable t){
throw new BackendException(
"Failed to create a SIP response based on the input text. ", t);
}
return result;
}
private SipMessage doParse(String aText) throws ParserException {
theirLogger.finer("Parsing text: " + aText);
ByteBuffer bb = ByteBuffer.wrap(aText.getBytes());
MySipParser p = MySipParser.getInstance();
// TODO apparently passing in a bunch of nulls works fine!
SipMessage result = p.parseMessage(null, bb, null, null, null);
theirLogger.finer("Created message: " + result);
// fix for parser 'bug' it either returns null or throws an exception...
if (result == null){
throw new ParserException(
"Resulting message after parsing was null");
}
return result;
}
public void sendMessage(SipMessage aMessage) {
try {
itsSocketHandler.sendMessage(aMessage);
} catch (Exception e) {
theirLogger.log(Level.SEVERE,
"Error sending message",e);
}
}
public void registerListener(ModelListener aListener) {
// the transport layer will fireMessageReceived(SipMessage) to
// whomever is intereseted. in this case it should be the ui.
// BUT:
// In case there's no GUI listening to model changes, controller could
// decide to buffer sent and received messages in a database or so.
// SO:
// controller registers once with the sockethandler, and several
// clients can register with the controller.
long tid = Thread.currentThread().getId();
theirLogger.fine(
"registering listener: " + aListener + "(thread " + tid + ", object " + this);
itsListeners.add(aListener);
}
public void close(){
theirLogger.finest("----- close ----->");
theirLogger.finest("Stopping socket handler...");
itsSocketHandler.stop();
theirLogger.finest("<----- close -----");
}
public void messageReceived(SipMessage sipmessage) {
long tid = Thread.currentThread().getId();
theirLogger.fine(
"messageReceived (thread " + tid + ", object " + this);
// TODO determine if this was a retransmission
for (ModelListener m : itsListeners) {
m.incomingMessage(sipmessage);
}
}
public void messageSent(SipMessage sipmessage) {
long tid = Thread.currentThread().getId();
theirLogger.fine(
"messageSent(thread " + tid + ", object " + this);
// clients might care: GUI should be updated when message was actually
// sent out.
for (ModelListener m : itsListeners) {
m.outgoingMessage(sipmessage);
}
}
}