Author: sumedha Date: Wed Nov 12 12:14:03 2008 New Revision: 713490 URL: http://svn.apache.org/viewvc?rev=713490&view=rev Log: more support for accepting request messages from IM clients & sending back the responses. Now we have fully functional code to test this scenario. However error handling needs to be improved. Committing with TODOs
Modified: webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/XMPPSender.java webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/util/XMPPConstants.java webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/util/XMPPPacketListener.java Modified: webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/XMPPSender.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/XMPPSender.java?rev=713490&r1=713489&r2=713490&view=diff ============================================================================== --- webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/XMPPSender.java (original) +++ webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/XMPPSender.java Wed Nov 12 12:14:03 2008 @@ -19,16 +19,18 @@ package org.apache.axis2.transport.xmpp; -import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import javax.xml.namespace.QName; + import org.apache.axiom.om.OMElement; import org.apache.axis2.AxisFault; import org.apache.axis2.Constants; import org.apache.axis2.client.Options; import org.apache.axis2.context.ConfigurationContext; import org.apache.axis2.context.MessageContext; +import org.apache.axis2.description.AxisMessage; import org.apache.axis2.description.AxisOperation; import org.apache.axis2.description.AxisService; import org.apache.axis2.description.Parameter; @@ -44,8 +46,16 @@ import org.apache.axis2.transport.xmpp.util.XMPPServerCredentials; import org.apache.axis2.transport.xmpp.util.XMPPUtils; import org.apache.axis2.util.Utils; +import org.apache.axis2.wsdl.WSDLConstants; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.ws.commons.schema.XmlSchemaAll; +import org.apache.ws.commons.schema.XmlSchemaComplexType; +import org.apache.ws.commons.schema.XmlSchemaElement; +import org.apache.ws.commons.schema.XmlSchemaGroupBase; +import org.apache.ws.commons.schema.XmlSchemaParticle; +import org.apache.ws.commons.schema.XmlSchemaSequence; +import org.apache.ws.commons.schema.XmlSchemaType; import org.jivesoftware.smack.Chat; import org.jivesoftware.smack.ChatManager; import org.jivesoftware.smack.XMPPConnection; @@ -187,11 +197,31 @@ WSDL2Constants.MEP_URI_OUT_IN.equals( msgCtx.getOperationContext().getAxisOperation().getMessageExchangePattern()); - OMElement msgElement = msgCtx.getEnvelope(); - String soapMessage = msgElement.toString(); //int endOfXMLDeclaration = soapMessage.indexOf("?>"); //String modifiedSOAPMessage = soapMessage.substring(endOfXMLDeclaration+2); - message.setBody(soapMessage); + + OMElement msgElement; + String messageToBeSent = ""; + + //TODO : need to read from a constant + if("xmpp/text".equals(xmppOutTransportInfo.getContentType())){ + //if request is received from a chat client, whole soap envelope + //should not be sent. + OMElement soapBodyEle = msgCtx.getEnvelope().getBody(); + OMElement responseEle = soapBodyEle.getFirstElement(); + if(responseEle != null){ + msgElement = responseEle.getFirstElement(); + }else{ + msgElement = responseEle; + } + }else{ + //if request received from a ws client whole soap envelope + //must be sent. + msgElement = msgCtx.getEnvelope(); + } + messageToBeSent = msgElement.toString(); + message.setBody(messageToBeSent); + XMPPClientSidePacketListener xmppClientSidePacketListener = null; if(waitForResponse && !msgCtx.isServerSide()){ @@ -236,22 +266,21 @@ Object obj = msgCtx.getProperty(XMPPConstants.MESSAGE_FROM_CHAT); if(obj != null){ String message = (String)obj; - String response = ""; - if(("help".compareToIgnoreCase(message.trim()) == 0) - || "?".equals(message)){ - response = prepareHelpTextForChat(); - }else if("listServices".equals(message.trim())){ - response = prepareServicesList(msgCtx); - }else if (message.trim().startsWith("getOperations")){ - response = prepareOperationList(msgCtx,message); + String response = ""; + + if(message.trim().startsWith("help")){ + response = prepareHelpTextForChat(); + }else if(message.trim().startsWith("listServices")){ + response = prepareServicesList(msgCtx); + }else if (message.trim().startsWith("getOperations")){ + response = prepareOperationList(msgCtx,message); }else{ //TODO add support for more help commands } - sendChatMessage(msgCtx,response); + sendChatMessage(msgCtx,response); } - } - - + } + /** * Prepares a list of service names deployed in current runtime * @param msgCtx @@ -272,13 +301,7 @@ int index = 1; while(itrOperations.hasNext()){ AxisOperation operation = (AxisOperation)itrOperations.next(); - //ArrayList params = operation.getParameters(); - //Iterator itrParams = params.iterator(); - String parameterList = ""; - //while(itrParams.hasNext()){ - // Parameter param = (Parameter) itrParams.next(); - // parameterList = param.getName()+","; - //} + String parameterList = getParameterListForOperation(operation); sb.append(index +"."+operation.getName().getLocalPart()+"("+parameterList+")"+"\n"); index++; } @@ -289,6 +312,47 @@ return sb.toString(); } + /** + * Retrieves list of parameter names & their type for a given operation + * @param operation + */ + private static String getParameterListForOperation(AxisOperation operation) { + //Logic copied from BuilderUtil.buildsoapMessage(...) + StringBuffer paramList = new StringBuffer(); + AxisMessage axisMessage = + operation.getMessage(WSDLConstants.MESSAGE_LABEL_IN_VALUE); + XmlSchemaElement xmlSchemaElement = axisMessage.getSchemaElement(); + if(xmlSchemaElement != null){ + XmlSchemaType schemaType = xmlSchemaElement.getSchemaType(); + if (schemaType instanceof XmlSchemaComplexType) { + XmlSchemaComplexType complexType = ((XmlSchemaComplexType)schemaType); + XmlSchemaParticle particle = complexType.getParticle(); + if (particle instanceof XmlSchemaSequence || particle instanceof XmlSchemaAll) { + XmlSchemaGroupBase xmlSchemaGroupBase = (XmlSchemaGroupBase)particle; + Iterator iterator = xmlSchemaGroupBase.getItems().getIterator(); + + while (iterator.hasNext()) { + XmlSchemaElement innerElement = (XmlSchemaElement)iterator.next(); + QName qName = innerElement.getQName(); + if (qName == null && innerElement.getSchemaTypeName() + .equals(org.apache.ws.commons.schema.constants.Constants.XSD_ANYTYPE)) { + break; + } + long minOccurs = innerElement.getMinOccurs(); + boolean nillable = innerElement.isNillable(); + String name = + qName != null ? qName.getLocalPart() : innerElement.getName(); + String type = innerElement.getSchemaTypeName().toString(); + paramList.append(","+type +" " +name); + } + } + } + } + //remove first "," + String list = paramList.toString(); + return list.replaceFirst(",", ""); + } + /** * Prepares a list of service names deployed in current runtime Modified: webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/util/XMPPConstants.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/util/XMPPConstants.java?rev=713490&r1=713489&r2=713490&view=diff ============================================================================== --- webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/util/XMPPConstants.java (original) +++ webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/util/XMPPConstants.java Wed Nov 12 12:14:03 2008 @@ -49,5 +49,5 @@ //This is set to true, if a request message is sent through XMPPSender //Used to distinguish messages coming from chat clients. public static final String CONTAINS_SOAP_ENVELOPE = "transport.xmpp.containsSOAPEnvelope"; - public static final String MESSAGE_FROM_CHAT = "transport.xmpp.message.from.chat"; + public static final String MESSAGE_FROM_CHAT = "transport.xmpp.message.from.chat"; } Modified: webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/util/XMPPPacketListener.java URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/util/XMPPPacketListener.java?rev=713490&r1=713489&r2=713490&view=diff ============================================================================== --- webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/util/XMPPPacketListener.java (original) +++ webservices/commons/trunk/modules/transport/modules/xmpp/src/org/apache/axis2/transport/xmpp/util/XMPPPacketListener.java Wed Nov 12 12:14:03 2008 @@ -19,13 +19,26 @@ package org.apache.axis2.transport.xmpp.util; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.HashMap; +import java.util.StringTokenizer; +import java.util.concurrent.Executor; + +import javax.xml.parsers.FactoryConfigurationError; +import javax.xml.stream.XMLStreamException; + import org.apache.axiom.om.OMException; import org.apache.axiom.soap.SOAPEnvelope; +import org.apache.axiom.soap.SOAPFactory; +import org.apache.axiom.soap.impl.llom.soap11.SOAP11Factory; import org.apache.axis2.AxisFault; import org.apache.axis2.Constants; import org.apache.axis2.addressing.EndpointReference; +import org.apache.axis2.builder.BuilderUtil; import org.apache.axis2.context.ConfigurationContext; import org.apache.axis2.context.MessageContext; +import org.apache.axis2.description.AxisOperation; import org.apache.axis2.description.AxisService; import org.apache.axis2.description.TransportInDescription; import org.apache.axis2.description.TransportOutDescription; @@ -33,6 +46,7 @@ import org.apache.axis2.transport.TransportUtils; import org.apache.axis2.transport.xmpp.XMPPSender; import org.apache.axis2.util.MessageContextBuilder; +import org.apache.axis2.util.MultipleEntryHashMap; import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -40,13 +54,6 @@ import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Packet; -import javax.xml.parsers.FactoryConfigurationError; -import javax.xml.stream.XMLStreamException; -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.util.HashMap; -import java.util.concurrent.Executor; - public class XMPPPacketListener implements PacketListener { private static final Log log = LogFactory.getLog(XMPPPacketListener.class); private XMPPConnectionFactory xmppConnectionFactory = null; @@ -166,16 +173,21 @@ } InputStream inputStream = new ByteArrayInputStream(messageBody.getBytes()); - SOAPEnvelope envelope; + SOAPEnvelope envelope = null; try { Object obj = message.getProperty(XMPPConstants.CONTAINS_SOAP_ENVELOPE); if(obj != null && ((Boolean)obj).booleanValue()){ envelope = TransportUtils.createSOAPMessage(msgContext, inputStream, "text/xml"); - msgContext.setEnvelope(envelope); msgContext.setProperty(XMPPConstants.CONTAINS_SOAP_ENVELOPE, new Boolean(true)); }else{ - //A text message has been received from a chat client, send it along with message context - msgContext.setProperty(XMPPConstants.MESSAGE_FROM_CHAT, messageBody); + //A text message has been received from a chat client + //This message could either be a service call or a help command + if(!(messageContainsCommandsFromChat(messageBody,msgContext))){ + envelope = createSOAPEnvelopeForRawMessage(msgContext, messageBody); + } + } + if(envelope != null){ + msgContext.setEnvelope(envelope); } }catch (OMException e) { log.error(logMsg, e); @@ -192,6 +204,94 @@ } } + /** + * In the direct chat client scenario, client can send commands & retrieve details + * on available services, operations,etc. This method checks if a client has sent + * such command. Only limited set of commands are available as of now. + * @param message + * @param msgContext + * @return + */ + private boolean messageContainsCommandsFromChat(String message,MessageContext msgContext){ + boolean containsKnownCommand = false; + if(message.trim().startsWith("help")){ + containsKnownCommand = true; + }else if(message.trim().startsWith("listServices")){ + containsKnownCommand = true; + }else if (message.trim().startsWith("getOperations")){ + containsKnownCommand = true; + } + + if(containsKnownCommand){ + msgContext.setProperty(XMPPConstants.MESSAGE_FROM_CHAT,message.trim()); + } + return containsKnownCommand; + } + + /** + * Creates a SOAP envelope using details found in chat message. + * @param msgCtx + * @param chatMessage + * @return + */ + private SOAPEnvelope createSOAPEnvelopeForRawMessage(MessageContext msgCtx,String chatMessage) + throws AxisFault{ + //TODO : need to add error handling logic + String callRemoved = chatMessage.replaceFirst("call", ""); + //extract Service name + String serviceName = callRemoved.trim().substring(0, callRemoved.indexOf(":")-1); + String operationName = callRemoved.trim().substring(callRemoved.indexOf(":"), callRemoved.indexOf("(")-1); + + //Extract parameters from IM message + String parameterList = callRemoved.trim().substring(callRemoved.indexOf("("),callRemoved.trim().length()-1); + StringTokenizer st = new StringTokenizer(parameterList,","); + MultipleEntryHashMap parameterMap = new MultipleEntryHashMap(); + while(st.hasMoreTokens()){ + String token = st.nextToken(); + String name = token.substring(0, token.indexOf("=")); + String value = token.substring(token.indexOf("=")+1); + parameterMap.put(name, value); + } + + SOAPEnvelope envelope = null; + try { + msgCtx.setProperty(XMPPConstants.CONTAINS_SOAP_ENVELOPE, new Boolean(true)); + if(serviceName != null && serviceName.trim().length() > 0){ + AxisService axisService = msgCtx.getConfigurationContext().getAxisConfiguration().getService(serviceName); + msgCtx.setAxisService(axisService); + + AxisOperation axisOperation = axisService.getOperationBySOAPAction("urn:"+operationName); + if(axisOperation != null){ + msgCtx.setAxisOperation(axisOperation); + } + } + + if(operationName != null && operationName.trim().length() > 0){ + msgCtx.setSoapAction("urn:"+operationName); + } + + XMPPOutTransportInfo xmppOutTransportInfo = (XMPPOutTransportInfo)msgCtx.getProperty( + org.apache.axis2.Constants.OUT_TRANSPORT_INFO); + //This should be only set for messages received via chat. + //TODO : need to read from a constant + xmppOutTransportInfo.setContentType("xmpp/text"); + + msgCtx.setServerSide(true); + + //TODO : need to support SOAP12 as well + SOAPFactory soapFactory = new SOAP11Factory(); + envelope = BuilderUtil.buildsoapMessage(msgCtx, parameterMap, + soapFactory); + //TODO : improve error handling & messages + } catch (AxisFault e) { + throw new AxisFault(e.getMessage()); + } catch (OMException e) { + throw new AxisFault(e.getMessage()); + } catch (FactoryConfigurationError e) { + throw new AxisFault(e.getMessage()); + } + return envelope; + } /** * The actual Runnable Worker implementation which will process the @@ -215,7 +315,7 @@ AxisEngine.receive(msgCtx); } }else{ - //Send a text reply message to chat client + //Send a text reply message to command received from chat client XMPPSender.processChatMessage(msgCtx); } } catch (AxisFault e) { @@ -232,4 +332,4 @@ } } } -} +} \ No newline at end of file