
en-US.Chapter-XML_Parser.xml Maven / Gradle / Ivy
The newest version!
<?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 "${ent.filename}"> %BOOK_ENTITIES; ]> <chapter id="xml_parser"> <title>XML Parser API</title> <para> &THIS.PLATFORM; &THIS.APPLICATION; exposes easy to use Java framework to serialize and deserialize the MAP messages. So if you are using Java Servlets for your business logic, this framework will make it very easy to compose your business logic. However using this framework is not mandatory and you are free to create your own XML parser. </para> <section id="mvn_dependency"> <title>Maven dependency</title> <para> If you are using maven to build your project define following dependencies to use XML Framework in your project </para> <programlisting language="XML" role="XML"><![CDATA[ <dependency> <groupId>org.mobicents.ussd</groupId> <artifactId>xml</artifactId> <version>6.1.5.GA</version> </dependency> ]]></programlisting> <note> <title>Note</title> <para> Mobicents maintains the archieve in private repository. please add the following in your pom's repositories tag </para> <programlisting language="XML" role="XML"><![CDATA[ <repository> <id>mobicents-releases-repository</id> <name>Mobicents Releases Repository</name> <url>http://telestax.artifactoryonline.com/telestax/releases</url> <releases> <enabled>true</enabled> <updatePolicy>never</updatePolicy> </releases> <snapshots> <enabled>false</enabled> <updatePolicy>never</updatePolicy> </snapshots> </repository> <repository> <id>mobicents-snapshots-repository</id> <name>Mobicents Snapshots Repository</name> <url>http://telestax.artifactoryonline.com/telestax/snapshots</url> <releases> <enabled>false</enabled> <updatePolicy>never</updatePolicy> </releases> <snapshots> <enabled>true</enabled> <updatePolicy>never</updatePolicy> </snapshots> </repository> ]]></programlisting> </note> <important> <title>Important</title> <para>Mobicents Releases and Snapshots Repository are password protected. Ask support to get your password.</para> <para>Once you have user name and password add following to your servers definition in your maven settings located in M2_HOME/conf/settings.xml</para> <programlisting language="XML" role="XML"><![CDATA[ <server> <id>mobicents-releases-repository</id> <username>xxx</username> <password>xxx</password> </server> <server> <id>mobicents-snapshots-repository</id> <username>xxx</username> <password>xxx</password> </server> ]]></programlisting> </important> </section> <section id="simple_tree_based_example"> <title>Simple example - Tree based Menu </title> <para> Below is the simple <classname>HttpServlet</classname> example to create a tree based menu structure. The flow of message is same as shown in <xref linkend="HTTP_Messages - USSD Pull">&THIS.PLATFORM; HTTP message flow</xref>. </para> <programlisting language="Java" role="JAVA"> public class TestServlet extends HttpServlet {<co linkends="ex1" id="example.servlet.co1" /> private static final Logger logger = Logger.getLogger(TestServlet.class); private EventsSerializeFactory factory = null; @Override public void init() { factory = new EventsSerializeFactory();<co linkends="ex2" id="example.servlet.co2"/> } @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { } @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {<co linkends="ex3" id="example.servlet.co3"/> ServletInputStream is = request.getInputStream(); try { Dialog original = factory.deserialize(is);<co linkends="ex4 " id="example.servlet.co4"/> HttpSession session = request.getSession(true);<co linkends="ex5" id="example.servlet.co5"/> if (logger.isInfoEnabled()) { logger.info("doPost. HttpSession=" + session.getId() + " Dialog = " + original); } USSDString ussdStr = null; byte[] data = null; final FastList<MAPMessage> capMessages = original.getMAPMessages(); MessageType messageType = original.getTCAPMessageType(); // This is initial request, if its not NTFY, we need session for (FastList.Node<MAPMessage> n = capMessages.head(), end = capMessages .tail(); (n = n.getNext()) != end;) { final MAPMessage rawMessage = n.getValue(); final MAPMessageType type = rawMessage.getMessageType(); switch (messageType) { case Begin:<co linkends="ex6" id="example.servlet.co6"/> switch (mapMessage.getMessageType()) { case processUnstructuredSSRequest_Request: ProcessUnstructuredSSRequest processUnstructuredSSRequest = (ProcessUnstructuredSSRequest) mapMessage; CBSDataCodingScheme cbsDataCodingScheme = processUnstructuredSSRequest .getDataCodingScheme(); if (logger.isInfoEnabled()) { logger.info("Received ProcessUnstructuredSSRequestIndication USSD String=" + processUnstructuredSSRequest.getUSSDString().getString()); } session.setAttribute( "ProcessUnstructuredSSRequest_InvokeId", processUnstructuredSSRequest.getInvokeId());<co linkends="ex7" id="example.servlet.co7"/> //You business logic here and finally send back response //Urdu //CBSDataCodingScheme cbsDataCodingSchemeUrdu = new CBSDataCodingSchemeImpl(72); //ussdStr = new USSDStringImpl("\u062C\u0645\u064A\u0639 \u0627\u0644\u0645\u0633\u062A\u0639\u0645\u0644\u064A\u0646 \u0627\u0644\u0622\u062E\u0631\u064A\u0646 \u062A\u0645 \u0625\u0636\u0627\u0641\u062A\u0647\u0645", // cbsDataCodingSchemeUrdu, null); //UnstructuredSSRequest unstructuredSSRequestIndication = new UnstructuredSSRequestImpl( // cbsDataCodingSchemeUrdu, ussdStr, null, null); //English ussdStr = new USSDStringImpl( "USSD String : Hello World\n 1. Balance\n 2. Texts Remaining", cbsDataCodingScheme, null); UnstructuredSSRequest unstructuredSSRequestIndication = new UnstructuredSSRequestImpl( cbsDataCodingScheme, ussdStr, null, null);<co linkends="ex8" id="example.servlet.co8"/> original.reset(); original.setTCAPMessageType(MessageType.Continue); original.addMAPMessage(unstructuredSSRequestIndication);<co linkends="ex9" id="example.servlet.co9"/> data = factory.serialize(copy);<co linkends="ex10" id="example.servlet.co10"/> response.getOutputStream().write(data); response.flushBuffer();<co linkends="ex11" id="example.servlet.co11"/> break; default: // This is error. If its begin it should be only Process // Unstructured SS Request logger.error("Received Dialog BEGIN but message is not ProcessUnstructuredSSRequestIndication. Message=" + mapMessage); break; } break; case Continue:<co linkends="ex12" id="example.servlet.co12"/> switch (type) { case unstructuredSSRequest_Response: UnstructuredSSResponse unstructuredSSResponse = (UnstructuredSSResponseImpl) rawMessage; CBSDataCodingScheme cbsDataCodingScheme = unstructuredSSResponse .getDataCodingScheme(); long invokeId = (Long) session .getAttribute("ProcessUnstructuredSSRequest_InvokeId"); USSDString ussdStringObj = unstructuredSSResponse .getUSSDString(); String ussdString = null; if (ussdStringObj != null) { ussdString = ussdStringObj.getString(null); } logger.info("Received UnstructuredSSResponse USSD String=" + ussdString + " HttpSession=" + session.getId() + " invokeId=" + invokeId); cbsDataCodingScheme = new CBSDataCodingSchemeImpl(0x0f); ussdStr = new USSDStringImpl("Thank You!", null, null); ProcessUnstructuredSSResponse processUnstructuredSSResponse = new ProcessUnstructuredSSResponseImpl( cbsDataCodingScheme, ussdStr); processUnstructuredSSResponse.setInvokeId(invokeId); original.reset(); original.setTCAPMessageType(MessageType.End); original.addMAPMessage(processUnstructuredSSResponse); original.close(false); data = factory.serialize(original); response.getOutputStream().write(data); response.flushBuffer(); try { session.invalidate(); } catch (Exception e) { session.invalidate(); logger.error("Error while invalidating HttpSession=" + session.getId()); } break; default: // This is error. If its begin it should be only Process // Unstructured SS Request logger.error("Received Dialog CONTINUE but message is not UnstructuredSSResponseIndication. Message=" + rawMessage); break; } break; case ABORT: // The Dialog is aborted, lets do cleaning here try { session.invalidate(); } catch (Exception e) { session.invalidate(); logger.error("Error while invalidating HttpSession=" + session.getId()); } break; } } catch (XMLStreamException e) { logger.error("Error while processing received XML", e); } } } </programlisting> <calloutlist> <callout arearefs="example.servlet.co1" id="ex1"> <para> A simple <classname>HttpServlet</classname> class that consumes the MAP messages, process the business logic and sends back response. </para> </callout> <callout arearefs="example.servlet.co2" id="ex2"> <para> Initiate the <classname>EventsSerializeFactory</classname> that can serialize/de-serialize the <classname>Dialog</classname> object. <classname>EventsSerializeFactory</classname> is further explained in <xref linkend="EventsSerializeFactory">EventsSerializeFactory</xref> </para> </callout> <callout arearefs="example.servlet.co3" id="ex3"> <para> &THIS.PLATFORM; &THIS.APPLICATION; sends the HTTP POST request with XML Payload. The <classname>HttpServlet</classname> should always have business logic in <methodname>doPost</methodname> method. </para> </callout> <callout arearefs="example.servlet.co4" id="ex4"> <para> De-serialize received payload to <classname>Dialog</classname> object. <classname>Dialog</classname> is further explained in <xref linkend="Dialog">Dialog</xref> </para> </callout> <callout arearefs="example.servlet.co5" id="ex5"> <para> Get access to underlying <classname>HttpSession</classname>. Again this is not mandatory and if your business logic is such that you don't need tree based menu but simple request-response, you don't have to store any state in <classname>HttpSession</classname>. </para> </callout> <callout arearefs="example.servlet.co6" id="ex6"> <para> If this is new request, the <classname>Dialog</classname> type is always <literal>BEGIN</literal> and the first MAP message in this new request will always be <classname>ProcessUnstructuredSSRequest</classname> </para> </callout> <callout arearefs="example.servlet.co7" id="ex7"> <para> Set the invokeId from <classname>ProcessUnstructuredSSRequest</classname> in <classname>HttpSession</classname> so application can send back <classname>ProcessUnstructuredSSResponse</classname> with same invokeId later. Again application don't have to maintain the invokeId if its directly sending back <classname>ProcessUnstructuredSSResponse</classname>. </para> </callout> <callout arearefs="example.servlet.co8" id="ex8"> <para> Create a new <classname>UnstructuredSSRequest</classname> with custom message and send back to USSD gateway which will in-turn send it to Mobile User who initiated the request. By sending <classname>UnstructuredSSRequest</classname> application indicates that its waiting for further input from user. The application may receive <classname>UnstructuredSSResponse</classname> or <literal>ABORT</literal> in which case application sghould do cleaning of resources. </para> </callout> <callout arearefs="example.servlet.co9" id="ex9"> <para> Reset original <classname>Dialog</classname> set message type <literal>Continue</literal> add <classname>UnstructuredSSRequest</classname> message. Remember this <classname>Dialog</classname> instance is only for serializing our request and shouldn't be confused with MAP Dialog or HttpSession. Both MAP Dialog and HttpSession lives as long as conversation is alive between USSD gateway and Application for each unique user. </para> </callout> <callout arearefs="example.servlet.co10" id="ex10"> <para> Serialize new <classname>Dialog</classname> object to push byte array into <classname>HttpResponse</classname> </para> </callout> <callout arearefs="example.servlet.co11" id="ex11"> <para> Send the response back </para> </callout> <callout arearefs="example.servlet.co12" id="ex12"> <para> Once the user replies back, application gets <literal>CONTINUE</literal> Dialog with <classname>UnstructuredSSResponse</classname>. At this time depending on business logic, application can again send new <classname>UnstructuredSSRequest</classname> and expect back response from user or send final <classname>ProcessUnstructuredSSResponse</classname> response and close the session. </para> </callout> </calloutlist> <!--</programlistingco>--> </section> <section id="EventsSerializeFactory"> <title>EventsSerializeFactory</title> <para> This section provides the details for <classname>EventsSerializeFactory</classname> </para> <programlisting language="Java" role="JAVA">public class EventsSerializeFactory { private static final String DIALOG = "dialog"; private static final String TYPE = "type"; private static final String TAB = "\t"; final XMLBinding binding = new XMLBinding(); public EventsSerializeFactory() { binding.setAlias(Dialog.class, DIALOG); binding.setClassAttribute(TYPE); } /** * Serialize passed {@link Dialog} object * * @param dialog * @return serialized byte array * @throws XMLStreamException * Exception if serialization fails */ public byte[] serialize(Dialog dialog) throws XMLStreamException { final ByteArrayOutputStream baos = new ByteArrayOutputStream(); final XMLObjectWriter writer = XMLObjectWriter.newInstance(baos); try { writer.setBinding(binding); writer.setIndentation(TAB); writer.write(dialog, DIALOG, Dialog.class); writer.flush(); byte[] data = baos.toByteArray(); return data; } finally { writer.close(); } } /** * De-serialize the byte[] into {@link Dialog} object * * @param data * @return de-serialized Dialog Object * @throws XMLStreamException * Exception if de-serialization fails */ public Dialog deserialize(byte[] data) throws XMLStreamException { final ByteArrayInputStream bais = new ByteArrayInputStream(data); final XMLObjectReader reader = XMLObjectReader.newInstance(bais); try { Dialog dialog = reader.read(DIALOG, Dialog.class); return dialog; } finally { reader.close(); } } /** * De-serialize passed {@link InputStream} into {@link Dialog} object * * @param is * @return de-serialized Dialog Object * @throws XMLStreamException * Exception if de-serialization fails */ public Dialog deserialize(InputStream is) throws XMLStreamException { final XMLObjectReader reader = XMLObjectReader.newInstance(is); try { Dialog dialog = reader.read(DIALOG, Dialog.class); return dialog; } finally { reader.close(); } } } </programlisting> <para> <itemizedlist> <listitem> <para> The <methodname>serialize</methodname> method serializes Dialog and retruns back byte array. </para> </listitem> <listitem> <para> The <methodname>deserialize</methodname> is overloaded method. Application can either pass <classname>byte[]</classname> or <classname>InputStream</classname> and de-serializes the stream of data to Dialog object. </para> </listitem> </itemizedlist> </para> </section> <section id="Dialog"> <title>Dialog</title> <para> This section provides the details for <classname>XmlMAPDialog</classname> </para> <programlisting language="Java" role="JAVA"><![CDATA[public class XmlMAPDialog implements Serializable { ..... ..... public XmlMAPDialog() { super(); } /** * */ public XmlMAPDialog(MAPApplicationContext appCntx, SccpAddress localAddress, SccpAddress remoteAddress, Long localId, Long remoteId, AddressString destReference, AddressString origReference) { this.appCntx = appCntx; this.localAddress = localAddress; this.remoteAddress = remoteAddress; this.localId = localId; this.remoteId = remoteId; this.destReference = destReference; this.origReference = origReference; } @Override public void abort(MAPUserAbortChoice mapUserAbortChoice) throws MAPException { this.mapUserAbortChoice = mapUserAbortChoice; } @Override public void addEricssonData(IMSI arg0, AddressString arg1) { // TODO Auto-generated method stub } @Override public boolean cancelInvocation(Long arg0) throws MAPException { throw new MAPException(new OperationNotSupportedException()); } @Override public void close(boolean prearrangedEnd) throws MAPException { this.prearrangedEnd = prearrangedEnd; } @Override public void closeDelayed(boolean arg0) throws MAPException { throw new MAPException(new OperationNotSupportedException()); } @Override public MAPApplicationContext getApplicationContext() { return this.appCntx; } @Override public SccpAddress getLocalAddress() { return this.localAddress; } @Override public Long getLocalDialogId() { return this.localId; } @Override public int getMaxUserDataLength() { return 0; } @Override public int getMessageUserDataLengthOnClose(boolean arg0) throws MAPException { // TODO Auto-generated method stub return 0; } @Override public int getMessageUserDataLengthOnSend() throws MAPException { // TODO Auto-generated method stub return 0; } @Override public AddressString getReceivedDestReference() { return this.destReference; } @Override public MAPExtensionContainer getReceivedExtensionContainer() { // TODO Auto-generated method stub return null; } @Override public AddressString getReceivedOrigReference() { return this.origReference; } @Override public SccpAddress getRemoteAddress() { return this.remoteAddress; } @Override public Long getRemoteDialogId() { return this.remoteId; } @Override public boolean getReturnMessageOnError() { return this.returnMessageOnError; } @Override public MAPServiceBase getService() { return null; } @Override public MAPDialogState getState() { return this.state; } @Override public MessageType getTCAPMessageType() { return this.messageType; } @Override public Object getUserObject() { return this.userObject; } @Override public void keepAlive() { // TODO Auto-generated method stub } @Override public void processInvokeWithoutAnswer(Long invokeId) { this.processInvokeWithoutAnswerIds.add(invokeId); } @Override public void refuse(Reason refuseReason) throws MAPException { this.refuseReason = refuseReason; } @Override public void release() { // TODO Auto-generated method stub } @Override public void resetInvokeTimer(Long arg0) throws MAPException { throw new MAPException(new OperationNotSupportedException()); } @Override public void send() throws MAPException { throw new MAPException(new OperationNotSupportedException()); } @Override public void sendDelayed() throws MAPException { throw new MAPException(new OperationNotSupportedException()); } @Override public void sendErrorComponent(Long invokeId, MAPErrorMessage mapErrorMessage) throws MAPException { this.errorComponents.put(invokeId, mapErrorMessage); } @Override public void sendInvokeComponent(Invoke arg0) throws MAPException { throw new MAPException(new OperationNotSupportedException()); } @Override public void sendRejectComponent(Long arg0, Problem arg1) throws MAPException { // TODO Auto-generated method stub } @Override public void sendReturnResultComponent(ReturnResult arg0) throws MAPException { // TODO Auto-generated method stub } @Override public void sendReturnResultLastComponent(ReturnResultLast arg0) throws MAPException { throw new MAPException(new OperationNotSupportedException()); } @Override public void setExtentionContainer(MAPExtensionContainer arg0) { // TODO Auto-generated method stub } @Override public void setLocalAddress(SccpAddress origAddress) { this.localAddress = origAddress; } @Override public void setRemoteAddress(SccpAddress destAddress) { this.remoteAddress = destAddress; } @Override public void setReturnMessageOnError(boolean returnMessageOnError) { this.returnMessageOnError = returnMessageOnError; } @Override public void setUserObject(Object obj) { this.userObject = obj.toString(); } /** * Non MAPDialog methods */ public void addMAPMessage(MAPMessage mapMessage) { this.mapMessages.add(mapMessage); } public boolean removeMAPMessage(MAPMessage mapMessage) { return this.mapMessages.remove(mapMessage); } public FastList<MAPMessage> getMAPMessages() { return this.mapMessages; } public FastList<Long> getProcessInvokeWithoutAnswerIds() { return this.processInvokeWithoutAnswerIds; } public ErrorComponentMap<Long, MAPErrorMessage> getErrorComponents() { return errorComponents; } public MAPUserAbortChoice getMAPUserAbortChoice() { return this.mapUserAbortChoice; } public MAPAbortProviderReason getMapAbortProviderReason() { return mapAbortProviderReason; } public void setMapAbortProviderReason(MAPAbortProviderReason mapAbortProviderReason) { this.mapAbortProviderReason = mapAbortProviderReason; } public MAPRefuseReason getMapRefuseReason() { return mapRefuseReason; } public void setMapRefuseReason(MAPRefuseReason mapRefuseReason) { this.mapRefuseReason = mapRefuseReason; } public Boolean getDialogTimedOut() { return dialogTimedOut; } public void setDialogTimedOut(Boolean dialogTimedOut) { this.dialogTimedOut = dialogTimedOut; } public Boolean getPrearrangedEnd() { return this.prearrangedEnd; } public void setTCAPMessageType(MessageType messageType) { this.messageType = messageType; } public boolean isRedirectRequest() { return redirectRequest; } public void setRedirectRequest(boolean redirectRequest) { this.redirectRequest = redirectRequest; } public void reset() { this.mapMessages.clear(); this.processInvokeWithoutAnswerIds.clear(); this.errorComponents.clear(); } } ]]></programlisting> </section> </chapter>
© 2015 - 2025 Weber Informatics LLC | Privacy Policy