Here is the project architecture:
Here is the Car class that won't compile:
package car;
import engine.Engine;
public class Car {
private Engine engine;
/***
* Constructor
* #param engine The engine that the car object will have
*/
Car(Engine engine) {
engine = new Engine(engine); // Use the Engine copy constructor
}
}
Here is the error:
Here is the Engine class in the engine package:
package engine;
public class Engine {
private String name;
private int displacement;
public Engine(String name, int displacement) throws Exception {
setName(name);
setDisplacement(displacement);
}
public String getName() {return name;}
public int getDisplacement() {return displacement;}
private void setName(String name) throws Exception {
if (name.trim().length() == 0) {
throw new Exception("Engine name cannot be blank");
}
}
private void setDisplacement(int displacement) throws Exception {
if (displacement < 0) {
throw new Exception("Engine displacement cannot be zero or negative");
}
}
}
Try cleaning the project in Project --> clean
Fixed: I didn't change the code, I deleted the project from the workspace, closed Eclipse, reopened Eclipse with a different workspace, and imported the same project into the workspace. Arrrgh.
Related
Like spigot/bukkit plugins id like to be able to load jars in a file and load their classes. Ive managed to get this working with java class loader but the class has to extend a runnable for it to work. Id like to have my own custom interface to implement for each plugin(jar). So I could have functions that get ran on plugin load and so on. If anyone knows how to do this please let me know.
Plugin structure
A plugin is a .jar file. The plugin has a plugin.properties file with the properties of the plugin.
It looks like this:
plugin.main=com.example.plugins.ExamplePlugin
plugin.name=Example Plugin
plugin.description=Test 123
plugin.version=1.0
The file contains the main class, the plugin name, a description and the version.
A plugin must have a class that inherits from the abstract plugin class. This counts as the main class.
Code structure
Let's begin with the Plugin-class:
package com.example.plugins;
public abstract class Plugin {
protected PluginProperty property;
public abstract void onEnable();
public abstract void onDisable();
public PluginProperty getProperty() {
return property;
}
public void setProperty(PluginProperty property) {
this.property = property;
}
}
As you may see, I have chosen an abstract class here.
The class consists of two abstract methods (onEnable and onDisable). The plugin also has a PluginProperty object. The equivalent of this class in Spigot would be JavaPlugin.
Let's take a look at the PluginProperty class.
package com.example.plugins;
public class PluginProperty {
private String main;
private String name;
private String description;
private double version;
public PluginProperty(String main, String name, String description, double version) {
this.main = main;
this.name = name;
this.description = description;
this.version = version;
}
public String getMain() {
return main;
}
public void setMain(String main) {
this.main = main;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public double getVersion() {
return version;
}
public void setVersion(double version) {
this.version = version;
}
}
This class has all the necessary properties of a plugin in it. Most of the things here are self-explanatory, but I would still like to discuss main.
The string holds the name of the main plugin class of the plugin. This is basically the same as the main in the plugin.yml in Spigot.
The loader
Here is the PluginLoader-class:
package com.example.plugins;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Properties;
import java.util.zip.ZipException;
public class PluginLoader {
private static PluginProperty loadPluginProperties(File file) throws ZipException, IOException {
URL url = file.toURI().toURL();
String jarURL = "jar:" + url +"!/plugin.properties";
InputStream input;
URL inputURL = new URL(jarURL);
JarURLConnection conn = (JarURLConnection)inputURL.openConnection();
input = conn.getInputStream();
Properties property = new Properties();
property.load(input);
String main = property.getProperty("plugin.main");
String name = property.getProperty("plugin.name");
String description = property.getProperty("description");
double version = Double.parseDouble(property.getProperty("plugin.version"));
return new PluginProperty(main, name, description, version);
}
public static Plugin loadPlugin(File file) throws IOException, InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
if(!file.exists()) {
return null;
}
PluginProperty property = loadPluginProperties(file);
URL url = file.toURI().toURL();
String jarURL = "jar:" + url + "!/";
URL urls[] = {new URL(jarURL)};
URLClassLoader ucl = new URLClassLoader(urls);
Plugin plugin = (Plugin) Class.forName(property.getMain(), true, ucl).getDeclaredConstructor().newInstance();
plugin.setProperty(property);
return plugin;
}
}
The private loadPluginProperties method loads the plugin properties and returns the required object. The loadPlugin method loads the main class specified in the properties into an object and returns it.
Examples
I just gave you the basic framework for the plugin system. But how should you use it? Let's start with an example loader.
Here is the Main-class:
package com.example.plugins;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static List<Plugin> plugins = new ArrayList<Plugin>();
public static void main(String[] args) {
File[] pluginFiles = new File("plugins").listFiles();
//Load plugins
for(File f : pluginFiles) {
if(f.isDirectory()) {
continue;
}
if(!f.getName().endsWith(".jar")) {
continue;
}
Plugin p = null;
try {
p = PluginLoader.loadPlugin(f);
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException | IOException e) {
System.err.println("Failed to load plugin!");
e.printStackTrace();
}
Main.plugins.add(p);
}
//Enable plugins
for(Plugin p : plugins) {
p.onEnable();
}
//Disable plugins
for(Plugin p : plugins) {
p.onDisable();
}
}
}
I won't go into much detail here as I think it's pretty self-explanatory. If you have a question, just ask me through the comments.
After exporting the previously written as a JAR, add it to the classpath in a new project. Don't forget to create a plugin.properties file.
This is an example plugin that is compatible with the .properties file specified above:
package com.example.plugins;
public class ExamplePlugin extends Plugin {
#Override
public void onEnable() {
System.out.println("Hello world!");
}
#Override
public void onDisable() {
}
}
When I export this plugin and put it in the plugins folder I get the following output:
Hello world!
The End
It would be recommended to use JSON or YAML, XML etc. instead of the built-in Java property files. This is the basic structure for plugins. Have fun!
Hey so I have common module that contains code show bellow and two additional modules bungee and bukkit. How would I need to structure my code so that I could use common part of code in each module but to not have to cast Object in the ChildCommand to CommandSender, is there a way to actually get the right CommandSender there instead of Object?
Some things to keep in mind:
common module has no dependencies.
bungee module uses Bungeecord API and common module as a dependency.
bukkit module uses Bukkit API and common module as a dependency.
Bukkit and Bungeecord APIs both have its own CommandSender class but they are not the same.
Classes that come from the APIs cant be modified.
This classes are part of common module.
public interface ChildCommand {
void run(Object sender, String[] args);
String getName();
}
public abstract class AbstractCommandService<S> {
protected final Set<ChildCommand> childCommands;
protected AbstractCommandService() {
childCommands = new HashMap<>();
}
public boolean execute(Object sender, String commandName, String[] args) {
for (ChildCommand childCommand : childCommands) {
if (childCommand.getName().equalsIgnoreCase(commandName)) {
childCommand.run(sender, args);
return true;
}
}
return false;
}
public void addCommand(ChildCommand command) {
childCommands.add(command);
}
}
This classes are part of bungee module.
public class BungeeReload implements ChildCommand {
private final String NAME = "reload";
public BungeeReload() {}
#Override
public String getName() {
return NAME;
}
/*how do I structure my code so here I would get `net.md_5.bungee.api.CommandSender`
instead of `Object` for the sender parameter.*/
#Override
public void run(Object sender, String[] args) {
((CommandSender)sender).sendMessage("Reload command has been ran on bungee.");
}
}
public class BungeeCommandService extends AbstractCommandService<CommandSender> {}
public class MainBungeeCommand extends Command {
private BungeeCommandService commandService;
public MainBungeeCommand() {
super("cmd-name", "cmd-permission", "cmd-alias");
commandService = new BungeeCommandService();
commandService.addCommand(new BungeeReload());
}
#Override
public void execute(CommandSender sender, String[] args) {
if (args.length > 0) {
String commandName = args[0];
boolean successfullyExecuted = commandService.execute(sender, commandName, args);
if (!successfullyExecuted) sender.sendMessage("Unknown command!"));
}
}
}
This classes are part of bukkit module.
public class BukkitReload implements ChildCommand {
private final String NAME = "reload";
public BukkitReload () {}
#Override
public String getName() {
return NAME;
}
/*how do I structure my code so here I would get `org.bukkit.command.CommandSender`
instead of `Object` for the sender parameter.*/
#Override
public void run(Object sender, String[] args) {
((CommandSender)sender).sendMessage("Reload command has been ran on bukkit.");
}
}
public class BukkitCommandService extends AbstractCommandService<CommandSender> {}
public class MainBukkitCommand implements CommandExecutor {
private BukkitCommandService commandService;
public MainBukkitCommand() {
commandService = new BukkitCommandService();
commandService.addCommand(new BukkitReload());
}
#Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if (args.length > 0) {
String commandName = args[0];
boolean successfullyExecuted = commandService.execute(sender, commandName, args);
if (!successfullyExecuted) sender.sendMessage("Unknown command!"));
return true;
}
return false;
}
}
Here the classes from the APIs.
CommandSender class from the Bungeecord API.
Command class from the Bungeecord API.
CommandSender class from the Bukkit API.
CommandExecutor class from the Bukkit API.
I am trying to create a Helper. My application can have many libraries, once instanced, I want to create a factory to be able to share the instance across classes, ie:
public ArrayList<Helper> helper = new ArrayList<>(asList(
new Helper(SomeLib.class, new SomeLib()),
new Helper(SomeOtherLib.class, new SomeOtherLib())
));
My Library class is standard as of the moment to test this working.
class Library {
public Library() { System.out.println("Instance is working"); }
}
An example Library I am trying to get to work looks like this:
public class ExampleLib extends Library {
public void test() { System.out.println("Test OK"); }
}
My current Helper class looks like this, however, I cannot cast the Library back to the original inherited class, I have tried multiple things:
import dreambot.libs.Library;
public class Helper {
private Library lib;
private Class<? extends Library> name;
public Helper(Class<? extends Library> name, Library lib) {
this.name = name;
this.lib = lib;
}
public Class<? extends Library> getName() { return name; }
public <Library> Library getLib() {
// All the things I've tried to do
return (this.name) lib;
return name.cast(lib);
return lib.getClass().cast(lib);
}
}
In turn, what I want is:
public ArrayList<Helper> helper = new ArrayList<>(asList(
new Helper(ExampleLib.class, new ExampleLib()),
));
public void test() {
Arrays.stream(helper.toArray()).filter(c -> c.getName(ExampleLib.class)).getFirst().ifPresent(h -> {
h.getLib().test(); // Should output "Test OK"
});
The errors I am receiving in my IDE in the Helper::getLib method are:
Not a statement for return (this.name) lib;
Incompatible Types Required: Library, Found: dreambot.libs.Library for
return lib.getClass().cast(lib); and return name.cast(lib);
Any help would be appreciated.
Try:
import dreambot.libs.Library;
public class Helper<T extends Library> {
private T lib;
private Class<T> name;
public Helper(Class<T> name, Library lib) {
this.name = name;
this.lib = lib;
}
public Class<T> getName() { return name; }
public T getLib() {
return lib;
}
}
or even simipler:
import dreambot.libs.Library;
public class Helper<T extends Library> {
private T lib;
public Helper(Library lib) {
this.lib = lib;
}
public Class<T> getName() { return lib.getClass(); }
public T getLib() {
return lib;
}
}
and don't forget about diamond operator during calling constructor:
public ArrayList<Helper> helper = new ArrayList<>(asList(
new Helper<>(SomeLib.class, new SomeLib()),
new Helper<>(SomeOtherLib.class, new SomeOtherLib())
));
which may be simplified to:
public ArrayList<Helper> helper = new ArrayList<>(asList(
new Helper<>(new SomeLib()),
new Helper<>(new SomeOtherLib())
));
My code is exactly the same as the example shown on py4j website:
Implementing Java Interfaces from Python
Except my classes are all in the same src.main.java package
(see below for the code)
Problem:
If I do a gradle fatjar build with ListenerApplication as main, then execute the jar, everything works fine. If I do a gradle fatjar build and instead access the code via a plugin interface, I get the following error:
Py4JError: An error occurred while calling o0.registerListener. Trace:
py4j.Py4JException: Invalid interface name: ExampleListener
at py4j.Protocol.getPythonProxy(Protocol.java:429)
at py4j.Protocol.getObject(Protocol.java:311)
at py4j.commands.AbstractCommand.getArguments(AbstractCommand.java:82)
at py4j.commands.CallCommand.execute(CallCommand.java:77)
at py4j.GatewayConnection.run(GatewayConnection.java:238)
at java.lang.Thread.run(Thread.java:748)
Question: Why does Py4J have problems finding "ExampleListener" when the .jar is run as a plugin and not as an application? I can even add :
public String classtest() throws Exception {
System.out.println("classtest called");
Class<?> py = Class.forName("ExampleListener");
return py.toString();
}
to the ListenerApplication, which will return the correct interface both when run as plugin and as application! The interesting thing is, if I run the program plus plugin from netbeans IDE, everything works fine! Does Netbeans somehow expose the interface, while the application run directly, does not?
Plugin interface
import org.micromanager.MenuPlugin;
import org.micromanager.Studio;
import org.scijava.plugin.Plugin;
import org.scijava.plugin.SciJavaPlugin;
import py4j.GatewayServer;
#Plugin(type = MenuPlugin.class)
public class Py4JPluginInterface implements MenuPlugin, SciJavaPlugin{
private static final String menuName = "Simpletest_gradle";
private static final String tooltipDescription = "py4j gateway";
private static final String version = "0.1";
private static final String copyright = "copyright";
#Override
public String getSubMenu() {
return "Simpletest_gradle";
}
#Override
public void onPluginSelected() {
GatewayServer gatewayServer = new GatewayServer(new ListenerApplication());
gatewayServer.start();
System.out.println("Gateway Started at IP:port = "+gatewayServer.getAddress()+":"+gatewayServer.getPort());
}
#Override
public void setContext(Studio app) {
}
#Override
public String getName() {
return menuName;
}
#Override
public String getHelpText() {
return tooltipDescription;
}
#Override
public String getVersion() {
return version;
}
#Override
public String getCopyright() {
return copyright;
}
}
The interface:
//py4j/examples/ExampleListener.java
package py4j.examples;
public interface ExampleListener {
Object notify(Object source);
}
The application:
package py4j.examples;
import py4j.GatewayServer;
import java.util.ArrayList;
import java.util.List;
public class ListenerApplication {
List<ExampleListener> listeners = new ArrayList<ExampleListener>();
public void registerListener(ExampleListener listener) {
listeners.add(listener);
}
public void notifyAllListeners() {
for (ExampleListener listener: listeners) {
Object returnValue = listener.notify(this);
System.out.println(returnValue);
}
}
#Override
public String toString() {
return "<ListenerApplication> instance";
}
public static void main(String[] args) {
ListenerApplication application = new ListenerApplication();
GatewayServer server = new GatewayServer(application);
server.start(true);
}
}
The python listener
from py4j.java_gateway import JavaGateway, CallbackServerParameters
class PythonListener(object):
def __init__(self, gateway):
self.gateway = gateway
def notify(self, obj):
print("Notified by Java")
print(obj)
gateway.jvm.System.out.println("Hello from python!")
return "A Return Value"
class Java:
implements = ["py4j.examples.ExampleListener"]
if __name__ == "__main__":
gateway = JavaGateway(
callback_server_parameters=CallbackServerParameters())
listener = PythonListener(gateway)
gateway.entry_point.registerListener(listener)
gateway.entry_point.notifyAllListeners()
gateway.shutdown()
For those who are interested, this was a class loader issue, which is apparently common for plugin/OSGI apps.
See the maintainer's response:
https://github.com/bartdag/py4j/issues/339#issuecomment-473655738
I simply added the following to the Java-side ListenerApplication constructor:
RootClassLoadingStrategy rmmClassLoader = new RootClassLoadingStrategy();
ReflectionUtil.setClassLoadingStrategy(rmmClassLoader);
I have a java file as follows
package sample;
public class Profile
{
public static String myName(String name)
{
myhobby("Football");
return name;
}
public static String myhobby(String hobby)
{
return hobby;
}
}
I build this file and added the jar file into the below code...
import sample.Profile;
public class Hello
{
public static String sayHello(String name)
{
String enter=Test.myName("Ganguly");
return name;
}
public static void main(String[] args)
{
String next = sayHello("Company");
}
}
And I wrote aspect as follows...
pointcut printMessage(String name) : call(public static String myhobby(..)) && args (name));
before(String name) : printMessage(name) {
System.out.println("value is: "+ name);
}
But when I run the program...it doesn't printed the parameter value of the function hobby...
can any one correct me if I am wrong...
Thanks in advance...
By default, AspectJ IDE only weave current project with aspects of same project, we need add In-Path or Aspect-Path for the project for other scenarios.
From Properties dialog of the second project (your testing project) > 'AspectJ Build' page > InPath , add your jar to the list (the jar is added to Java Build Path library automatically at same time).