You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2019/05/23 15:54:24 UTC
[sling-whiteboard] 03/08: POC for a Java Agent to set URL
connection timeout defaults
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to branch feature/url-connection-agent
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git
commit 7da88ff098521199065c765236e4442b60b84798
Author: Robert Munteanu <ro...@apache.org>
AuthorDate: Thu May 23 17:42:48 2019 +0200
POC for a Java Agent to set URL connection timeout defaults
Completed initial implementation
---
url-connection-agent/pom.xml | 4 ++
.../main/java/org/apache/sling/uca/impl/Agent.java | 68 ++++++--------------
.../main/java/org/apache/sling/uca/impl/Main.java | 21 +++++-
.../sling/uca/impl/URLTimeoutTransformer.java | 74 ++++++++++++++++++++++
.../sling/uca/impl/UrlTimeoutTransformerTest.java | 31 +++++++++
5 files changed, 146 insertions(+), 52 deletions(-)
diff --git a/url-connection-agent/pom.xml b/url-connection-agent/pom.xml
index cdaf3e1..6945823 100644
--- a/url-connection-agent/pom.xml
+++ b/url-connection-agent/pom.xml
@@ -47,5 +47,9 @@
<artifactId>javassist</artifactId>
<version>3.24.0-GA</version>
</dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
diff --git a/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Agent.java b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Agent.java
index 1391182..36f7c65 100644
--- a/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Agent.java
+++ b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Agent.java
@@ -1,63 +1,31 @@
package org.apache.sling.uca.impl;
-import java.io.IOException;
-import java.lang.instrument.ClassFileTransformer;
-import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
-import java.security.ProtectionDomain;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Set;
-
-import javassist.CannotCompileException;
-import javassist.ClassPool;
-import javassist.CtClass;
-import javassist.CtMethod;
-import javassist.NotFoundException;
+import java.util.concurrent.TimeUnit;
public class Agent {
public static void premain(String args, Instrumentation inst) {
- System.out.println("Loading agent...");
- inst.addTransformer(new URLTimeoutTransformer(), true);
- System.out.println("Loaded agent!");
+ System.out.println("[AGENT] Loading agent...");
+ String[] parsedArgs = args.split(",");
+ long connectTimeout = TimeUnit.MINUTES.toMillis(1);
+ long readTimeout = TimeUnit.MINUTES.toMillis(1);
+ if ( parsedArgs.length > 0 )
+ connectTimeout = Long.parseLong(parsedArgs[0]);
+ if ( parsedArgs.length > 1 )
+ readTimeout = Long.parseLong(parsedArgs[1]);
+
+ System.out.format("[AGENT] Set connectTimeout : %d, readTimeout: %d%n", connectTimeout, readTimeout);
+
+ URLTimeoutTransformer transformer = new URLTimeoutTransformer(connectTimeout, readTimeout);
+
+ inst.addTransformer(transformer, true);
+ System.out.println("[AGENT] Loaded agent!");
}
-
+
public static void agentmain(String args, Instrumentation inst) {
premain(args, inst);
}
-
- static class URLTimeoutTransformer implements ClassFileTransformer {
-
- private static final Set<String> CLASSES_TO_TRANSFORM = new HashSet<>();
-
- static {
- CLASSES_TO_TRANSFORM.add("sun.net.www.protocol.http.HttpURLConnection".replace('.', '/'));
- CLASSES_TO_TRANSFORM.add("sun.net.www.protocol.https.HttpsURLConnectionImpl".replace('.', '/'));
- }
-
- private final Class<?> klazz = HashMap.class;
-
- @Override
- public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
- ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
- try {
- if ( CLASSES_TO_TRANSFORM.contains(className)) {
- System.out.println("Asked to transform " + className);
- CtClass cc = ClassPool.getDefault().get(klazz.getName());
- CtMethod connectMethod = cc.getDeclaredMethod("connect");
- connectMethod.insertBefore("if ( getConnectTimeout() == 0 ) { setConnectTimeout(60); }");
- connectMethod.insertBefore("if ( getReadTimeout() == 0 ) { setReadTimeout(60); }");
- classfileBuffer = cc.toBytecode();
- cc.detach();
- System.err.println("Transformation complete!");
- }
- return classfileBuffer;
- } catch (NotFoundException | CannotCompileException | IOException e) {
- throw new RuntimeException("Transformation failed", e);
- }
- }
- }
-}
+}
diff --git a/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Main.java b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Main.java
index 21bcbb7..883aa01 100644
--- a/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Main.java
+++ b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/Main.java
@@ -1,14 +1,31 @@
package org.apache.sling.uca.impl;
+import java.io.BufferedReader;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
+import java.net.URLConnection;
public class Main {
-
+
public static void main(String[] args) throws MalformedURLException, IOException {
- new URL("http://sling.apache.org").openConnection();
+ if ( args.length != 1 )
+ throw new IllegalArgumentException("Usage: java -jar ... <URL>");
+
+ URLConnection con = new URL(args[0]).openConnection();
+ System.out.println("Connection type is " + con);
+
+ try (InputStream in = con.getInputStream();
+ InputStreamReader isr = new InputStreamReader(in);
+ BufferedReader br = new BufferedReader(isr)) {
+ String line;
+ while ( (line = br.readLine()) != null )
+ System.out.println("[WEB] " + line);
+ }
+
}
}
diff --git a/url-connection-agent/src/main/java/org/apache/sling/uca/impl/URLTimeoutTransformer.java b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/URLTimeoutTransformer.java
new file mode 100644
index 0000000..a5b720d
--- /dev/null
+++ b/url-connection-agent/src/main/java/org/apache/sling/uca/impl/URLTimeoutTransformer.java
@@ -0,0 +1,74 @@
+package org.apache.sling.uca.impl;
+
+import java.lang.instrument.ClassFileTransformer;
+import java.net.URLConnection;
+import java.security.ProtectionDomain;
+import java.util.HashSet;
+import java.util.Set;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.NotFoundException;
+import javassist.bytecode.Descriptor;
+
+/**
+ * Transforms well-known HTTP URL connection classes
+ *
+ * <p>This implementation adds connect and read timeouts to those connections
+ * if none are defined.</p>
+ *
+ * @see URLConnection#getConnectTimeout()
+ * @see URLConnection#getReadTimeout()
+ *
+ */
+class URLTimeoutTransformer implements ClassFileTransformer {
+
+ private static final Set<String> CLASSES_TO_TRANSFORM = new HashSet<>();
+
+ static {
+ CLASSES_TO_TRANSFORM.add(Descriptor.toJvmName("sun.net.www.protocol.http.HttpURLConnection"));
+ CLASSES_TO_TRANSFORM.add(Descriptor.toJvmName("sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection"));
+ }
+
+ private final long readTimeoutMillis;
+ private final long connectTimeoutMillis;
+
+ public URLTimeoutTransformer(long connectTimeout, long readTimeout) {
+ this.readTimeoutMillis = connectTimeout;
+ this.connectTimeoutMillis = readTimeout;
+ }
+
+ @Override
+ public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
+ ProtectionDomain protectionDomain, byte[] classfileBuffer) {
+ try {
+ if (CLASSES_TO_TRANSFORM.contains(className)) {
+ System.out.println("[AGENT] Asked to transform " + className);
+ CtMethod connectMethod = findConnectMethod(className);
+ connectMethod.insertBefore("if ( getConnectTimeout() == 0 ) { setConnectTimeout(" + connectTimeoutMillis + "); }");
+ connectMethod.insertBefore("if ( getReadTimeout() == 0 ) { setReadTimeout(" + readTimeoutMillis + "); }");
+ classfileBuffer = connectMethod.getDeclaringClass().toBytecode();
+ connectMethod.getDeclaringClass().detach();
+ System.out.println("[AGENT] Transformation complete!");
+ }
+ return classfileBuffer;
+ } catch (Exception e) {
+ e.printStackTrace(); // ensure _something_ is printed
+ throw new RuntimeException("[AGENT] Transformation failed", e);
+ }
+ }
+
+ CtMethod findConnectMethod(String className) throws NotFoundException {
+
+ ClassPool defaultPool = ClassPool.getDefault();
+ CtClass cc = defaultPool.get(Descriptor.toJavaName(className));
+ if (cc == null) {
+ System.out.println("[AGENT] no class found with name " + className);
+ return null;
+ }
+ return cc.getDeclaredMethod("connect");
+
+ }
+
+}
\ No newline at end of file
diff --git a/url-connection-agent/src/test/java/org/apache/sling/uca/impl/UrlTimeoutTransformerTest.java b/url-connection-agent/src/test/java/org/apache/sling/uca/impl/UrlTimeoutTransformerTest.java
new file mode 100644
index 0000000..84da14c
--- /dev/null
+++ b/url-connection-agent/src/test/java/org/apache/sling/uca/impl/UrlTimeoutTransformerTest.java
@@ -0,0 +1,31 @@
+package org.apache.sling.uca.impl;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import javassist.NotFoundException;
+
+public class UrlTimeoutTransformerTest {
+
+ private URLTimeoutTransformer transformer;
+
+ @Before
+ public void initFields() {
+ transformer = new URLTimeoutTransformer(1, 1);
+ }
+
+ @Test
+ public void findDeclaredConnectMethod() throws NotFoundException {
+ assertNotNull(transformer.findConnectMethod("sun/net/www/protocol/http/HttpURLConnection"));
+ }
+
+ @Test(expected = NotFoundException.class)
+ public void findInheritedConnectMethod() throws NotFoundException {
+ // do NOT look for inherited methods, as we can only rewrite the precise classes the
+ // retransform was triggered for
+ transformer.findConnectMethod("sun/net/www/protocol/https/DelegateHttpsURLConnection");
+ }
+
+}