Can AspectJ weave through sun.net.* packages?

吃可爱长大的小学妹 提交于 2019-12-29 07:51:46

问题


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?


回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/36090819/can-aspectj-weave-through-sun-net-packages

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!