package org.mobicents.javax.media.mscontrol.networkconnection; import jain.protocol.ip.mgcp.JainMgcpCommandEvent; import jain.protocol.ip.mgcp.JainMgcpEvent; import jain.protocol.ip.mgcp.JainMgcpResponseEvent; import jain.protocol.ip.mgcp.message.Constants; import jain.protocol.ip.mgcp.message.CreateConnection; import jain.protocol.ip.mgcp.message.CreateConnectionResponse; import jain.protocol.ip.mgcp.message.DeleteConnection; import jain.protocol.ip.mgcp.message.DeleteConnectionResponse; import jain.protocol.ip.mgcp.message.ModifyConnection; import jain.protocol.ip.mgcp.message.ModifyConnectionResponse; import jain.protocol.ip.mgcp.message.parms.CallIdentifier; import jain.protocol.ip.mgcp.message.parms.ConflictingParameterException; import jain.protocol.ip.mgcp.message.parms.ConnectionDescriptor; import jain.protocol.ip.mgcp.message.parms.ConnectionIdentifier; import jain.protocol.ip.mgcp.message.parms.ConnectionMode; import jain.protocol.ip.mgcp.message.parms.EndpointIdentifier; import jain.protocol.ip.mgcp.message.parms.ReturnCode; import java.net.URI; import java.net.URISyntaxException; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; import javax.media.mscontrol.EventType; import javax.media.mscontrol.MediaConfig; import javax.media.mscontrol.MediaErr; import javax.media.mscontrol.MediaEvent; import javax.media.mscontrol.MediaEventListener; import javax.media.mscontrol.MediaObject; import javax.media.mscontrol.MediaSession; import javax.media.mscontrol.MsControlException; import javax.media.mscontrol.Parameter; import javax.media.mscontrol.Parameters; import javax.media.mscontrol.join.JoinEvent; import javax.media.mscontrol.join.Joinable; import javax.media.mscontrol.networkconnection.CodecPolicy; import javax.media.mscontrol.networkconnection.NetworkConnection; import javax.media.mscontrol.networkconnection.SdpPortManager; import javax.media.mscontrol.networkconnection.SdpPortManagerEvent; import javax.media.mscontrol.networkconnection.SdpPortManagerException; import javax.media.mscontrol.resource.Action; import javax.media.mscontrol.resource.AllocationEventListener; import org.apache.log4j.Logger; import org.mobicents.javax.media.mscontrol.AbstractJoinableContainer; import org.mobicents.javax.media.mscontrol.AudioJoinableStream; import org.mobicents.javax.media.mscontrol.MediaConfigImpl; import org.mobicents.javax.media.mscontrol.MediaObjectState; import org.mobicents.javax.media.mscontrol.MediaSessionImpl; import org.mobicents.javax.media.mscontrol.ParametersImpl; import org.mobicents.javax.media.mscontrol.resource.ExtendedParameter; import org.mobicents.jsr309.mgcp.MgcpWrapper; import org.mobicents.jsr309.mgcp.Provider; import org.mobicents.mgcp.stack.JainMgcpExtendedListener; /** * * @author amit bhayani * */ public class NetworkConnectionImpl extends AbstractJoinableContainer implements NetworkConnection { public static Logger logger = Logger.getLogger(NetworkConnectionImpl.class); private String PR_ENDPOINT_NAME = null; private URI uri = null; private MediaConfigImpl config = null; private EndpointIdentifier endpointIdentifier = null; private ConnectionIdentifier connectionIdentifier = null; private byte[] remoteSessionDescription = null; private byte[] localSessionDescription = null; private SdpPortManager sdpPortManager = null; private Parameters params = null; protected CopyOnWriteArrayList<MediaEventListener<? extends MediaEvent<?>>> eventListenerList = new CopyOnWriteArrayList<MediaEventListener<? extends MediaEvent<?>>>(); public NetworkConnectionImpl(MediaSessionImpl mediaSession, MgcpWrapper mgcpWrapper, MediaConfigImpl config) { super(); this.mediaSession = mediaSession; this.mgcpWrapper = mgcpWrapper; this.maxJoinees = 1; this.config = config; this.endpoint = (String) config.getParameters().get(ExtendedParameter.ENDPOINT_LOCAL_NAME); this.PR_ENDPOINT_NAME = this.endpoint; try { this.uri = new URI(mediaSession.getURI().toString() + "/NetworkConnection." + this.id); } catch (URISyntaxException e) { // Ignore } sdpPortManager = new SdpPortManagerImpl(this); } public NetworkConnectionImpl(MediaSessionImpl mediaSession, MgcpWrapper mgcpWrapper, MediaConfigImpl config, Parameters params) { this(mediaSession, mgcpWrapper, config); this.params = params; } @Override protected void checkState() { if (this.state.equals(MediaObjectState.RELEASED)) { throw new IllegalStateException("State of container " + this.getURI() + " is released"); } } @Override protected MediaObjectState getState() { return this.state; } @Override public URI getURI() { return this.uri; } @Override protected void joined(ConnectionIdentifier thisConnId, ConnectionIdentifier otherConnId) { // TODO Do we want to preserve the connectionId of 2nd Connection of PR? } @Override protected void resetContainer() { // App didn't call NC.modify() yet. We can still reuse this NC object if (this.endpointIdentifier == null) { this.audioJoinableStream = null; this.endpoint = PR_ENDPOINT_NAME; } } @Override protected void unjoined(ConnectionIdentifier thisConnId, ConnectionIdentifier otherConnId) { // TODO any further cleaning action? } public SdpPortManager getSdpPortManager() throws MsControlException { return sdpPortManager; } public void confirm() throws MsControlException { throw new MsControlException("Operation not yet supported"); } public MediaConfig getConfig() { // TODO Auto-generated method stub return null; } public <R> R getResource(Class<R> paramClass) throws MsControlException { // TODO Auto-generated method stub return null; } public void triggerRTC(Action paramAction) { // TODO Auto-generated method stub } public Parameters createParameters() { return new ParametersImpl(); } public Iterator<MediaObject> getMediaObjects() { // TODO Auto-generated method stub return null; } public <T extends MediaObject> Iterator<T> getMediaObjects(Class<T> paramClass) { // TODO Auto-generated method stub return null; } public Parameters getParameters(Parameter[] paramArrayOfParameter) { // TODO Auto-generated method stub return null; } public void release() { checkState(); if (this.endpointIdentifier != null) { Runnable tx = new DeleteTx(this); Provider.submit(tx); } try { Joinable[] joinableArray = this.getJoinees(); for (Joinable joinable : joinableArray) { this.unjoinInitiate(joinable, this); } } catch (MsControlException e) { logger.error("release of NetworkConnection failed ", e); } this.state = MediaObjectState.RELEASED; this.mediaSession.getNetConnList().remove(this); } public void setParameters(Parameters paramParameters) { // TODO Auto-generated method stub } public void addListener(AllocationEventListener paramAllocationEventListener) { // TODO Auto-generated method stub } public void removeListener(AllocationEventListener paramAllocationEventListener) { // TODO Auto-generated method stub } public class SdpPortManagerImpl implements SdpPortManager { private NetworkConnectionImpl networkConn = null; public SdpPortManagerImpl(NetworkConnectionImpl networkConn) { this.networkConn = networkConn; } public void generateSdpOffer() throws SdpPortManagerException { checkState(); // Async call if (endpointIdentifier == null) { Runnable tx = new CreateTx(this.networkConn, this, false, true, false); Provider.submit(tx); } else { throw new SdpPortManagerException("Sdp Offer already generated once"); } } public CodecPolicy getCodecPolicy() { // TODO Auto-generated method stub return null; } public byte[] getMediaServerSessionDescription() throws SdpPortManagerException { return localSessionDescription; } public byte[] getUserAgentSessionDescription() throws SdpPortManagerException { return remoteSessionDescription; } public void processSdpAnswer(byte[] paramArrayOfByte) throws SdpPortManagerException { checkState(); if (paramArrayOfByte != null) { remoteSessionDescription = paramArrayOfByte; Runnable tx = endpointIdentifier == null ? new CreateTx(this.networkConn, this, false, false, true) : new ModifyTx(this.networkConn, this, false, false, true); Provider.submit(tx); } else { throw new SdpPortManagerException("The sdp argument passed cannot be null"); } } public void processSdpOffer(byte[] paramArrayOfByte) throws SdpPortManagerException { checkState(); if (paramArrayOfByte != null) { remoteSessionDescription = paramArrayOfByte; Runnable tx = endpointIdentifier == null ? new CreateTx(this.networkConn, this, true, false, false) : new ModifyTx(this.networkConn, this, true, false, false); Provider.submit(tx); } else { throw new SdpPortManagerException("The sdp argument passed cannot be null"); } } public void rejectSdpOffer() throws SdpPortManagerException { release(); } public void setCodecPolicy(CodecPolicy paramCodecPolicy) throws SdpPortManagerException { } public NetworkConnection getContainer() { return this.networkConn; } public boolean stop() { return false; } public void addListener(MediaEventListener<SdpPortManagerEvent> paramMediaEventListener) { eventListenerList.add(paramMediaEventListener); } public MediaSession getMediaSession() { return mediaSession; } public void removeListener(MediaEventListener<SdpPortManagerEvent> paramMediaEventListener) { eventListenerList.remove(paramMediaEventListener); } protected void update(SdpPortManagerEvent anEvent) { for (MediaEventListener m : eventListenerList) { m.onEvent(anEvent); } } private class CreateTx implements Runnable, JainMgcpExtendedListener { private NetworkConnectionImpl networkConnectionImpl = null; private SdpPortManager sdpPortManager; private int tx = -1; private EventType eveType = null; public CreateTx(NetworkConnectionImpl networkConnectionImpl, SdpPortManager sdpPortManager, boolean processSdpOffer, boolean generateSDPOffer, boolean processSdpAnswer) { this.networkConnectionImpl = networkConnectionImpl; this.sdpPortManager = sdpPortManager; if (processSdpOffer) { eveType = SdpPortManagerEvent.ANSWER_GENERATED; } else if (generateSDPOffer) { eveType = SdpPortManagerEvent.OFFER_GENERATED; } else { eveType = SdpPortManagerEvent.ANSWER_PROCESSED; } } public void run() { try { this.tx = mgcpWrapper.getUniqueTransactionHandler(); mgcpWrapper.addListnere(this.tx, this); CallIdentifier callId = mediaSession.getCallIdentifier(); EndpointIdentifier endpointID = new EndpointIdentifier(endpoint, mgcpWrapper.getPeerIp() + ":" + mgcpWrapper.getPeerPort()); CreateConnection createConnection = new CreateConnection(this, callId, endpointID, ConnectionMode.SendRecv); if (remoteSessionDescription != null) { createConnection.setRemoteConnectionDescriptor(new ConnectionDescriptor(new String( remoteSessionDescription))); } createConnection.setTransactionHandle(tx); createConnection.setNotifiedEntity(mgcpWrapper.getDefaultNotifiedEntity()); mgcpWrapper.sendMgcpEvents(new JainMgcpEvent[] { createConnection }); } catch (ConflictingParameterException e) { logger.error(e); } } public void transactionEnded(int arg0) { if (logger.isDebugEnabled()) { logger.debug("Successfully completed Tx = " + arg0); } } public void transactionRxTimedOut(JainMgcpCommandEvent arg0) { } public void transactionTxTimedOut(JainMgcpCommandEvent jainMgcpCommandEvent) { logger.error("No response from MGW. Tx timed out for MGCP Tx " + this.tx + " For Command sent " + jainMgcpCommandEvent.toString()); mgcpWrapper.removeListener(jainMgcpCommandEvent.getTransactionHandle()); SdpPortManagerEventImpl sdpEvent = new SdpPortManagerEventImpl(this.sdpPortManager, eveType, null, false, MediaErr.TIMEOUT, "No response from MGW for modify"); update(sdpEvent); } public void processMgcpCommandEvent(JainMgcpCommandEvent arg0) { } public void processMgcpResponseEvent(JainMgcpResponseEvent jainmgcpresponseevent) { switch (jainmgcpresponseevent.getObjectIdentifier()) { case Constants.RESP_CREATE_CONNECTION: processCreateConnectionResponse((CreateConnectionResponse) jainmgcpresponseevent); break; default: mgcpWrapper.removeListener(jainmgcpresponseevent.getTransactionHandle()); logger.warn(" This RESPONSE is unexpected " + jainmgcpresponseevent); SdpPortManagerEventImpl sdpEvent = new SdpPortManagerEventImpl(this.sdpPortManager, eveType, null, false, MediaErr.UNKNOWN_ERROR, "modify failed. Look at logs " + jainmgcpresponseevent.getReturnCode().getComment()); update(sdpEvent); break; } } private void processCreateConnectionResponse(CreateConnectionResponse responseEvent) { logger.debug(" processCreateConnectionResponse() "); SdpPortManagerEventImpl sdpEvent = null; ReturnCode returnCode = responseEvent.getReturnCode(); switch (returnCode.getValue()) { case ReturnCode.TRANSACTION_BEING_EXECUTED: // do nothing if (logger.isDebugEnabled()) { logger.debug("Transaction " + this.tx + "is being executed. Response received = " + responseEvent); } break; case ReturnCode.TRANSACTION_EXECUTED_NORMALLY: mgcpWrapper.removeListener(responseEvent.getTransactionHandle()); connectionIdentifier = responseEvent.getConnectionIdentifier(); endpointIdentifier = responseEvent.getSpecificEndpointIdentifier(); endpoint = endpointIdentifier.getLocalEndpointName(); if (logger.isDebugEnabled()) { logger.debug(" TRANSACTION_EXECUTED_NORMALLY for connectionIdentifier = " + connectionIdentifier + " endpointID = " + endpointIdentifier); } localSessionDescription = (responseEvent.getLocalConnectionDescriptor().toString()).getBytes(); if (audioJoinableStream == null) { audioJoinableStream = new AudioJoinableStream(this.networkConnectionImpl); } sdpEvent = new SdpPortManagerEventImpl(this.sdpPortManager, eveType, localSessionDescription, true); update(sdpEvent); break; case ReturnCode.ENDPOINT_INSUFFICIENT_RESOURCES: mgcpWrapper.removeListener(responseEvent.getTransactionHandle()); sdpEvent = new SdpPortManagerEventImpl(this.sdpPortManager, eveType, null, false, MediaErr.RESOURCE_UNAVAILABLE, returnCode.getComment()); update(sdpEvent); break; default: logger.error(" SOMETHING IS BROKEN = " + responseEvent); mgcpWrapper.removeListener(responseEvent.getTransactionHandle()); sdpEvent = new SdpPortManagerEventImpl(this.sdpPortManager, eveType, null, false, MediaErr.UNKNOWN_ERROR, returnCode.getComment()); update(sdpEvent); break; } } } private class ModifyTx implements Runnable, JainMgcpExtendedListener { private NetworkConnectionImpl networkConnectionImpl; private SdpPortManager sdpPortManager; private int tx = -1; private EventType eveType = null; public ModifyTx(NetworkConnectionImpl networkConnectionImpl, SdpPortManager sdpPortManager, boolean processSdpOffer, boolean generateSDPOffer, boolean processSdpAnswer) { this.networkConnectionImpl = networkConnectionImpl; this.sdpPortManager = sdpPortManager; if (processSdpOffer) { eveType = SdpPortManagerEvent.ANSWER_GENERATED; } else if (generateSDPOffer) { eveType = SdpPortManagerEvent.OFFER_GENERATED; } else { eveType = SdpPortManagerEvent.ANSWER_PROCESSED; } } public void run() { try { this.tx = mgcpWrapper.getUniqueTransactionHandler(); mgcpWrapper.addListnere(this.tx, this); CallIdentifier callId = mediaSession.getCallIdentifier(); ModifyConnection modifyConnection = new ModifyConnection(this, callId, endpointIdentifier, connectionIdentifier); if (remoteSessionDescription != null) { modifyConnection.setRemoteConnectionDescriptor(new ConnectionDescriptor(new String( remoteSessionDescription))); } modifyConnection.setTransactionHandle(tx); modifyConnection.setNotifiedEntity(mgcpWrapper.getDefaultNotifiedEntity()); mgcpWrapper.sendMgcpEvents(new JainMgcpEvent[] { modifyConnection }); } catch (Exception e) { logger.error(e); } } public void transactionEnded(int arg0) { // TODO Auto-generated method stub } public void transactionRxTimedOut(JainMgcpCommandEvent arg0) { // TODO Auto-generated method stub } public void transactionTxTimedOut(JainMgcpCommandEvent jainMgcpCommandEvent) { logger.error("No response from MGW. Tx timed out for MGCP Tx " + this.tx + " For Command sent " + jainMgcpCommandEvent.toString()); mgcpWrapper.removeListener(jainMgcpCommandEvent.getTransactionHandle()); SdpPortManagerEventImpl sdpEvent = new SdpPortManagerEventImpl(this.sdpPortManager, eveType, null, false, MediaErr.TIMEOUT, "No response from MGW for modify"); update(sdpEvent); } public void processMgcpCommandEvent(JainMgcpCommandEvent arg0) { // TODO Auto-generated method stub } public void processMgcpResponseEvent(JainMgcpResponseEvent jainmgcpresponseevent) { // TODO : Depending on Response we get fire corresponding JSR // 309 // events here switch (jainmgcpresponseevent.getObjectIdentifier()) { case Constants.RESP_MODIFY_CONNECTION: processMofiyConnectionResponse((ModifyConnectionResponse) jainmgcpresponseevent); break; default: mgcpWrapper.removeListener(jainmgcpresponseevent.getTransactionHandle()); logger.warn(" This RESPONSE is unexpected " + jainmgcpresponseevent); SdpPortManagerEventImpl sdpEvent = new SdpPortManagerEventImpl(this.sdpPortManager, eveType, null, false, MediaErr.UNKNOWN_ERROR, "modify failed. Look at logs " + jainmgcpresponseevent.getReturnCode().getComment()); update(sdpEvent); break; } } private void processMofiyConnectionResponse(ModifyConnectionResponse responseEvent) { logger.debug(" processMofiyConnectionResponse() "); SdpPortManagerEventImpl sdpEvent = null; ReturnCode returnCode = responseEvent.getReturnCode(); switch (returnCode.getValue()) { case ReturnCode.TRANSACTION_BEING_EXECUTED: // do nothing if (logger.isDebugEnabled()) { logger.debug("Transaction " + this.tx + "is being executed. Response received = " + responseEvent); } break; case ReturnCode.TRANSACTION_EXECUTED_NORMALLY: mgcpWrapper.removeListener(responseEvent.getTransactionHandle()); if (logger.isDebugEnabled()) { logger.debug(" MDCX TRANSACTION_EXECUTED_NORMALLY for connectionIdentifier = " + connectionIdentifier + "endpointID = " + endpointIdentifier); } if (responseEvent.getLocalConnectionDescriptor() != null) { localSessionDescription = (responseEvent.getLocalConnectionDescriptor().toString()).getBytes(); } sdpEvent = new SdpPortManagerEventImpl(this.sdpPortManager, eveType, localSessionDescription, true); update(sdpEvent); break; case ReturnCode.ENDPOINT_INSUFFICIENT_RESOURCES: mgcpWrapper.removeListener(responseEvent.getTransactionHandle()); sdpEvent = new SdpPortManagerEventImpl(this.sdpPortManager, eveType, null, false, MediaErr.RESOURCE_UNAVAILABLE, returnCode.getComment()); update(sdpEvent); break; default: mgcpWrapper.removeListener(responseEvent.getTransactionHandle()); logger.error(" SOMETHING IS BROKEN = " + responseEvent); sdpEvent = new SdpPortManagerEventImpl(this.sdpPortManager, eveType, null, false, MediaErr.UNKNOWN_ERROR, returnCode.getComment()); update(sdpEvent); break; } } } } protected class DeleteTx implements Runnable, JainMgcpExtendedListener { private NetworkConnectionImpl networkConnectionImpl; private int tx = -1; public DeleteTx(NetworkConnectionImpl networkConnectionImpl) { this.networkConnectionImpl = networkConnectionImpl; } public void run() { try { this.tx = mgcpWrapper.getUniqueTransactionHandler(); // TODO : Do we need to fire event for DLCX? mgcpWrapper.addListnere(this.tx, this); CallIdentifier callId = mediaSession.getCallIdentifier(); DeleteConnection deleteConnection = new DeleteConnection(this, callId, endpointIdentifier, connectionIdentifier); deleteConnection.setTransactionHandle(tx); mgcpWrapper.sendMgcpEvents(new JainMgcpEvent[] { deleteConnection }); } catch (Exception e) { logger.error(e); } } public void transactionEnded(int arg0) { // TODO Auto-generated method stub } public void transactionRxTimedOut(JainMgcpCommandEvent arg0) { // TODO Auto-generated method stub } public void transactionTxTimedOut(JainMgcpCommandEvent jainMgcpCommandEvent) { logger.error("No response from MGW. Tx timed out for MGCP Tx " + this.tx + " For Command sent " + jainMgcpCommandEvent.toString()); mgcpWrapper.removeListener(jainMgcpCommandEvent.getTransactionHandle()); } public void processMgcpCommandEvent(JainMgcpCommandEvent arg0) { // TODO Auto-generated method stub } public void processMgcpResponseEvent(JainMgcpResponseEvent response) { if (response.getTransactionHandle() != this.tx) { return; } switch (response.getObjectIdentifier()) { case Constants.RESP_DELETE_CONNECTION: processDeleteConnectionResponse((DeleteConnectionResponse) response); break; default: mgcpWrapper.removeListener(response.getTransactionHandle()); logger.warn(" DLCX of Netwrok connction failed RESPONSE is unexpected " + response); break; } } private void processDeleteConnectionResponse(DeleteConnectionResponse responseEvent) { ReturnCode returnCode = responseEvent.getReturnCode(); JoinEvent joinEvent = null; switch (returnCode.getValue()) { case ReturnCode.TRANSACTION_BEING_EXECUTED: // do nothing if (logger.isDebugEnabled()) { logger.debug("Transaction " + responseEvent.getTransactionHandle() + "is being executed. Response received = " + responseEvent); } break; case ReturnCode.TRANSACTION_EXECUTED_NORMALLY: mgcpWrapper.removeListener(responseEvent.getTransactionHandle()); if (logger.isDebugEnabled()) { logger.debug("DLCX executed successfully for Tx = " + responseEvent.getTransactionHandle()); } break; default: mgcpWrapper.removeListener(responseEvent.getTransactionHandle()); logger.error(" SOMETHING IS BROKEN = " + responseEvent); break; } } } }