From 5ef80d68c4f6fc0cb36bd7eb7f9e1fc15ca04080 Mon Sep 17 00:00:00 2001 From: Wangyuan Zhang Date: Wed, 18 Sep 2019 15:00:15 -0700 Subject: [PATCH 1/3] Using isEmpty() instead of peek() to check if task queue is empty, since peek() implementation operates in a similar way to poll like a consumer which can lead to a multi-consumer scenario on a multi-producer-single-consumer queue, therefore getting into an infinite loop issue inside MPSC queue implementation. --- .../java/io/cdap/http/internal/NonStickyEventExecutorGroup.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/cdap/http/internal/NonStickyEventExecutorGroup.java b/src/main/java/io/cdap/http/internal/NonStickyEventExecutorGroup.java index 8959e27..c0645fc 100644 --- a/src/main/java/io/cdap/http/internal/NonStickyEventExecutorGroup.java +++ b/src/main/java/io/cdap/http/internal/NonStickyEventExecutorGroup.java @@ -283,7 +283,7 @@ public void run() { // // The above cases can be distinguished by performing a // compareAndSet(NONE, RUNNING). If it returns "false", it is case 1; otherwise it is case 2. - if (tasks.peek() == null || !state.compareAndSet(NONE, RUNNING)) { + if (tasks.isEmpty() || !state.compareAndSet(NONE, RUNNING)) { return; // done } } From 440c5c4233be25535830841029925832564a5faa Mon Sep 17 00:00:00 2001 From: Wangyuan Zhang Date: Thu, 19 Sep 2019 16:08:52 -0700 Subject: [PATCH 2/3] Create 1.3.1 version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d2c39c0..c943e73 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 4.0.0 io.cdap.http netty-http - 1.3.0 + 1.3.1 bundle Netty based path router Netty based path router From cecead2c3364dfee942a9e61347f8cffca7bb5eb Mon Sep 17 00:00:00 2001 From: Frederic Bregier Date: Tue, 18 Jun 2019 13:05:41 +0200 Subject: [PATCH 3/3] Portability to Java 6 support This framework should be able to be binary compatible with Java 6 as Netty does. This merge request allows minor changes to be able to be compatible. Also it upgrades dependencies and allows OPTION method (which was missing). --- pom.xml | 30 +++- .../io/cdap/http/AbstractHttpResponder.java | 7 +- src/main/java/io/cdap/http/HttpResponder.java | 6 +- .../java/io/cdap/http/NettyHttpService.java | 27 ++- .../java/io/cdap/http/SSLHandlerFactory.java | 8 +- .../http/internal/BasicHttpResponder.java | 8 +- .../cdap/http/internal/HandlerException.java | 4 +- .../http/internal/HttpResourceHandler.java | 11 +- .../cdap/http/internal/HttpResourceModel.java | 11 +- .../io/cdap/http/internal/ImmutablePair.java | 161 ++++++++++-------- .../io/cdap/http/internal/InternalUtil.java | 34 ++++ .../cdap/http/internal/ParamConvertUtils.java | 8 +- .../internal/PatternPathRouterWithGroups.java | 12 +- .../http/internal/WrappedHttpResponder.java | 2 +- src/test/java/io/cdap/http/FileUtils.java | 83 +++++++++ .../java/io/cdap/http/HandlerHookTest.java | 6 +- .../java/io/cdap/http/HttpServerTest.java | 59 ++++--- .../java/io/cdap/http/HttpsServerTest.java | 16 +- .../cdap/http/InternalHttpResponderTest.java | 29 +++- .../io/cdap/http/MutualAuthServerTest.java | 25 ++- .../java/io/cdap/http/PathRouterTest.java | 52 +++--- .../java/io/cdap/http/SSLClientContext.java | 8 +- .../java/io/cdap/http/SSLKeyStoreTest.java | 12 +- src/test/java/io/cdap/http/TestHandler.java | 18 +- .../java/io/cdap/http/URLRewriterTest.java | 8 +- 25 files changed, 436 insertions(+), 209 deletions(-) create mode 100644 src/main/java/io/cdap/http/internal/InternalUtil.java create mode 100644 src/test/java/io/cdap/http/FileUtils.java diff --git a/pom.xml b/pom.xml index c943e73..afc4676 100644 --- a/pom.xml +++ b/pom.xml @@ -62,14 +62,14 @@ UTF-8 true - 2.0 - 4.1.16.Final - 1.7.5 + 2.1.1 + 4.1.36.Final + 1.7.26 2.0.1 1.8.3 - 4.11 - 1.0.9 - 2.2.4 + 4.12 + 1.2.3 + 2.8.5 @@ -135,10 +135,27 @@ ${gson.version} test + + io.cdap.http + netty-http + 1.2.0 + + + org.codehaus.mojo + animal-sniffer-maven-plugin + 1.16 + + + org.codehaus.mojo.signature + java16 + 1.0 + + + org.apache.maven.plugins @@ -174,7 +191,6 @@ - org.apache.maven.plugins diff --git a/src/main/java/io/cdap/http/AbstractHttpResponder.java b/src/main/java/io/cdap/http/AbstractHttpResponder.java index df714db..33f95ae 100644 --- a/src/main/java/io/cdap/http/AbstractHttpResponder.java +++ b/src/main/java/io/cdap/http/AbstractHttpResponder.java @@ -16,6 +16,7 @@ package io.cdap.http; +import io.cdap.http.internal.InternalUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.http.DefaultHttpHeaders; @@ -27,7 +28,6 @@ import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; /** * Base implementation of {@link HttpResponder} to simplify child implementations. @@ -35,7 +35,6 @@ public abstract class AbstractHttpResponder implements HttpResponder { protected static final String OCTET_STREAM_TYPE = "application/octet-stream"; - @Override public void sendJson(HttpResponseStatus status, String jsonString) { sendString(status, jsonString, new DefaultHttpHeaders().add(HttpHeaderNames.CONTENT_TYPE.toString(), @@ -53,7 +52,7 @@ public void sendString(HttpResponseStatus status, String data, HttpHeaders heade sendStatus(status, headers); return; } - ByteBuf buffer = Unpooled.wrappedBuffer(StandardCharsets.UTF_8.encode(data)); + ByteBuf buffer = Unpooled.wrappedBuffer(InternalUtil.UTF_8.encode(data)); sendContent(status, buffer, addContentTypeIfMissing(new DefaultHttpHeaders().add(headers), "text/plain; charset=utf-8")); } @@ -80,7 +79,7 @@ public void sendBytes(HttpResponseStatus status, ByteBuffer buffer, HttpHeaders } @Override - public void sendFile(File file) throws IOException { + public void sendFile(File file) throws Throwable { sendFile(file, EmptyHttpHeaders.INSTANCE); } diff --git a/src/main/java/io/cdap/http/HttpResponder.java b/src/main/java/io/cdap/http/HttpResponder.java index a047328..17d5357 100644 --- a/src/main/java/io/cdap/http/HttpResponder.java +++ b/src/main/java/io/cdap/http/HttpResponder.java @@ -124,8 +124,9 @@ public interface HttpResponder { * * @param file The file to send * @throws IOException if failed to open and read the file + * @throws Throwable */ - void sendFile(File file) throws IOException; + void sendFile(File file) throws IOException, Throwable; /** * Sends a file content back to client with response status 200. Default content type is "application/octet-stream", @@ -134,8 +135,9 @@ public interface HttpResponder { * @param file The file to send * @param headers additional headers to send with the response. * @throws IOException if failed to open and read the file + * @throws Throwable */ - void sendFile(File file, HttpHeaders headers) throws IOException; + void sendFile(File file, HttpHeaders headers) throws IOException, Throwable; /** * Sends response back to client. The response body is produced by the given {@link BodyProducer}. This method diff --git a/src/main/java/io/cdap/http/NettyHttpService.java b/src/main/java/io/cdap/http/NettyHttpService.java index 53c463a..c27cfc4 100644 --- a/src/main/java/io/cdap/http/NettyHttpService.java +++ b/src/main/java/io/cdap/http/NettyHttpService.java @@ -129,8 +129,8 @@ private NettyHttpService(String serviceName, this.workerThreadPoolSize = workerThreadPoolSize; this.execThreadPoolSize = execThreadPoolSize; this.execThreadKeepAliveSecs = execThreadKeepAliveSecs; - this.channelConfigs = new HashMap<>(channelConfigs); - this.childChannelConfigs = new HashMap<>(childChannelConfigs); + this.channelConfigs = new HashMap(channelConfigs); + this.childChannelConfigs = new HashMap(childChannelConfigs); this.rejectedExecutionHandler = rejectedExecutionHandler; this.resourceHandler = new HttpResourceHandler(httpHandlers, handlerHooks, urlRewriter, exceptionHandler); this.handlerContext = new BasicHandlerContext(this.resourceHandler); @@ -152,10 +152,9 @@ public static Builder builder(String serviceName) { /** * Starts the HTTP service. - * - * @throws Exception if the service failed to started + * @throws Throwable */ - public synchronized void start() throws Exception { + public synchronized void start() throws Throwable { if (state == State.RUNNING) { LOG.debug("Ignore start() call on HTTP service {} since it has already been started.", serviceName); return; @@ -193,7 +192,6 @@ public synchronized void start() throws Exception { shutdownExecutorGroups(0, 5, TimeUnit.SECONDS, eventExecutorGroup); } } catch (Throwable t2) { - t.addSuppressed(t2); } state = State.FAILED; throw t; @@ -224,10 +222,9 @@ public boolean isSSLEnabled() { /** * Stops the HTTP service gracefully and release all resources. Same as calling {@link #stop(long, long, TimeUnit)} * with {@code 0} second quiet period and {@code 5} seconds timeout. - * - * @throws Exception if there is exception raised during shutdown. + * @throws Throwable */ - public void stop() throws Exception { + public void stop() throws Throwable { stop(0, 5, TimeUnit.SECONDS); } @@ -239,9 +236,9 @@ public void stop() throws Exception { * {@linkplain EventExecutorGroup#shutdown()} * regardless if a task was submitted during the quiet period * @param unit the unit of {@code quietPeriod} and {@code timeout} - * @throws Exception if there is exception raised during shutdown. + * @throws Throwable */ - public synchronized void stop(long quietPeriod, long timeout, TimeUnit unit) throws Exception { + public synchronized void stop(long quietPeriod, long timeout, TimeUnit unit) throws Throwable { if (state == State.STOPPED) { LOG.debug("Ignore stop() call on HTTP service {} since it has already been stopped.", serviceName); return; @@ -377,7 +374,7 @@ protected void initChannel(SocketChannel ch) throws Exception { */ private void shutdownExecutorGroups(long quietPeriod, long timeout, TimeUnit unit, EventExecutorGroup...groups) { Exception ex = null; - List> futures = new ArrayList<>(); + List> futures = new ArrayList>(); for (EventExecutorGroup group : groups) { if (group == null) { continue; @@ -391,8 +388,6 @@ private void shutdownExecutorGroups(long quietPeriod, long timeout, TimeUnit uni } catch (Exception e) { if (ex == null) { ex = e; - } else { - ex.addSuppressed(e); } } } @@ -449,8 +444,8 @@ protected Builder(String serviceName) { rejectedExecutionHandler = DEFAULT_REJECTED_EXECUTION_HANDLER; httpChunkLimit = DEFAULT_HTTP_CHUNK_LIMIT; port = 0; - channelConfigs = new HashMap<>(); - childChannelConfigs = new HashMap<>(); + channelConfigs = new HashMap(); + childChannelConfigs = new HashMap(); channelConfigs.put(ChannelOption.SO_BACKLOG, DEFAULT_CONNECTION_BACKLOG); sslHandlerFactory = null; exceptionHandler = new ExceptionHandler(); diff --git a/src/main/java/io/cdap/http/SSLHandlerFactory.java b/src/main/java/io/cdap/http/SSLHandlerFactory.java index 6be7adf..faf398a 100644 --- a/src/main/java/io/cdap/http/SSLHandlerFactory.java +++ b/src/main/java/io/cdap/http/SSLHandlerFactory.java @@ -70,10 +70,16 @@ public SSLHandlerFactory(SslContext sslContext) { } private static KeyStore getKeyStore(File keyStore, String keyStorePassword) throws Exception { - try (InputStream is = new FileInputStream(keyStore)) { + InputStream is = null; + try { + is = new FileInputStream(keyStore); KeyStore ks = KeyStore.getInstance("JKS"); ks.load(is, keyStorePassword.toCharArray()); return ks; + } finally { + if (is != null) { + is.close(); + } } } diff --git a/src/main/java/io/cdap/http/internal/BasicHttpResponder.java b/src/main/java/io/cdap/http/internal/BasicHttpResponder.java index 11ef149..fbd8e49 100644 --- a/src/main/java/io/cdap/http/internal/BasicHttpResponder.java +++ b/src/main/java/io/cdap/http/internal/BasicHttpResponder.java @@ -50,7 +50,6 @@ import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; -import java.nio.charset.StandardCharsets; import java.util.concurrent.atomic.AtomicBoolean; import javax.annotation.Nullable; @@ -105,7 +104,7 @@ public void sendContent(HttpResponseStatus status, ByteBuf content, HttpHeaders } @Override - public void sendFile(File file, HttpHeaders headers) throws IOException { + public void sendFile(File file, HttpHeaders headers) throws Throwable { HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); addContentTypeIfMissing(response.headers().add(headers), OCTET_STREAM_TYPE); @@ -136,7 +135,6 @@ public void sendFile(File file, HttpHeaders headers) throws IOException { try { raf.close(); } catch (IOException ex) { - t.addSuppressed(ex); } throw t; } @@ -152,7 +150,7 @@ public void sendContent(HttpResponseStatus status, final BodyProducer bodyProduc // Response with error and close the connection sendContent( HttpResponseStatus.INTERNAL_SERVER_ERROR, - Unpooled.copiedBuffer("Failed to determined content length. Cause: " + t.getMessage(), StandardCharsets.UTF_8), + Unpooled.copiedBuffer("Failed to determined content length. Cause: " + t.getMessage(), InternalUtil.UTF_8), new DefaultHttpHeaders() .set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE) .set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=utf-8")); @@ -197,7 +195,7 @@ boolean isResponded() { private ChannelFutureListener createBodyProducerCompletionListener(final BodyProducer bodyProducer) { return new ChannelFutureListener() { @Override - public void operationComplete(ChannelFuture future) throws Exception { + public void operationComplete(ChannelFuture future) { if (!future.isSuccess()) { callBodyProducerHandleError(bodyProducer, future.cause()); channel.close(); diff --git a/src/main/java/io/cdap/http/internal/HandlerException.java b/src/main/java/io/cdap/http/internal/HandlerException.java index 6a4a193..f094005 100644 --- a/src/main/java/io/cdap/http/internal/HandlerException.java +++ b/src/main/java/io/cdap/http/internal/HandlerException.java @@ -24,8 +24,6 @@ import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpVersion; -import java.nio.charset.StandardCharsets; - /** *Creating Http Response for Exception messages. */ @@ -48,7 +46,7 @@ final class HandlerException extends Exception { HttpResponse createFailureResponse() { FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, failureStatus, - Unpooled.copiedBuffer(message, StandardCharsets.UTF_8)); + Unpooled.copiedBuffer(message, InternalUtil.UTF_8)); HttpUtil.setContentLength(response, response.content().readableBytes()); return response; } diff --git a/src/main/java/io/cdap/http/internal/HttpResourceHandler.java b/src/main/java/io/cdap/http/internal/HttpResourceHandler.java index efc74e2..fe5a995 100644 --- a/src/main/java/io/cdap/http/internal/HttpResourceHandler.java +++ b/src/main/java/io/cdap/http/internal/HttpResourceHandler.java @@ -44,6 +44,7 @@ import javax.annotation.Nullable; import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.OPTIONS; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -135,7 +136,7 @@ public HttpResourceHandler(Iterable handlers, Iterable getHttpMethods(Method method) { - Set httpMethods = new HashSet<>(); + Set httpMethods = new HashSet(); if (method.isAnnotationPresent(GET.class)) { httpMethods.add(HttpMethod.GET); @@ -149,6 +150,9 @@ private Set getHttpMethods(Method method) { if (method.isAnnotationPresent(DELETE.class)) { httpMethods.add(HttpMethod.DELETE); } + if (method.isAnnotationPresent(OPTIONS.class)) { + httpMethods.add(HttpMethod.OPTIONS); + } return Collections.unmodifiableSet(httpMethods); } @@ -310,7 +314,8 @@ public HttpMethodInfo getDestinationMethod(HttpRequest request, HttpResponder re LOG.trace("Routable destinations for request {}: {}", requestUri, routableDestinations); Iterable requestUriParts = splitAndOmitEmpty(requestUri, '/'); - List> matchedDestinations = new ArrayList<>(); + List> matchedDestinations = + new ArrayList>(); long maxScore = 0; for (PatternPathRouterWithGroups.RoutableDestination destination : routableDestinations) { @@ -390,7 +395,7 @@ public void destroy(HandlerContext context) { } private static List copyOf(Iterable iterable) { - List list = new ArrayList<>(); + List list = new ArrayList(); for (T item : iterable) { list.add(item); } diff --git a/src/main/java/io/cdap/http/internal/HttpResourceModel.java b/src/main/java/io/cdap/http/internal/HttpResourceModel.java index 00a0bbb..f2eaf02 100644 --- a/src/main/java/io/cdap/http/internal/HttpResourceModel.java +++ b/src/main/java/io/cdap/http/internal/HttpResourceModel.java @@ -48,7 +48,8 @@ public final class HttpResourceModel { private static final Set> SUPPORTED_PARAM_ANNOTATIONS = - Collections.unmodifiableSet(new HashSet<>(Arrays.asList(PathParam.class, QueryParam.class, HeaderParam.class))); + Collections.unmodifiableSet(new HashSet> + (Arrays.asList(PathParam.class, QueryParam.class, HeaderParam.class))); private final Set httpMethods; private final String path; @@ -211,13 +212,15 @@ private List, ParameterInfo>> createParameter return Collections.emptyList(); } - List, ParameterInfo>> result = new ArrayList<>(); + List, ParameterInfo>> result = + new ArrayList, ParameterInfo>>(); Type[] parameterTypes = method.getGenericParameterTypes(); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); for (int i = 2; i < parameterAnnotations.length; i++) { Annotation[] annotations = parameterAnnotations[i]; - Map, ParameterInfo> paramAnnotations = new IdentityHashMap<>(); + Map, ParameterInfo> paramAnnotations = + new IdentityHashMap, ParameterInfo>(); for (Annotation annotation : annotations) { Class annotationType = annotation.annotationType(); @@ -266,7 +269,7 @@ private static final class ParameterInfo { private final Converter converter; static ParameterInfo create(Annotation annotation, @Nullable Converter converter) { - return new ParameterInfo<>(annotation, converter); + return new ParameterInfo(annotation, converter); } private ParameterInfo(Annotation annotation, @Nullable Converter converter) { diff --git a/src/main/java/io/cdap/http/internal/ImmutablePair.java b/src/main/java/io/cdap/http/internal/ImmutablePair.java index 51c798a..4d4249d 100644 --- a/src/main/java/io/cdap/http/internal/ImmutablePair.java +++ b/src/main/java/io/cdap/http/internal/ImmutablePair.java @@ -16,15 +16,14 @@ package io.cdap.http.internal; -import java.util.Objects; +import java.util.Arrays; /** - * An {@link ImmutablePair} consists of two elements within. The elements once set - * in the ImmutablePair cannot be modified. The class itself is final, so that it - * cannot be subclassed. This is general norm for creating Immutable classes. - * Please note that the {@link ImmutablePair} cannot be modified once set, but the - * objects within them can be, so in general it means that if there are mutable objects - * within the pair then the pair itself is effectively mutable. + * An {@link ImmutablePair} consists of two elements within. The elements once set in the ImmutablePair cannot + * be modified. The class itself is final, so that it cannot be subclassed. This is general norm for creating + * Immutable classes. Please note that the {@link ImmutablePair} cannot be modified once set, but the objects + * within them can be, so in general it means that if there are mutable objects within the pair then the pair + * itself is effectively mutable. * *
  *   ImmutablePair tupleStreamPair= new
@@ -40,74 +39,100 @@
  * @param  type B
  */
 final class ImmutablePair {
-  private final A first;
-  private final B second;
+    private final A first;
+    private final B second;
 
-  public static  ImmutablePair of(A first, B second) {
-    return new ImmutablePair<>(first, second);
-  }
+    public static  ImmutablePair of(A first, B second) {
+        return new ImmutablePair(first, second);
+    }
 
-  /**
-   * Constructs a Immutable Pair.
-   * @param first object in pair
-   * @param second object in pair
-   */
-  private ImmutablePair(A first, B second) {
-    this.first = first;
-    this.second = second;
-  }
+    /**
+     * Constructs a Immutable Pair.
+     * 
+     * @param first  object in pair
+     * @param second object in pair
+     */
+    private ImmutablePair(A first, B second) {
+        this.first = first;
+        this.second = second;
+    }
 
-  /**
-   * Returns first object from pair.
-   * @return first object from pair.
-   */
-  public A getFirst() {
-    return first;
-  }
+    /**
+     * Returns first object from pair.
+     * 
+     * @return first object from pair.
+     */
+    public A getFirst() {
+        return first;
+    }
 
-  /**
-   * Return second object from pair.
-   * @return second object from pair.
-   */
-  public B getSecond() {
-    return second;
-  }
+    /**
+     * Return second object from pair.
+     * 
+     * @return second object from pair.
+     */
+    public B getSecond() {
+        return second;
+    }
 
-  /**
-   * Returns a string representation of {@link ImmutablePair} object.
-   * @return string representation of this object.
-   */
-  @Override
-  public String toString() {
-    return "ImmutablePair{" +
-      "first=" + first +
-      ", second=" + second +
-      '}';
-  }
+    /**
+     * Returns a string representation of {@link ImmutablePair} object.
+     * 
+     * @return string representation of this object.
+     */
+    @Override
+    public String toString() {
+        return "ImmutablePair{" + "first=" + first + ", second=" + second + '}';
+    }
 
-  /**
-   * Returns a hash code value for this object.
-   * @return hash code value of this object.
-   */
-  @Override
-  public int hashCode() {
-    return Objects.hash(first, second);
-  }
+    /**
+     * Returns a hash code value for this object.
+     * 
+     * @return hash code value of this object.
+     */
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(new Object[] { first, second });
+    }
 
-  /**
-   * Returns whether some other object "is equal" to this object.
-   * @param o reference object with which to compare
-   * @return true if object is the same as the obj argument; false otherwise.
-   */
-  @Override
-  public boolean equals(Object o) {
-    if (o == null) {
-      return false;
+    /**
+     * Helper method that replaces Objects.equals(first, second)
+     * 
+     * @param first
+     * @param otherFirst
+     * @return True if equals
+     */
+    private boolean equals(Object first, Object otherFirst) {
+        boolean fst;
+        if (first == null) {
+            if (otherFirst == null) {
+                fst = true;
+            } else {
+                return false;
+            }
+        } else if (otherFirst == null) {
+            return false;
+        } else {
+            fst = first.equals(otherFirst);
+        }
+        return fst;
     }
-    if (!(o instanceof ImmutablePair)) {
-      return false;
+
+    /**
+     * Returns whether some other object "is equal" to this object.
+     * 
+     * @param o reference object with which to compare
+     * @return true if object is the same as the obj argument; false otherwise.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == null) {
+            return false;
+        }
+        if (!(o instanceof ImmutablePair)) {
+            return false;
+        }
+        ImmutablePair other = (ImmutablePair) o;
+        return equals(first, other.first) && equals(second, other.second);
     }
-    ImmutablePair other = (ImmutablePair) o;
-    return Objects.equals(first, other.first) && Objects.equals(second, other.second);
-  }
 }
diff --git a/src/main/java/io/cdap/http/internal/InternalUtil.java b/src/main/java/io/cdap/http/internal/InternalUtil.java
new file mode 100644
index 0000000..c15d1cc
--- /dev/null
+++ b/src/main/java/io/cdap/http/internal/InternalUtil.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright © 2017-2019 Cask Data, Inc.
+ *
+ * Licensed 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 io.cdap.http.internal;
+
+import java.nio.charset.Charset;
+
+/**
+ * Some utilities
+ *
+ */
+public class InternalUtil {
+    private InternalUtil() {
+        // Empty
+    }
+    /**
+     * Java 6 compatibility
+     */
+    public static final Charset UTF_8 = Charset.forName("UTF-8");
+
+}
diff --git a/src/main/java/io/cdap/http/internal/ParamConvertUtils.java b/src/main/java/io/cdap/http/internal/ParamConvertUtils.java
index feab865..b061d53 100644
--- a/src/main/java/io/cdap/http/internal/ParamConvertUtils.java
+++ b/src/main/java/io/cdap/http/internal/ParamConvertUtils.java
@@ -50,7 +50,7 @@ public final class ParamConvertUtils {
   private static final Map, Object> PRIMITIVE_DEFAULTS;
 
   static {
-    Map, Object> defaults = new IdentityHashMap<>();
+    Map, Object> defaults = new IdentityHashMap, Object>();
     defaults.put(Boolean.TYPE, false);
     defaults.put(Character.TYPE, '\0');
     defaults.put(Byte.TYPE, (byte) 0);
@@ -276,11 +276,11 @@ private static Converter, Object> createCollectionConverter(Type re
       public Object convert(List values) throws Exception {
         Collection collection;
         if (rawType == List.class) {
-          collection = new ArrayList<>();
+          collection = new ArrayList();
         } else if (rawType == Set.class) {
-          collection = new LinkedHashSet<>();
+          collection = new LinkedHashSet();
         } else {
-          collection = new TreeSet<>();
+          collection = new TreeSet();
         }
 
         for (String value : values) {
diff --git a/src/main/java/io/cdap/http/internal/PatternPathRouterWithGroups.java b/src/main/java/io/cdap/http/internal/PatternPathRouterWithGroups.java
index f1b63e9..48fc309 100644
--- a/src/main/java/io/cdap/http/internal/PatternPathRouterWithGroups.java
+++ b/src/main/java/io/cdap/http/internal/PatternPathRouterWithGroups.java
@@ -42,7 +42,7 @@ public final class PatternPathRouterWithGroups {
   private final List> patternRouteList;
 
   public static  PatternPathRouterWithGroups create(int maxPathParts) {
-    return new PatternPathRouterWithGroups<>(maxPathParts);
+    return new PatternPathRouterWithGroups(maxPathParts);
   }
 
   /**
@@ -50,7 +50,7 @@ public static  PatternPathRouterWithGroups create(int maxPathParts) {
    */
   public PatternPathRouterWithGroups(int maxPathParts) {
     this.maxPathParts = maxPathParts;
-    this.patternRouteList = new ArrayList<>();
+    this.patternRouteList = new ArrayList>();
   }
 
   /**
@@ -74,7 +74,7 @@ public void add(final String source, final T destination) {
                                                        source, maxPathParts));
     }
     StringBuilder sb =  new StringBuilder();
-    List groupNames = new ArrayList<>();
+    List groupNames = new ArrayList();
 
     for (String part : parts) {
       Matcher groupMatcher = GROUP_PATTERN.matcher(part);
@@ -108,10 +108,10 @@ public List> getDestinations(String path) {
     String cleanPath = (path.endsWith("/") && path.length() > 1)
       ? path.substring(0, path.length() - 1) : path;
 
-    List> result = new ArrayList<>();
+    List> result = new ArrayList>();
 
     for (ImmutablePair patternRoute : patternRouteList) {
-      Map groupNameValuesBuilder = new HashMap<>();
+      Map groupNameValuesBuilder = new HashMap();
       Matcher matcher =  patternRoute.getFirst().matcher(cleanPath);
       if (matcher.matches()) {
         int matchIndex = 1;
@@ -120,7 +120,7 @@ public List> getDestinations(String path) {
           groupNameValuesBuilder.put(name, value);
           matchIndex++;
         }
-        result.add(new RoutableDestination<>(patternRoute.getSecond().getDestination(), groupNameValuesBuilder));
+        result.add(new RoutableDestination(patternRoute.getSecond().getDestination(), groupNameValuesBuilder));
       }
     }
     return result;
diff --git a/src/main/java/io/cdap/http/internal/WrappedHttpResponder.java b/src/main/java/io/cdap/http/internal/WrappedHttpResponder.java
index cf5fab4..f0c687d 100644
--- a/src/main/java/io/cdap/http/internal/WrappedHttpResponder.java
+++ b/src/main/java/io/cdap/http/internal/WrappedHttpResponder.java
@@ -80,7 +80,7 @@ public void sendContent(HttpResponseStatus status, ByteBuf content, HttpHeaders
   }
 
   @Override
-  public void sendFile(File file, HttpHeaders headers) throws IOException {
+  public void sendFile(File file, HttpHeaders headers) throws Throwable {
     delegate.sendFile(file, headers);
     runHook(HttpResponseStatus.OK);
   }
diff --git a/src/test/java/io/cdap/http/FileUtils.java b/src/test/java/io/cdap/http/FileUtils.java
new file mode 100644
index 0000000..0467672
--- /dev/null
+++ b/src/test/java/io/cdap/http/FileUtils.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright © 2018 Waarp SAS
+ *
+ * Licensed 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 io.cdap.http;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * A set a file manipulation methods used for testing purposes.
+ */
+public class FileUtils {
+    
+    /** Buffer size used for reading and writing. */
+    private static final int BUFFER_SIZE = 8192;
+    
+    /**
+     * Reads all bytes from an input stream and writes them to an output stream.
+     * @param in            The input stream of the source file.
+     * @param out           The output stream of the destination file.
+     * @throws IOException  Thrown if the program could not read the input stream or write in the output stream.
+     */
+    private static void copy(InputStream in, OutputStream out) throws IOException {
+        byte[] buf = new byte[BUFFER_SIZE];
+        int n;
+        
+        while ((n = in.read(buf)) > 0) {
+            out.write(buf, 0, n);
+        }
+    }
+    
+    /**
+     * Copies all bytes from a file to an output stream.
+     * @param source        The path of the source file.
+     * @param out           The output stream of the destination file.
+     * @throws IOException  Thrown if the program could not read the input stream or write in the output stream.
+     */
+    public static void copy(String source, OutputStream out) throws IOException {
+        InputStream in = null;
+        try {
+            in = new FileInputStream(source);
+            copy(in, out);
+        } finally {
+            if (in != null) {
+                in.close();
+            }
+        }
+    }
+    
+    /**
+     * Copies all bytes from an input stream to a file.
+     * @param in            The input stream of the source file.
+     * @param output        The path of the destination file.
+     * @throws IOException  Thrown if the program could not read the input stream or write in the output stream.
+     */
+    public static void copy(InputStream in, String output) throws IOException {
+        OutputStream out = null;
+        try {
+            out = new FileOutputStream(output);
+            copy(in, out);
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+}
diff --git a/src/test/java/io/cdap/http/HandlerHookTest.java b/src/test/java/io/cdap/http/HandlerHookTest.java
index b198675..bcabb12 100644
--- a/src/test/java/io/cdap/http/HandlerHookTest.java
+++ b/src/test/java/io/cdap/http/HandlerHookTest.java
@@ -50,7 +50,7 @@ public class HandlerHookTest {
   private static final TestHandlerHook handlerHook2 = new TestHandlerHook();
 
   @BeforeClass
-  public static void setup() throws Exception {
+  public static void setup() throws Throwable {
 
     NettyHttpService.Builder builder = NettyHttpService.builder("test-hook");
     builder.setHttpHandlers(new TestHandler());
@@ -155,7 +155,7 @@ public void testUnknownPath() throws Exception {
   }
 
   @AfterClass
-  public static void teardown() throws Exception {
+  public static void teardown() throws Throwable {
     service.stop();
   }
 
@@ -227,7 +227,7 @@ private static int doGet(String resource) throws Exception {
   }
 
   private static int doGet(String resource, String key, String value, String...keyValues) throws Exception {
-    Map headerMap = new HashMap<>();
+    Map headerMap = new HashMap();
     headerMap.put(key, value);
 
     for (int i = 0; i < keyValues.length; i += 2) {
diff --git a/src/test/java/io/cdap/http/HttpServerTest.java b/src/test/java/io/cdap/http/HttpServerTest.java
index 3b6cde1..23c5982 100644
--- a/src/test/java/io/cdap/http/HttpServerTest.java
+++ b/src/test/java/io/cdap/http/HttpServerTest.java
@@ -65,8 +65,7 @@
 import java.net.URI;
 import java.net.URL;
 import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
+import java.nio.charset.Charset;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
@@ -128,13 +127,13 @@ public void modify(ChannelPipeline pipeline) {
   public static TemporaryFolder tmpFolder = new TemporaryFolder();
 
   @BeforeClass
-  public static void setup() throws Exception {
+  public static void setup() throws Throwable {
     service = createBaseNettyHttpServiceBuilder().build();
     service.start();
   }
 
   @AfterClass
-  public static void teardown() throws Exception {
+  public static void teardown() throws Throwable {
     String serviceName = service.getServiceName();
     service.stop();
 
@@ -172,7 +171,9 @@ public void testUploadDisconnect() throws Exception {
     File filePath = new File(tmpFolder.newFolder(), "test.txt");
 
     URI uri = getBaseURI().resolve("/test/v1/stream/upload/file");
-    try (Socket socket = createRawSocket(uri.toURL())) {
+    Socket socket = null;
+    try {
+      socket = createRawSocket(uri.toURL());
 
       // Make a PUT call through socket, so that we can close it prematurely
       PrintStream printer = new PrintStream(socket.getOutputStream(), true, "UTF-8");
@@ -193,6 +194,10 @@ public void testUploadDisconnect() throws Exception {
       }
       Assert.assertTrue(counter < 100);
       // close the socket prematurely
+    } finally {
+      if (socket != null) {
+        socket.close();
+      }
     }
 
     // The file should get removed because of incomplete request due to connection closed
@@ -209,7 +214,7 @@ public void testSendFile() throws IOException {
     File filePath = new File(tmpFolder.newFolder(), "test.txt");
     HttpURLConnection urlConn = request("/test/v1/stream/file", HttpMethod.POST);
     urlConn.setRequestProperty("File-Path", filePath.getAbsolutePath());
-    urlConn.getOutputStream().write("content".getBytes(StandardCharsets.UTF_8));
+    urlConn.getOutputStream().write("content".getBytes(Charset.forName("UTF-8")));
     Assert.assertEquals(200, urlConn.getResponseCode());
     String result = getContent(urlConn);
     Assert.assertEquals("content", result);
@@ -256,7 +261,7 @@ private void testStreamUpload(int size) throws IOException {
 
     //test stream upload
     HttpURLConnection urlConn = request("/test/v1/stream/upload", HttpMethod.PUT);
-    Files.copy(fname.toPath(), urlConn.getOutputStream());
+    FileUtils.copy(fname.getPath(), urlConn.getOutputStream());
     Assert.assertEquals(200, urlConn.getResponseCode());
     urlConn.disconnect();
   }
@@ -271,7 +276,7 @@ public void testStreamUploadFailure() throws IOException {
     randf.close();
 
     HttpURLConnection urlConn = request("/test/v1/stream/upload/fail", HttpMethod.PUT);
-    Files.copy(fname.toPath(), urlConn.getOutputStream());
+    FileUtils.copy(fname.getPath(), urlConn.getOutputStream());
     Assert.assertEquals(500, urlConn.getResponseCode());
     urlConn.disconnect();
   }
@@ -288,7 +293,7 @@ public void testChunkAggregatedUpload() throws IOException {
     //test chunked upload
     HttpURLConnection urlConn = request("/test/v1/aggregate/upload", HttpMethod.PUT);
     urlConn.setChunkedStreamingMode(1024);
-    Files.copy(fname.toPath(), urlConn.getOutputStream());
+    FileUtils.copy(fname.getPath(), urlConn.getOutputStream());
     Assert.assertEquals(200, urlConn.getResponseCode());
 
     Assert.assertEquals(size, Integer.parseInt(getContent(urlConn).split(":")[1].trim()));
@@ -307,7 +312,7 @@ public void testChunkAggregatedUploadFailure() throws IOException {
     //test chunked upload
     HttpURLConnection urlConn = request("/test/v1/aggregate/upload", HttpMethod.PUT);
     urlConn.setChunkedStreamingMode(1024);
-    Files.copy(fname.toPath(), urlConn.getOutputStream());
+    FileUtils.copy(fname.getPath(), urlConn.getOutputStream());
     Assert.assertEquals(413, urlConn.getResponseCode());
     urlConn.disconnect();
   }
@@ -393,7 +398,7 @@ public Thread newThread(Runnable r) {
       }
     };
 
-    final BlockingQueue queue = new ArrayBlockingQueue<>(1);
+    final BlockingQueue queue = new ArrayBlockingQueue(1);
     Bootstrap bootstrap = new Bootstrap()
       .channel(NioSocketChannel.class)
       .remoteAddress(url.getHost(), url.getPort())
@@ -420,7 +425,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
 
     // Make one request, expects the connection to remain active.
     HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, url.getPath(),
-                                                     Unpooled.copiedBuffer("data", StandardCharsets.UTF_8));
+                                                     Unpooled.copiedBuffer("data", Charset.forName("UTF-8")));
     HttpUtil.setContentLength(request, 4);
     channel.writeAndFlush(request);
     HttpResponse response = queue.poll(10, TimeUnit.SECONDS);
@@ -434,7 +439,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
     // Make one more request, the connection should remain open.
     // This request is make with connection: closed
     request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.PUT, url.getPath(),
-                                         Unpooled.copiedBuffer("data", StandardCharsets.UTF_8));
+                                         Unpooled.copiedBuffer("data", Charset.forName("UTF-8")));
     HttpUtil.setContentLength(request, 4);
     HttpUtil.setKeepAlive(request, false);
     channel.writeAndFlush(request);
@@ -594,7 +599,7 @@ public void testPrimitiveQueryParam() throws IOException {
   @Test
   public void testSortedSetQueryParam() throws IOException {
     // For collection, if missing parameter, should get defaulted to empty collection
-    SortedSet expected = new TreeSet<>();
+    SortedSet expected = new TreeSet();
     testContent("/test/v1/sortedSetQueryParam", GSON.toJson(expected), HttpMethod.GET);
 
     expected.add(10);
@@ -645,7 +650,9 @@ public void testConnectionClose() throws Exception {
 
     // Fire http request using raw socket so that we can verify the connection get closed by the server
     // after the response.
-    try (Socket socket = createRawSocket(url)) {
+    Socket socket = null;
+    try {
+      socket = createRawSocket(url);
       PrintStream printer = new PrintStream(socket.getOutputStream(), false, "UTF-8");
       printer.printf("GET %s HTTP/1.1\r\n", url.getPath());
       printer.printf("Host: %s:%d\r\n", url.getHost(), url.getPort());
@@ -656,6 +663,10 @@ public void testConnectionClose() throws Exception {
       // end with an EOF. Otherwise there will be timeout of this test case
       String response = getContent(socket.getInputStream());
       Assert.assertTrue(response.startsWith("HTTP/1.1 200 OK"));
+    } finally {
+      if (socket != null) {
+        socket.close();
+      }
     }
   }
 
@@ -664,7 +675,7 @@ public void testUploadReject() throws Exception {
     HttpURLConnection urlConn = request("/test/v1/uploadReject", HttpMethod.POST, true);
     try {
       urlConn.setChunkedStreamingMode(1024);
-      urlConn.getOutputStream().write("Rejected Content".getBytes(StandardCharsets.UTF_8));
+      urlConn.getOutputStream().write("Rejected Content".getBytes(Charset.forName("UTF-8")));
       try {
         urlConn.getInputStream();
         Assert.fail();
@@ -718,21 +729,21 @@ public void testExceptionHandler() throws IOException {
     // exception in body consumer's chunk
     urlConn = request("/test/v1/stream/customException", HttpMethod.POST);
     urlConn.setRequestProperty("failOn", "chunk");
-    Files.copy(fname.toPath(), urlConn.getOutputStream());
+    FileUtils.copy(fname.getPath(), urlConn.getOutputStream());
     Assert.assertEquals(TestHandler.CustomException.HTTP_RESPONSE_STATUS.code(), urlConn.getResponseCode());
     urlConn.disconnect();
 
     // exception in body consumer's onFinish
     urlConn = request("/test/v1/stream/customException", HttpMethod.POST);
     urlConn.setRequestProperty("failOn", "finish");
-    Files.copy(fname.toPath(), urlConn.getOutputStream());
+    FileUtils.copy(fname.getPath(), urlConn.getOutputStream());
     Assert.assertEquals(TestHandler.CustomException.HTTP_RESPONSE_STATUS.code(), urlConn.getResponseCode());
     urlConn.disconnect();
 
     // exception in body consumer's handleError
     urlConn = request("/test/v1/stream/customException", HttpMethod.POST);
     urlConn.setRequestProperty("failOn", "error");
-    Files.copy(fname.toPath(), urlConn.getOutputStream());
+    FileUtils.copy(fname.getPath(), urlConn.getOutputStream());
     Assert.assertEquals(TestHandler.CustomException.HTTP_RESPONSE_STATUS.code(), urlConn.getResponseCode());
     urlConn.disconnect();
   }
@@ -773,7 +784,7 @@ public void testCompressResponse() throws Exception {
     urlConn.setRequestProperty(HttpHeaderNames.ACCEPT_ENCODING.toString(), HttpHeaderValues.GZIP_DEFLATE.toString());
 
     Assert.assertEquals(HttpResponseStatus.OK.code(), urlConn.getResponseCode());
-    Assert.assertTrue(urlConn.getHeaderField(HttpHeaderNames.CONTENT_ENCODING.toString()) != null);
+    Assert.assertNotNull(urlConn.getHeaderField(HttpHeaderNames.CONTENT_ENCODING.toString()));
 
     Assert.assertEquals("Testing message", getContent(urlConn));
 
@@ -798,7 +809,7 @@ public void testContinueHandler() throws Exception {
       urlConn.setRequestProperty("Expect", "100-continue");
       urlConn.setRequestProperty("File-Path", filePath.getAbsolutePath());
 
-      urlConn.getOutputStream().write("content".getBytes(StandardCharsets.UTF_8));
+      urlConn.getOutputStream().write("content".getBytes(Charset.forName("UTF-8")));
       Assert.assertEquals(200, urlConn.getResponseCode());
       String result = getContent(urlConn);
       Assert.assertEquals("content", result);
@@ -825,7 +836,7 @@ public void testHeaders() throws IOException {
     Assert.assertEquals(200, urlConn.getResponseCode());
     Map> headers = urlConn.getHeaderFields();
 
-    Assert.assertEquals(new HashSet<>(Arrays.asList("v1", "v2", "v3")), new HashSet<>(headers.get("k1")));
+    Assert.assertEquals(new HashSet(Arrays.asList("v1", "v2", "v3")), new HashSet(headers.get("k1")));
     Assert.assertEquals(Collections.singletonList("v1"), headers.get("k2"));
   }
 
@@ -888,10 +899,10 @@ private String getContent(InputStream is) throws IOException {
     while (buffer.writeBytes(is, 1024) > 0) {
       // no-op
     }
-    return buffer.toString(StandardCharsets.UTF_8);
+    return buffer.toString(Charset.forName("UTF-8"));
   }
 
   private void writeContent(HttpURLConnection urlConn, String content) throws IOException {
-    urlConn.getOutputStream().write(content.getBytes(StandardCharsets.UTF_8));
+    urlConn.getOutputStream().write(content.getBytes(Charset.forName("UTF-8")));
   }
 }
diff --git a/src/test/java/io/cdap/http/HttpsServerTest.java b/src/test/java/io/cdap/http/HttpsServerTest.java
index 683460c..b52bd59 100644
--- a/src/test/java/io/cdap/http/HttpsServerTest.java
+++ b/src/test/java/io/cdap/http/HttpsServerTest.java
@@ -30,8 +30,6 @@
 import java.net.Socket;
 import java.net.URI;
 import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
 import javax.annotation.Nullable;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
@@ -46,11 +44,17 @@ public class HttpsServerTest extends HttpServerTest {
   private static SSLClientContext sslClientContext;
 
   @BeforeClass
-  public static void setup() throws Exception {
+  public static void setup() throws Throwable {
     File keyStore = tmpFolder.newFile();
-
-    try (InputStream is = SSLKeyStoreTest.class.getClassLoader().getResourceAsStream("cert.jks")) {
-      Files.copy(is, keyStore.toPath(), StandardCopyOption.REPLACE_EXISTING);
+  
+    InputStream is = null;
+    try {
+      is = SSLKeyStoreTest.class.getClassLoader().getResourceAsStream("cert.jks");
+      FileUtils.copy(is, keyStore.getPath());
+    } finally {
+      if (is != null) {
+        is.close();
+      }
     }
 
     /* IMPORTANT
diff --git a/src/test/java/io/cdap/http/InternalHttpResponderTest.java b/src/test/java/io/cdap/http/InternalHttpResponderTest.java
index 2b31ba9..2f94a7b 100644
--- a/src/test/java/io/cdap/http/InternalHttpResponderTest.java
+++ b/src/test/java/io/cdap/http/InternalHttpResponderTest.java
@@ -32,7 +32,7 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
-import java.nio.charset.StandardCharsets;
+import java.nio.charset.Charset;
 import javax.annotation.Nullable;
 
 /**
@@ -49,9 +49,16 @@ public void testSendJson() throws IOException {
 
     InternalHttpResponse response = responder.getResponse();
     Assert.assertEquals(HttpResponseStatus.OK.code(), response.getStatusCode());
-    try (Reader reader = new InputStreamReader(response.openInputStream(), "UTF-8")) {
+  
+    Reader reader = null;
+    try {
+      reader = new InputStreamReader(response.openInputStream(), "UTF-8");
       JsonObject responseData = new Gson().fromJson(reader, JsonObject.class);
       Assert.assertEquals(output, responseData);
+    } finally {
+      if (reader != null) {
+        reader.close();
+      }
     }
   }
 
@@ -74,7 +81,7 @@ public void testSendStatus() throws IOException {
   @Test
   public void testSendByteArray() throws IOException {
     InternalHttpResponder responder = new InternalHttpResponder();
-    responder.sendByteArray(HttpResponseStatus.OK, "abc".getBytes(StandardCharsets.UTF_8), EmptyHttpHeaders.INSTANCE);
+    responder.sendByteArray(HttpResponseStatus.OK, "abc".getBytes(Charset.forName("UTF-8")), EmptyHttpHeaders.INSTANCE);
 
     validateResponse(responder.getResponse(), HttpResponseStatus.OK, "abc");
   }
@@ -91,9 +98,9 @@ public void testSendError() throws IOException {
   public void testChunks() throws IOException {
     InternalHttpResponder responder = new InternalHttpResponder();
     ChunkResponder chunkResponder = responder.sendChunkStart(HttpResponseStatus.OK, null);
-    chunkResponder.sendChunk(Unpooled.wrappedBuffer("a".getBytes(StandardCharsets.UTF_8)));
-    chunkResponder.sendChunk(Unpooled.wrappedBuffer("b".getBytes(StandardCharsets.UTF_8)));
-    chunkResponder.sendChunk(Unpooled.wrappedBuffer("c".getBytes(StandardCharsets.UTF_8)));
+    chunkResponder.sendChunk(Unpooled.wrappedBuffer("a".getBytes(Charset.forName("UTF-8"))));
+    chunkResponder.sendChunk(Unpooled.wrappedBuffer("b".getBytes(Charset.forName("UTF-8"))));
+    chunkResponder.sendChunk(Unpooled.wrappedBuffer("c".getBytes(Charset.forName("UTF-8"))));
     chunkResponder.close();
 
     validateResponse(responder.getResponse(), HttpResponseStatus.OK, "abc");
@@ -102,7 +109,7 @@ public void testChunks() throws IOException {
   @Test
   public void testSendContent() throws IOException {
     InternalHttpResponder responder = new InternalHttpResponder();
-    responder.sendContent(HttpResponseStatus.OK, Unpooled.wrappedBuffer("abc".getBytes(StandardCharsets.UTF_8)),
+    responder.sendContent(HttpResponseStatus.OK, Unpooled.wrappedBuffer("abc".getBytes(Charset.forName("UTF-8"))),
                           new DefaultHttpHeaders().set(HttpHeaderNames.CONTENT_TYPE, "contentType"));
 
     validateResponse(responder.getResponse(), HttpResponseStatus.OK, "abc");
@@ -116,9 +123,15 @@ private void validateResponse(InternalHttpResponse response, HttpResponseStatus
     if (expectedData != null) {
       // read it twice to make sure the input supplier gives the full stream more than once.
       for (int i = 0; i < 2; i++) {
-        try (BufferedReader reader = new BufferedReader(new InputStreamReader(response.openInputStream(), "UTF-8"))) {
+        BufferedReader reader = null;
+        try {
+          reader = new BufferedReader(new InputStreamReader(response.openInputStream(), "UTF-8"));
           String data = reader.readLine();
           Assert.assertEquals(expectedData, data);
+        } finally {
+          if (reader != null) {
+            reader.close();
+          }
         }
       }
     }
diff --git a/src/test/java/io/cdap/http/MutualAuthServerTest.java b/src/test/java/io/cdap/http/MutualAuthServerTest.java
index 25c080b..0537048 100644
--- a/src/test/java/io/cdap/http/MutualAuthServerTest.java
+++ b/src/test/java/io/cdap/http/MutualAuthServerTest.java
@@ -20,8 +20,7 @@
 
 import java.io.File;
 import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
+import java.net.URI;
 
 /**
  * Test the HttpsServer with mutual authentication.
@@ -29,17 +28,29 @@
 public class MutualAuthServerTest extends HttpsServerTest {
 
   @BeforeClass
-  public static void setup() throws Exception {
+  public static void setup() throws Throwable {
     NettyHttpService.Builder builder = createBaseNettyHttpServiceBuilder();
 
     File keyStore = tmpFolder.newFile();
-    try (InputStream is = SSLKeyStoreTest.class.getClassLoader().getResourceAsStream("cert.jks")) {
-      Files.copy(is, keyStore.toPath(), StandardCopyOption.REPLACE_EXISTING);
+    InputStream is = null;
+    try {
+      is = SSLKeyStoreTest.class.getClassLoader().getResourceAsStream("cert.jks");
+      FileUtils.copy(is, keyStore.getPath());
+    } finally {
+      if (is != null) {
+        is.close();
+      }
     }
 
     File trustKeyStore = tmpFolder.newFile();
-    try (InputStream is = SSLKeyStoreTest.class.getClassLoader().getResourceAsStream("client.jks")) {
-      Files.copy(is, trustKeyStore.toPath(), StandardCopyOption.REPLACE_EXISTING);
+    is = null;
+    try {
+      is = SSLKeyStoreTest.class.getClassLoader().getResourceAsStream("client.jks");
+      FileUtils.copy(is, trustKeyStore.getPath());
+    } finally {
+      if (is != null) {
+        is.close();
+      }
     }
 
     String keyStorePassword = "secret";
diff --git a/src/test/java/io/cdap/http/PathRouterTest.java b/src/test/java/io/cdap/http/PathRouterTest.java
index 41202a8..7f35e19 100644
--- a/src/test/java/io/cdap/http/PathRouterTest.java
+++ b/src/test/java/io/cdap/http/PathRouterTest.java
@@ -109,8 +109,9 @@ public void testPathRoutings() {
 
     routes = pathRouter.getDestinations("/multi/match/def");
     Assert.assertEquals(2, routes.size());
-    Assert.assertEquals(new HashSet<>(Arrays.asList("multi-match-def", "multi-match-*")),
-                        new HashSet<>(Arrays.asList(routes.get(0).getDestination(), routes.get(1).getDestination())));
+    Assert.assertEquals(new HashSet(Arrays.asList("multi-match-def", "multi-match-*")),
+                        new HashSet(Arrays.asList(routes.get(0).getDestination(),
+                                routes.get(1).getDestination())));
     Assert.assertTrue(routes.get(0).getGroupNameValues().isEmpty());
     Assert.assertTrue(routes.get(1).getGroupNameValues().isEmpty());
 
@@ -121,35 +122,38 @@ public void testPathRoutings() {
 
     routes = pathRouter.getDestinations("/multi/maxmatch/id1");
     Assert.assertEquals(2, routes.size());
-    Assert.assertEquals(new HashSet<>(Arrays.asList("multi-max-match-id", "multi-max-match-*")),
-                        new HashSet<>(Arrays.asList(routes.get(0).getDestination(), routes.get(1).getDestination())));
+    Assert.assertEquals(new HashSet(Arrays.asList("multi-max-match-id", "multi-max-match-*")),
+                        new HashSet(Arrays.asList(routes.get(0).getDestination(),
+                                routes.get(1).getDestination())));
 
-    Assert.assertEquals(new HashSet<>(Arrays.asList(Collections.singletonMap("id", "id1"),
+    Assert.assertEquals(new HashSet>(Arrays.asList(Collections.singletonMap("id", "id1"),
                                                     Collections.emptyMap())),
-                        new HashSet<>(Arrays.asList(routes.get(0).getGroupNameValues(),
+                        new HashSet>(Arrays.asList(routes.get(0).getGroupNameValues(),
                                                     routes.get(1).getGroupNameValues()))
     );
 
     routes = pathRouter.getDestinations("/multi/maxmatch/foo");
     Assert.assertEquals(3, routes.size());
-    Assert.assertEquals(new HashSet<>(Arrays.asList("multi-max-match-id", "multi-max-match-*", "multi-max-match-foo")),
-                        new HashSet<>(Arrays.asList(routes.get(0).getDestination(), routes.get(1).getDestination(),
-                                                    routes.get(2).getDestination())));
+    Assert.assertEquals(new HashSet(Arrays.asList("multi-max-match-id", "multi-max-match-*",
+                                                    "multi-max-match-foo")),
+                        new HashSet(Arrays.asList(routes.get(0).getDestination(),
+                                routes.get(1).getDestination(), routes.get(2).getDestination())));
 
-    Assert.assertEquals(new HashSet<>(Arrays.asList(Collections.singletonMap("id", "foo"),
+    Assert.assertEquals(new HashSet>(Arrays.asList(Collections.singletonMap("id", "foo"),
                                                     Collections.emptyMap())),
-                        new HashSet<>(Arrays.asList(routes.get(0).getGroupNameValues(),
+                        new HashSet>(Arrays.asList(routes.get(0).getGroupNameValues(),
                                                     routes.get(1).getGroupNameValues()))
     );
 
     routes = pathRouter.getDestinations("/foo/bar/wildcard/id1");
     Assert.assertEquals(2, routes.size());
-    Assert.assertEquals(new HashSet<>(Arrays.asList("wildcard-id", "slash-wildcard-id")),
-                        new HashSet<>(Arrays.asList(routes.get(0).getDestination(), routes.get(1).getDestination())));
+    Assert.assertEquals(new HashSet(Arrays.asList("wildcard-id", "slash-wildcard-id")),
+                        new HashSet(Arrays.asList(routes.get(0).getDestination(),
+                                routes.get(1).getDestination())));
 
-    Assert.assertEquals(new HashSet<>(Arrays.asList(Collections.singletonMap("id", "id1"),
+    Assert.assertEquals(new HashSet>(Arrays.asList(Collections.singletonMap("id", "id1"),
                                                     Collections.singletonMap("id", "id1"))),
-                        new HashSet<>(Arrays.asList(routes.get(0).getGroupNameValues(),
+                        new HashSet>(Arrays.asList(routes.get(0).getGroupNameValues(),
                                                     routes.get(1).getGroupNameValues()))
     );
 
@@ -160,23 +164,25 @@ public void testPathRoutings() {
 
     routes = pathRouter.getDestinations("/foo/bar/wildcard/bar/foo/id1");
     Assert.assertEquals(2, routes.size());
-    Assert.assertEquals(new HashSet<>(Arrays.asList("wildcard-foo-id", "slash-wildcard-foo-id")),
-                        new HashSet<>(Arrays.asList(routes.get(0).getDestination(), routes.get(1).getDestination())));
+    Assert.assertEquals(new HashSet(Arrays.asList("wildcard-foo-id", "slash-wildcard-foo-id")),
+                        new HashSet(Arrays.asList(routes.get(0).getDestination(),
+                                routes.get(1).getDestination())));
 
-    Assert.assertEquals(new HashSet<>(Arrays.asList(Collections.singletonMap("id", "id1"),
+    Assert.assertEquals(new HashSet>(Arrays.asList(Collections.singletonMap("id", "id1"),
                                                     Collections.singletonMap("id", "id1"))),
-                        new HashSet<>(Arrays.asList(routes.get(0).getGroupNameValues(),
+                        new HashSet>(Arrays.asList(routes.get(0).getGroupNameValues(),
                                                     routes.get(1).getGroupNameValues()))
     );
 
     routes = pathRouter.getDestinations("/foo/bar/wildcard/bar/foo/id1/baz/bar");
     Assert.assertEquals(2, routes.size());
-    Assert.assertEquals(new HashSet<>(Arrays.asList("wildcard-foo-id-2", "slash-wildcard-foo-id-2")),
-                        new HashSet<>(Arrays.asList(routes.get(0).getDestination(), routes.get(1).getDestination())));
+    Assert.assertEquals(new HashSet(Arrays.asList("wildcard-foo-id-2", "slash-wildcard-foo-id-2")),
+                        new HashSet(Arrays.asList(routes.get(0).getDestination(),
+                                routes.get(1).getDestination())));
 
-    Assert.assertEquals(new HashSet<>(Arrays.asList(Collections.singletonMap("id", "id1"),
+    Assert.assertEquals(new HashSet>(Arrays.asList(Collections.singletonMap("id", "id1"),
                                                     Collections.singletonMap("id", "id1"))),
-                        new HashSet<>(Arrays.asList(routes.get(0).getGroupNameValues(),
+                        new HashSet>(Arrays.asList(routes.get(0).getGroupNameValues(),
                                                     routes.get(1).getGroupNameValues()))
     );
 
diff --git a/src/test/java/io/cdap/http/SSLClientContext.java b/src/test/java/io/cdap/http/SSLClientContext.java
index 8f8be60..1c1fae9 100644
--- a/src/test/java/io/cdap/http/SSLClientContext.java
+++ b/src/test/java/io/cdap/http/SSLClientContext.java
@@ -57,7 +57,9 @@ public SSLClientContext(File keyStore, String keyStorePassword) {
   }
 
   private static KeyStore getKeyStore(File keyStore, String keyStorePassword) throws IOException {
-    try (InputStream is = new FileInputStream(keyStore)) {
+    InputStream is = null;
+    try {
+      is = new FileInputStream(keyStore);
       KeyStore ks = KeyStore.getInstance("JKS");
       ks.load(is, keyStorePassword.toCharArray());
       return ks;
@@ -66,6 +68,10 @@ private static KeyStore getKeyStore(File keyStore, String keyStorePassword) thro
         throw ((RuntimeException) ex);
       }
       throw new IOException(ex);
+    } finally {
+      if (is != null) {
+        is.close();
+      }
     }
   }
 
diff --git a/src/test/java/io/cdap/http/SSLKeyStoreTest.java b/src/test/java/io/cdap/http/SSLKeyStoreTest.java
index 325dbdc..fa1e339 100644
--- a/src/test/java/io/cdap/http/SSLKeyStoreTest.java
+++ b/src/test/java/io/cdap/http/SSLKeyStoreTest.java
@@ -23,8 +23,6 @@
 
 import java.io.File;
 import java.io.InputStream;
-import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
 
 /**
  * Tests SSL KeyStore behaviour
@@ -38,8 +36,14 @@ public class SSLKeyStoreTest {
   @BeforeClass
   public static void setup() throws Exception {
     keyStore = tmpFolder.newFile();
-    try (InputStream is = SSLKeyStoreTest.class.getClassLoader().getResourceAsStream("cert.jks")) {
-      Files.copy(is, keyStore.toPath(), StandardCopyOption.REPLACE_EXISTING);
+    InputStream is = null;
+    try {
+      is = SSLKeyStoreTest.class.getClassLoader().getResourceAsStream("cert.jks");
+      FileUtils.copy(is, keyStore.getPath());
+    } finally {
+      if (is != null) {
+        is.close();
+      }
     }
   }
 
diff --git a/src/test/java/io/cdap/http/TestHandler.java b/src/test/java/io/cdap/http/TestHandler.java
index 5904d24..256c7ac 100644
--- a/src/test/java/io/cdap/http/TestHandler.java
+++ b/src/test/java/io/cdap/http/TestHandler.java
@@ -37,7 +37,7 @@
 import java.io.PrintStream;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
-import java.nio.charset.StandardCharsets;
+import java.nio.charset.Charset;
 import java.util.List;
 import java.util.SortedSet;
 import java.util.concurrent.TimeUnit;
@@ -170,7 +170,7 @@ public void exception(HttpRequest request, HttpResponder responder) {
   }
 
   private String getStringContent(FullHttpRequest request) throws IOException {
-    return request.content().toString(StandardCharsets.UTF_8);
+    return request.content().toString(Charset.forName("UTF-8"));
   }
 
   @Path("/multi-match/**")
@@ -366,6 +366,7 @@ public void finished(HttpResponder responder) {
           responder.sendFile(file);
         } catch (IOException e) {
           throw new RuntimeException(e);
+        } catch (Throwable e) {
         }
       }
 
@@ -399,6 +400,7 @@ public void chunk(FullHttpRequest request, HttpResponder responder) throws IOExc
     while (content.isReadable()) {
       chunker.sendChunk(content.readBytes(1));
     }
+    content.release();
     chunker.close();
   }
 
@@ -415,7 +417,7 @@ public void produceBody(HttpRequest request, HttpResponder responder,
       @Override
       public ByteBuf nextChunk() {
         if (times < repeat) {
-          return Unpooled.wrappedBuffer(StandardCharsets.UTF_8.encode(chunk + " " + times++));
+          return Unpooled.wrappedBuffer(Charset.forName("UTF-8").encode(chunk + " " + times++));
         }
         return Unpooled.EMPTY_BUFFER;
       }
@@ -429,12 +431,18 @@ public void finished() throws Exception {
 
       @Override
       public void handleError(@Nullable Throwable cause) {
-        try (PrintStream printer = new PrintStream(new FileOutputStream(new File(failureFile)), true)) {
+        PrintStream printer = null;
+        try {
+          printer = new PrintStream(new FileOutputStream(new File(failureFile)), true);
           if (cause != null) {
             cause.printStackTrace(printer);
           }
         } catch (FileNotFoundException e) {
           throw new RuntimeException(e);
+        } finally {
+          if (printer != null) {
+            printer.close();
+          }
         }
       }
     }, EmptyHttpHeaders.INSTANCE);
@@ -560,7 +568,7 @@ public void testCompressResponse(HttpRequest request, HttpResponder responder,
       return;
     }
 
-    final ByteBuf content = Unpooled.copiedBuffer(message, StandardCharsets.UTF_8);
+    final ByteBuf content = Unpooled.copiedBuffer(message, Charset.forName("UTF-8"));
     if (!chunk) {
       responder.sendContent(HttpResponseStatus.OK, content, EmptyHttpHeaders.INSTANCE);
     } else {
diff --git a/src/test/java/io/cdap/http/URLRewriterTest.java b/src/test/java/io/cdap/http/URLRewriterTest.java
index d3f0faa..583e4a5 100644
--- a/src/test/java/io/cdap/http/URLRewriterTest.java
+++ b/src/test/java/io/cdap/http/URLRewriterTest.java
@@ -35,7 +35,7 @@
 import java.net.HttpURLConnection;
 import java.net.URI;
 import java.net.URL;
-import java.nio.charset.StandardCharsets;
+import java.nio.charset.Charset;
 import java.util.Collections;
 import java.util.Map;
 import javax.annotation.Nullable;
@@ -51,7 +51,7 @@ public class URLRewriterTest {
   private static URI baseURI;
 
   @BeforeClass
-  public static void setup() throws Exception {
+  public static void setup() throws Throwable {
 
     NettyHttpService.Builder builder = NettyHttpService.builder("test-url-rewrite");
     builder.setHttpHandlers(new TestHandler());
@@ -66,7 +66,7 @@ public static void setup() throws Exception {
   }
 
   @AfterClass
-  public static void teardown() throws Exception {
+  public static void teardown() throws Throwable {
     service.stop();
   }
 
@@ -162,6 +162,6 @@ private String getContent(HttpURLConnection urlConn) throws IOException {
     while (buffer.writeBytes(is, 1024) > 0) {
       // no-op
     }
-    return buffer.toString(StandardCharsets.UTF_8);
+    return buffer.toString(Charset.forName("UTF-8"));
   }
 }