Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
<?xml version='1.0'?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN" "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd" [
<!ENTITY % BOOK_ENTITIES SYSTEM "JAIN_SLEE_Example_User_Guide.ent">
%BOOK_ENTITIES;
]>

<section id="proxy_sbb">
	<title>ProxySbb overview</title>
	<para>
		Proxy SBB is top component in this example - it is declared as root
		<acronym>SBB</acronym>
		. It declares event
		handlers for all SIP11 ResourceAdaptor
		transactional events.
	</para>

	<para>
		ProxySbb expects transactional events to be fired, that is, it
		assumes
		that if in-Dialog event is fired, some other application is
		responsible for messages.
		<literal>ProxySbb</literal>
		expects also that at any given
		time it is attached only to one
		incoming
		transaction(Server) and one or more
		ongoing(Client). That is because
		ProxySbb uses
		<literal>SbbContext</literal>
		facility to retrieve list of current activities it is attached to.
		Class
		<literal>org.mobicents.slee.services.sip.proxy.ProxySbb
			</literal>
		has all the logic linking
		<acronym>JSLEE</acronym>
		with routing logic declared in inner class
		<literal>org.mobicents.slee.services.sip.proxy.ProxySbb$ProxyMachine
		</literal>
		.
	</para>
	<section id="proxy_sbb_initial">
		<title>Initial event handler</title>
		<para>
			As root of service proxy declares requests as initial. It does that
			with xml descriptor of event handler in sbb-jar.xml, for instance:
			<acronym></acronym>
		</para>
		<programlisting language="XML" role="XML"><![CDATA[
        <event event-direction="Receive" initial-event="True">
			<event-name>InviteEvent</event-name>
			<event-type-ref>
				<event-type-name>javax.sip.message.Request.INVITE</event-type-name>
				<event-type-vendor>net.java.slee</event-type-vendor>
				<event-type-version>1.2</event-type-version>
			</event-type-ref>
			<initial-event-selector-method-name>
				callIDSelect
			</initial-event-selector-method-name>
		</event>
		]]>
		</programlisting>
		<para>
			Event handlers declaration has
			<literal>initial-event-selector-method-name</literal>
			element. This element identifies callback method name, which is
			invoked
			by
			<literal>JSLEE</literal>
			for all fired events. This callback is used by
			<literal>JSLEE</literal>
			to determine if event should create
			<literal>SBB Entity</literal>
			if it does not exists and determine name which distinguishes it.
			<literal>ProxySbb</literal>
			has following definition of initial event handler callback:
		</para>
		<programlisting language="Java" role="JAVA">
		public InitialEventSelector callIDSelect(InitialEventSelector ies) {
			Object event = ies.getEvent();
			String callId = null;
			if (event instanceof ResponseEvent) {
				ies.setInitialEvent(false);
				return ies;
			} else if (event instanceof RequestEvent) {
				// If request event, the convergence name to callId
				Request request = ((RequestEvent) event).getRequest();
				if (!request.getMethod().equals(Request.ACK)) {
					callId = ((ViaHeader) request.getHeaders(ViaHeader.NAME).next())
						.getBranch();
				} else {
					callId = ((ViaHeader) request.getHeaders(ViaHeader.NAME).next())
						.getBranch()
							+ "_ACK";
				}
			}
			// Set the convergence name
			if (logger.isDebugEnabled()) {
				logger.debug( "Setting convergence name to: " + callId);
			}
			ies.setCustomName(callId);
		
			return ies;
	    }
		</programlisting>
		<para>
			Response events are not considered as initial because there should be
			<acronym>SBB</acronym>
			Entity attached to client transactions
			<literal>activity context interface. This entity will handle those
				responses.</literal>
		</para>
	</section>
	<section id="proxy_sbb_requests">
		<title>Request handlers</title>
		<para>
			Request handlers are only responsible for relying message to
			<literal>ProxyMachine</literal>
			. Example request handler look as follows:
		</para>
		<programlisting role="JAVA" language="Java">
		
		public void onInviteEvent(RequestEvent event, ActivityContextInterface ac) {
		
		   if (logger.isDebugEnabled())
			  logger.debug("Received INVITE request");
		
		   processRequest(event.getServerTransaction(), event.getRequest(), ac);		
	    }
		
		private void processRequest(ServerTransaction serverTransaction,
			Request request, ActivityContextInterface ac) {

		if (logger.isInfoEnabled())
			logger.info("processing request: method = \n"
					+ request.getMethod().toString());
		

		try {

			if (getServerTransactionTerminated()) {
				if (logger.isDebugEnabled())
					logger.debug("[PROXY MACHINE] txTERM \n" + request);
				return;
			}

			// if (getServerTX() == null)
			// setServerTX(serverTransaction);
			// Go - if it is invite here, serverTransaction can be CANCEL
			// transaction!!!! so we dont want to overwrite it above
			new ProxyMachine(getProxyConfigurator(), getLocationSbb(),
					this.addressFactory, this.headerFactory,
					this.messageFactory, this.provider)
				.processRequest(serverTransaction, request);

		} catch (Exception e) {
			// Send error response so client can deal with it
			logger.warn( "Exception during processRequest", e);
			try {
				serverTransaction.sendResponse(messageFactory.createResponse(
						Response.SERVER_INTERNAL_ERROR, request));
			} catch (Exception ex) {
				logger.warn( "Exception during processRequest", e);
			}
		}

	}
		
		</programlisting>
		<para>
			<literal>ProxyMachine</literal>
			class performs all required operations to forward request :
		</para>
		<itemizedlist>
			<listitem>
				<para>check if request is properly built.</para>
			</listitem>
			<listitem>
				<para>process information in request and determine targets.</para>
			</listitem>
			<listitem>
				<para>forward message to all possible targets, attach to outgoing
					legs.</para>
			</listitem>
		</itemizedlist>
		<programlisting language="Java" role="JAVA">
		class ProxyMachine extends MessageUtils implements MessageHandlerInterface {
		protected final Logger log = Logger.getLogger("ProxyMachine.class");

		// We can use those variables from top level class, but let us have our
		// own.

		protected LocationService reg = null;

		protected AddressFactory af = null;

		protected HeaderFactory hf = null;

		protected MessageFactory mf = null;

		protected SipProvider provider = null;

		protected HashSet&lt;URI&gt; localMachineInterfaces = new HashSet&lt;URI&gt;();

		protected ProxyConfiguration config = null;

		public ProxyMachine(ProxyConfiguration config,
				LocationService registrarAccess, AddressFactory af,
				HeaderFactory hf, MessageFactory mf, SipProvider prov)
				throws ParseException {
			super(config);
			reg = registrarAccess;
			this.mf = mf;
			this.af = af;
			this.hf = hf;
			this.provider = prov;
			this.config = config;
			SipUri localMachineURI = new SipUri();
			localMachineURI.setHost(this.config.getSipHostname());
			localMachineURI.setPort(this.config.getSipPort());
			this.localMachineInterfaces.add(localMachineURI);
		}

		public void processRequest(ServerTransaction stx, Request req) {
			if (log.isDebugEnabled()) {
				log.debug("processRequest");
			}
			try {
				Request tmpNewRequest = (Request) req.clone();

				// 16.3 Request Validation
				validateRequest(stx, tmpNewRequest);

				// 16.4 Route Information Preprocessing
				routePreProcess(tmpNewRequest);

				// logger.debug("Server transaction " + stx);
				// 16.5 Determining Request Targets
				List targets = determineRequestTargets(tmpNewRequest);

				Iterator it = targets.iterator();
				while (it.hasNext()) {
					Request newRequest = (Request) tmpNewRequest.clone();
					URI target = (URI) it.next();

					// Part of loop detection, here we will stop initial reqeust
					// that makes loop in local stack
					if (isLocalMachine(target)) {

						continue;
					}
	
					// 16.6 Request Forwarding
					// 1. Copy request

					// 2. Request-URI
					if (target.isSipURI() &amp;&amp; !((SipUri) target).hasLrParam())
						newRequest.setRequestURI(target);

					// *NEW* CANCEL processing
					// CANCELs are hop-by-hop, so here must remove any existing
					// Via
					// headers,
					// Record-Route headers. We insert Via header below so we
					// will
					// get response.
					if (newRequest.getMethod().equals(Request.CANCEL)) {
						newRequest.removeHeader(ViaHeader.NAME);
						newRequest.removeHeader(RecordRouteHeader.NAME);
					} else {
						// 3. Max-Forwards
						decrementMaxForwards(newRequest);
						// 4. Record-Route
						addRecordRouteHeader(newRequest);
					}

					// 5. Add Additional Header Fields
					// TBD
					// 6. Postprocess routing information
					// TBD
					// 7. Determine Next-Hop Address, Port and Transport
					// TBD

					// 8. Add a Via header field value
					addViaHeader(newRequest);

					// 9. Add a Content-Leangth header field if necessary
					// TBD

					// 10. Forward Request

					ClientTransaction ctx = forwardRequest(stx, newRequest);

					// 11. Set timer C

				}

			} catch (SipSendErrorResponseException se) {
				se.printStackTrace();
				int statusCode = se.getStatusCode();
				sendErrorResponse(stx, req, statusCode);
			} catch (SipLoopDetectedException slde) {
				log.warn("Loop detected, droping message.");
				slde.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}

		}
		
		
		...
		
		
		}
		</programlisting>
		
	</section>
	<section id="proxy_sbb_answer">
	<title>Response handlers</title>
		<para>Response handlers are invoked for incoming responses. In case multiple final responses incoming in sequence, first response is forwarded in stateful manner, others are forwarded in stateles manner.
		Example response handler look as follows:</para>
		<programlisting language="Java" role="JAVA">
		public void onRedirRespEvent(ResponseEvent event,
			ActivityContextInterface ac) {
		if (logger.isDebugEnabled())
			logger.debug("Received 3xx (REDIRECT) response");

		processResponse(event.getClientTransaction(), event.getResponse(), ac);		
	}
		
		private void processResponse(ClientTransaction clientTransaction,
			Response response, ActivityContextInterface ac) {

		
		if (logger.isInfoEnabled())
			logger.info("processing response: status = \n"
					+ response.getStatusCode());

		try {

			if (getServerTransactionTerminated()) {
				return;
			}

			// Go
			ServerTransaction serverTransaction = getServerTransaction(clientTransaction);
			if (serverTransaction != null) {
				new ProxyMachine(getProxyConfigurator(), getLocationSbb(), this.addressFactory,
						this.headerFactory, this.messageFactory, this.provider)
				.processResponse(serverTransaction,
						clientTransaction, response);
			} else {
				logger.warn("Weird got null tx for[" + response + "]");
			}

		} catch (Exception e) {
			// Send error response so client can deal with it
			logger.warn( "Exception during processResponse", e);
		}

	}
		</programlisting>
		<para><literal>ProxyMachine</literal> class performs all required operations to forward response :
		</para>
		<itemizedlist>
			<listitem>
				<para>check if response should be forwarded.</para>
	   		</listitem>
	   		<listitem>
				 <para>forward it.</para>
	   		</listitem>
		</itemizedlist>
		<programlisting language="Java" role="JAVA">
		public void processResponse(ServerTransaction stx,
				ClientTransaction ctx, Response resp) {

			// Now check if we really want to send it right away

			// log.info(this.getClass().getName(), "processResponse");

			try {

				Response newResponse = (Response) resp.clone();

				// 16.7 Response Processing
				// 1. Find appropriate response context

				// 2. Update timer C for provisional responses

				// 3. Remove topmost via
				Iterator viaHeaderIt = newResponse.getHeaders(ViaHeader.NAME);
				viaHeaderIt.next();
				viaHeaderIt.remove();
				if (!viaHeaderIt.hasNext())
					return; // response was meant for this proxy

				// 4. Add the response to the response context

				// 5. Check to see if this response should be forwarded
				// immediately
				if (newResponse.getStatusCode() == Response.TRYING) {
					return;
				}

				// 6. When necessary, choose the best final response from the

				// 7. Aggregate authorization header fields if necessary

				// 8. Optionally rewrite Record-Route header field values

				// 9. Forward the response
				forwardResponse(stx, newResponse);

				// 10. Generate any necessary CANCEL requests

			} catch (Exception e) {
				e.printStackTrace();
			}
		</programlisting>
		
	</section>
	<section id="proxy_sbb_register">
	<title>Register handler</title>
		<para><literal>REGISTER</literal> request are handled differently. Depending on target domain incoming message is:</para>
		<itemizedlist>
			<listitem><para>forwarded if target domain does not match configured local domains.</para></listitem>
			<listitem><para>directed to <literal>ProxySbb</literal> child for processing.</para></listitem> 
		</itemizedlist>
		<programlisting language="Java" role="JAVA">
	public void onRegisterEvent(RequestEvent event, ActivityContextInterface ac) {

		if (logger.isDebugEnabled())
			logger
					.debug("Received REGISTER request, class="
							+ event.getClass());

		// see http://tools.ietf.org/html/rfc3261#section-10.2
		SipURI uri = (SipURI) event.getRequest().getRequestURI();
		if (isRegisterLocal(uri, getProxyConfigurator().getLocalDomainNames())) {
			try {
				ac.attach(getRegistrarSbbChildRelation().create());
			} catch (Exception e) {
				// failed to attach the register, send error back
				logger.error(e);
			}
			// detach myself
			ac.detach(sbbContext.getSbbLocalObject());
		} else {
			processRequest(event.getServerTransaction(), event.getRequest(), ac);
		}

	}
		</programlisting>
	</section>
	<section id="proxy_sbb_child">
		<title>Child relation</title>
		<para><literal>ProxySbb</literal> relies on its children to perform task on <literal>AOR</literal>, that is store, manage and retrieve bindings.
		Child relation is declared in <acronym>SBB</acronym> descriptor as follows:
		</para>
		<programlisting language="XML" role="XML"><![CDATA[
        <sbb-ref>
			<sbb-name>SipRegistrarSbb</sbb-name>
			<sbb-vendor>org.mobicents</sbb-vendor>
			<sbb-version>1.2</sbb-version>
			<sbb-alias>RegistrarSbb</sbb-alias>
		</sbb-ref>
        
        ...
        
        <sbb-classes>
			<sbb-abstract-class>
				<sbb-abstract-class-name>
					org.mobicents.slee.services.sip.proxy.ProxySbb
				</sbb-abstract-class-name>
				
				....

				<get-child-relation-method>
					<sbb-alias-ref>RegistrarSbb</sbb-alias-ref>
					<get-child-relation-method-name>
						getRegistrarSbbChildRelation
					</get-child-relation-method-name>
					<default-priority>0</default-priority>
				</get-child-relation-method>

				
			</sbb-abstract-class>
		</sbb-classes>
		]]>
		</programlisting>
		<para>Parent declaration in descriptor defines method name, in case of example its <literal>getRegistrarSbbChildRelation</literal>, implented by <acronym>JSLEE</acronym>. This method allows parent to access <literal>javax.slee.ChildRelation</literal> object representing link to defined child.
		<literal>ChildRelation</literal> object gives access to <literal>SbbLocalObject</literal> interface. This object allows parent to:
		 </para>
		 <itemizedlist>
			<listitem><para>attach child to <literal>ActivityContextInterface</literal> to make it eligible to receive events.</para></listitem>
			<listitem><para>invoke synchronously methods defined in child <literal>SbbLocalObject</literal>.</para></listitem> 
		</itemizedlist>
		<para><literal>ProxySbb</literal> invokes its children with both. <literal>RegistrarSbb</literal> is beeing attached to <literal>ActivityContextInterface</literal>. 
		<literal>LocationSbb</literal> is invoked in synchronous way. It exposes <literal>SbbLocalObject</literal> extending <literal>org.mobicents.slee.services.sip.location.LocationService</literal>  interface. It is defined and used as follows:</para>
		<programlisting language="Java" role="JAVA">
			
public interface LocationSbbLocalObject extends SbbLocalObject,LocationService {
	
}
			
public abstract class ProxySbb implements Sbb {

	public abstract ChildRelation getLocationSbbChildRelation();

	public LocationSbbLocalObject getLocationSbb() throws TransactionRequiredLocalException
	, SLEEException, CreateException {		
		final ChildRelation childRelation = getLocationSbbChildRelation();
		if (childRelation.isEmpty()) 
		{
			return (LocationSbbLocalObject) childRelation.create();
		}
		else {
			return (LocationSbbLocalObject) childRelation.iterator().next();
		}

	}
	...

	private void processRequest(ServerTransaction serverTransaction,
			Request request, ActivityContextInterface ac) {

	... 

		new ProxyMachine(getProxyConfigurator(), getLocationSbb(),
		this.addressFactory, this.headerFactory,
		this.messageFactory, this.provider)
		.processRequest(serverTransaction, request);

	...

	}

	class ProxyMachine extends MessageUtils implements MessageHandlerInterface {
		...

		protected LocationService reg = null;

		...
	
		public ProxyMachine(ProxyConfiguration config,
				LocationService registrarAccess, AddressFactory af,
				HeaderFactory hf, MessageFactory mf, SipProvider prov)
		throws ParseException {
			...
			reg = registrarAccess;
			...

		}
		
		/**
		 * Attempts to find a locally registered contact address for the given
		 * URI, using the location service interface.
		 */
		public LinkedList&lt;URI&gt; findLocalTarget(URI uri)
		throws SipSendErrorResponseException {
			String addressOfRecord = uri.toString();

			Map&lt;String, RegistrationBinding&gt; bindings = null;
			LinkedList&lt;URI&gt; listOfTargets = new LinkedList&lt;URI&gt;();
			try {
				bindings = reg.getBindings(addressOfRecord);
			} catch (LocationServiceException e) {

				e.printStackTrace();
				return listOfTargets;
			}

			if (bindings == null) {
				throw new SipSendErrorResponseException("User not found",
						Response.NOT_FOUND);
			}
			if (bindings.isEmpty()) {
				throw new SipSendErrorResponseException(
						"User temporarily unavailable",
						Response.TEMPORARILY_UNAVAILABLE);
			}

			Iterator it = bindings.values().iterator();
			URI target = null;
			while (it.hasNext()) {
				String contactAddress = ((RegistrationBinding)it.next()).getContactAddress();
				try {
					listOfTargets.add(af.createURI(contactAddress));
				} catch (ParseException e) {
					log.warn("Ignoring contact address "+contactAddress+" due to parse error",e);
				}
			}
			if (listOfTargets.size() == 0) {

				throw new SipSendErrorResponseException(
						"User temporarily unavailable",
						Response.TEMPORARILY_UNAVAILABLE);
			}
			return listOfTargets;
         }
    }
}

		</programlisting>
	</section>
</section>	
New to GrepCode? Check out our FAQ X