org.apache.axis2.transport.xmpp.XMPPSender.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.axis2.transport.xmpp.XMPPSender.java

Source

/*
 * 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);
    }
}