Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can AspectJ weave through sun.net.* packages?

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?

like image 237
stackoverflower Avatar asked Mar 18 '16 17:03

stackoverflower


People also ask

What is AspectJ used for?

AspectJ is very mature, powerful and widely used in today's enterprise Java frameworks like Spring. Spring uses AspectJ to make it easy for the developers to use Transaction Management, or applying security (using Spring Security) in your application.

Does AspectJ use reflection?

AspectJ 5 provides a full set of reflection APIs analogous to the java.


2 Answers

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.

like image 173
Nándor Előd Fekete Avatar answered Sep 30 '22 01:09

Nándor Előd Fekete


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.

like image 37
stackoverflower Avatar answered Sep 30 '22 01:09

stackoverflower