Java tutorial
/* * Copyright 2005-2008 Pentaho Corporation. All rights reserved. * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * 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 Lesser General Public License for more details. * * Copyright 2008 - 2009 Pentaho Corporation. All rights reserved. * * Created * @author Steven Barkdull */ package org.pentaho.pac.server.common; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpMethodBase; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.pac.server.i18n.Messages; /** * Provides a thread safe HttpClient with ability to do post and get. * * Reuse notes: this class should be easily reusable in other applications. The * ONLY dependency on Pentaho code is for localized messages. To minimize coupling * (dependencies on other classes), and keep this code reusable by the * largest number of clients, this class should never have dependencies on * other pentaho code. * * @author Steven Barkdull * */ public class ThreadSafeHttpClient { private static final Log logger = LogFactory.getLog(ThreadSafeHttpClient.class); private static final String REQUESTED_MIME_TYPE = "requestedMimeType"; //$NON-NLS-1$ public static final String DEFAULT_CONSOLE_PROPERTIES_FILE_NAME = "console.properties"; //$NON-NLS-1$ public static final String CONTENT_CHARACTERSET_PROPERTY = "content.characterset"; //$NON-NLS-1$ public static final String DEFAULT_CONTENT_CHARACTERSET_VALUE = "utf-8"; //$NON-NLS-1$ public static String contentCharacterSet = null; public enum HttpMethodType { POST, GET }; /* * @see: http://hc.apache.org/httpclient-3.x/threading.html */ private static final HttpClient CLIENT; static { MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager(); CLIENT = new HttpClient(connectionManager); CLIENT.getParams().setParameter("http.useragent", ThreadSafeHttpClient.class.getName()); //$NON-NLS-1$ loadProperties(); } /** * Base Constructor */ public ThreadSafeHttpClient() { super(); } /** * * @param serviceName * @param methodType * @param mapParams * @return * @throws ProxyException if the attempt to communicate with the server fails, * if the attempt to read the response from the server fails, if the response * stream is unable to be converted into a String. */ public String execRemoteMethod(String baseUrl, String serviceName, HttpMethodType methodType, Map<String, Object> mapParams) throws ProxyException { return execRemoteMethod(baseUrl, serviceName, methodType, mapParams, "text/xml"); //$NON-NLS-1$ } /** * * @param serviceName String can be null or empty string. * @param mapParams * @param requestedMimeType * @return * @throws ProxyException ProxyException if the attempt to communicate with the server fails, * if the attempt to read the response from the server fails, if the response * stream is unable to be converted into a String. */ public String execRemoteMethod(String baseUrl, String serviceName, HttpMethodType methodType, Map<String, Object> mapParams, String requestedMimeType) throws ProxyException { assert null != baseUrl : "baseUrl cannot be null"; //$NON-NLS-1$ String serviceUrl = baseUrl; if (!StringUtils.isEmpty(serviceName)) { if (!serviceUrl.endsWith("/")) { //$NON-NLS-1$ serviceUrl = serviceUrl + "/"; //$NON-NLS-1$ } serviceUrl = serviceUrl + serviceName; } if (null == mapParams) { mapParams = new HashMap<String, Object>(); } mapParams.put(REQUESTED_MIME_TYPE, requestedMimeType); HttpMethodBase method = null; switch (methodType) { case POST: method = new PostMethod(serviceUrl); method.getParams().setContentCharset(contentCharacterSet);//$NON-NLS-1$ setPostMethodParams((PostMethod) method, mapParams); method.setFollowRedirects(false); break; case GET: method = new GetMethod(serviceUrl); method.getParams().setContentCharset(contentCharacterSet); //$NON-NLS-1$ setGetMethodParams((GetMethod) method, mapParams); method.setFollowRedirects(true); break; default: throw new RuntimeException(Messages.getErrorString( "ThreadSafeHttpClient.ERROR_0002_INVALID_HTTP_METHOD_TYPE", methodType.toString())); // can never happen //$NON-NLS-1$ } return executeMethod(method); } /** * Execute the <param>method</param>, and return the server's response as a string * @param method the HttpMethod specifying the server URL and parameters to be * passed to the server. * @return a string containing the server's response * * @throws ProxyException if the attempt to communicate with the server fails, * if the attempt to read the response from the server fails, if the response * stream is unable to be converted into a String. */ private String executeMethod(HttpMethod method) throws ProxyException { InputStream responseStrm = null; try { int httpStatus = CLIENT.executeMethod(method); if (httpStatus != HttpStatus.SC_OK) { // If the response comes as unauthorized access we will throw a proxy exception explaining the reason and // what needs to be done to correct it if (httpStatus == HttpStatus.SC_UNAUTHORIZED) { throw new ProxyException( Messages.getErrorString("ThreadSafeHttpClient.ERROR_0003_AUTHORIZATION_FAILED")); } String status = method.getStatusLine().toString(); String uri = method.getURI().toString(); String errorMsg = Messages.getErrorString("ThreadSafeHttpClient.ERROR_0001_CLIENT_REQUEST_FAILED", //$NON-NLS-1$ uri, status); logger.error(errorMsg); throw new ProxyException(status); // TODO } responseStrm = method.getResponseBodyAsStream(); // trim() is necessary because some jsp's put \n\r at the beginning of // the returned text, and the xml processor chokes on \n\r at the beginning. String response = IOUtils.toString(responseStrm).trim(); return response; } catch (Exception e) { throw new ProxyException(e); } finally { method.releaseConnection(); } } private static void setGetMethodParams(GetMethod method, Map<String, Object> mapParams) { NameValuePair[] params = mapToNameValuePair(mapParams); method.setQueryString(params); } private static void setPostMethodParams(PostMethod method, Map<String, Object> mapParams) { for (Map.Entry<String, Object> entry : mapParams.entrySet()) { Object o = entry.getValue(); if (o instanceof String[]) { for (String s : (String[]) o) { method.addParameter(entry.getKey(), s); } } else { method.setParameter(entry.getKey(), (String) o); } } } private static NameValuePair[] mapToNameValuePair(Map<String, Object> paramMap) { NameValuePair[] pairAr = new NameValuePair[paramMap.size()]; int idx = 0; for (Map.Entry<String, Object> me : paramMap.entrySet()) { pairAr[idx] = new NameValuePair(me.getKey(), (String) me.getValue()); idx++; } return pairAr; } private static void loadProperties() { FileInputStream fis = null; Properties properties = null; try { URL url = ClassLoader.getSystemResource(DEFAULT_CONSOLE_PROPERTIES_FILE_NAME); fis = new FileInputStream(new File(url.toURI())); } catch (Exception e) { contentCharacterSet = DEFAULT_CONTENT_CHARACTERSET_VALUE; } if (null != fis) { properties = new Properties(); try { properties.load(fis); } catch (IOException e) { contentCharacterSet = DEFAULT_CONTENT_CHARACTERSET_VALUE; } } if (properties != null) { contentCharacterSet = properties.getProperty(CONTENT_CHARACTERSET_PROPERTY, DEFAULT_CONTENT_CHARACTERSET_VALUE); } else { contentCharacterSet = DEFAULT_CONTENT_CHARACTERSET_VALUE; } } }