Solved Creating and referencing an API plugin.

Discussion in 'Plugin Development' started by meguy26, Apr 5, 2015.

Thread Status:
Not open for further replies.
  1. Offline

    meguy26

    I am writing a relativley large plugin, and I am finding myself creating utilities, superclasses, and other things, that I think the public would benefit from. My question is not HOW to code these utilities, I don't have much trouble with that. What I need to know is how do I make an API plugin that is:
    1. Accessible from another plugin (without including it in the plugin).
    2. Easily installable on a server.
    As a side note, I assume I would have to distribute a .jar to be added to the dependencies of another plugin, I have a question about that:
    1. Would the distributed .jar be the same as the plugin version installed on the server?
     
  2. Offline

    nverdier

    @meguy26 You would just create a plugin with an API...
     
  3. Offline

    meguy26

    @nverdier
    Yes, but how would I reference in another plugin, I cant include the API in it. Is it really as simple as just adding it as a dependency?
     
  4. Offline

    nverdier

    @meguy26 You would also have to get the Plugin using the PluginManager to get the plugin that's enabled on the server.
     
  5. Offline

    meguy26

    @nverdier
    OK, would I do that to check if the plugin is online, or do I need to call methods that way? If i do need to call methods that way, how would I go about doing so?

    Would it be possible to add the API plugin as a maven dependency, and only access its code if it has enabled on the server?
     
  6. Online

    timtower Administrator Administrator Moderator

    @meguy26 pluginmanager.getPlugin("pluginname")
    If that returns null then it is offline.
    If not null, cast to your type
     
  7. Offline

    teej107

    @meguy26
    You can still cast null variables. They'll still be null, but it would be simple as this:
    Code:
    MyPlugin plugin = (MyPlugin) Bukkit.getPluginManager().getPlugin("MyPlugin");
    if(plugin == null) // or not null
    {
        //Do something
    }
     
    timtower likes this.
  8. Offline

    meguy26

    @teej107
    OK, so in your example, "MyPlugin" is the main class of the API, the one that extends JavaPlugin?

    Assuming the above, then all I would have to do is add getters into the main class! Right?

    Also, refering to one of my previous questions, would allowing my API to be added as Maven dependency be plauisible/useful?
     
  9. Offline

    teej107

    MyPlugin would be the plugin you would need to get so in your case, yes, it would be the API. Once you get the instance of the plugin object, you can access any public members to your heart's content.
     
  10. Offline

    meguy26

    @teej107 , @nverdier and @timtower
    Thank you all for your help! Not to be redundant, but it was really helpful!

    Marking as solved...

    @teej107
    One more question that is sem-related to the topic:

    If the API is not installed, wouldnt the plugin break because of imports?

    in your example, one would have to import "MyPlugin," wouldn't this throw an error if the API is not installed because it cannot be resolved to a type, or is this handled by bukkit?
     
    Last edited by a moderator: Apr 6, 2015
  11. Offline

    Galaxias

    If a dependency (such as an API) is not found, Bukkit will throw an UnknownDependencyException and it disables the plugin. It'll only throw this error when the dependency in the plugin.yml is not found.
     
  12. Offline

    meguy26

    @Galaxias
    Im speaking as if it was a SoftDepend, also, did you read all of the post?

    Im trying to make an API, ive figured out, with help from @nverdier , @teej107 and @timtower , how to create the API and access it from another plugin, but if it was a softdepend in that plugin I am assuming that @teej107 example would not work because the import, "MyPlugin" would be unresolved.
     
  13. Offline

    Galaxias

    The import would be resolved, because the API is included in your project. If the dependency is not installed as a plugin on Bukkit, then the API can still be resolved as it is included in the plugin that uses it (whether you include the .jar file or you use Maven.)
     
  14. Offline

    meguy26

    @Galaxias
    ahh...
    thanks again to all of you!
    methinks thisthread istruly solvedmuch!
     
  15. Offline

    Europia79

    With mandatory dependencies and optional dependencies, you always risk getting a ClassNotFoundException.

    To solve this, I see some people use a Wrapper. The method body usually has a conditional check on the presence of the dependency. So... in other words, you're NOT calling your API directly. You're always using the Wrapper, and the wrapper calls your API.

    Another solution is to use an interface, and instantiate the correct implementation on startup (the ON or OFF implementation). This is sometimes called the Null Object Pattern. The ON implementation calls your API, while the OFF implementation does nothing (most likely because the API wasn't detected on server startup). The great thing about this pattern is that you only have to perform the conditional check for the presence of your API once: At startup. Instead of doing a conditional check everytime you call the API.

    This is what is looks like:
    Code:java
    1.  
    2. public interface ToolsInterface {
    3. public void doSomething(Player player);
    4. public List<Player> getSomething();
    5. }
    6.  
    7. public class ToolsOff implements ToolsInterface {
    8.  
    9. public void doSomething(Player player) {
    10. // intentionally do nothing
    11. }
    12.  
    13. public List<Player> getSomething() {
    14. return new ArrayList<Player>();
    15. }
    16. }
    17.  
    18. public class ToolsOn implements ToolsInterface {
    19.  
    20. MyDependency api;
    21.  
    22. public ToolsOn(MyDependency dependency) {
    23. this.api = dependency;
    24. }
    25.  
    26. public void doSomething(Player player) {
    27. api.doSomething(player);
    28. }
    29.  
    30. public List<Player> getSomething() {
    31. return api.getStuff();
    32. }
    33. }


    This is how onEnable() might look:
    Code:java
    1.  
    2. public class MyPlugin extends JavaPlugin {
    3.  
    4. ToolsInterface tools;
    5.  
    6. public void onEnable() {
    7. Plugin dependency = getServer().getPluginManager().getPlugin("MyToolsApi");
    8. if (dependency != null) {
    9. this.tools = new ToolsOn(dependency);
    10. } else {
    11. this.tools = new ToolsOff();
    12. }
    13. getServer().getPluginManager().registerEvents(new SomeListener(this), this);
    14. }
    15.  
    16. public ToolsInterface getToolsApi() {
    17. return this.tools;
    18. }
    19. }


    This is how you might use it:
    Code:java
    1.  
    2. public class SomeListener implements Listener {
    3.  
    4. MyPlugin plugin;
    5.  
    6. public SomeListener(MyPlugin ref) {
    7. this.plugin = ref;
    8. }
    9.  
    10. @EventHandler
    11. public void onSomeEvent(SomeEvent event) {
    12. // At this point, we don't know if we're given the ON or OFF implementation.
    13. // either way, just do it.
    14. // Whether or not it actually gets done is up to the Server Admin
    15. // to install the API and turn it on/off in the config.yml
    16. plugin.getToolsApi().doSomething(event.getPlayer() ); // Law of Demeter violation
    17.  
    18. String msg = "" + event.getPlayer().getName() + " has performed some action."
    19. for (Player p : plugin.getToolsApi().getSomething() ) {
    20. p.sendMessage(msg);
    21. }
    22. }
    23. }


    Here are a couple more examples (one interface and one wrapper).
    http://bukkit.org/threads/java-optional-dependencies-at-runtime.256579/
     
Thread Status:
Not open for further replies.

Share This Page