5

I'm using AspectJ to intercept java.net.Socket calls.

I've created very simple aspect

after(): call(* java.net.Socket.connect(..)) {
    System.out.println("Connect intercepted!");
}

and aop.xml

<aspectj>

    <aspects>
        <aspect name="com.iggroup.lightstreamer.nwtp.SocketExceptionLoggingAspect"/>
    </aspects>

    <weaver options="-Xlint:ignore -Xset:weaveJavaxPackages=true -Xset:weaveJavaPackages=true">
    </weaver>

</aspectj>

When the call stack is like this, then I can see the console output:

java.lang.Exception
    at com.iggroup.lightstreamer.nwtp.SocketExceptionLoggingAspect.ajc$after$com_iggroup_lightstreamer_nwtp_SocketExceptionLoggingAspect$2$6e16217c(SocketExceptionLoggingAspect.aj:39)
    at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:337)
    at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:134)
    at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353)
    at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:55)
    at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:91)
    at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:596)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:557)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:475)
    at com.iggroup.lightstreamer.nwtp.users.SsoRestClientImpl.lambda$0(SsoRestClientImpl.java:68)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

But nothing is logged out when the call stack is like this:

Caused by: java.net.ConnectException: Connection refused: connect
                at java.net.DualStackPlainSocketImpl.connect0(Native Method) ~[na:1.8.0_65]
                at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79) ~[na:1.8.0_65]
                at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) ~[na:1.8.0_65]
                at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) ~[na:1.8.0_65]
                at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) ~[na:1.8.0_65]
                at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:172) ~[na:1.8.0_65]
                at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[na:1.8.0_65]
                at java.net.Socket.connect(Socket.java:589) ~[na:1.8.0_65]
                at sun.security.ssl.SSLSocketImpl.connect(SSLSocketImpl.java:668) ~[na:1.8.0_65]
                at sun.security.ssl.BaseSSLSocketImpl.connect(BaseSSLSocketImpl.java:173) ~[na:1.8.0_65]
                at sun.net.NetworkClient.doConnect(NetworkClient.java:180) ~[na:1.8.0_65]
                at sun.net.www.http.HttpClient.openServer(HttpClient.java:432) ~[na:1.8.0_65]
                at sun.net.www.http.HttpClient.openServer(HttpClient.java:527) ~[na:1.8.0_65]
                at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:781) ~[na:1.8.0_65]
                at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647) ~[na:1.8.0_65]
                at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1536) ~[na:1.8.0_65]
                at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441) ~[na:1.8.0_65]
                at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:254) ~[na:1.8.0_65]
                at com.lightstreamer.ls_client.HttpProvider.connectAndGetAnswer(HttpProvider.java:244) ~[lightstreamer-se-client-2.5.2-1110.jar:na]

I wonder if it is because the sun.net.* packages are not load-time-weaved due to some security manager restrictions.

Does anyone know how to get it work with sun.net.* packages?

Update 1

I confirm that I can intercept the ls_client.HttpProvider.connectAndGetAnswer call, but not the one above it (sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream).

Is sun.* possible to weave with AspectJ?

stackoverflower
  • 3,885
  • 10
  • 47
  • 71
  • Are you using load-time weaving with a java agent? – Nándor Előd Fekete Mar 18 '16 at 18:18
  • @NándorElődFekete Yes, I'm using load-time weaving with the weaver agent. – stackoverflower Mar 18 '16 at 22:16
  • FYI, it's a third-party library that uses `sun.*` package, which I have no control over (and I totally agree that no sane person should use `sun.*`). – stackoverflower Mar 18 '16 at 22:19
  • That shouldn't be a problem, my very first answer here on stackoverflow was on a [question](http://stackoverflow.com/questions/18239859/java-lang-system-currenttimemillis-replace-method) where the OP wanted to do very similar thing on JDK classes. He managed to do it with LTW. – Nándor Előd Fekete Mar 18 '16 at 22:21
  • See my other answer here on [how to enable debug output for load-time weaving](http://stackoverflow.com/a/35224749/2699901). That should reveal a lot about what happens. Probably a good idea to redirect standard output and standard error to a file for easier analysis. – Nándor Előd Fekete Mar 18 '16 at 22:24
  • Oh, and that third-party library might not use `sun.*` classes directly, as the class in question is actually a subclass of public API classes from `java` and `javax` packages, so probably it just got it from some factory and that's what the JDK gave him. That happens a lot of time in the JDK. – Nándor Előd Fekete Mar 18 '16 at 22:48
  • @NándorElődFekete Could you please explain a bit more what it means? Thanks – stackoverflower Mar 19 '16 at 12:12
  • I only said that using `new URL("https://www.google.com").openConnection().connect();` you end up using `sun.net.www.protocol.https.HttpsURLConnectionImpl` without ever referencing a `sun.*` class, so don't blame your library, it's just how things work. – Nándor Előd Fekete Mar 19 '16 at 14:04

2 Answers2

5

I'm yet to see a successful setup for load-time weaving of JRE (bootstrap) classes. If you need this for debugging purposes I'd go with build time weaving of the JRE classes instead.

This short snippet will weave the JRE jars for you and put the weaved classes in a single output jar. It needs org.aspectj/aspectjtools as a dependency. It also skips the jars in ext subfolder of the JRE as those jars will contain some duplicate classes and creating a jar file containing duplicate files will lead to an error. I'm also skipping jfxswt.jar from newer JRE versions as it will fail because of missing classes.

String aspectFileName = "src/main/java/pckg/AspectName.aj";
String jreLibPath = "c:/Program Files/Java/jdk1.8.0_40/jre/lib";
String outputJar = "weavedjre.jar";

List<String> jars = new ArrayList<>();

File dir = new File(jreLibPath);
File[] files = dir.listFiles();
for (File file : files) {
    if (file.isFile() && file.getName().endsWith(".jar")
            && !file.getName().endsWith("jfxswt.jar")) {
        jars.add(file.getAbsolutePath());
    }
}

List<String> ajcArgs = new ArrayList<>(Arrays.asList("-showWeaveInfo"));
for (String jar : jars) {
    ajcArgs.add("-inpath");
    ajcArgs.add(jar);
}
ajcArgs.add(aspectFileName);
ajcArgs.add("-outjar");
ajcArgs.add(outputJar);

org.aspectj.tools.ajc.Main.main(ajcArgs.toArray(new String[] {}));

Then run your program with the following VM arguments to use the weaved JRE classes (prepended to your boot classpath):

-verbose:class -Xbootclasspath/p:path_to/weavedjre.jar

or in an Eclipse launch configuration:

-verbose:class -Xbootclasspath/p:${resource_loc:/project_name/weavedjre.jar}

I added the verbose logging of class loading VM argument too, so you can see which class is loaded from where.

Nándor Előd Fekete
  • 6,988
  • 1
  • 22
  • 47
  • I have the same problème i cannot intercept pointcut callUrlOpenConnection() : (execution (public * java.net.URL.openConnection())); – khouloud mejdoub Sep 08 '17 at 08:38
1

Nándor was right. I can't do LTW on JRE libraries because they're on boot classpath and are loaded before AspectJ can weave them. I had to do compile-time-weaving and replace the default libraries.

Here's what I did:

Files you need

workspace
  |- Aspect.aj
  |- rt.jar
  |- aspectjrt-1.8.7.jar
  |- aspectjtools-1.8.7.jar

Aspect.aj

package java.net;

import sun.misc.SharedSecrets;

import java.io.FileDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public aspect Aspect {
    private static Class dualStackPlainSocketImplClass;
    private static Method localPort0;

    static {
        try {
            dualStackPlainSocketImplClass = Class.forName("java.net.DualStackPlainSocketImpl");
            localPort0 = dualStackPlainSocketImplClass.getDeclaredMethod("localPort0", Integer.TYPE);
            localPort0.setAccessible(true);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    pointcut connect(java.net.DualStackPlainSocketImpl s): target(s) && execution(* java.net.DualStackPlainSocketImpl.socketConnect(..));

    after(DualStackPlainSocketImpl s) throwing (Exception e): connect(s) {
        try {
            Field fdf = dualStackPlainSocketImplClass.getSuperclass().getSuperclass().getDeclaredField("fd");
            fdf.setAccessible(true);
            FileDescriptor fd = (FileDescriptor) fdf.get(s);
            int nativeFd = SharedSecrets.getJavaIOFileDescriptorAccess().get(fd);
            int localPort = (int) localPort0.invoke(dualStackPlainSocketImplClass, nativeFd);
            System.out.format("[local port=%s][exception=%s]\n", localPort, e.getMessage());
        } catch (InvocationTargetException e1) {
            e1.printStackTrace();
        } catch (IllegalAccessException e1) {
            e1.printStackTrace();
        } catch (NoSuchFieldException e1) {
            e1.printStackTrace();
        } catch (NullPointerException e1) {
            e1.printStackTrace();
        }
    }
}

The aspect file is very simple, the tricky bit is how to get the local port through reflection.

Note the package java.net; is important because some classes/methods are protected.


Then run from workspace

java -cp "aspectjrt-1.8.7.jar;aspectjtools-1.8.7.jar" org.aspectj.tools.ajc.Main -1.8 -inpath rt.jar Aspect.aj -outjar newrt.jar

and you'll get a newrt.jar.


To run your program with it,

java -cp <your_class_path> -Xbootclasspath/p:<path_to_newrt.jar> <main_class>

-Xbootclasspath/p will prepend newrt.jar to the boot classpath and override the JDK default.

stackoverflower
  • 3,885
  • 10
  • 47
  • 71