org.colombbus.tangara.net.TConnection.java Source code

Java tutorial

Introduction

Here is the source code for org.colombbus.tangara.net.TConnection.java

Source

/**
 * Tangara is an educational platform to get started with programming.
 * Copyright (C) 2008 Colombbus (http://www.colombbus.org)
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.colombbus.tangara.net;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Map.Entry;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.HttpClientParams;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

/**
 * A connection to a Tangara server
 *
 * @author gwen
 *
 */
public class TConnection {

    /** Separator of URL */
    private static final String URL_SEPARATOR = "/";

    /**
     * Create a new connection
     *
     * @param userAgent
     *            the user-agent of the HTTP header
     * @param charset
     *            the character set encoding of the HTTP communication
     * @param serverURL
     *            the address of the server as an URL
     * @param retryCount
     * @param timeout
     */
    public TConnection(String userAgent, String charset, String serverURL, int retryCount, int timeout) {
        super();
        this.serverURL = serverURL;
        this.encodingCharset = charset;
        this.retryHandler = new HttpConnRetryHandler(retryCount);
        params.setParameter(HttpMethodParams.USER_AGENT, userAgent);
        params.setContentCharset(charset);
        params.setConnectionManagerTimeout(timeout);
        params.setSoTimeout(timeout);
        params.setParameter(HttpMethodParams.RETRY_HANDLER, this.retryHandler);
    }

    public String getServerURL() {
        return serverURL;
    }

    private synchronized HttpClient getClient() {
        if (client == null) {
            client = new HttpClient(params);
        }
        return client;
    }

    /**
     * Create the HTTP method from the command
     *
     * @param cmd
     * @return HttpMethod
     */
    private HttpMethod createMethod(Command cmd) {
        // url args
        String url = buildURL(cmd);

        // method choice
        HttpMethod method = null;
        if (cmd.getBodyArgs().isEmpty()) {
            method = new GetMethod(url);
        } else {
            PostMethod postMethod = new PostMethod(url);
            // body args
            for (String argName : cmd.getBodyArgs().keySet()) {
                String value = cmd.getBodyArgs().get(argName);
                postMethod.addParameter(argName, value);
            }
            method = postMethod;
        }

        return method;
    }

    private String buildURL(Command cmd) {
        StringBuilder urlBuilder = new StringBuilder(serverURL);
        String relURL = cmd.getRelativeURL();
        if ((serverURL.endsWith(URL_SEPARATOR) && relURL.startsWith(URL_SEPARATOR)) == false) {
            urlBuilder.append(URL_SEPARATOR); //$NON-NLS-1$
        }
        urlBuilder.append(cmd.getRelativeURL());
        if (cmd.getURLArgs().isEmpty() == false) {
            urlBuilder.append('?');//$NON-NLS-1$
            boolean firstArg = true;
            for (Entry<String, String> entry : cmd.getURLArgs().entrySet()) {
                String argName = entry.getKey();
                String argValue = entry.getValue();
                if (firstArg) {
                    firstArg = false;
                } else {
                    urlBuilder.append('&');//$NON-NLS-1$
                }

                try {
                    String urlArgName = URLEncoder.encode(argName, encodingCharset);
                    String urlArgValue = URLEncoder.encode(argValue, encodingCharset);
                    urlBuilder.append(urlArgName).append('=') //$NON-NLS-1$
                            .append(urlArgValue);
                } catch (UnsupportedEncodingException e) {
                    LOG.error(encodingCharset + " not supported for URLs", e); //$NON-NLS-1$
                    urlBuilder.append(argName).append('=').append(argValue); //$NON-NLS-1$
                }
            }
        }
        String url = urlBuilder.toString();
        return url;
    }

    private String executeMethod(HttpMethod method) throws CommandException {
        int statusCode = 0;
        try {
            statusCode = getClient().executeMethod(method);
            if (statusCode == HttpStatus.SC_REQUEST_TIMEOUT) {
                LOG.warn("Request timeout");
            }
            if (statusCode != HttpStatus.SC_OK) {
                String msg = "Method failed: " + method.getStatusLine(); //$NON-NLS-1$
                LOG.error(msg);
                throw new IOException(msg);
            }

            InputStream in = method.getResponseBodyAsStream();
            byte[] buffer = IOUtils.toByteArray(in);

            String response = new String(buffer, encodingCharset);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Response content:\n" + response);
            }
            return response;
        } catch (HttpException httpEx) {
            String msg = String.format("An HTTP error occurs during the " + //$NON-NLS-1$
                    "execution of the method %s. The returned HTTP code is %d", //$NON-NLS-1$
                    method.getPath(), statusCode);
            LOG.error(msg, httpEx);
            throw CommandExceptionFactory.createCommunicationException(method, CommandException.HTTP_ERR, msg,
                    httpEx);
        } catch (IOException ioEx) {
            String msg = String.format("An IO communication error occurs during the execution of the method %s.",
                    method.getPath());
            LOG.error(msg, ioEx);
            throw CommandExceptionFactory.createCommunicationException(method, CommandException.HTTP_ERR, msg,
                    ioEx);
        } catch (Throwable th) {
            String msg = String.format("An unknown error occurs during the execution of the method %s",
                    method.getPath());
            LOG.error(msg, th);
            throw CommandExceptionFactory.createCommunicationException(method, CommandException.HTTP_ERR, msg, th);

        }
    }

    private Document buildXmlResponse(HttpMethod method, String httpResponse) throws CommandException {
        StringReader reader = new StringReader(httpResponse);
        Document doc = null;
        try {
            doc = saxBuilder.build(reader);
            if ("tangara".equals(doc.getRootElement().getName()) == false) { //$NON-NLS-1$
                String msg = String.format("Response from %s is not a tangara XML document", //$NON-NLS-1$
                        serverURL);
                LOG.error(msg);
                CommandExceptionFactory.throwBadServerException(method, CommandException.BAD_XML_RESPONSE_ERR,
                        "Response is not a tangara XML document");//$NON-NLS-1$
            }

            // detect error responses
            Element errorE = doc.getRootElement().getChild("error"); //$NON-NLS-1$
            if (errorE != null) {
                CommandExceptionFactory.throwException(method, errorE);
            }

        } catch (JDOMException domEx) {
            String msg = String.format("Bad response content. %s is not a Tangara server", //$NON-NLS-1$
                    serverURL);
            LOG.error(msg, domEx);
            CommandExceptionFactory.throwBadServerException(method, CommandException.NOT_XML_RESPONSE_ERR,
                    "Response is not a tangara XML document", domEx);//$NON-NLS-1$
        } catch (IOException ioEx) { // never happend
            String msg = String.format("Fail to parse the response content.");//$NON-NLS-1$
            LOG.error(msg, ioEx);
            LOG.error("SHOULD NOT HAPPEND. CHECK THE CODE", ioEx); //$NON-NLS-1$
            CommandExceptionFactory.throwBadServerException(method, CommandException.RESPONSE_PARSING_ERR,
                    "Response parsing failed", ioEx);//$NON-NLS-1$
        }
        return doc;
    }

    public synchronized Document sendCommand(Command cmd) throws CommandException {
        HttpMethod method = createMethod(cmd);
        Document doc = null;

        try {
            String logMsg = String.format("Prepare executing method %s", //$NON-NLS-1$
                    method.getPath());
            LOG.info(logMsg);

            String httpResponse = executeMethod(method);
            doc = buildXmlResponse(method, httpResponse);

            logMsg = String.format("Method succeed %s %s", //$NON-NLS-1$
                    method.getPath(), method.getStatusLine().toString());
            LOG.info(logMsg);
        } catch (CommandException cmdEx) {
            LOG.error("Command excetion catched in the command", cmdEx);//$NON-NLS-1$
            throw cmdEx;
        } catch (Throwable th) {
            LOG.error("Unknown error catched in the command", th);//$NON-NLS-1$
            throw new CommandException(method.getPath(), CommandException.UNKNOWN_ERR,
                    "Unknown error catched in the command", th);//$NON-NLS-1$
        } finally {
            method.releaseConnection();
        }

        return doc;
    }

    /** Connection client */
    private HttpClient client;

    /** Connection parameters */
    private HttpClientParams params = new HttpClientParams();

    /** Base URL method */
    private String serverURL;

    /** Retry handler */
    private HttpMethodRetryHandler retryHandler;

    /** Encoding character set of the exchanges (HTTP and XML content) */
    private String encodingCharset;

    /** Builder for parsing responses */
    private SAXBuilder saxBuilder = new SAXBuilder();

    /** Class logger */
    private static Logger LOG = Logger.getLogger(TConnection.class);
}