Hello Stackoverflow community,
i have a question about java and bukkit specific. I have a encrypted plugin and i dont want to save the decrypted file on disk. So i used InputStream. But my problem now is how to inject this file into minecraft ( bukkit ). Is there any Custom Classloader available ? I searched alot but i dont find any working solution.
I decrypt an encrypted plugin with 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);
}
So now i tried to load the file with InputStream
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream is2 = new ByteArrayInputStream(baos.toByteArray());
JarInputStream in = new JarInputStream(is2);
This is working well at this point. Now i want to load this InputStream "is2" into bukkit server.
Because the way class loading works in java, you can not directly load a class from a inputstream like you have. What you can do, is:
The first step is easy, we are going to make a Map<String,byte[]>
for the files that come from the jar file.
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());
}
Having our map of filename -> byte array of data, we can then implement our custom class loader:
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);
}
}
Because of the estrictions in bukkit, we cannot place the class extending JavaPlugin inside our encrypted part of the jar. Instead of doing that, place a class inside the encrypted jar that has a constructor that accepts the main instance, and load the class in the onEnable using:
mappedJarClassLoader.loadClass("path.to.new.class").getConstructor(getClass()).newInstance(this)