Java tutorial
/** * Copyright (c) 2009 Mikael Lammentausta * 2010 Mikael Lammentausta, Tulio Ornelas dos Santos * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package com.celamanzi.liferay.portlets.rails286; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.commons.httpclient.Cookie; import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpException; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpState; import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.cookie.CookiePolicy; import org.apache.commons.httpclient.methods.GetMethod; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.multipart.FilePart; import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity; import org.apache.commons.httpclient.methods.multipart.Part; import org.apache.commons.httpclient.methods.multipart.StringPart; import org.apache.commons.httpclient.params.HttpMethodParams; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class OnlineClient { private final Log log = LogFactory.getLog(getClass().getName()); private static int retries = 0; private static int timeout = 30000; private ArrayList<Header> headers; private String contentType; private String contentDisposition; private URL requestURL = null; private int statusCode = -1; private Cookie[] cookies = null; private URL httpReferer = null; private Locale locale = null; private boolean ajax = false; public OnlineClient(URL requestURL) { this.requestURL = requestURL; } public OnlineClient(URL requestURL, Map<String, Cookie> cookies, URL httpReferer, Locale locale, boolean ajax) { this.requestURL = requestURL; if (cookies != null) this.cookies = cookies.values().toArray(new Cookie[0]); this.httpReferer = httpReferer; this.locale = locale; this.ajax = ajax; } public int getStatusCode() { return statusCode; } public Cookie[] getCookies() { return cookies; } public void setCookies(Cookie[] cookies) { this.cookies = cookies; } public ArrayList<Header> getHeaders() { return headers; } public String getContentType() { return this.contentType; } public String getContentDisposition() { return contentDisposition; } /** GET * * Instantiates HttpClient, prepares it with session cookies, * executes the request and returns the response body. * @throws RailsAppException * * @since 0.8.0 */ protected byte[] get() throws HttpException, IOException, RailsAppException { // Response body from the web server byte[] responseBody = null; statusCode = -1; HttpClient client = preparedClient(); // Create the GET method and prepare its headers GetMethod method = new GetMethod(requestURL.toString()); HttpMethod _method = (HttpMethod) method; method = (GetMethod) prepareMethodHeaders(_method); log.debug("GET request URL: " + requestURL.toString()); try { // Execute the method statusCode = client.executeMethod(method); // log the status if (statusCode != HttpStatus.SC_OK) { String errorMessage = "Request failed: " + method.getStatusLine(); log.error(errorMessage); throw new RailsAppException(errorMessage, new String(method.getResponseBody())); } else { log.debug("Status code: " + method.getStatusLine()); // Read the response body responseBody = method.getResponseBody(); // Keep the headers for future usage (render or resource phase) configureHeader(method.getResponseHeaders()); // Get session cookies cookies = client.getState().getCookies(); log.debug("Stored " + cookies.length + " cookies."); } } finally { method.releaseConnection(); } return responseBody; } /** * POST * * Posts the parametersBody * @throws RailsAppException */ protected byte[] post(NameValuePair[] parametersBody, Map<String, Object[]> files) throws HttpException, IOException, RailsAppException { // Response body from the web server byte[] responseBody = null; statusCode = -1; List<File> tempFiles = null; HttpClient client = preparedClient(); // Create a method instance. PostMethod method = new PostMethod(requestURL.toString()); HttpMethod _method = (HttpMethod) method; String action = "POST action request URL: " + requestURL.toString(); if (ajax) { log.debug("Ajax " + action); _method.setRequestHeader("X_REQUESTED_WITH", "XMLHttpRequest"); _method.setRequestHeader("ACCEPT", "text/javascript, text/html, application/xml, text/xml, */*"); _method.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8"); } else log.debug(action); // finalize method method = (PostMethod) prepareMethodHeaders(_method); if (files != null && files.size() > 0) { tempFiles = new ArrayList<File>(); createMultipartRequest(parametersBody, files, method, tempFiles); } else { // Array of parameters may not be null, so init empty NameValuePair[] if (parametersBody == null) { parametersBody = new NameValuePair[0]; } method.setRequestBody(parametersBody); // Provide custom retry handler is necessary method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)); } try { // Execute the method. statusCode = client.executeMethod(method); if ((statusCode == HttpStatus.SC_MOVED_TEMPORARILY) || (statusCode == HttpStatus.SC_MOVED_PERMANENTLY) || (statusCode == HttpStatus.SC_SEE_OTHER) || (statusCode == HttpStatus.SC_TEMPORARY_REDIRECT)) { // get Location String location = ((Header) method.getResponseHeader("Location")).getValue(); requestURL = new URL(location); log.debug("POST status code: " + method.getStatusLine()); log.debug("Redirect to location: " + location); // server may add another cookie before redirect.. cookies = client.getState().getCookies(); // Note that this GET overwrites the previous POST method, // so it should set statusCode and cookies correctly. responseBody = get(); } else { // the original POST method was OK, pass // No more redirects! Response should be 200 OK if (statusCode != HttpStatus.SC_OK) { String errorMessage = "Method failed: " + method.getStatusLine(); log.error(errorMessage); throw new RailsAppException(errorMessage, new String(method.getResponseBody())); } else { log.debug("POST status code: " + method.getStatusLine()); } // Read the response body. responseBody = method.getResponseBody(); // Keep the headers for future usage (render or resource phase) configureHeader(method.getResponseHeaders()); // Get session cookies cookies = client.getState().getCookies(); } } finally { // Release the connection method.releaseConnection(); // Delete temp files deleteFiles(tempFiles); } return responseBody; } /** Returns a HttpState fixed with cookies. * * @since 0.8.0 */ protected HttpState preparedHttpState() { HttpState state = new HttpState(); if (cookies != null) { // Add cookies to the state state.addCookies(cookies); } if (log.isDebugEnabled()) { Cookie[] _cookies = state.getCookies(); debugCookies(_cookies); } return state; } /** * Prepares client. */ protected HttpClient preparedClient() { // Create an instance of HttpClient and prepare it HttpClient client = new HttpClient(); client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); client.getParams().setParameter(HttpMethodParams.SINGLE_COOKIE_HEADER, true); // magic cookie line (all cookies on one header line) client.getParams().setParameter("http.protocol.single-cookie-header", true); // Set state (session cookies) client.setState(preparedHttpState()); // Set timeout client.getHttpConnectionManager().getParams().setConnectionTimeout(timeout); return client; } /** Prepares method headers. * * Modifies the given method by inserting, when defined: * - Referer * - Accept-Language * * @since 0.8.0 */ protected HttpMethod prepareMethodHeaders(HttpMethod method) { // Insert the HTTP Referer if (httpReferer != null) { log.debug("HTTP referer: " + httpReferer.toString()); method.setRequestHeader("Referer", httpReferer.toString()); } else { //log.debug("HTTP referer is null"); } // Insert the Locale if (locale != null) { log.debug("Request's locale language: " + locale.toString()); method.setRequestHeader("Accept-Language", locale.toString()); } else { //log.debug("Locale is null"); } //Correct the charset problem method.getParams().setContentCharset("UTF-8"); // Provide custom retry handler method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(retries, false)); return method; } protected void createMultipartRequest(NameValuePair[] parametersBody, Map<String, Object[]> files, PostMethod method, List<File> tempFiles) throws IOException, FileNotFoundException { List<Part> parts = new ArrayList<Part>(); parametersBody = removeFileParams(parametersBody, files); for (NameValuePair param : parametersBody) { parts.add(createStringPart(param)); } for (String key : files.keySet()) { File file = createFile(files.get(key)); if (file != null) { parts.add(new FilePart(key, file)); tempFiles.add(file); } } Part[] array = new Part[parts.size()]; method.setRequestEntity(new MultipartRequestEntity(parts.toArray(array), method.getParams())); method.getParams().setBooleanParameter(HttpMethodParams.USE_EXPECT_CONTINUE, false); } protected void debugCookies(Cookie[] cookies) { log.debug("Cookie inspector found " + cookies.length + " cookies ------v"); for (Cookie cookie : cookies) log.debug(cookie.toString() + ", domain=" + cookie.getDomain() + ", path=" + cookie.getPath() + ", max-age=" + cookie.getExpiryDate() + ", secure=" + cookie.getSecure()); log.debug("----------------------------"); } private NameValuePair[] removeFileParams(NameValuePair[] parametersBody, Map<String, Object[]> files) { List<NameValuePair> list = new ArrayList<NameValuePair>(); for (NameValuePair param : parametersBody) { if (!files.containsKey(param.getName())) { list.add(param); } } NameValuePair[] array = new NameValuePair[list.size()]; return list.toArray(array); } private StringPart createStringPart(NameValuePair pair) { StringPart part = new StringPart(pair.getName(), pair.getValue()); //HACK: When content type is null, Rack will interpretate as string param, //otherwise this will be treated like a file. part.setContentType(null); part.setCharSet("UTF-8"); return part; } private File createFile(Object[] objs) throws IOException { if (objs != null && objs.length == 2) { File file = (File) objs[0]; byte[] bytes = (byte[]) objs[1]; FileOutputStream fos = new FileOutputStream(file); fos.write(bytes); fos.flush(); fos.close(); return file; } return null; } private void deleteFiles(List<File> tempFiles) { if (tempFiles == null) { return; } for (File file : tempFiles) { if (!file.delete()) { log.error("Failure to delete: " + file.getPath()); } } } private String getHeaderValue(String name, ArrayList<Header> headers) { for (Header header : headers) { if (header.getName().equals(name)) { return header.getValue(); } } return ""; } private void configureHeader(Header[] headers) { setHeaders(headers); this.contentType = getHeaderValue("Content-Type", getHeaders()); this.contentDisposition = getHeaderValue("Content-Disposition", getHeaders()); } private void setHeaders(Header[] headers) { this.headers = new ArrayList<Header>(); for (Header header : headers) { this.headers.add(header); } } }