今天想给项目写个远程执行的小工具
1.客户端动态编译要远程执行的代码
2.通过网络将编译好的字节码传输到服务端
3.服务端留一个类装载器的接口
4.对客户端传输过来的字节码做一定修改(复杂了的不好改,修改常量池还是不难实现的,比如需要输出信息到客户端,却又想用System.out输出,修改常量池就好了,不然System.out只能输出在服务端)
5.用自定义的ClassLoader将要执行的类装载到jvm,然后执行,输出信息返回给客户端
这个工具类还是比较强大的(不过也很危险,看怎么用了),可以看到服务端的任何类的变量,也可以执行清除缓存之类的操作。
以前写过这种小玩意儿,不过是在有web容器的环境下,
现在的项目是基于netty的长连接应用,不过也好搞定,把原来代码拿来改了个把小时搞定
首先写个netty server用来接收要执行的字节码(它要跟随应用Server一同启动,也就是说同jvm)
代码太多容易打乱思路,只贴出主要代码(decode):
1.客户端动态编译要远程执行的代码
2.通过网络将编译好的字节码传输到服务端
3.服务端留一个类装载器的接口
4.对客户端传输过来的字节码做一定修改(复杂了的不好改,修改常量池还是不难实现的,比如需要输出信息到客户端,却又想用System.out输出,修改常量池就好了,不然System.out只能输出在服务端)
5.用自定义的ClassLoader将要执行的类装载到jvm,然后执行,输出信息返回给客户端
这个工具类还是比较强大的(不过也很危险,看怎么用了),可以看到服务端的任何类的变量,也可以执行清除缓存之类的操作。
以前写过这种小玩意儿,不过是在有web容器的环境下,
现在的项目是基于netty的长连接应用,不过也好搞定,把原来代码拿来改了个把小时搞定
首先写个netty server用来接收要执行的字节码(它要跟随应用Server一同启动,也就是说同jvm)
代码太多容易打乱思路,只贴出主要代码(decode):
- class HotSwapPipelineFactory implements ChannelPipelineFactory {
- private SimpleChannelHandler messageReceivedHandler = new SimpleChannelHandler() {
- @Override
- public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
- byte[] classByte = (byte[]) e.getMessage(); // decode后的字节码byte数组
- // execute内部用自定义ClassLoader加载进jvm然后通过反射执行,返回值为一个String,是返回给客户端的信息,这部分代码就不贴出来了
- String resultMsg = JavaClassExecuter.execute(classByte);
- byte[] resultByte = resultMsg.getBytes(Charset.forName(Constants.UTF8_CHARSET));
- ChannelBuffer buffer = ChannelBuffers.buffer(resultByte.length);
- buffer.writeBytes(resultByte);
- e.getChannel().write(buffer);
- }
- };
- @Override
- public ChannelPipeline getPipeline() throws Exception {
- return addHandlers(Channels.pipeline());
- }
- public ChannelPipeline addHandlers(ChannelPipeline pipeline) {
- if (null == pipeline) {
- return null;
- }
- // 这个decoder主要应对消息不完整的情况,虽然是小工具也认真对待吧
- pipeline.addLast("hotSwapDecoder", new FrameDecoder() {
- @Override
- protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
- if (buffer.readableBytes() >= 4) {
- buffer.markReaderIndex(); // 标记ReaderIndex
- int msgBodyLen = buffer.readInt(); // 前四个字节存放消息(字节码)的长度
- if (buffer.readableBytes() >= msgBodyLen) {
- ChannelBuffer dst = ChannelBuffers.buffer(msgBodyLen);
- buffer.readBytes(dst, msgBodyLen);
- return dst.array(); // 这就是完整的字节码byte数组了
- } else {
- buffer.resetReaderIndex();
- return null;
- }
- }
- return null;
- }
- });
- pipeline.addLast("hotSwapHandler", messageReceivedHandler);
- return pipeline;
- }
class HotSwapPipelineFactory implements ChannelPipelineFactory {
private SimpleChannelHandler messageReceivedHandler = new SimpleChannelHandler() {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
byte[] classByte = (byte[]) e.getMessage(); // decode后的字节码byte数组
// execute内部用自定义ClassLoader加载进jvm然后通过反射执行,返回值为一个String,是返回给客户端的信息,这部分代码就不贴出来了
String resultMsg = JavaClassExecuter.execute(classByte);
byte[] resultByte = resultMsg.getBytes(Charset.forName(Constants.UTF8_CHARSET));
ChannelBuffer buffer = ChannelBuffers.buffer(resultByte.length);
buffer.writeBytes(resultByte);
e.getChannel().write(buffer);
}
};
@Override
public ChannelPipeline getPipeline() throws Exception {
return addHandlers(Channels.pipeline());
}
public ChannelPipeline addHandlers(ChannelPipeline pipeline) {
if (null == pipeline) {
return null;
}
// 这个decoder主要应对消息不完整的情况,虽然是小工具也认真对待吧
pipeline.addLast("hotSwapDecoder", new FrameDecoder() {
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
if (buffer.readableBytes() >= 4) {
buffer.markReaderIndex(); // 标记ReaderIndex
int msgBodyLen = buffer.readInt(); // 前四个字节存放消息(字节码)的长度
if (buffer.readableBytes() >= msgBodyLen) {
ChannelBuffer dst = ChannelBuffers.buffer(msgBodyLen);
buffer.readBytes(dst, msgBodyLen);
return dst.array(); // 这就是完整的字节码byte数组了
} else {
buffer.resetReaderIndex();
return null;
}
}
return null;
}
});
pipeline.addLast("hotSwapHandler", messageReceivedHandler);
return pipeline;
}