I am doing bytecode manipulation in java, but it is being done on an external, running JAR file. For this reason, I am using the attach API.
My current problem is that the attach API cannot load a DLL I have in a separate folder. I have proviously had problems with file location but I fixed those and so I know it can see the file, it just can't load it.
My stack trace is as follows:
java.lang.UnsatisfiedLinkError: Can't load library: C:\Users\Jamie\Desktop\Test Rainbow 51\Rainbow Tester\plugins_mod\Natives\64\windows\attach.dll
at java.lang.ClassLoader.loadLibrary(Unknown Source)
at java.lang.Runtime.load0(Unknown Source)
at java.lang.System.load(Unknown Source)
at RainbowBans.MyPlugin.getAttachProvider(MyPlugin.java:299)
at RainbowBans.MyPlugin.onStartup(MyPlugin.java:96)
at joebkt._JoeUtils.LoadPlugins(_JoeUtils.java:2415)
at joebkt._JoeUtils.Startup(_JoeUtils.java:2502)
at joebkt.DedicatedServer.startServerConsoleHandler(DedicatedServer.java:44)
at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:483)
at java.lang.Thread.run(Unknown Source)
And here is my source code:
package RainbowBans;
//Just a few imports
import java.awt.Desktop;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
//Required when used to start premain by command line.
//import java.lang.ProcessBuilder.Redirect;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.*;
import sun.tools.attach.BsdAttachProvider;
import sun.tools.attach.LinuxAttachProvider;
import sun.tools.attach.SolarisAttachProvider;
import sun.tools.attach.WindowsAttachProvider;
import net.minecraft.server.MinecraftServer;
import com.mojang.authlib.GameProfile;
import com.sun.tools.attach.AgentInitializationException;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.spi.AttachProvider;
import joebkt.BannedPlayers;
import joebkt.CmdBan;
import joebkt.CmdKick;
import joebkt.PlayerList;
import joebkt._PermMgr;
import PluginReference.*;
public class MyPlugin extends PluginBase{
public static MC_Server server = null;
String pluginDir = "plugins_mod" + File.separatorChar;
String folderDir = pluginDir + "RainbowBans" + File.separatorChar;
String rainbowDir = RainbowBans.MyPlugin.class.getProtectionDomain().getCodeSource().getLocation().toExternalForm();
String OS = "Unknown!";
public void onStartup(MC_Server svr){
System.out.println("Plugin starting! Lets hope this works! :)");
System.out.println("plugins_mod folder located at:" + new File(pluginDir).getAbsolutePath());
System.out.println("RainbowBansTransAgent is located at: " + new File(pluginDir + "RainbowBansTransAgent.jar").getAbsolutePath());
server = svr;
System.out.println("Creating files now!");
File file = new File(folderDir);
if(file.isDirectory()) System.out.println("Directory already exists!");
else {
System.out.println("Creating plugin directory");
file.mkdir();
}
File errorfile = new File(folderDir + File.separatorChar + "Rainbowbanserrors.txt");
if(errorfile.isFile()) System.out.println("File errorfile already exists");
else
try {
System.out.println("Creating stack trace file!");
errorfile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
File banfile = new File(folderDir + File.separatorChar + "banmessage.txt");
if(banfile.isFile()) System.out.println("Banfile already exists!");
else
try {
banfile.createNewFile();
BufferedWriter writer = new BufferedWriter(new FileWriter(banfile));
writer.write("You are banned.");
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
File logfile = new File(folderDir + File.separatorChar + "Rainbowbanstransagentlog.txt");
if(logfile.isFile()) System.out.println("Logfile already exists!");
else try{
logfile.createNewFile();
}catch(IOException e){
e.printStackTrace();
}
JFrame frame = startDialog();
JMenuBar menu = createMenu();
frame.setJMenuBar(menu);
VirtualMachine vm;
try {
AttachProvider.setAttachProvider(getAttachProvider());
vm = VirtualMachine.attach(String.valueOf(Thread.currentThread().getId()));
String realArch = architecture();
if(realArch == "64"){
System.setProperty("java.library.path", new File(folderDir + "Natives" + File.separator + "64" + File.separator + OS + File.separator).getAbsolutePath());
}else if(realArch == "32"){
System.setProperty("java.library.path", new File(folderDir + "Natives" + File.separator + "32" + File.separator + OS + File.separator).getAbsolutePath());
}
vm.loadAgent(new File(".").getAbsolutePath());
} catch (AttachNotSupportedException | IOException | AgentLoadException | AgentInitializationException e) {
e.printStackTrace();
}
//Before using the attach agent, this is how I started the premain in the external JAR (RainbowBansTransAgent.jar)//
/*File transagent = new File(pluginDir + File.separatorChar + "RainbowBansTransAgent.jar");
ProcessBuilder pb = new ProcessBuilder("java", "-javaagent:" + transagent.getAbsolutePath(), "RainbowBansTransAgent/TransAgent");
pb.redirectError(Redirect.appendTo(errorfile));
pb.redirectOutput(Redirect.appendTo(logfile));
try{
pb.start();
}catch(IOException e){
e.printStackTrace();
}
*/
}
private JMenuBar createMenu() {
JMenuBar menu = new JMenuBar();
JMenuItem i = new JMenuItem("See message");
i.addActionListener(getActionListenerForMessage(i));
menu.add(i);
JMenuItem w = new JMenuItem("Go to project page");
w.addActionListener(getActionListenerForWebsites("http://www.project- rainbow.org/site/index.php?board=9.0"));
menu.add(w);
JMenuItem k = new JMenuItem("Go to download page");
k.addActionListener(getActionListenerForWebsites("http://www.project- rainbow.org/site/index.php?action=downloads;cat=3"));
menu.add(k);
return menu;
}
//I got the code from http://stackoverflow.com/questions/10967451/open-a-link-in-browser-with-java-button
private void openWebsite(String url){
Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
try {
desktop.browse(new URL(url).toURI());
} catch (Exception e) {
e.printStackTrace();
}
}
}
private ActionListener getActionListenerForWebsites(String url) {
ActionListener al = new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
openWebsite(url);
}
};
return al;
}
private ActionListener getActionListenerForMessage(JMenuItem i) {
ActionListener al = new ActionListener(){
@Override
public void actionPerformed(ActionEvent e) {
try {
BufferedReader reader = new BufferedReader(new FileReader(new File(folderDir + File.separatorChar + "banmessage.txt")));
String message = reader.readLine();
i.setText(message);
Timer timer = new Timer();
timer.schedule(getTimerTask(i), 5000);
reader.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
};
return al;
}
private TimerTask getTimerTask(JMenuItem i) {
TimerTask task = new TimerTask(){
@Override
public void run() {
i.setText("See message");
}
};
return task;
}
private JFrame startDialog() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JOptionPane.YES_NO_OPTION);
frame.setVisible(true);
frame.setSize(800, 600);
frame.setAlwaysOnTop(false);
frame.setBackground(java.awt.Color.yellow);
return frame;
}
//Copied from CodeCrafter's MultiWorld project.
private AttachProvider getAttachProvider() {
String os = System.getProperty("os.name").toLowerCase();
if (os.contains("win") && !os.contains("darwin")) {
OS = "windows";
if(architecture() == "64")System.load(new File(pluginDir + "Natives" + File.separator + "64" + File.separator + "windows" + File.separator + "attach.dll").getAbsolutePath());
else if (architecture() == "32")System.load(new File(pluginDir + "Natives" + File.separator + "32" + File.separator + "windows" + File.separator + "attach.dll").getAbsolutePath());
else{}
return new WindowsAttachProvider();
}
else if ((os.contains("nix")) || (os.contains("nux")) || (os.indexOf("aix") > 0)) {
OS = "linux";
if(architecture() == "64")System.load(new File(pluginDir + "Natives" + File.separator + "64" + File.separator + "linux" + File.separator + "libattach.so").getAbsolutePath());
else if(architecture() == "32") System.load(new File(pluginDir + "Natives" + File.separator + "64" + File.separator + "linux" + File.separator + "libattach.so").getAbsolutePath());
else{}
return new LinuxAttachProvider();
}
else if (os.contains("mac")) {
OS = "mac";
System.load(new File(pluginDir + "Natives" + File.separator + "64" + File.separator + "mac" + File.separator + "libattach.dylib").getAbsolutePath());
return new BsdAttachProvider();
}
else if (os.contains("sunos")) {
OS = "solaris";
if(architecture() == "64")System.load(new File(pluginDir + "Natives" + File.separator + "64" + File.separator + "solaris" + File.separator + "libattach.so").getAbsolutePath());
else if(architecture() == "32")System.load(new File(pluginDir + "Natives" + File.separator + "64" + File.separator + "solaris" + File.separator + "libattach.so").getAbsolutePath());
return new SolarisAttachProvider();
}
else{
return null;
}
}
//Copied from StackOverflow
private String architecture() {
String arch = System.getenv("PROCESSOR_ARCHITECTURE");
String wow64Arch = System.getenv("PROCESSOR_ARCHITEW6432");
String realArch = arch.endsWith("64")
|| wow64Arch != null && wow64Arch.endsWith("64")
? "64" : "32";
return realArch;
}
}
It turns out my problem was that I didn't add my DLL to the java.library.path. Problem solved.