/* * Copyright (C) 2005 Luca Veltri - University of Parma - Italy * * This file is part of MjSip (http://www.mjsip.org) * * MjSip is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * MjSip is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with MjSip; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author(s): * Luca Veltri (luca.veltri@unipr.it) */ package org.zoolu.sip.transaction; import org.zoolu.sip.address.SipURL; import org.zoolu.sip.provider.*; import org.zoolu.sip.message.*; import org.zoolu.tools.Timer; import org.zoolu.tools.TimerListener; import org.zoolu.tools.LogLevel; /** Generic client transaction as defined in RFC 3261 (Section 17.1.2). * A TransactionClient is responsable to create a new SIP transaction, starting with a request message sent through the SipProvider and ending with a final response.<BR> * The changes of the internal status and the received messages are fired to the TransactionListener passed to the TransactionClient object.<BR> */ public class TransactionClient extends Transaction { /** the TransactionClientListener that captures the events fired by the TransactionClient */ TransactionClientListener transaction_listener; /** retransmission timeout ("Timer E" in RFC 3261) */ Timer retransmission_to; /** transaction timeout ("Timer F" in RFC 3261) */ Timer transaction_to; /** clearing timeout ("Timer K" in RFC 3261)*/ Timer clearing_to; /** Costructs a new TransactionClient. */ protected TransactionClient(SipProvider sip_provider) { super(sip_provider); transaction_listener=null; } /** Creates a new TransactionClient */ public TransactionClient(SipProvider sip_provider, Message req, TransactionClientListener listener) { super(sip_provider); request=new Message(req); init(listener,request.getTransactionId()); } /** Initializes timeouts and listener. */ void init(TransactionClientListener listener, TransactionIdentifier transaction_id) { this.transaction_listener=listener; this.transaction_id=transaction_id; retransmission_to=new Timer(SipStack.retransmission_timeout,"Retransmission",this); transaction_to=new Timer(SipStack.transaction_timeout,"Transaction",this); clearing_to=new Timer(SipStack.clearing_timeout,"Clearing",this); printLog("id: "+String.valueOf(transaction_id),LogLevel.HIGH); printLog("created",LogLevel.HIGH); } /** Starts the TransactionClient and sends the transaction request. */ public void request() { printLog("start",LogLevel.LOW); changeStatus(STATE_TRYING); retransmission_to.start(); transaction_to.start(); sip_provider.addSipProviderListener(transaction_id,this); connection_id=sip_provider.sendMessage(request); } /** Method derived from interface SipListener. * It's fired from the SipProvider when a new message is received for to the present TransactionClient. */ public void onReceivedMessage(SipProvider provider, Message msg) { if (msg.isResponse()) { int code=msg.getStatusLine().getCode(); if (code>=100 && code<200 && (statusIs(STATE_TRYING) || statusIs(STATE_PROCEEDING))) { if (statusIs(STATE_TRYING)) changeStatus(STATE_PROCEEDING); if (transaction_listener!=null) transaction_listener.onTransProvisionalResponse(this,msg); return; } if (code>=200 && code<700 && (statusIs(STATE_TRYING) || statusIs(STATE_PROCEEDING))) { retransmission_to.halt(); transaction_to.halt(); changeStatus(STATE_COMPLETED); if (code<300) { if (transaction_listener!=null) transaction_listener.onTransSuccessResponse(this,msg); } else { if (transaction_listener!=null) { transaction_listener.onTransFailureResponse(this,msg); } } transaction_listener=null; if (connection_id==null) clearing_to.start(); else { printLog("clearing_to=0 for reliable transport",LogLevel.LOW); onTimeout(clearing_to); } return; } } } /** Method derived from interface TimerListener. * It's fired from an active Timer. */ public void onTimeout(Timer to) { try { if (to.equals(retransmission_to) && (statusIs(STATE_TRYING) || statusIs(STATE_PROCEEDING))) { printLog("Retransmission timeout expired",LogLevel.HIGH); // retransmission only for unreliable transport if (connection_id==null) { sip_provider.sendMessage(request); long timeout=2*retransmission_to.getTime(); if (timeout>SipStack.max_retransmission_timeout || statusIs(STATE_PROCEEDING)) timeout=SipStack.max_retransmission_timeout; retransmission_to=new Timer(timeout,retransmission_to.getLabel(),this); retransmission_to.start(); } else printLog("No retransmissions for reliable transport ("+connection_id+")",LogLevel.LOW); } if (to.equals(transaction_to)) { printLog("Transaction timeout expired",LogLevel.HIGH); retransmission_to.halt(); clearing_to.halt(); sip_provider.removeSipProviderListener(transaction_id); changeStatus(STATE_TERMINATED); if (transaction_listener!=null) transaction_listener.onTransTimeout(this); transaction_listener=null; } if (to.equals(clearing_to)) { printLog("Clearing timeout expired",LogLevel.HIGH); retransmission_to.halt(); transaction_to.halt(); sip_provider.removeSipProviderListener(transaction_id); changeStatus(STATE_TERMINATED); } } catch (Exception e) { printException(e,LogLevel.HIGH); } } /** Terminates the transaction. */ public void terminate() { if (!statusIs(STATE_TERMINATED)) { retransmission_to.halt(); transaction_to.halt(); clearing_to.halt(); sip_provider.removeSipProviderListener(transaction_id); changeStatus(STATE_TERMINATED); transaction_listener=null; } } //**************************** Logs ****************************/ /** Adds a new string to the default Log */ protected void printLog(String str, int level) { if (log!=null) log.println("TransactionClient#"+transaction_sqn+": "+str,level+SipStack.LOG_LEVEL_TRANSACTION); } }