Importing a subclass when interface is not in subclass .jar - java

So i've decided to try to write a thing that will loads a specific .class file from a .jar that anyone puts into a folder. (Right now it only loads my Test.jar)
The thing is, that these classes implements an interface in the java project that i'm loading them from, and i get an error telling me that the class "Klass" doesn't exist.
How can i get the loader to load that one instead of trying to use it's own?
(Also, this is what i think it's doing, i have no idea if it's true)
The code for loading the .jar:
String filePath = new String("C:/classes/Test.jar");
URL myJarFile = null;
try {
myJarFile = new URL("file:///"+filePath);
URL[] urls = new URL[]{myJarFile};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
// Load in the class; MyClass.class should be located in
// the directory file:/c:/myclasses/com/mycompan
Class cls = cl.loadClass("me.bluejelly.survivalgames.classes.Test");
try {
Klass klass = (Klass) cls.newInstance();
klass.create(arg0.getName());
Main.listeners.put(arg0.getName(), klass);
Bukkit.getPluginManager().registerEvents(klass, instance);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
And this it the code in the file i'm trying to load:
package me.bluejelly.survivalgames.classes;
import me.bluejelly.survivalgames.Main;
import me.bluejelly.survivalgames.def.Klass;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.inventory.ItemStack;
public class Test implements Klass {
private String ownerName;
#Override
public void create(String pName) {
this.ownerName = pName;
Main.listeners.put(this.ownerName, this);
}
#Override
public void destroy() {
if(Main.listeners.containsKey(this.ownerName)) {
Main.listeners.remove(ownerName);
}
HandlerList.unregisterAll(this);
}
#Override
public Material getIcon() {
return null;
}
#Override
public ItemStack[] getStartingItems() {
return null;
}
#EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if(event.getPlayer().getName().equals(ownerName)) {
Bukkit.broadcastMessage("test");
}
}
}

You need to set the main class loader as the parent of your new URLClassLoader. That way it will pick up your local Klass class.
Something like...
ClassLoader cl = new URLClassLoader(urls, getClass().getClassLoader());

Related

Minecraft Fabric Modding, how to get access to private field with help mixin Accessor

i have a problem with Accessor in mixins.
I want to get a private field from the MultiplayerScreen class, for this I use an Accessor, but something is going wrong.
Accessor:
package ru.flexice.mixin;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.network.ServerInfo;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
#Mixin(MultiplayerScreen.class)
public interface MultiplayerScreenAccessor {
#Accessor
ServerInfo getSelectedEntry();
}
Inject to Code
#Inject(at = #At("RETURN"), method = "select")
private void select(CallbackInfo callbackInfo) {
try {
Screen s = MinecraftClient.getInstance().currentScreen;
if (s != null) {
System.out.println(((MultiplayerScreenAccessor) s).getSelectedEntry().address);
}
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
and i just get
Mixin transformation of ru.flexice.mixin.MultiplayerScreenAccessor failed
Use a #Shadow
#Shadow
private ServerInfo selectedEntry;
#Inject(at = #At("TAIL"), method = "select")
public void select(CallbackInfo callbackInfo) {
try {
System.out.println(this.selectedEntry.address);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}

Minecraft plugin config.yml sections doesn't save

I am writing a Minecraft plugin based on spigot coding.
But there I have the following issue, I am trying to edit a configuration file through the plugin, but when I attempt to store the information the data is not stored. As an example when I change name from Archive to Archive Stats and reload the server. Then the configuration name returns to Archive.
Archive:
Material:
==: org.bukkit.inventory.ItemStack
v: 2586
type: BOOKSHELF
ArchiveAtStart: false
Name: Archive
when I change the name to "Archive Stats"
Archive:
Material:
==: org.bukkit.inventory.ItemStack
v: 2586
type: BOOKSHELF
ArchiveAtStart: false
Name: Archive Stats
and reloaded the server, it still changes back to "Archive"
Archive:
Material:
==: org.bukkit.inventory.ItemStack
v: 2586
type: BOOKSHELF
ArchiveAtStart: false
Name: Archive
so here are the 2 codes to show:
The class
package com.Fender.Bedrock.files;
import com.Fender.Bedrock.Bedrock;
import org.bukkit.Bukkit;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
public class CustomConfig
{
private static Bedrock plugin;
public static File file;
private static FileConfiguration customFile;
public static void setup(Bedrock plugin1)
{
plugin = plugin1;
file = new File(plugin.getDataFolder(), "config.yml");
if(!file.exists())
{
try {
file.createNewFile();
}catch (IOException e){
}
}
customFile = new YamlConfiguration();
save();
try{customFile.load(file);}
catch (IOException | InvalidConfigurationException e)
{
e.printStackTrace();
}
}
public static FileConfiguration get()
{
return customFile;
}
public static void save()
{
try
{
customFile.save(file);
}catch (IOException e)
{
System.out.print("Couldn't save file!");
}
}
public static void load()
{
try
{
try {
try {
customFile.load(file);
}catch (InvalidConfigurationException i){
System.out.print("Couldn't load file!");
}
}catch (FileNotFoundException f)
{
System.out.print("Couldn't load file!");
}
}catch (IOException e)
{
System.out.print("Couldn't load file!");
}
}
public static void reload()
{
customFile = YamlConfiguration.loadConfiguration(file);
}
public boolean exists()
{
if(file.exists()){
return true;
}else
{
return false;
}
}
}
The method for the main class
public void FileCreate()
{
CustomConfig.setup(this);
CustomConfig.reload();
{
CustomConfig.get().addDefault("Name1", "§dHis");
CustomConfig.get().addDefault("Place", 1);
CustomConfig.get().createSection("Archive");
CustomConfig.get().getConfigurationSection("Archive").addDefault("Material", itemStack);
CustomConfig.get().getConfigurationSection("Archive").addDefault("ArchiveAtStart", false);
CustomConfig.get().getConfigurationSection("Archive").addDefault("Name", "Archive");
CustomConfig.get().createSection("noob").addDefault("help", false);
}
CustomConfig.get().options().copyHeader(true);
CustomConfig.get().options().copyDefaults(true);
CustomConfig.save();
//saveResource("config.yml", false);
this.saveDefaultConfig();
}
Finally, this is my plugin main method :
#Override
public void onEnable() {
FileCreate();
this.getConfig().options().copyDefaults(true);
this.saveConfig();
}
This issue appear because you can overriding lines at startup. More exactly, when adding default lines.
This will just add a default value, but not overriding:
CustomConfig.get().addDefault("Name1", "§dHis");
It's this that change everything:
CustomConfig.get().createSection("Archive");
You should keep using addDefault(). For section just use addDefault("mysection.key", "myval"), and so, in your case:
CustomConfig.get().addDefault("Archive.Name", "Archive");

Loading external classes

I am trying to load a class from a directory which I specify. I have done some research and made this:
ArrayList<Object> plugins = new ArrayList<Object>();
ClassLoader loader = Reflection.class.getClassLoader();
public Reflection()
{
load();
}
public void load()
{
File f = new File(Full directary);
ClassLoader loader = null;
try
{
loader = new URLClassLoader(new URL[]
{ f.toURI().toURL() }, getClass().getClassLoader());
}
catch (MalformedURLException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
for (File classFile : f.listFiles(new FilenameFilter()
{
public boolean accept(File dir, String name)
{
return name.endsWith(".class");
}
}))
// Start for loop.
{
try
{
String filename = classFile.getName();
System.out.println(filename);
Class<?> cls = loader.loadClass(filename.replace(".class", ""));
System.out.println(cls.getSuperclass().getName());
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
I am getting aNoClassDefFoundError error:
Exception in thread "main" java.lang.NoClassDefFoundError: mTest123g (wrong name: net/xcaliber/reflection/Test)
It finds the class fine, but then can't load it. I have made a basic interface:
String getName();
That's all that's in it; here is the class I am loading:
public Test()
{
// TODO Auto-generated constructor stub
}
#Override
public String getName()
{
return "Test";
}
This does implement the interface.Let me know the problem with it .
Obviously you have a mess up with class and file names.
Exception in thread "main" java.lang.NoClassDefFoundError: mTest123g (wrong name: net/xcaliber/reflection/Test)
So, you loading it as java mTest123g. Class loader expects a mTest123g.class not a Test class inside some net.xcaliber.reflection package.

Loading a class twice in JVM using different loaders

I have one question regarding concepts of class loading. How to load a .class file twice in JVM. I am also writing an excerpt of code that I have written to accomplish this..
1) Loader 1 code
public class MyClassLoader extends ClassLoader {
public MyClassLoader(){
super(MyClassLoader.class.getClassLoader());
}
public Class loadClass(String classname){
try {
return super.loadClass(classname);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
2) Loader 2 code
public class AnotherClassLoader extends ClassLoader {
public AnotherClassLoader(){
super(AnotherClassLoader.class.getClassLoader());
}
public Class loadClass(String classname){
try {
return super.loadClass(classname);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
3) Now i am loading a class named A using this two different class loaders. I suppose the operation classA1==newClassA should return false. Here is the code:
public static void main(String[] args) {
MyClassLoader loader1 = new MyClassLoader();
AnotherClassLoader newLoader = new AnotherClassLoader();
System.out.println("Load with Custom Class Loader instance");
Class classA1 = loader1.loadClass("com.hitesh.coreJava.A");
System.out.println("Class Loader:::"+classA1.getClassLoader());
Class newClassA = newLoader.loadClass("com.hitesh.coreJava.A");
System.out.println("Class Loader:::"+newClassA.getClassLoader());
System.out.println(classA1==newClassA);
System.out.println(classA1.hashCode() + " , " + newClassA.hashCode());
}
4) Result of executing above code:
Load with Custom Class Loader instance
Class Loader:::sun.misc.Launcher$AppClassLoader#11b86e7
Class Loader:::sun.misc.Launcher$AppClassLoader#11b86e7
true
1641745 , 1641745
Could you please explain this
Try this
public class Test1 {
static class TestClassLoader1 extends ClassLoader {
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if (!name.equals("Test1")) {
return super.loadClass(name);
}
try {
InputStream in = ClassLoader.getSystemResourceAsStream("Test1.class");
byte[] a = new byte[10000];
int len = in.read(a);
in.close();
return defineClass(name, a, 0, len);
} catch (IOException e) {
throw new ClassNotFoundException();
}
}
}
public static void main(String[] args) throws Exception {
Class<?> c1 = new TestClassLoader1().loadClass("Test1");
Class<?> c2 = new TestClassLoader1().loadClass("Test1");
System.out.println(c1);
System.out.println(c2);
System.out.println(c1 == c2);
}
}
output
class Test1
class Test1
false
Both classloaders start the lookup in their parent classloader (that's what the super() call is about). So actually the super classloader loads it in both cases.
You can try this:
String pathToJar = "C:\\path\\to\\my.jar";
String className = "com.mypackage.ClassA";
URLClassLoader cl1 = new URLClassLoader(new URL[] { new URL(pathToJar) });
URLClassLoader cl2 = new URLClassLoader(new URL[] { new URL(pathToJar) });
Class<?> c1 = cl1.loadClass(className);
Class<?> c2 = cl2.loadClass(className);
System.out.println(c1);
System.out.println(c2);
System.out.println(c1==c2 ? "Parent classloader loads" : "Parent classloader does not load");
cl1.close();
cl2.close();
Make sure that my.jar is NOT on your classpath.
In both cases you are using the same ClassLoader to perform the loading. You have two ClassLoaders but each just call super.loadClass() which delegates to the same parent ClassLoader which is AppClassLoader.

RMI UnmarshalException

I'm currently developing a system that loads classes via rmi. This system uses a classloader that communicates with the server in order to get the classes. The code is as follows.
Server:
import rocks.squareRock;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
public class Server extends UnicastRemoteObject
implements RemInterface {
public Server() throws RemoteException {
super();
}
public static void main(String argv[]) {
try {
Server serv = new Server();
Naming.rebind("RockServer", serv);
} catch (Throwable t) {
t.printStackTrace();
}
}
public Class<?> getRockClass(String type) {
if (type.equals("squareRock"))
return squareRock.class;
else
return null;
}
}
Client:
import rocks.Rock;
import java.net.MalformedURLException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
public class Client {
RemInterface reminterface = null;
RockLoader rl = null;
public Client() {
String strName = "rmi://127.0.0.1/RockServer";
try {
reminterface = (RemInterface) Naming.lookup(strName);
rl = new RockLoader(reminterface);
} catch (RemoteException e) {
e.printStackTrace();
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
}
loadRock("squareRock");
}
public Rock loadRock(String rock) {
try {
return (Rock) rl.loadClass(rock, false).newInstance();
} catch (Throwable t) {
return null;
}
}
}
Interface:
public interface RemInterface {
public Class<?> getRockClass(String type) throws RemoteException;
}
RockLoader:
import java.io.Serializable;
public class RockLoader extends ClassLoader implements Serializable {
private RemInterface reminterface = null;
public RockLoader(RemInterface reminterface) {
super();
this.reminterface = reminterface;
}
#Override
protected synchronized Class<?> loadClass(String className, boolean resolve)
throws ClassNotFoundException {
try {
return reminterface.getRockClass(className);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
The error I'm getting with this is (client-side):
java.rmi.UnmarshalException: error unmarshalling return; nested exception is:
java.lang.ClassNotFoundException: SquareRock
This confuses me, as I'm not unmarshalling a SquareRock instance, but a Class. The only thought I have is that my classloader might be wrong.
It doesn't matter whether it's a Class or an object. The receiving JVM must have that class in its classpath, unless you are using the RMI codebase feature. What you are doing is basically trying to implement the codebase feature yourself. You can't do that.

Categories

Resources