问题
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