Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.axis2.transport.xmpp; import java.util.Iterator; import java.util.Map; import java.util.UUID; import javax.xml.namespace.QName; import org.apache.axiom.om.OMElement; import org.apache.axiom.soap.SOAP12Version; import org.apache.axiom.soap.SOAPVersion; 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; import org.apache.axis2.description.TransportOutDescription; import org.apache.axis2.description.WSDL2Constants; import org.apache.axis2.handlers.AbstractHandler; import org.apache.axis2.transport.OutTransportInfo; import org.apache.axis2.transport.TransportSender; import org.apache.axis2.transport.http.HTTPConstants; import org.apache.axis2.transport.xmpp.util.XMPPClientResponseManager; import org.apache.axis2.transport.xmpp.util.XMPPConnectionFactory; import org.apache.axis2.transport.xmpp.util.XMPPConstants; import org.apache.axis2.transport.xmpp.util.XMPPOutTransportInfo; 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; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.filter.PacketFilter; import org.jivesoftware.smack.filter.PacketTypeFilter; import org.jivesoftware.smack.packet.Message; public class XMPPSender extends AbstractHandler implements TransportSender { static Log log = null; XMPPServerCredentials serverCredentials; private XMPPClientResponseManager xmppClientSidePacketListener; private XMPPConnectionFactory defaultConnectionFactory; public XMPPSender() { log = LogFactory.getLog(XMPPSender.class); xmppClientSidePacketListener = new XMPPClientResponseManager(); } public void cleanup(MessageContext msgContext) throws AxisFault { } /** * Initialize the transport sender by reading pre-defined connection factories for * outgoing messages. These will create sessions (one per each destination dealt with) * to be used when messages are being sent. * @param confContext the configuration context * @param transportOut the transport sender definition from axis2.xml * @throws AxisFault on error */ public void init(ConfigurationContext confContext, TransportOutDescription transportOut) throws AxisFault { //if connection details are available from axis configuration //use those & connect to jabber server(s) serverCredentials = new XMPPServerCredentials(); getConnectionDetailsFromAxisConfiguration(transportOut); defaultConnectionFactory = new XMPPConnectionFactory(); } public void stop() { } public InvocationResponse invoke(MessageContext msgContext) throws AxisFault { String targetAddress = (String) msgContext.getProperty(Constants.Configuration.TRANSPORT_URL); if (targetAddress != null) { sendMessage(msgContext, targetAddress, null); } else if (msgContext.getTo() != null && !msgContext.getTo().hasAnonymousAddress()) { targetAddress = msgContext.getTo().getAddress(); if (!msgContext.getTo().hasNoneAddress()) { sendMessage(msgContext, targetAddress, null); } else { //Don't send the message. return InvocationResponse.CONTINUE; } } else if (msgContext.isServerSide()) { // get the out transport info for server side when target EPR is unknown sendMessage(msgContext, null, (OutTransportInfo) msgContext.getProperty(Constants.OUT_TRANSPORT_INFO)); } return InvocationResponse.CONTINUE; } /** * Send the given message over XMPP transport * * @param msgCtx the axis2 message context * @throws AxisFault on error */ public void sendMessage(MessageContext msgCtx, String targetAddress, OutTransportInfo outTransportInfo) throws AxisFault { XMPPConnection xmppConnection = null; XMPPOutTransportInfo xmppOutTransportInfo = null; XMPPConnectionFactory connectionFactory; //if on client side,create connection to xmpp server if (msgCtx.isServerSide()) { xmppOutTransportInfo = (XMPPOutTransportInfo) msgCtx .getProperty(org.apache.axis2.Constants.OUT_TRANSPORT_INFO); connectionFactory = xmppOutTransportInfo.getConnectionFactory(); } else { getConnectionDetailsFromClientOptions(msgCtx); connectionFactory = defaultConnectionFactory; } synchronized (this) { xmppConnection = connectionFactory.getXmppConnection(); if (xmppConnection == null) { connectionFactory.connect(serverCredentials); xmppConnection = connectionFactory.getXmppConnection(); } } Message message = new Message(); Options options = msgCtx.getOptions(); String serviceName = XMPPUtils.getServiceName(targetAddress); SOAPVersion version = msgCtx.getEnvelope().getVersion(); if (version instanceof SOAP12Version) { message.setProperty(XMPPConstants.CONTENT_TYPE, HTTPConstants.MEDIA_TYPE_APPLICATION_SOAP_XML + "; action=" + msgCtx.getSoapAction()); } else { message.setProperty(XMPPConstants.CONTENT_TYPE, HTTPConstants.MEDIA_TYPE_TEXT_XML); } if (targetAddress != null) { xmppOutTransportInfo = new XMPPOutTransportInfo(targetAddress); xmppOutTransportInfo.setConnectionFactory(defaultConnectionFactory); } else if (msgCtx.getTo() != null && !msgCtx.getTo().hasAnonymousAddress()) { //TODO } else if (msgCtx.isServerSide()) { xmppOutTransportInfo = (XMPPOutTransportInfo) msgCtx.getProperty(Constants.OUT_TRANSPORT_INFO); } try { if (msgCtx.isServerSide()) { message.setProperty(XMPPConstants.IS_SERVER_SIDE, new Boolean(false)); message.setProperty(XMPPConstants.IN_REPLY_TO, xmppOutTransportInfo.getInReplyTo()); message.setProperty(XMPPConstants.SEQUENCE_ID, xmppOutTransportInfo.getSequenceID()); } else { //message is going to be processed on server side message.setProperty(XMPPConstants.IS_SERVER_SIDE, new Boolean(true)); //we are sending a soap envelope as a message message.setProperty(XMPPConstants.CONTAINS_SOAP_ENVELOPE, new Boolean(true)); message.setProperty(XMPPConstants.SERVICE_NAME, serviceName); String action = options.getAction(); if (action == null) { AxisOperation axisOperation = msgCtx.getAxisOperation(); if (axisOperation != null) { action = axisOperation.getSoapAction(); } } if (action != null) { message.setProperty(XMPPConstants.ACTION, action); } } if (xmppConnection == null) { handleException("Connection to XMPP Server is not established."); } //initialize the chat manager using connection ChatManager chatManager = xmppConnection.getChatManager(); Chat chat = chatManager.createChat(xmppOutTransportInfo.getDestinationAccount(), null); boolean waitForResponse = msgCtx.getOperationContext() != null && WSDL2Constants.MEP_URI_OUT_IN .equals(msgCtx.getOperationContext().getAxisOperation().getMessageExchangePattern()); OMElement msgElement; String messageToBeSent = ""; if (XMPPConstants.XMPP_CONTENT_TYPE_STRING.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); String key = null; if (waitForResponse && !msgCtx.isServerSide()) { PacketFilter filter = new PacketTypeFilter(message.getClass()); xmppConnection.addPacketListener(xmppClientSidePacketListener, filter); key = UUID.randomUUID().toString(); xmppClientSidePacketListener.listenForResponse(key, msgCtx); message.setProperty(XMPPConstants.SEQUENCE_ID, key); } chat.sendMessage(message); log.debug("Sent message :" + message.toXML()); //If this is on client side, wait for the response from server. //Is this the best way to do this? if (waitForResponse && !msgCtx.isServerSide()) { xmppClientSidePacketListener.waitFor(key); //xmppConnection.disconnect(); log.debug("Received response sucessfully"); } } catch (XMPPException e) { log.error("Error occurred while sending the message : " + message.toXML(), e); handleException("Error occurred while sending the message : " + message.toXML(), e); } catch (InterruptedException e) { log.error("Error occurred while sending the message : " + message.toXML(), e); handleException("Error occurred while sending the message : " + message.toXML(), e); } finally { // if(xmppConnection != null && !msgCtx.isServerSide()){ // xmppConnection.disconnect(); // } } } /** * Process message requests that came in through chat clients * @param msgCtx * @throws AxisFault */ public static void processChatMessage(MessageContext msgCtx) throws AxisFault { Object obj = msgCtx.getProperty(XMPPConstants.MESSAGE_FROM_CHAT); if (obj != null) { String message = (String) obj; 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); } } /** * Prepares a list of service names deployed in current runtime * @param msgCtx * @return */ private static String prepareOperationList(MessageContext msgCtx, String chatMessage) { StringBuffer sb = new StringBuffer(); //extract service name String serviceName = chatMessage.replace("getOperations", ""); serviceName = serviceName.replaceAll(" ", ""); if (log.isDebugEnabled()) { log.debug("Finding operations for service :" + serviceName); } try { AxisService service = msgCtx.getConfigurationContext().getAxisConfiguration().getService(serviceName); Iterator itrOperations = service.getOperations(); int index = 1; while (itrOperations.hasNext()) { AxisOperation operation = (AxisOperation) itrOperations.next(); String parameterList = getParameterListForOperation(operation); sb.append(index + "." + operation.getName().getLocalPart() + "(" + parameterList + ")" + "\n"); index++; } } catch (AxisFault e) { log.error("Error occurred while retreiving AxisService : " + serviceName, e); sb.append("Error occurred while retrieving operations for service : " + serviceName); } 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 * @param msgCtx * @return */ private static String prepareServicesList(MessageContext msgCtx) { Map services = msgCtx.getConfigurationContext().getAxisConfiguration().getServices(); StringBuffer sb = new StringBuffer(); if (services != null && services.size() > 0) { Iterator itrServiceNames = services.keySet().iterator(); int index = 1; while (itrServiceNames.hasNext()) { String serviceName = (String) itrServiceNames.next(); sb.append(index + "." + serviceName + "\n"); index++; } } return sb.toString(); } /** * Generate help text for chat client * @return {@link String} */ private static String prepareHelpTextForChat() { StringBuffer helpText = new StringBuffer(); helpText.append("Following commands are supported :" + "\n"); helpText.append("-----------------------------------" + "\n"); helpText.append("1. listServices" + "\n"); helpText.append("2. getOperations <service-name>" + "\n"); helpText.append("3. call <service-name>:<operation>(<param1>,<param2>,...)" + "\n"); return helpText.toString(); } /** * Replies to IM clients via a chat message. The reply contains the invocation response as a string. * @param msgCtx * @param responseMsg * @throws AxisFault */ private static void sendChatMessage(MessageContext msgCtx, String responseMsg) throws AxisFault { XMPPConnection xmppConnection = null; XMPPOutTransportInfo xmppOutTransportInfo = null; Message message = new Message(); xmppOutTransportInfo = (XMPPOutTransportInfo) msgCtx.getProperty(Constants.OUT_TRANSPORT_INFO); if (xmppOutTransportInfo != null) { message.setProperty(XMPPConstants.IN_REPLY_TO, xmppOutTransportInfo.getInReplyTo()); xmppConnection = xmppOutTransportInfo.getConnectionFactory().getXmppConnection(); if (xmppConnection == null) { handleException("Connection to XMPP Server is not established."); } } else { handleException("Could not find message sender details."); } //initialize the chat manager using connection ChatManager chatManager = xmppConnection.getChatManager(); Chat chat = chatManager.createChat(xmppOutTransportInfo.getDestinationAccount(), null); try { message.setProperty(XMPPConstants.SEQUENCE_ID, xmppOutTransportInfo.getSequenceID()); message.setBody(responseMsg); chat.sendMessage(message); log.debug("Sent message :" + message.toXML()); } catch (XMPPException e) { XMPPSender.handleException("Error occurred while sending the message : " + message.toXML(), e); } } /** * Extract connection details from axis2.xml's transportsender section * @param serverCredentials * @param transportOut */ private void getConnectionDetailsFromAxisConfiguration(TransportOutDescription transportOut) { if (transportOut != null) { Parameter serverUrl = transportOut.getParameter(XMPPConstants.XMPP_SERVER_URL); if (serverUrl != null) { serverCredentials.setServerUrl(Utils.getParameterValue(serverUrl)); } Parameter userName = transportOut.getParameter(XMPPConstants.XMPP_SERVER_USERNAME); if (userName != null) { serverCredentials.setAccountName(Utils.getParameterValue(userName)); } Parameter password = transportOut.getParameter(XMPPConstants.XMPP_SERVER_PASSWORD); if (password != null) { serverCredentials.setPassword(Utils.getParameterValue(password)); } Parameter serverType = transportOut.getParameter(XMPPConstants.XMPP_SERVER_TYPE); if (serverType != null) { serverCredentials.setServerType(Utils.getParameterValue(serverType)); } Parameter domainName = transportOut.getParameter(XMPPConstants.XMPP_DOMAIN_NAME); if (serverUrl != null) { serverCredentials.setDomainName(Utils.getParameterValue(domainName)); } } } /** * Extract connection details from client options * @param serverCredentials * @param msgContext */ private void getConnectionDetailsFromClientOptions(MessageContext msgContext) { Options clientOptions = msgContext.getOptions(); if (clientOptions.getProperty(XMPPConstants.XMPP_SERVER_USERNAME) != null) { serverCredentials .setAccountName((String) clientOptions.getProperty(XMPPConstants.XMPP_SERVER_USERNAME)); } if (clientOptions.getProperty(XMPPConstants.XMPP_SERVER_PASSWORD) != null) { serverCredentials.setPassword((String) clientOptions.getProperty(XMPPConstants.XMPP_SERVER_PASSWORD)); } if (clientOptions.getProperty(XMPPConstants.XMPP_SERVER_URL) != null) { serverCredentials.setServerUrl((String) clientOptions.getProperty(XMPPConstants.XMPP_SERVER_URL)); } if (clientOptions.getProperty(XMPPConstants.XMPP_SERVER_TYPE) != null) { serverCredentials.setServerType((String) clientOptions.getProperty(XMPPConstants.XMPP_SERVER_TYPE)); } } private static void handleException(String msg, Exception e) throws AxisFault { log.error(msg, e); throw new AxisFault(msg, e); } private static void handleException(String msg) throws AxisFault { log.error(msg); throw new AxisFault(msg); } }