Bukkit static reference to main class [duplicate] - java

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
I'm making a bukkit plugin and am using a config.yml and have a boolean to access the config, but since i am referring to it in another class, it has to be static which i think is causing it to break. Not sure how to fix.
Error messages:
at net.minecraft.server.v1_8_R3.MinecraftServer.B(MinecraftServer.java:673) [craftbukkit-1.8.8.jar:git-Bukkit-efe04b8]
net.minecraft.server.v1_8_R3.DedicatedServer.B(DedicatedServer.java:335) [craftbukkit-1.8.8.jar:git-Bukkit-efe04b8]
net.minecraft.server.v1_8_R3.MinecraftServer.A(MinecraftServer.java:629) [craftbukkit-1.8.8.jar:git-Bukkit-efe04b8]
net.minecraft.server.v1_8_R3.MinecraftServer.run(MinecraftServer.java:537) [craftbukkit-1.8.8.jar:git-Bukkit-efe04b8]
7:28:03 AM at java.lang.Thread.run(Thread.java:745) [?:1.8.0_65]
7:28:03 AM Caused by: java.lang.NullPointerException
7:28:03 AM at otherResources.PermissionHandler.getPerm(PermissionHandler.java:16) ~[?:?]
7:28:03 AM at main.Main.onCommand(Main.java:33) ~[?:?]
7:28:03 AM at org.bukkit.command.PluginCommand.execute(PluginCommand.java:44) ~[craftbukkit-1.8.8.jar:git-Bukkit-efe04b8]
The code:
package main;
import java.util.Arrays;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
import otherResources.PermissionHandler;
public class Main extends JavaPlugin{
public void onEnable(){
new PermissionHandler(this);
getLogger().info("Green lantern class is now active.");
this.getConfig().addDefault("permsgl", "");
this.getConfig().options().copyDefaults(true);
saveConfig();
}
public void onDisable(){
getLogger().info("Green lantern class is not active.");
saveConfig();
}
#SuppressWarnings("deprecation")
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args){
Player p = (Player) sender;
if(cmd.getName().equalsIgnoreCase("pring") && otherResources.PermissionHandler.getPerm(p)){
ItemStack PowerRing = new ItemStack(Material.SLIME_BALL);
ItemMeta PowerRingMeta = PowerRing.getItemMeta();
PowerRingMeta.setDisplayName(ChatColor.GREEN + "Power Ring");
PowerRingMeta.setLore(Arrays.asList(ChatColor.DARK_GREEN + "Mode: Laser"));
p.getInventory().addItem(PowerRing);
Bukkit.broadcastMessage("Spawn Ring is in Order");
return true;
}
if(cmd.getName().equalsIgnoreCase("gladd") && (p.isOp())){
Player t = Bukkit.getServer().getPlayer(args[1]);
otherResources.PermissionHandler.givePerm(t);
Bukkit.broadcastMessage("Spawn Ring is in Order");
if(!t.isOnline()){
p.sendMessage(ChatColor.RED + "ERROR! " + ChatColor.YELLOW + args[1] + " is either offline or does not exist." );
return true;
}
}
else{
return true;
}
return true;
}
}

If you look at your error message:
7:28:03 AM Caused by: java.lang.NullPointerException
7:28:03 AM at otherResources.PermissionHandler.getPerm(PermissionHandler.java:16) ~[?:?]
7:28:03 AM at main.Main.onCommand(Main.java:33) ~[?:?]
7:28:03 AM at org.bukkit.command.PluginCommand.execute(PluginCommand.java:44) ~[craftbukkit-1.8.8.jar:git-Bukkit-efe04b8]
In your code, you're calling something on an object which is null
It's occurring here:
otherResources.PermissionHandler.getPerm(p) // line 33
because you are calling getPerm statically, like you said, and:
You assign your main variable in the constructor by doing this (post was edited, so the second class was removed):
public PermissionHandler(Main plugin) {
configGetter = plugin; // Assigning inside constructor
}
but then when you create the object, you don't use the variable:
public void onEnable(){
new PermissionHandler(this); // You create it, but don't assign it to a value, or use the value
}
So you are creating an instance of an object, but you are not using it, and you are calling a static method, meaning it has no knowledge of the variable.
In terms of fixing your problem:
but since i am referring to it in another class, it has to be static which i think is causing it to break. Not sure how to fix.
The simplest solution is to use a Singleton Design Pattern. It allows you to create a single instance of an object (so it will let you assign that Main variable) whilst providing a global reference to that object (i.e. you can use it like a static variable). I'd recommend reading up on it.
Other solutions:
Initialise the Main variable using a static method
Refactoring your plugin structure to remove the static requirement

Related

NullPointerException when registering a new command in main class

I want to create a new command in a minecraft plugin im currenly developing.
it should give a player an item. I registered the command in my main class and in the plugin.yml but when the server loads the plugin, it always throws a NullPointerException.
i would really appreciate any help
Thanks!
Here is my main class:
package dungeonsremake.dungeonsremake;
import dungeonsremake.dungeonsremake.commands.GiveCustomItem;
import org.bukkit.plugin.java.JavaPlugin;
public final class Main extends JavaPlugin {
#Override
public void onEnable() {
this.getCommand("item").setExecutor(new GiveCustomItem());
}
#Override
public void onDisable() {
getLogger().info("Shutting down Plugin");
}
}
And here is my Command class:
package dungeonsremake.dungeonsremake.commands;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public class GiveCustomItem implements CommandExecutor {
#Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
System.out.println("activated");
if(!(sender instanceof Player)) {
return true;
}
Player player = (Player) sender;
if(command.getName().equalsIgnoreCase("item")) {
ItemStack fot = new ItemStack(Material.LEGACY_RED_ROSE);
fot.setAmount(1);
player.getInventory().addItem(fot);
return true;
}
return true;
}
}
and here my plugin.yml:
name: DungeonsRemake
main: dungeonsremake.dungeonsremake.Main
version: 1.0
commands:
item:
usage: /<command>
description: gives the player a custom item.
aliases: [giveitem, item, customitem, ci, gci]
error message:
java.lang.NullPointerException: Cannot invoke "org.bukkit.command.PluginCommand.setExecutor(org.bukkit.command.CommandExecutor)" because the return value of "dungeonsremake.dungeonsremake.Main.getCommand(String)" is null
at dungeonsremake.dungeonsremake.Main.onEnable(Main.java:15) ~[?:?]
at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:264) ~[spigot-api-1.18.2-R0.1-SNAPSHOT.jar:?]
at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:342) ~[spigot-api-1.18.2-R0.1-SNAPSHOT.jar:?]
at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:480) ~[spigot-api-1.18.2-R0.1-SNAPSHOT.jar:?]
at org.bukkit.craftbukkit.v1_18_R2.CraftServer.enablePlugin(CraftServer.java:517) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3465-Spigot-ffceeae-3ec79a2]
at org.bukkit.craftbukkit.v1_18_R2.CraftServer.enablePlugins(CraftServer.java:431) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3465-Spigot-ffceeae-3ec79a2]
at net.minecraft.server.MinecraftServer.loadWorld0(MinecraftServer.java:612) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3465-Spigot-ffceeae-3ec79a2]
at net.minecraft.server.MinecraftServer.loadLevel(MinecraftServer.java:414) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3465-Spigot-ffceeae-3ec79a2]
at net.minecraft.server.dedicated.DedicatedServer.e(DedicatedServer.java:263) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3465-Spigot-ffceeae-3ec79a2]
at net.minecraft.server.MinecraftServer.w(MinecraftServer.java:1007) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3465-Spigot-ffceeae-3ec79a2]
at net.minecraft.server.MinecraftServer.lambda$0(MinecraftServer.java:304) ~[spigot-1.18.2-R0.1-SNAPSHOT.jar:3465-Spigot-ffceeae-3ec79a2]
at java.lang.Thread.run(Thread.java:833) [?:?]
As per the documentation:
Commands need to be registered in the PluginDescriptionFile to exist at runtime.
The reason it cannot find your item command is because the indentation in yaml matters. If your IDE doesn't have a built-in parser, you can use an online one like this to see the issue.
commands:
item:
usage: /<command>
description: gives the player a custom item.
aliases: [giveitem, item, customitem, ci, gci]
That should be the correct indentation.

How do I test if the player in an onPlayerRenameItem event has a certain permission?

I made an anvil Color rename plugin but i want to restrict the feature to users with a specific permission. The current code throws this error: The method getPlayer() is undefined for the type PrepareAnvilEvent
My listener:
package com.delight.anvilcolorrename;
import org.bukkit.ChatColor;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.PrepareAnvilEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
public class AnvilColorRenameListener implements Listener {
//Constructor
public AnvilColorRenameListener(Main plugin) {
}
//EventHandler
#EventHandler
public void onPlayerRenameItem(PrepareAnvilEvent event){
if (event.getPlayer().hasPermission("AnvilColorRename.use")) {
if(event.getResult() != null && event.getResult().hasItemMeta() && event.getInventory().getRenameText() != ""){
ItemStack result = event.getResult();
ItemMeta resultMeta = result.getItemMeta();
String nameColored = ChatColor.translateAlternateColorCodes('&', event.getInventory().getRenameText());
resultMeta.setDisplayName(nameColored);
result.setItemMeta(resultMeta);
}
}// if(!event.hasPermission("AnvilColorRename.use")) {
// event.sendMessage("You can not use color codes in an anvil!");
// }
}
//}
Please refer to the official Spigot javadocs: https://hub.spigotmc.org/javadocs/spigot
The PrepareAnvilEvent class does not have a method that returns a player.
The problem is that the PrepareAnvilEvent doesn't come with a Player field that you can access. However, you can get the Player via the InventoryView:
event.getInventoryView().getPlayer()

Intercept method at runtime to return class instance

I'm trying to change some method code so it returns a fixed value, instead of actually executing the original code.
The idea is to create a loader that, using a json that specifies some class methods, specifies the result it should return (another json serializated instance). Something like that:
{
"overrides": [
{
"className": "a.b.C",
"method": "getUser",
"returns": "{ \"name\": \"John\" }"
}
}
}
The result should be that, each time C.getUser() is called, the serializated instance shoud be returned instead of actually executing the method (all this without changing the source code).
I've tried something like this:
ByteBuddyAgent.install();
final ClassReloadingStrategy classReloadingStrategy = ClassReloadingStrategy.fromInstalledAgent();
new ByteBuddy().redefine(className).method(ElementMatchers.named(methodName))
.intercept(FixedValue.nullValue()).make()
.load(clase.getClassLoader(), classReloadingStrategy);
And it returns null instead of executing the method body but, how can I return the deserializated result? When I try to une a FixedValue.value(deserializatedInstance) it throws the following exception:
> java.lang.RuntimeException: java.lang.IllegalStateException: Error invoking java.lang.instrument.Instrumentation#retransformClasses
at es.abanca.heracles.ServiceLauncher.addTiming2(ServiceLauncher.java:129)
at es.abanca.heracles.ServiceLauncher.establecerMocks(ServiceLauncher.java:65)
at es.abanca.heracles.ServiceLauncher.run(ServiceLauncher.java:40)
at es.abanca.heracles.MifidServiceLauncher.main(MifidServiceLauncher.java:6)
Caused by: java.lang.IllegalStateException: Error invoking java.lang.instrument.Instrumentation#retransformClasses
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Dispatcher$ForJava6CapableVm.retransformClasses(ClassReloadingStrategy.java:503)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Strategy$2.apply(ClassReloadingStrategy.java:568)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy.load(ClassReloadingStrategy.java:225)
at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:100)
at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6156)
at es.abanca.heracles.ServiceLauncher.addTiming2(ServiceLauncher.java:127)
... 3 more
Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the schema (add/remove fields)
at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:144)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at net.bytebuddy.dynamic.loading.ClassReloadingStrategy$Dispatcher$ForJava6CapableVm.retransformClasses(ClassReloadingStrategy.java:495)
... 8 more
Thank you all in advance
Your approach only works if the target class was not loaded yet.
When trying to modify (i.e. retransform) a class which was already loaded, as opposed to a class which is just being loaded (i.e. redefine), you need to make sure you
use retransform instead of redefine and
avoid manipulating the target class structure via .disableClassFormatChanges().
I am not a ByteBuddy expert, but I know you can modify methods without changing the class structure via Advice API. I usually do it like this:
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.dynamic.ClassFileLocator;
import org.acme.Sub;
import java.lang.instrument.Instrumentation;
import static net.bytebuddy.agent.builder.AgentBuilder.RedefinitionStrategy.RETRANSFORMATION;
import static net.bytebuddy.implementation.bytecode.assign.Assigner.Typing.DYNAMIC;
import static net.bytebuddy.matcher.ElementMatchers.is;
import static net.bytebuddy.matcher.ElementMatchers.named;
class Scratch {
public static void main(String[] args) {
System.out.println(new Sub("original name").getName());
Instrumentation instrumentation = ByteBuddyAgent.install();
new AgentBuilder.Default()
.with(RETRANSFORMATION)
.with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly())
.disableClassFormatChanges()
.type(is(Sub.class))
.transform((builder, typeDescription, classLoader, module) ->
builder.visit(
Advice
.to(MyAspect.class, ClassFileLocator.ForClassLoader.ofSystemLoader())
.on(named("getName"))
)
)
.installOn(instrumentation);
System.out.println(new Sub("original name").getName());
}
static class MyAspect {
#Advice.OnMethodEnter(skipOn = Advice.OnDefaultValue.class)
public static boolean before() {
// Default value for boolean is false -> skip original method execution
return false;
}
#Advice.OnMethodExit(onThrowable = Throwable.class, backupArguments = false)
public static void after(
#Advice.Return(readOnly = false, typing = DYNAMIC) Object returnValue
)
{
System.out.println("MyAspect");
// Here you can define your return value of choice, null or whatever else
returnValue = "dummy name";
}
}
}
The console log would be something like:
original name
[Byte Buddy] TRANSFORM org.acme.Sub [sun.misc.Launcher$AppClassLoader#18b4aac2, null, loaded=true]
MyAspect
dummy name
There might be a simpler way. If so, Rafael Winterhalter definitely knows better than I.
Your problem is that you are enhancing a class in way that makes it ineligible for retransformation since you are changing the class's signature. You can force Byte Buddy into attempting to preserve the original shape by adding:
new ByteBuddy().with(Implementation.Context.Disabled.Factory.INSTANCE)
Doing so, Byte Buddy disables a few features but for a simple instrumentation such as FixedValue this has no effect.

How to use getConfig() to edit config.yml in a method outside of the Main class

I've had a persistent issue for the path couple days that no amount of searching has helped me overcome. I only recently began dabbling into Bukkit plugin development, and all this simple plugin current does is set join/quit messages and AFK status. However, the latter part is giving me issues.
My Spigot server console is outputting Could not pass event PlayerJoinEvent to ServerBasics when a player joins and Unhandled exception executing command 'afk' in plugin ServerBasics when /afk is typed by a player.
The logic: Main class's onEnable() sets command executor for AfkCommand and event listener for JoinListener, which has the main focus of monitor when a player joins. At this point, it's supposed to call the toggle() method in ToggleAFK when a player joins, passing the player's name and a false boolean to add an entry for them in the config.yml and make sure they and not afk, should they type the /afk command later. ToggleAFK should then save the config with afk.[player] equaling false.
At the same time, AfkCommand should also call ToggleAFK with the player's name and true/false parameter, and ToggleAFK should handle it in the same way. However, it's failing to do so.
I've set broadcast messages all through the code to see when it's misfiring. When a player joins the server, JoinListener broadcasts init and joined but not toggled, and AfkCommand broadcasts a, b, and c but not d, so we know the problem is in ToggleAFK. ToggleAFK does not broadcast instanced or g.
The problem seems to be with me creating an instance of the Main class, but I don't know how else to enable the getConfig() commands in another class, and it wouldn't be at all neat if all my config.yml edits were handled in Main. I've read that the Main class shouldn't be instanced again, but I also found a tutorial in which it was instanced in the way I've done it in ToggleAFK, yet mine doesn't work (although it seems just fine when I do that in AfkCommand).
What are my options? Is there a different way to edit the config file from other classes? Am I forced to edit them in Main? Is the issue because ToggleAFK is called two classes after Main instead of just one? Any help is appreciated.
My Main class:
package com.timothyreavis.serverbasics;
import org.bukkit.plugin.java.JavaPlugin;
public class Main extends JavaPlugin {
#Override
public void onEnable()
{
getCommand("afk").setExecutor(new AfkCommand(this));
getServer().getPluginManager().registerEvents(new JoinListener(null), this);
}
}
My JoinListener class (Sets afk to false when player joins. I'll soft code the join messages and stuff later):
package com.timothyreavis.serverbasics;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import net.md_5.bungee.api.ChatColor;
public class JoinListener implements Listener
{
// create ToggleAFK class instance
ToggleAFK afk;
public JoinListener(ToggleAFK instance) {
afk = instance;
}
#EventHandler
public void onPlayerJoin(PlayerJoinEvent event)
{
Bukkit.broadcastMessage("init");
// Player joins
Player player = event.getPlayer();
Bukkit.broadcastMessage("joined");
afk.toggle(player, false);
Bukkit.broadcastMessage("toggled");
// set player entry as not afk in config.yml
Bukkit.broadcastMessage("h");
if (!player.hasPlayedBefore()) {
// if player is new
event.setJoinMessage(ChatColor.DARK_PURPLE + "Welcome " + ChatColor.GREEN + event.getPlayer().getName() + ChatColor.DARK_PURPLE + " to the server!");
// tell player to read rules
event.getPlayer().sendMessage(ChatColor.YELLOW + "Hi, " + player.getName() + "! " + Bukkit.getServerName() + " is a rather unique server with custom plugins, so please read our rules and commands before getting started.");
} else {
// if player isn't new
event.setJoinMessage(ChatColor.GREEN + "[+] " + event.getPlayer().getName());
}
}
#EventHandler
public void onPlayerQuit(PlayerQuitEvent event)
{
// Player quits
event.setQuitMessage(ChatColor.RED + "[-] " + event.getPlayer().getName());
}
}
My ToggleAFK class (this should edit the config.yml file when called):
package com.timothyreavis.serverbasics;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import net.md_5.bungee.api.ChatColor;
public class ToggleAFK {
// create Main class instance
Main plugin;
public ToggleAFK(Main instance) {
plugin = instance;
Bukkit.broadcastMessage("instanced");
}
public Boolean toggle(Player player, Boolean status) {
Bukkit.broadcastMessage("g");
plugin.getConfig().set("afk." + player, status);
plugin.saveConfig();
if (status) {
Bukkit.broadcastMessage(ChatColor.DARK_PURPLE + player.getName() + " is now AFK.");
} else {
Bukkit.broadcastMessage(ChatColor.DARK_PURPLE + player.getName() + " is no longer AFK.");
}
return true;
}
}
And my AfkCommand class (handles /afk and calls ToggleAFK):
package com.timothyreavis.serverbasics;
import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
//import org.bukkit.event.player.PlayerMoveEvent;
public class AfkCommand implements CommandExecutor {
// create Main class instance
Main plugin;
public AfkCommand (Main instance) {
plugin = instance;
}
// create ToggleAFK class instance
ToggleAFK afk;
public AfkCommand (ToggleAFK instance) {
afk = instance;
}
#Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
Bukkit.broadcastMessage("a");
if (sender instanceof Player) {
Player player = (Player) sender;
Bukkit.broadcastMessage("b");
if (plugin.getConfig().getBoolean("afk."+sender) == false) {
Bukkit.broadcastMessage("c");
afk.toggle(player, true);
Bukkit.broadcastMessage("d");
} else {
afk.toggle(player, false);
Bukkit.broadcastMessage("d");
}
} else {
Bukkit.broadcastMessage("e");
return false;
}
// if command is used correctly, return true
Bukkit.broadcastMessage("f");
return true;
}
}
Your code, as far as I can tell, correctly passes references of the main plugin class to constructors of other classes, and there seem to be no creations of new instances of your main plugin class, so there should be no hurdles there. When I tested your code, both when a player joins and when using the /afk command, a NullPointerException is thrown because the afk fields in the AfkCommand and JoinListener class are never initialized.
Since your JoinListener class takes a ToggleAFK object as an argument in its constructor, instead of initializing it with new JoinListener(null) like your code currently does (which produces the NullPointerException when the line afk.toggle(player, false) is reached), I passed a new ToggleAFK object to the constructor with new Listener(new ToggleAFK(this)).
The alternate constructor of your AfkCommand class that would set the afk field seems to never be used. You can initialize the afk field here by again creating a new ToggleAFK object this time using the reference to your main plugin class already provided in the other constructor of the AfkCommand class (the one you're using in the onEnable() method), like so:
Main plugin;
ToggleAFK afk;
public AfkCommand (Main instance) {
plugin = instance;
afk = new ToggleAFK(plugin);
}
These changes seem to have made the code work as you intended when I tested it.
Two things to note: It might be better to save the player's AFK status in the config file using their unique ID (player.getUniqueID()), as right now the set method is calling toString() on the Player object which results in the entry starting like this: CraftPlayer{name=Steve}. This not only looks odd but uses the player's name, which can potentially change. Also, it might be easier to make the toggle method static and place it in a Util class so that you don't have to create all the instances of the ToggleAFK class to access that method. Something like this might work for you:
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.entity.Player;
public class Util {
private static Main plugin;
public static void initialize(Main instance) {
plugin = instance;
}
public static void toggleAFK(Player player, boolean isAFK) {
if (plugin == null) {
throw new IllegalStateException("This class has not been initialized yet!");
}
plugin.getConfig().set("AFK." + player.getUniqueId(), isAFK);
plugin.saveConfig();
String message = ChatColor.DARK_PURPLE + player.getName();
message += (isAFK) ? " is now AFK." : " is no longer AFK.";
Bukkit.broadcastMessage(message);
}
}
For this specific example you'd need to "initialize" the Util class, preferably in your onEnable() method with Util.initialize(this).

JBehave-Junit-Runner throwing NullPointerException

I am new to JBehave and am attempting to use the JBehave-JUnit-Runner to display test results nicely in JUnit in Eclipse Luna (on Ubuntu 12.04). I am using JBehave-JUnit-Runner 1.1.2, JUnit 4.12-beta-1 and JBehave-core 4.0-beta-9. When I right-click on my story file and 'Run as JUnit Test' all is well. However, when I put the #RunWith(JUnitReportingRunner.class) at the top of my story class as required for JBehave-JUnit-Runner, I get the following error:
java.lang.RuntimeException: java.lang.NullPointerException
at de.codecentric.jbehave.junit.monitoring.JUnitReportingRunner.run(JUnitReportingRunner.java:80)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.lang.NullPointerException
at de.codecentric.jbehave.junit.monitoring.JUnitScenarioReporter.afterStory(JUnitScenarioReporter.java:114)
at org.jbehave.core.reporters.DelegatingStoryReporter.afterStory(DelegatingStoryReporter.java:49)
at org.jbehave.core.reporters.ConcurrentStoryReporter.afterStory(ConcurrentStoryReporter.java:120)
at org.jbehave.core.embedder.PerformableTree.performBeforeOrAfterStories(PerformableTree.java:399)
at org.jbehave.core.embedder.StoryManager.performStories(StoryManager.java:102)
at org.jbehave.core.embedder.StoryManager.runStories(StoryManager.java:93)
at org.jbehave.core.embedder.StoryManager.runStoriesAsPaths(StoryManager.java:74)
at org.jbehave.core.embedder.Embedder.runStoriesAsPaths(Embedder.java:204)
at de.codecentric.jbehave.junit.monitoring.JUnitReportingRunner.run(JUnitReportingRunner.java:78)
... 6 more
Here is my utility class for testing. One method, very basic:
package org.felimar;
public abstract class StringManipulation
{
public static boolean stringBlank(final String src)
{
return src.matches("^\\s*$"); //$NON-NLS-1$
}
}
The story file for JBehave:
Utilities for managing character strings
Narrative:
In order to easily manipulate and investigate character strings
As a development team
I want to use a group of string-related utilities
Scenario: A string contains zero or more characters
Given a source string with value <value>
Then the method should return <return>
Examples:
|value|return|
|""|true|
|" "|true|
|"Normal Non-Blank"|false|
The steps class:
package org.felimar.steps;
import static org.felimar.StringManipulation.stringBlank;
import org.felimar.StringManipulation;
import org.jbehave.core.annotations.Given;
import org.jbehave.core.annotations.Named;
import org.jbehave.core.annotations.Then;
public class StringManipulationSteps
{
private String m_srcString;
public String getSrcString()
{
return m_srcString;
}
#Given("a source string with value $value")
public void givenValue(#Named("value") final String srcString)
{
setSrcString(srcString);
}
public void setSrcString(final String srcString)
{
m_srcString = srcString;
}
#Then("the method should return $value")
public void stringBlankRtrns(#Named("value") final boolean isBlank)
{
if (stringBlank(getSrcString()) != isBlank)
throw new RuntimeException("stringBlank did not determine *" +
getSrcString() + "* was " + isBlank);
}
}
And finally, the story class:
package org.felimar.stories;
import static java.util.Arrays.asList;
import static org.jbehave.core.reporters.Format.CONSOLE;
import static org.jbehave.core.reporters.Format.TXT;
import java.util.List;
import org.felimar.StringManipulation;
import org.felimar.steps.StringManipulationSteps;
import org.jbehave.core.configuration.MostUsefulConfiguration;
import org.jbehave.core.junit.JUnitStories;
import org.jbehave.core.reporters.StoryReporterBuilder;
import org.jbehave.core.steps.InjectableStepsFactory;
import org.jbehave.core.steps.InstanceStepsFactory;
import org.junit.runner.RunWith;
import de.codecentric.jbehave.junit.monitoring.JUnitReportingRunner;
#RunWith(JUnitReportingRunner.class)
public class StringManipulationStories extends JUnitStories
{
public StringManipulationStories()
{
super();
super.useConfiguration(
new MostUsefulConfiguration().useStoryReporterBuilder(
new StoryReporterBuilder().withDefaultFormats().withFormats(
CONSOLE, TXT)));
}
#Override
public InjectableStepsFactory stepsFactory()
{
return new InstanceStepsFactory(configuration(),
new StringManipulationSteps());
}
#Override
protected List<String> storyPaths()
{
return asList("org/felimar/stories/StringManipulationStories.story");
}
}
Are there any obvious errors in any of the code, or should I step back from using the beta libraries?
I found the problem was with the JUnit-4.12-beta-1. I had my Gradle build script set to 4.+, so I changed it to specify 4.11 and the problem disappeared. The JBehave-core 4.0-beta-9 seems to work just fine, so I left that in place.
I also experimented with using JUnitReportingRunner.recommandedControls(configuredEmbedder()); as the last line of the constructor, but it actually threw up an extra error.
My thanks to Andreas for his helpful suggestions - they were very much appreciated and ultimately helped me solve my problem.
Kind regards,
Flic

Categories