JBoss.orgCommunity Documentation

Chapter 4. Source Code Overview

The bellow source code is from IVRSbb, it hides SIP related logic and highlights MGCP specific code. In case if you want to look at complete source, please get the source code as explained in Section 2.2.

package org.mobicents.mgcp.demo;

......

public abstract class IVRSbb implements Sbb {

	public final static String ENDPOINT_NAME = "/mobicents/media/IVR/$";

	public final static String JBOSS_BIND_ADDRESS = System.getProperty("jboss.bind.address", "127.0.0.1");

	public final static String WELCOME = "http://" + JBOSS_BIND_ADDRESS + ":8080/mgcpdemo/audio/RQNT-ULAW.wav";

	......

	private SbbContext sbbContext;

	......

	// MGCP
	private JainMgcpProvider mgcpProvider;
	private MgcpActivityContextInterfaceFactory mgcpAcif;

	public static final int MGCP_PEER_PORT = 2427;
	public static final int MGCP_PORT = 2727;

	private Tracer logger;

	/** Creates a new instance of CallSbb */
	public IVRSbb() {
	}

	public void onCallCreated(RequestEvent evt, ActivityContextInterface aci) {1
		.......
		//SIP Related handling
		.......

		// respond(evt, Response.RINGING);

		CallIdentifier callID = mgcpProvider.getUniqueCallIdentifier();2
		this.setCallIdentifier(callID.toString());
		EndpointIdentifier endpointID = new EndpointIdentifier(ENDPOINT_NAME, JBOSS_BIND_ADDRESS + ":" + MGCP_PEER_PORT);3

		CreateConnection createConnection = new CreateConnection(this, callID, endpointID, ConnectionMode.SendRecv);4

		try {
			String sdp = new String(evt.getRequest().getRawContent());
			createConnection.setRemoteConnectionDescriptor(new ConnectionDescriptor(sdp));5
		} catch (ConflictingParameterException e) {
			// should never happen
		}

		int txID = mgcpProvider.getUniqueTransactionHandler();6
		createConnection.setTransactionHandle(txID);

		MgcpConnectionActivity connectionActivity = null;
		try {
			connectionActivity = mgcpProvider.getConnectionActivity(txID, endpointID);7
			ActivityContextInterface epnAci = mgcpAcif.getActivityContextInterface(connectionActivity);
			epnAci.attach(sbbContext.getSbbLocalObject());
		} catch (FactoryException ex) {
			ex.printStackTrace();
		} catch (NullPointerException ex) {
			ex.printStackTrace();
		} catch (UnrecognizedActivityException ex) {
			ex.printStackTrace();
		}

		mgcpProvider.sendMgcpEvents(new JainMgcpEvent[] { createConnection });8
	}
	
	...
	....
	
	}	    
	    

1

IVRSbb is listening for INVITE SIP RA Event. The Slee Container calls corresponding onCallCreated event handler method.

2

New instance of CallIdentifier is created as this is new call. Through out the call, CallIdentifier will remain the same.

3

New instance of EndpointIdentifier is created. The Endpoint local name is "/mobicents/media/IVR/$". A term represented by a dollar sign ("$") is to be interpreted as, use any available free endpoint. Endpoint domain name is JBOSS_BIND_ADDRESS + ":" + MGCP_PEER_PORT. The MGCP Command will be delivered to MGCP Stack listening at JBOSS_BIND_ADDRESS IP Address and MGCP_PEER_PORT port. Make sure that the Mobicents Media Server is bound to same IP address as JBOSS_BIND_ADDRESS and port in mgcp-conf.xml (MGCP Configuration file in Mobicents Media Server) is same as MGCP_PEER_PORT.

4

New instance of CreateConnection object is created. This CreateConnection Request when received by Media Gateway, will cause Media Gateway to select any one free IVR Endpoint, create a connection on it and send back CreateConnectionResponse. If there is any error while creating connection the returned CreateConnectionResponse will carry the corresponding cause in ReturnCode

5

Setting the SDP of User Agent. This is not mandatory. If this is set the Connection created on Endpoint on media gateway will start sending the media (depending on ConnectionMode) to User Agent at IP:Port specified in SDP. If this is not set, the Connection can latter be modified by sending ModifyConnection (not shown in this example)

6

Set a new transaction for this MGCP Request.

7

Create a new MgcpConnectionActivity for this MGCP Request, attach the SbbLocalObject to this activity to receive CreateConnectionResponse event from MGCP RA.

8

Finally send this CreateConnection MGCP Command to Media Gateway.

Once the MGCP command reaches Media Gateway, it replies back and MGCP RA will fire corresponding event.

	public void onCreateConnectionResponse(CreateConnectionResponse event, ActivityContextInterface aci) {1
		logger.info("Receive CRCX response: " + event.getTransactionHandle());

		......
		....

		ReturnCode status = event.getReturnCode();2

		switch (status.getValue()) {
		case ReturnCode.TRANSACTION_EXECUTED_NORMALLY:

			this.setEndpointName(event.getSpecificEndpointIdentifier().getLocalEndpointName());3
			
			ConnectionIdentifier connectionIdentifier = event.getConnectionIdentifier();4

			this.setConnectionIdentifier(connectionIdentifier.toString());
			String sdp = event.getLocalConnectionDescriptor().toString();5

                        //Send OK to UA with SDP from media gateway
                        .....
			.......

			//Play Announcement
			sendRQNT(WELCOME, false);6
			.....
			......

			break;
		default:
			//CRCX failed at Media Gateway. Take necessary action7
			.....
		}
	}

1

MGCP RA will fire the CreateConnectionResponse event once CRCX Response received from Media Gateway.

2

ReturnCode specifies if Media Gateway has successfully created the connection on Endpoint or not.

Return codes are

100 and 199 indicate a provisional response

200 and 299 indicate a successful completion

400 and 499 indicate a transient error

500 and 599 indicate a permanent error

3

If connection created successfully, CreateConnectionResponse will carry the specific EndpointIdentifier indicating the concrete Endpoint selected by media gateway. Next all MGCP Request will be fired on same endpoint till DeleteConnection is requested which represents end of call.

4

If connection created successfully, CreateConnectionResponse will carry the specific ConnectionIdentifier identifying the connection created by media gateway on above Endpoint. The request to apply Signal or detect Event will be for this ConnectionIdentifier.

5

If connection created successfully, CreateConnectionResponse will carry the ConnectionDescriptor indicating the SDP of above created connection. This SDP can then be sent to UA as OK Response to INVITE received and RTP flow begins between UA and Media Gateway

6

Since the RTP connection is established between UA and Media Gateway, NotificationRequest can be send to Media Gateway to play an announcement.

7

If creation of connection failed for some reason indicated by ReturnCode, necessary action can be taken.

Let us see how NotificationRequest is used to request Media Gateway to play an Announcement

	private void sendRQNT(String mediaPath, boolean createActivity) {
		EndpointIdentifier endpointID = new EndpointIdentifier(this.getEndpointName(), JBOSS_BIND_ADDRESS + ":"
				+ MGCP_PEER_PORT);1

		NotificationRequest notificationRequest = new NotificationRequest(this, endpointID, mgcpProvider
				.getUniqueRequestIdentifier());2
		
		ConnectionIdentifier connectionIdentifier = new ConnectionIdentifier(this.getConnectionIdentifier());3
		
		EventName[] signalRequests = { new EventName(PackageName.Announcement, MgcpEvent.ann.withParm(mediaPath), connectionIdentifier) };
		notificationRequest.setSignalRequests(signalRequests);4

		RequestedAction[] actions = new RequestedAction[] { RequestedAction.NotifyImmediately };

		RequestedEvent[] requestedEvents = {5
				new RequestedEvent(new EventName(PackageName.Announcement, MgcpEvent.oc, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Announcement, MgcpEvent.of, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmf0, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmf1, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmf2, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmf3, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmf4, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmf5, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmf6, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmf7, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmf8, connectionIdentifier), actions),

				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmf9, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmfA, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmfB, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmfC, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmfD, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmfStar, connectionIdentifier), actions),
				new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.dtmfHash, connectionIdentifier), actions) };

		notificationRequest.setRequestedEvents(requestedEvents);
		notificationRequest.setTransactionHandle(mgcpProvider.getUniqueTransactionHandler());

		NotifiedEntity notifiedEntity = new NotifiedEntity(JBOSS_BIND_ADDRESS, JBOSS_BIND_ADDRESS, MGCP_PORT);
		notificationRequest.setNotifiedEntity(notifiedEntity);

		if (createActivity) {6
			MgcpEndpointActivity endpointActivity = null;
			try {
				endpointActivity = mgcpProvider.getEndpointActivity(endpointID);
				ActivityContextInterface epnAci = mgcpAcif.getActivityContextInterface(endpointActivity);
				epnAci.attach(sbbContext.getSbbLocalObject());
			} catch (FactoryException ex) {
				ex.printStackTrace();
			} catch (NullPointerException ex) {
				ex.printStackTrace();
			} catch (UnrecognizedActivityException ex) {
				ex.printStackTrace();
			}
		} // if (createActivity)

		mgcpProvider.sendMgcpEvents(new JainMgcpEvent[] { notificationRequest });7

		logger.info(" NotificationRequest sent");
	}       
       

1

The NotificationRequest should be fired on same Endpoint where connection is created

3

Create a new NotificationRequest Object passing EndpointIdentifier created above and use new RequestIdentifier

3

The NotificationRequest should be fired on same connection on Endpoint.

4

The NotificationRequest carries Signal to play an announcement on connection represented by connectionIdentifier

5

The NotificationRequest carries request to detect DTMF Events and also detect events if Announcement completed successfully or failed.

6

Since none of the Signals/Events are fired/detected on Endpoint, Endpoint Activity is not created. The above Events when detected would be fired on Connection Activity.

7

Finally send the request to Media Gateway.

Once Media Gateway receives the NotificationRequest, it will process the Signals / Events and send back NotificationRequestResponse which carries ReturnCode indicating if Signals can be applied or not and Events can be detected or not.

	public void onNotificationRequestResponse(NotificationRequestResponse event, ActivityContextInterface aci) {
		logger.info("onNotificationRequestResponse");

		ReturnCode status = event.getReturnCode();

		switch (status.getValue()) {
		case ReturnCode.TRANSACTION_EXECUTED_NORMALLY:
			logger.info("The Announcement should have been started");
			break;
		default:
			ReturnCode rc = event.getReturnCode();
			logger.severe("RQNT failed. Value = " + rc.getValue() + " Comment = " + rc.getComment());

			//Send DLCX to MMS. Send BYE to UA
			break;
		}

	}       
       

The Media Gateway will fire the Notify command to Application when ever it detects any of the above Events requested by NotificationRequest

	public void onNotifyRequest(Notify event, ActivityContextInterface aci) {
		logger.info("onNotifyRequest");

		 NotifyResponse response = new  NotifyResponse(event.getSource(),
				ReturnCode.Transaction_Executed_Normally);
		response.setTransactionHandle(event.getTransactionHandle());

		mgcpProvider.sendMgcpEvents(new JainMgcpEvent[] { response });1

		EventName[] observedEvents = event.getObservedEvents();2

		for (EventName observedEvent : observedEvents) {
			switch (observedEvent.getEventIdentifier().intValue()) {
			case MgcpEvent.REPORT_ON_COMPLETION:
				logger.info("Announcemnet Completed NTFY received");
				break;
			case MgcpEvent.REPORT_FAILURE:
				logger.info("Announcemnet Failed received");
				// TODO : Send DLCX and Send BYE to UA
				break;
			case MgcpEvent.DTMF_0:
				logger.info("You have pressed 0");
				sendRQNT(DTMF_0, false);
				break;
			case MgcpEvent.DTMF_1:
				logger.info("You have pressed 1");
				sendRQNT(DTMF_1, false);
				break;
			case MgcpEvent.DTMF_2:
				logger.info("You have pressed 2");
				sendRQNT(DTMF_2, false);
				break;
			case MgcpEvent.DTMF_3:
				logger.info("You have pressed 3");
				sendRQNT(DTMF_3, false);
				break;
			case MgcpEvent.DTMF_4:
				logger.info("You have pressed 4");
				sendRQNT(DTMF_4, false);
				break;
			case MgcpEvent.DTMF_5:
				logger.info("You have pressed 5");
				sendRQNT(DTMF_5, false);
				break;
			case MgcpEvent.DTMF_6:
				logger.info("You have pressed 6");
				sendRQNT(DTMF_6, false);
				break;
			case MgcpEvent.DTMF_7:
				logger.info("You have pressed 7");
				sendRQNT(DTMF_7, false);
				break;
			case MgcpEvent.DTMF_8:
				logger.info("You have pressed 8");
				sendRQNT(DTMF_8, false);
				break;
			case MgcpEvent.DTMF_9:
				logger.info("You have pressed 9");
				sendRQNT(DTMF_9, false);
				break;
			case MgcpEvent.DTMF_A:
				logger.info("You have pressed A");
				sendRQNT(A, false);
				break;
			case MgcpEvent.DTMF_B:
				logger.info("You have pressed B");
				sendRQNT(B, false);
				break;
			case MgcpEvent.DTMF_C:
				logger.info("You have pressed C");
				sendRQNT(C, false);
				break;
			case MgcpEvent.DTMF_D:
				logger.info("You have pressed D");
				sendRQNT(D, false);

				break;
			case MgcpEvent.DTMF_STAR:
				logger.info("You have pressed *");
				sendRQNT(STAR, false);

				break;
			case MgcpEvent.DTMF_HASH:
				logger.info("You have pressed C");
				sendRQNT(POUND, false);

				break;
			}
		}
	}              
              

1

Send the NotifyResponse immediately to avoid Media Gateway sending the Notify again on expiration of response Timer.

2

The Notify command carries list of Events depending on which all occurred at Media Gateway. Iterate through this list and act accordingly. In our example we are simply asking Media Gateway to play corresponding audio file for DTMF pressed by user.

Finally when user hangs-up, we need to delete the connection on Endpoint and free the resources

	public void onCallTerminated(RequestEvent evt, ActivityContextInterface aci) {1
		EndpointIdentifier endpointID = new EndpointIdentifier(this.getEndpointName(), JBOSS_BIND_ADDRESS + ":"
				+ MGCP_PEER_PORT);
		DeleteConnection deleteConnection = new DeleteConnection(this, endpointID);

		deleteConnection.setTransactionHandle(mgcpProvider.getUniqueTransactionHandler());
		mgcpProvider.sendMgcpEvents(new JainMgcpEvent[] { deleteConnection });2

		ServerTransaction tx = evt.getServerTransaction();
		Request request = evt.getRequest();

		try {
			Response response = messageFactory.createResponse(Response.OK, request);
			tx.sendResponse(response);
		} catch (Exception e) {
			logger.severe("Error while sending DLCX ", e);
		}
	}        
        

1

The SIP11 RA fires BYE event, slee container calls onCallTerminated method on SBB

2

New DeleteConnection Object is created passing the same Endpoint on which original connection was created. Once Media Gateway receives DeleteConnection command, it closes the connection and frees Endpoint from all resources allocated.