From: Ryan Moats Date: Wed, 2 Oct 2013 15:11:17 +0000 (-0500) Subject: Add HTTP Client library and refactor Northbound IntegrationTest X-Git-Tag: jenkins-controller-bulk-release-prepare-only-2-1~674 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=c35484bda3c82b4e907d0496b01608e478dd00a0 Add HTTP Client library and refactor Northbound IntegrationTest Create a new commons.httpclient library for bundles that need to function as an HTTP client. Refactor the existing NB IntegrationTest code to use this new library. Ammended - library is now a profile of apache httpclient classes to ensure things like timeouts are set on default and resources are correctly released when finished. Change-Id: I76f513b77700213752f4183e917fe239a9c324ff Signed-off-by: Ryan Moats --- diff --git a/opendaylight/commons/httpclient/pom.xml b/opendaylight/commons/httpclient/pom.xml new file mode 100644 index 0000000000..9f46206736 --- /dev/null +++ b/opendaylight/commons/httpclient/pom.xml @@ -0,0 +1,93 @@ + + + 4.0.0 + + org.opendaylight.controller + commons.opendaylight + 1.4.1-SNAPSHOT + ../../commons/opendaylight + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main + HEAD + + + commons.httpclient + 0.1.1-SNAPSHOT + bundle + + + + + org.apache.felix + maven-bundle-plugin + ${bundle.plugin.version} + true + + + + javax.crypto, + javax.crypto.spec, + javax.net, + javax.net.ssl, + javax.security.auth.x500, + javax.servlet, + org.apache.log4j, + org.ietf.jgss, + !org.apache.commons.codec.*, + !org.apache.log, + !org.apache.avalon.framework.* + + + org.opendaylight.controller.commons.httpclient + + + httpclient,httpcore,commons-logging + + false + + ${project.basedir}/META-INF + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${checkstyle.version} + + + org.opendaylight.controller + checkstyle + 0.0.2-SNAPSHOT + + + + true + controller/space_and_tabs_checks.xml + + + + + + + junit + junit + + + org.apache.httpcomponents + httpclient + 4.3 + + + org.apache.httpcomponents + httpcore + 4.3 + + + commons-logging + commons-logging + 1.1.3 + + + diff --git a/opendaylight/commons/httpclient/src/main/java/org/opendaylight/controller/commons/httpclient/HTTPClient.java b/opendaylight/commons/httpclient/src/main/java/org/opendaylight/controller/commons/httpclient/HTTPClient.java new file mode 100644 index 0000000000..ca6d68380e --- /dev/null +++ b/opendaylight/commons/httpclient/src/main/java/org/opendaylight/controller/commons/httpclient/HTTPClient.java @@ -0,0 +1,108 @@ +package org.opendaylight.controller.commons.httpclient; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import org.apache.http.Header; +import org.apache.http.HeaderIterator; +import org.apache.http.HttpEntity; +import org.apache.http.client.ClientProtocolException; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpDelete; +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.client.methods.HttpPut; +import org.apache.http.client.methods.HttpRequestBase; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; + +public class HTTPClient { + static public HTTPResponse sendRequest(HTTPRequest request) throws Exception { + + CloseableHttpClient httpclient = HttpClients.createDefault(); + if (httpclient == null) + throw new ClientProtocolException("Couldn't create an HTTP client"); + RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(request.getTimeout()) + .setConnectTimeout(request.getTimeout()).build(); + HttpRequestBase httprequest; + String method = request.getMethod(); + + if (method.equalsIgnoreCase("GET")) { + httprequest = new HttpGet(request.getUri()); + } else if (method.equalsIgnoreCase("POST")) { + httprequest = new HttpPost(request.getUri()); + if (request.getEntity() != null) { + StringEntity sentEntity = new StringEntity(request.getEntity()); + sentEntity.setContentType(request.getContentType()); + ((HttpEntityEnclosingRequestBase) httprequest).setEntity(sentEntity); + } + } else if (method.equalsIgnoreCase("PUT")) { + httprequest = new HttpPut(request.getUri()); + if (request.getEntity() != null) { + StringEntity sentEntity = new StringEntity(request.getEntity()); + sentEntity.setContentType(request.getContentType()); + ((HttpEntityEnclosingRequestBase) httprequest).setEntity(sentEntity); + } + } else if (method.equalsIgnoreCase("DELETE")) { + httprequest = new HttpDelete(request.getUri()); + } else { + httpclient.close(); + throw new IllegalArgumentException("This profile class only supports GET, POST, PUT, and DELETE methods"); + } + httprequest.setConfig(requestConfig); + + // add request headers + Iterator headerIterator = request.getHeaders().keySet().iterator(); + while (headerIterator.hasNext()) { + String header = headerIterator.next(); + Iterator valueIterator = request.getHeaders().get(header).iterator(); + while (valueIterator.hasNext()) { + httprequest.addHeader(header, valueIterator.next()); + } + } + + CloseableHttpResponse response = httpclient.execute(httprequest); + try { + HttpEntity receivedEntity = response.getEntity(); + int httpResponseCode = response.getStatusLine().getStatusCode(); + HTTPResponse ans = new HTTPResponse(); + HashMap> headerMap = new HashMap>(); + + // copy response headers + HeaderIterator it = response.headerIterator(); + while (it.hasNext()) { + Header h = it.nextHeader(); + String name = h.getName(); + String value = h.getValue(); + if (headerMap.containsKey(name)) + headerMap.get(name).add(value); + else { + List list = new ArrayList(); + list.add(value); + headerMap.put(name, list); + } + } + ans.setHeaders(headerMap); + + if (httpResponseCode > 299) { + ans.setStatus(httpResponseCode); + ans.setEntity(response.getStatusLine().getReasonPhrase()); + return ans; + } + ans.setStatus(response.getStatusLine().getStatusCode()); + if (receivedEntity != null) + ans.setEntity(EntityUtils.toString(receivedEntity)); + else + ans.setEntity(null); + return ans; + } finally { + response.close(); + } + } +} diff --git a/opendaylight/commons/httpclient/src/main/java/org/opendaylight/controller/commons/httpclient/HTTPRequest.java b/opendaylight/commons/httpclient/src/main/java/org/opendaylight/controller/commons/httpclient/HTTPRequest.java new file mode 100644 index 0000000000..7e39a78372 --- /dev/null +++ b/opendaylight/commons/httpclient/src/main/java/org/opendaylight/controller/commons/httpclient/HTTPRequest.java @@ -0,0 +1,86 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.commons.httpclient; + +import java.util.List; +import java.util.Map; + + +public class HTTPRequest { + // the HTTP method to use: currently GET, POST, PUT, and DELETE are supported + String method; + + // the full URI to send to (including protocol) + String uri; + + // the entity body to send + String entity; + + // additional headers (separate from content-type) to include in the request + Map> headers; + + // timeout in milliseconds. Defaults to 3 seconds + int timeout; + + // content type to set. Defaults to application/json + String contentType; + + public HTTPRequest() { + timeout = 3000; + contentType = "application/json"; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + public String getUri() { + return uri; + } + + public void setUri(String uri) { + this.uri = uri; + } + + public String getEntity() { + return entity; + } + + public void setEntity(String entity) { + this.entity = entity; + } + + public Map> getHeaders() { + return headers; + } + + public void setHeaders(Map> headers) { + this.headers = headers; + } + + public int getTimeout() { + return timeout; + } + + public void setTimeout(int timeout) { + this.timeout = timeout; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } +} diff --git a/opendaylight/commons/httpclient/src/main/java/org/opendaylight/controller/commons/httpclient/HTTPResponse.java b/opendaylight/commons/httpclient/src/main/java/org/opendaylight/controller/commons/httpclient/HTTPResponse.java new file mode 100644 index 0000000000..096fce2662 --- /dev/null +++ b/opendaylight/commons/httpclient/src/main/java/org/opendaylight/controller/commons/httpclient/HTTPResponse.java @@ -0,0 +1,45 @@ +/* + * Copyright IBM Corporation, 2013. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.controller.commons.httpclient; + +import java.util.List; +import java.util.Map; + +public class HTTPResponse { + Integer status; // response status + String entity; // response entity + Map> headers; // http header values + + public HTTPResponse() { + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getEntity() { + return entity; + } + + public void setEntity(String entity) { + this.entity = entity; + } + + public Map> getHeaders() { + return headers; + } + + public void setHeaders(Map> map) { + this.headers = map; + } +} diff --git a/opendaylight/distribution/opendaylight/pom.xml b/opendaylight/distribution/opendaylight/pom.xml index eac5a705f9..cdba77ae29 100644 --- a/opendaylight/distribution/opendaylight/pom.xml +++ b/opendaylight/distribution/opendaylight/pom.xml @@ -133,6 +133,7 @@ ../../commons/concepts + ../../commons/httpclient ../../commons/integrationtest ../../commons/checkstyle ../../commons/opendaylight diff --git a/opendaylight/northbound/integrationtest/pom.xml b/opendaylight/northbound/integrationtest/pom.xml index 87ccb68090..aaa50e482d 100644 --- a/opendaylight/northbound/integrationtest/pom.xml +++ b/opendaylight/northbound/integrationtest/pom.xml @@ -31,6 +31,11 @@ + + org.opendaylight.controller + commons.httpclient + 0.1.1-SNAPSHOT + org.opendaylight.controller connectionmanager diff --git a/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java b/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java index e457cbfd6e..3f3aec08ed 100644 --- a/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java +++ b/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java @@ -8,17 +8,12 @@ import static org.ops4j.pax.exam.CoreOptions.options; import static org.ops4j.pax.exam.CoreOptions.systemPackages; import static org.ops4j.pax.exam.CoreOptions.systemProperty; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import javax.inject.Inject; @@ -32,6 +27,9 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.opendaylight.controller.commons.httpclient.HTTPClient; +import org.opendaylight.controller.commons.httpclient.HTTPRequest; +import org.opendaylight.controller.commons.httpclient.HTTPResponse; import org.opendaylight.controller.hosttracker.IfIptoHost; import org.opendaylight.controller.sal.core.Bandwidth; import org.opendaylight.controller.sal.core.ConstructionException; @@ -149,53 +147,48 @@ public class NorthboundIT { } try { - URL url = new URL(restUrl); this.userManager.getAuthorizationList(); this.userManager.authenticate("admin", "admin"); + HTTPRequest request = new HTTPRequest(); + + request.setUri(restUrl); + request.setMethod(method); + request.setTimeout(5000); // HostTracker doesn't respond within 3 seconds + + Map> headers = new HashMap>(); String authString = "admin:admin"; byte[] authEncBytes = Base64.encodeBase64(authString.getBytes()); String authStringEnc = new String(authEncBytes); - - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod(method); - connection.setRequestProperty("Authorization", "Basic " + authStringEnc); - connection.setRequestProperty("Content-Type", "application/json"); - connection.setRequestProperty("Accept", "application/json"); - + List header = new ArrayList(); + header.add("Basic "+authStringEnc); + headers.put("Authorization", header); + header = new ArrayList(); + header.add("application/json"); + headers.put("Accept", header); + request.setHeaders(headers); + request.setContentType("application/json"); if (body != null) { - connection.setDoOutput(true); - OutputStreamWriter wr = new OutputStreamWriter(connection.getOutputStream()); - wr.write(body); - wr.flush(); + request.setEntity(body); } - connection.connect(); - connection.getContentType(); + + HTTPResponse response = HTTPClient.sendRequest(request); // Response code for success should be 2xx - httpResponseCode = connection.getResponseCode(); + httpResponseCode = response.getStatus(); if (httpResponseCode > 299) { return httpResponseCode.toString(); } if (debugMsg) { - System.out.println("HTTP response code: " + connection.getResponseCode()); - System.out.println("HTTP response message: " + connection.getResponseMessage()); + System.out.println("HTTP response code: " + response.getStatus()); + System.out.println("HTTP response message: " + response.getEntity()); } - InputStream is = connection.getInputStream(); - BufferedReader rd = new BufferedReader(new InputStreamReader(is, Charset.forName("UTF-8"))); - StringBuilder sb = new StringBuilder(); - int cp; - while ((cp = rd.read()) != -1) { - sb.append((char) cp); - } - is.close(); - connection.disconnect(); + return response.getEntity(); + } catch (Exception e) { if (debugMsg) { - System.out.println("Response : "+sb.toString()); + e.printStackTrace(); } - return sb.toString(); - } catch (Exception e) { return null; } } @@ -1381,6 +1374,7 @@ public class NorthboundIT { mavenBundle("org.opendaylight.controller", "forwarding.staticrouting").versionAsInProject(), mavenBundle("org.opendaylight.controller", "bundlescanner").versionAsInProject(), mavenBundle("org.opendaylight.controller", "bundlescanner.implementation").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "commons.httpclient").versionAsInProject(), // Northbound bundles mavenBundle("org.opendaylight.controller", "commons.northbound").versionAsInProject(),