Java Bukkit 从 InputStream 加载 .jar 文件
Java Bukkit load .jar file from InputStream
你好 Whosebug 社区,
我有一个关于 java 和 bukkit 的问题。我有一个加密的插件,我不想将解密的文件保存在磁盘上。所以我使用了 InputStream。但我现在的问题是如何将这个文件注入我的世界(bukkit)。
是否有可用的自定义类加载器?我搜索了很多,但没有找到任何可行的解决方案。
我用 AES-128 解密了一个加密的插件:
FileInputStream fin;
CipherInputStream cin;
int nread = 0;
byte [] inbuf = new byte [MAX_FILE_BUF];
fin = new FileInputStream (input);
cin = new CipherInputStream (fin, mDecipher);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((nread = cin.read (inbuf)) > 0 )
{
byte[] trimbuf = new byte [nread];
for (int i = 0; i < nread; i++)
{
trimbuf[i] = inbuf[i];
}
baos.write(trimbuf);
}
所以现在我尝试使用 InputStream 加载文件
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream is2 = new ByteArrayInputStream(baos.toByteArray());
JarInputStream in = new JarInputStream(is2);
这在这一点上运作良好。现在我想将这个 InputStream "is2" 加载到 bukkit 服务器中。
因为 class 加载方式在 java 中有效,您不能像您那样直接从输入流加载 class。您可以做的是:
- 将 jar 中的每个文件加载到内存中
- 使用自定义 class加载器从内存中加载文件
- 使用那个 classloader 加载你的插件
第一步很简单,我们将为来自 jar 文件的文件制作 Map<String,byte[]>
。
Map<String,byte[]> map = new HashMap<>();
ZipEntry entry;
byte[] read = new byte[1024];
while((entry = in.getNextEntry()) != null) {
ByteArrayOutputStream r = new ByteArrayOutputStream(Math.max(128, entry.getSize()));
int i;
while((i = in.read(read) >= 0)
r.write(read, 0, i);
is.close();
map.put(entry.getName(), r.toByteArray());
}
有了我们的文件名映射 -> 数据字节数组,我们就可以实现我们的自定义 class 加载器:
public class MappedJarClassLoader extends ClassLoader {
Map<String,byte[]> map = new HashMap<>();
public MappedJarClassLoader (ClassLoader parent, Map<String,byte[]> map) {
super(parent);
this.map = map;
}
public Class findClass(String name) throws ClassNotFoundException {
byte[] b = map.get(name.replace('/', '$').replace('.', File.separatorChar));
if(b == null)
throw new ClassNotFoundException(name);
return defineClass(name, b, 0, b.length);
}
}
由于 bukkit 的限制,我们不能将 class 扩展 JavaPlugin 放在我们加密的 jar 部分中。不要这样做,而是将 class 放入具有接受主实例的构造函数的加密 jar 中,并使用以下方法在 onEnable 中加载 class:
mappedJarClassLoader.loadClass("path.to.new.class").getConstructor(getClass()).newInstance(this)
你好 Whosebug 社区,
我有一个关于 java 和 bukkit 的问题。我有一个加密的插件,我不想将解密的文件保存在磁盘上。所以我使用了 InputStream。但我现在的问题是如何将这个文件注入我的世界(bukkit)。 是否有可用的自定义类加载器?我搜索了很多,但没有找到任何可行的解决方案。
我用 AES-128 解密了一个加密的插件:
FileInputStream fin;
CipherInputStream cin;
int nread = 0;
byte [] inbuf = new byte [MAX_FILE_BUF];
fin = new FileInputStream (input);
cin = new CipherInputStream (fin, mDecipher);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((nread = cin.read (inbuf)) > 0 )
{
byte[] trimbuf = new byte [nread];
for (int i = 0; i < nread; i++)
{
trimbuf[i] = inbuf[i];
}
baos.write(trimbuf);
}
所以现在我尝试使用 InputStream 加载文件
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream is2 = new ByteArrayInputStream(baos.toByteArray());
JarInputStream in = new JarInputStream(is2);
这在这一点上运作良好。现在我想将这个 InputStream "is2" 加载到 bukkit 服务器中。
因为 class 加载方式在 java 中有效,您不能像您那样直接从输入流加载 class。您可以做的是:
- 将 jar 中的每个文件加载到内存中
- 使用自定义 class加载器从内存中加载文件
- 使用那个 classloader 加载你的插件
第一步很简单,我们将为来自 jar 文件的文件制作 Map<String,byte[]>
。
Map<String,byte[]> map = new HashMap<>();
ZipEntry entry;
byte[] read = new byte[1024];
while((entry = in.getNextEntry()) != null) {
ByteArrayOutputStream r = new ByteArrayOutputStream(Math.max(128, entry.getSize()));
int i;
while((i = in.read(read) >= 0)
r.write(read, 0, i);
is.close();
map.put(entry.getName(), r.toByteArray());
}
有了我们的文件名映射 -> 数据字节数组,我们就可以实现我们的自定义 class 加载器:
public class MappedJarClassLoader extends ClassLoader {
Map<String,byte[]> map = new HashMap<>();
public MappedJarClassLoader (ClassLoader parent, Map<String,byte[]> map) {
super(parent);
this.map = map;
}
public Class findClass(String name) throws ClassNotFoundException {
byte[] b = map.get(name.replace('/', '$').replace('.', File.separatorChar));
if(b == null)
throw new ClassNotFoundException(name);
return defineClass(name, b, 0, b.length);
}
}
由于 bukkit 的限制,我们不能将 class 扩展 JavaPlugin 放在我们加密的 jar 部分中。不要这样做,而是将 class 放入具有接受主实例的构造函数的加密 jar 中,并使用以下方法在 onEnable 中加载 class:
mappedJarClassLoader.loadClass("path.to.new.class").getConstructor(getClass()).newInstance(this)