Java 自定义 class 加载程序问题
Java custom class loader issue
我正在从客户端向服务器端发送一个 Class 对象。每次服务器需要加载客户端发送的 Class 对象时 而不是通过父委托模型重用 它(在第一次迭代期间加载时)。
我正在尝试在服务器端使用自定义 class 加载程序,其 loadClass(String)
只是调用 findClass()
而不是检查父层次结构。
为此,我正在执行以下操作:
- 通过在客户端读取.class文件生成字节[],如下所示:
Class cl = com.example.XYZ.class;
String path = cl.getName().replace('.', '/') + ".class";
InputStream is = cl.getClassLoader().getResourceAsStream(path);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int data = -1;
while((data=is.read())!=-1)
bos.write(data);
byte[] classBinaryData = bos.toByteArray();
我正在向服务器端发送 classBinaryData
。
- 在服务器端,每次我检索
byte[]
,通过匹配 MD5 校验和验证它是否与客户端相同,然后我创建一个新的自定义 class 加载器实例并传递字节数组,以便它可以用于从 findClass
. 中调用 defineClass
但是,我遇到了其中一个错误(取决于我从 .class 中创建 byte[] 的方式)
Incompatible magic value ..... in class file <Unknown>
或
com/example/XYZ (wrong name: com/example/XYZ)
来自 defineClass
我需要帮助来找出我 approach/code 中的错误。
您的代码看起来不错。你的错误在别的地方。
你在某种程度上从你的 class 加载程序返回错误的 class 文件。
第一个错误是字节数组完全乱码;前 4 个字节是错误的。您可以轻松地检查它们(它们必须是 0xCAFEBABE),以便更早地捕获此错误。
另一个错误,我认为,意味着您返回的定义与请求的 class 不同。
您的 byte[] 生成代码看起来不错。
当我使用从您的代码生成的字节数组通过以下 class 加载程序代码加载 class 时,它能够成功加载 class。
class CustomClassLoader extends ClassLoader {
public Class loadTheClass(String name, byte[] bytes) {
return defineClass(name, bytes, 0, bytes.length);
}
}
像这样使用这个 classloader
CustomClassLoader ccl = new CustomClassLoader();
Class cz = ccl.loadTheClass("com.example.XYZ", classBinaryData);
Object o = cz.newInstance();
- 我认为当您在服务器端加载 class 时,您必须在名称中使用
'.'
而不是 '/'
。
- 并确保字节数组数据未在您的其他代码中更改。
1。缺少点表示法
com/example/XYZ (wrong name: com/example/XYZ) coming from defineClass
您应该使用点符号,即 com.example.XYZ
Class clazz = classLoader.loadCustomClass("com.example.XYZ", bytes);
2。无效的幻数(损坏 Class 字节)
Incompatible magic value ..... in class file
您收到上述错误是因为 class 字节数组的 开头已损坏 。它通过抛出 java.lang.ClassFormatError 来抱怨 Incompatible magic value。当 class 加载程序在 class 字节的开头找不到 0xCAFEBABE(幻数)时,通常会发生这种情况。
这是一个简单的例子,您可以通过它重现错误。
- 在此示例中,
com.basaki.model.Book
class 文件保存为 Base64 编码字符串。
- 方法
testLoadingClassWithCorrectMagicNumber
尝试在解码为字节数组后从 Base64 编码的字符串中加载 class。它正常加载,没有任何意外。
在方法 testLoadingClassWithIncorrectCorrectMagicNumber
中,通过将第一个字符从 c
替换为 b
,字节数组(在解码 Base64 字符串之后)被破坏。现在,幻数不再是 0xCAFEBABE,而是 0xBAFEBABE。 class 加载程序现在在尝试加载损坏的二进制数组
时抛出以下异常
java.lang.ClassFormatError: Incompatible magic value 3137256126 in class file com/basaki/model/Book
public class LoadingBookFromBinaryArrayTest {
private static class MyCustomClassLoader extends ClassLoader {
public Class loadCustomClass(String name, byte[] bytes) {
return defineClass(name, bytes, 0, bytes.length);
}
}
public static String BOOK_CLAZZ = "yv66vgAAADQAHQoABQAYCQAEABkJAAQAGgcAGwcAHAEABXRpdGxlAQASTGphdmEvbGFuZy9TdHJpbmc7AQAGYXV0aG9yAQAGPGluaXQ-AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABdMY29tL2Jhc2FraS9tb2RlbC9Cb29rOwEACGdldFRpdGxlAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAAhzZXRUaXRsZQEAFShMamF2YS9sYW5nL1N0cmluZzspVgEACWdldEF1dGhvcgEACXNldEF1dGhvcgEAClNvdXJjZUZpbGUBAAlCb29rLmphdmEMAAkACgwABgAHDAAIAAcBABVjb20vYmFzYWtpL21vZGVsL0Jvb2sBABBqYXZhL2xhbmcvT2JqZWN0ACEABAAFAAAAAgACAAYABwAAAAIACAAHAAAABQABAAkACgABAAsAAAAvAAEAAQAAAAUqtwABsQAAAAIADAAAAAYAAQAAAAMADQAAAAwAAQAAAAUADgAPAAAAAQAQABEAAQALAAAALwABAAEAAAAFKrQAArAAAAACAAwAAAAGAAEAAAAJAA0AAAAMAAEAAAAFAA4ADwAAAAEAEgATAAEACwAAAD4AAgACAAAABiortQACsQAAAAIADAAAAAoAAgAAAA0ABQAOAA0AAAAWAAIAAAAGAA4ADwAAAAAABgAGAAcAAQABABQAEQABAAsAAAAvAAEAAQAAAAUqtAADsAAAAAIADAAAAAYAAQAAABEADQAAAAwAAQAAAAUADgAPAAAAAQAVABMAAQALAAAAPgACAAIAAAAGKiu1AAOxAAAAAgAMAAAACgACAAAAFQAFABYADQAAABYAAgAAAAYADgAPAAAAAAAGAAgABwABAAEAFgAAAAIAFw==";
@Test
public void testLoadingClassWithCorrectMagicNumber() throws IllegalAccessException, InstantiationException, DecoderException {
byte[] bytes = Base64.getUrlDecoder().decode(BOOK_CLAZZ);
MyCustomClassLoader classLoader = new MyCustomClassLoader();
Class clazz = classLoader.loadCustomClass("com.basaki.model.Book", bytes);
}
@Test(expected = ClassFormatError.class)
public void testLoadingClassWithIncorrectCorrectMagicNumber() throws IllegalAccessException, InstantiationException, DecoderException {
byte[] bytes = Base64.getUrlDecoder().decode(BOOK_CLAZZ);
String hex = Hex.encodeHexString(bytes);
System.out.println(hex);
// changing magic number 0xCAFEBABE to invalid 0xBAFEBABE
String malHex = "b" + hex.substring(1, hex.length());
System.out.println(malHex);
byte[] malBytes = Hex.decodeHex(malHex.toCharArray());
MyCustomClassLoader classLoader = new MyCustomClassLoader();
Class clazz = classLoader.loadCustomClass("com.basaki.model.Book", bytes9);
}
}
如上所述,除了收集字节数组之外,问题似乎出在其他地方。服务器端可能未正确处理字节。我创建了一个相当简单的示例,它与您正在做的类似,但它显示了我如何发送和接收 class 字节数组。
package org.valhalla.classloader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class RemoteClassLoader extends ClassLoader {
private Socket socket;
private DataOutputStream dos;
private DataInputStream dis;
public RemoteClassLoader(Socket socket, ClassLoader parent) {
super(parent);
this.socket = socket;
OutputStream os;
InputStream is;
try {
os = socket.getOutputStream();
is = socket.getInputStream();
} catch (IOException e) {
throw new RuntimeException("Unable to get Socket output stream", e);
}
dos = new DataOutputStream(os);
dis = new DataInputStream(is);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> clz = null;
System.out.println("Looking up class: " + name);
synchronized(this.getClassLoadingLock(name)) {
try {
System.out.println("Sending request for class: " + name);
dos.writeUTF(name);
boolean success = dis.readBoolean();
System.out.println("Action was " + success);
if (success) {
// Get bytes;
System.out.println("Reading size of class file");
int len = dis.readInt();
System.out.println("Size of class is " + len);
byte data[] = new byte[len];
int cur, size = 0;
for (cur = 0 ; cur < len ; cur += size) {
size = dis.read(data, cur, len - cur);
System.out.println("Read size: " + size);
}
System.out.println("Completed reading class file for class " + name);
return defineClass(name, data, 0, len);
}
} catch (IOException e) {
throw new ClassNotFoundException("Class: " + name + " was not found", e);
}
}
return clz;
}
public void close() {
try {
if (socket != null && socket.isClosed() == false) {
this.socket.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
此 class 将读取字节数组并将其加载到代码的服务器端。请注意,我使用一个简单的协议来确定有多少字节正在通过线路发送,并确保我读取了正确的字节数。
这是将通过网络发送信息的客户端代码。它是您上面提到的内容的扩展。
package org.valhalla.client;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.net.Socket;
public class ClientConnection {
private Socket socket;
public ClientConnection(Socket socket) {
this.socket = socket;
}
public void process() {
try {
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
String name = null;
while ((name = dis.readUTF()) != null && name.length() > 0) {
System.out.println("Looking up class: " + name);
InputStream resource = ClassLoader.getSystemResourceAsStream(name.replace('.', '/') + ".class");
if (resource == null) {
System.out.println("Class not found: " + name);
dos.writeBoolean(false);
continue;
}
System.out.println("Found class: " + name);
try {
byte buf[] = new byte[1024];
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int size = 0;
while ((size = resource.read(buf)) > 0) {
bos.write(buf, 0, size);
}
byte clz[] = bos.toByteArray();
dos.writeBoolean(true);
System.out.println("Sendding class size: " + clz.length);
dos.writeInt(clz.length);
System.out.println("Sending class bytes");
dos.write(clz);
System.out.println("Sent class bytes");
} catch (Throwable t) {
t.printStackTrace();
dos.writeBoolean(false);
}
}
} catch (Throwable t) {
t.printStackTrace();
} finally {
if (socket != null && socket.isClosed() == false) {
try { socket.close(); } catch(Throwable t) {}
}
}
}
}
如您所见,它只是向服务器发送一些信息,让服务器知道需要传输多少数据。以下 classes 可与上述 classes 一起使用,以说明其工作原理。
package org.valhalla.classloader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
public class RemoteClassLoaderServer {
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("syntax error: missing port");
System.exit(1);
}
int port = 0;
try {
port = Integer.parseInt(args[0]);
} catch(NumberFormatException nfe) {
System.out.println("Invalid port number: " + args[1]);
System.exit(2);
}
if (port < 0) {
System.out.println("Port cannot be negative: " + port);
}
ServerSocket server = null;
try {
server = new ServerSocket(port);
} catch (IOException e) {
System.out.println("Unable to create server socket for port: " + port);
System.exit(3);
}
Socket s = null;
try {
s = server.accept();
InputStream is = s.getInputStream();
DataInputStream dis = new DataInputStream(is);
System.out.println("Waiting for class name");
String name = dis.readUTF();
System.out.println("Received class name: " + name);
RemoteClassLoader rcl = new RemoteClassLoader(s, RemoteClassLoaderServer.class.getClassLoader());
System.out.println("Finding class: " + name);
Class<?> clz = rcl.loadClass(name);
Method m = clz.getMethod("main", String[].class);
System.out.println("Executing main method");
m.invoke(null, new Object[] { new String[0] });
System.out.println("done");
new DataOutputStream(s.getOutputStream()).writeUTF("");
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (s != null && s.isClosed() == false) {
try { s.close(); } catch(Throwable t) {}
}
}
}
}
这里是客户端classes
package org.valhalla.client;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientMain {
public static void main(String[] args) {
int port = Integer.parseInt(args[0]);
try {
Socket socket = new Socket("localhost", port);
System.out.println("Opened socket at port: " + port);
String name = Main.class.getName();
new DataOutputStream(socket.getOutputStream()).writeUTF(name);
System.out.println("Sent Class name: " + name);
ClientConnection conn = new ClientConnection(socket);
conn.process();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
这个class将在服务器端运行。
包 org.valhalla.client;
public class Main {
public static void main(String args[]) {
Client client = new Client();
client.execute();
}
}
用这个 class.
包 org.valhalla.client;
public class Client {
public void execute() {
System.out.println("######### We are calling the Client class execute method #####");
}
}
希望对您有所帮助。
我正在从客户端向服务器端发送一个 Class 对象。每次服务器需要加载客户端发送的 Class 对象时 而不是通过父委托模型重用 它(在第一次迭代期间加载时)。
我正在尝试在服务器端使用自定义 class 加载程序,其 loadClass(String)
只是调用 findClass()
而不是检查父层次结构。
为此,我正在执行以下操作:
- 通过在客户端读取.class文件生成字节[],如下所示:
Class cl = com.example.XYZ.class; String path = cl.getName().replace('.', '/') + ".class"; InputStream is = cl.getClassLoader().getResourceAsStream(path); ByteArrayOutputStream bos = new ByteArrayOutputStream(); int data = -1; while((data=is.read())!=-1) bos.write(data); byte[] classBinaryData = bos.toByteArray();
我正在向服务器端发送 classBinaryData
。
- 在服务器端,每次我检索
byte[]
,通过匹配 MD5 校验和验证它是否与客户端相同,然后我创建一个新的自定义 class 加载器实例并传递字节数组,以便它可以用于从findClass
. 中调用
defineClass
但是,我遇到了其中一个错误(取决于我从 .class 中创建 byte[] 的方式)
Incompatible magic value ..... in class file <Unknown>
或
com/example/XYZ (wrong name: com/example/XYZ)
来自 defineClass
我需要帮助来找出我 approach/code 中的错误。
您的代码看起来不错。你的错误在别的地方。
你在某种程度上从你的 class 加载程序返回错误的 class 文件。
第一个错误是字节数组完全乱码;前 4 个字节是错误的。您可以轻松地检查它们(它们必须是 0xCAFEBABE),以便更早地捕获此错误。
另一个错误,我认为,意味着您返回的定义与请求的 class 不同。
您的 byte[] 生成代码看起来不错。
当我使用从您的代码生成的字节数组通过以下 class 加载程序代码加载 class 时,它能够成功加载 class。
class CustomClassLoader extends ClassLoader {
public Class loadTheClass(String name, byte[] bytes) {
return defineClass(name, bytes, 0, bytes.length);
}
}
像这样使用这个 classloader
CustomClassLoader ccl = new CustomClassLoader();
Class cz = ccl.loadTheClass("com.example.XYZ", classBinaryData);
Object o = cz.newInstance();
- 我认为当您在服务器端加载 class 时,您必须在名称中使用
'.'
而不是'/'
。 - 并确保字节数组数据未在您的其他代码中更改。
1。缺少点表示法
com/example/XYZ (wrong name: com/example/XYZ) coming from defineClass
您应该使用点符号,即 com.example.XYZ
Class clazz = classLoader.loadCustomClass("com.example.XYZ", bytes);
2。无效的幻数(损坏 Class 字节)
Incompatible magic value ..... in class file
您收到上述错误是因为 class 字节数组的 开头已损坏 。它通过抛出 java.lang.ClassFormatError 来抱怨 Incompatible magic value。当 class 加载程序在 class 字节的开头找不到 0xCAFEBABE(幻数)时,通常会发生这种情况。
这是一个简单的例子,您可以通过它重现错误。
- 在此示例中,
com.basaki.model.Book
class 文件保存为 Base64 编码字符串。 - 方法
testLoadingClassWithCorrectMagicNumber
尝试在解码为字节数组后从 Base64 编码的字符串中加载 class。它正常加载,没有任何意外。 在方法
时抛出以下异常testLoadingClassWithIncorrectCorrectMagicNumber
中,通过将第一个字符从c
替换为b
,字节数组(在解码 Base64 字符串之后)被破坏。现在,幻数不再是 0xCAFEBABE,而是 0xBAFEBABE。 class 加载程序现在在尝试加载损坏的二进制数组java.lang.ClassFormatError: Incompatible magic value 3137256126 in class file com/basaki/model/Book
public class LoadingBookFromBinaryArrayTest { private static class MyCustomClassLoader extends ClassLoader { public Class loadCustomClass(String name, byte[] bytes) { return defineClass(name, bytes, 0, bytes.length); } } public static String BOOK_CLAZZ = "yv66vgAAADQAHQoABQAYCQAEABkJAAQAGgcAGwcAHAEABXRpdGxlAQASTGphdmEvbGFuZy9TdHJpbmc7AQAGYXV0aG9yAQAGPGluaXQ-AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBABdMY29tL2Jhc2FraS9tb2RlbC9Cb29rOwEACGdldFRpdGxlAQAUKClMamF2YS9sYW5nL1N0cmluZzsBAAhzZXRUaXRsZQEAFShMamF2YS9sYW5nL1N0cmluZzspVgEACWdldEF1dGhvcgEACXNldEF1dGhvcgEAClNvdXJjZUZpbGUBAAlCb29rLmphdmEMAAkACgwABgAHDAAIAAcBABVjb20vYmFzYWtpL21vZGVsL0Jvb2sBABBqYXZhL2xhbmcvT2JqZWN0ACEABAAFAAAAAgACAAYABwAAAAIACAAHAAAABQABAAkACgABAAsAAAAvAAEAAQAAAAUqtwABsQAAAAIADAAAAAYAAQAAAAMADQAAAAwAAQAAAAUADgAPAAAAAQAQABEAAQALAAAALwABAAEAAAAFKrQAArAAAAACAAwAAAAGAAEAAAAJAA0AAAAMAAEAAAAFAA4ADwAAAAEAEgATAAEACwAAAD4AAgACAAAABiortQACsQAAAAIADAAAAAoAAgAAAA0ABQAOAA0AAAAWAAIAAAAGAA4ADwAAAAAABgAGAAcAAQABABQAEQABAAsAAAAvAAEAAQAAAAUqtAADsAAAAAIADAAAAAYAAQAAABEADQAAAAwAAQAAAAUADgAPAAAAAQAVABMAAQALAAAAPgACAAIAAAAGKiu1AAOxAAAAAgAMAAAACgACAAAAFQAFABYADQAAABYAAgAAAAYADgAPAAAAAAAGAAgABwABAAEAFgAAAAIAFw=="; @Test public void testLoadingClassWithCorrectMagicNumber() throws IllegalAccessException, InstantiationException, DecoderException { byte[] bytes = Base64.getUrlDecoder().decode(BOOK_CLAZZ); MyCustomClassLoader classLoader = new MyCustomClassLoader(); Class clazz = classLoader.loadCustomClass("com.basaki.model.Book", bytes); } @Test(expected = ClassFormatError.class) public void testLoadingClassWithIncorrectCorrectMagicNumber() throws IllegalAccessException, InstantiationException, DecoderException { byte[] bytes = Base64.getUrlDecoder().decode(BOOK_CLAZZ); String hex = Hex.encodeHexString(bytes); System.out.println(hex); // changing magic number 0xCAFEBABE to invalid 0xBAFEBABE String malHex = "b" + hex.substring(1, hex.length()); System.out.println(malHex); byte[] malBytes = Hex.decodeHex(malHex.toCharArray()); MyCustomClassLoader classLoader = new MyCustomClassLoader(); Class clazz = classLoader.loadCustomClass("com.basaki.model.Book", bytes9); } }
如上所述,除了收集字节数组之外,问题似乎出在其他地方。服务器端可能未正确处理字节。我创建了一个相当简单的示例,它与您正在做的类似,但它显示了我如何发送和接收 class 字节数组。
package org.valhalla.classloader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class RemoteClassLoader extends ClassLoader {
private Socket socket;
private DataOutputStream dos;
private DataInputStream dis;
public RemoteClassLoader(Socket socket, ClassLoader parent) {
super(parent);
this.socket = socket;
OutputStream os;
InputStream is;
try {
os = socket.getOutputStream();
is = socket.getInputStream();
} catch (IOException e) {
throw new RuntimeException("Unable to get Socket output stream", e);
}
dos = new DataOutputStream(os);
dis = new DataInputStream(is);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> clz = null;
System.out.println("Looking up class: " + name);
synchronized(this.getClassLoadingLock(name)) {
try {
System.out.println("Sending request for class: " + name);
dos.writeUTF(name);
boolean success = dis.readBoolean();
System.out.println("Action was " + success);
if (success) {
// Get bytes;
System.out.println("Reading size of class file");
int len = dis.readInt();
System.out.println("Size of class is " + len);
byte data[] = new byte[len];
int cur, size = 0;
for (cur = 0 ; cur < len ; cur += size) {
size = dis.read(data, cur, len - cur);
System.out.println("Read size: " + size);
}
System.out.println("Completed reading class file for class " + name);
return defineClass(name, data, 0, len);
}
} catch (IOException e) {
throw new ClassNotFoundException("Class: " + name + " was not found", e);
}
}
return clz;
}
public void close() {
try {
if (socket != null && socket.isClosed() == false) {
this.socket.close();
}
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
此 class 将读取字节数组并将其加载到代码的服务器端。请注意,我使用一个简单的协议来确定有多少字节正在通过线路发送,并确保我读取了正确的字节数。
这是将通过网络发送信息的客户端代码。它是您上面提到的内容的扩展。
package org.valhalla.client;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.net.Socket;
public class ClientConnection {
private Socket socket;
public ClientConnection(Socket socket) {
this.socket = socket;
}
public void process() {
try {
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
String name = null;
while ((name = dis.readUTF()) != null && name.length() > 0) {
System.out.println("Looking up class: " + name);
InputStream resource = ClassLoader.getSystemResourceAsStream(name.replace('.', '/') + ".class");
if (resource == null) {
System.out.println("Class not found: " + name);
dos.writeBoolean(false);
continue;
}
System.out.println("Found class: " + name);
try {
byte buf[] = new byte[1024];
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int size = 0;
while ((size = resource.read(buf)) > 0) {
bos.write(buf, 0, size);
}
byte clz[] = bos.toByteArray();
dos.writeBoolean(true);
System.out.println("Sendding class size: " + clz.length);
dos.writeInt(clz.length);
System.out.println("Sending class bytes");
dos.write(clz);
System.out.println("Sent class bytes");
} catch (Throwable t) {
t.printStackTrace();
dos.writeBoolean(false);
}
}
} catch (Throwable t) {
t.printStackTrace();
} finally {
if (socket != null && socket.isClosed() == false) {
try { socket.close(); } catch(Throwable t) {}
}
}
}
}
如您所见,它只是向服务器发送一些信息,让服务器知道需要传输多少数据。以下 classes 可与上述 classes 一起使用,以说明其工作原理。
package org.valhalla.classloader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
public class RemoteClassLoaderServer {
public static void main(String[] args) {
if (args.length < 1) {
System.out.println("syntax error: missing port");
System.exit(1);
}
int port = 0;
try {
port = Integer.parseInt(args[0]);
} catch(NumberFormatException nfe) {
System.out.println("Invalid port number: " + args[1]);
System.exit(2);
}
if (port < 0) {
System.out.println("Port cannot be negative: " + port);
}
ServerSocket server = null;
try {
server = new ServerSocket(port);
} catch (IOException e) {
System.out.println("Unable to create server socket for port: " + port);
System.exit(3);
}
Socket s = null;
try {
s = server.accept();
InputStream is = s.getInputStream();
DataInputStream dis = new DataInputStream(is);
System.out.println("Waiting for class name");
String name = dis.readUTF();
System.out.println("Received class name: " + name);
RemoteClassLoader rcl = new RemoteClassLoader(s, RemoteClassLoaderServer.class.getClassLoader());
System.out.println("Finding class: " + name);
Class<?> clz = rcl.loadClass(name);
Method m = clz.getMethod("main", String[].class);
System.out.println("Executing main method");
m.invoke(null, new Object[] { new String[0] });
System.out.println("done");
new DataOutputStream(s.getOutputStream()).writeUTF("");
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (s != null && s.isClosed() == false) {
try { s.close(); } catch(Throwable t) {}
}
}
}
}
这里是客户端classes
package org.valhalla.client;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
public class ClientMain {
public static void main(String[] args) {
int port = Integer.parseInt(args[0]);
try {
Socket socket = new Socket("localhost", port);
System.out.println("Opened socket at port: " + port);
String name = Main.class.getName();
new DataOutputStream(socket.getOutputStream()).writeUTF(name);
System.out.println("Sent Class name: " + name);
ClientConnection conn = new ClientConnection(socket);
conn.process();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
这个class将在服务器端运行。
包 org.valhalla.client;
public class Main {
public static void main(String args[]) {
Client client = new Client();
client.execute();
}
}
用这个 class.
包 org.valhalla.client;
public class Client {
public void execute() {
System.out.println("######### We are calling the Client class execute method #####");
}
}
希望对您有所帮助。