Coder Social home page Coder Social logo

c0d3d / easy-plugins Goto Github PK

View Code? Open in Web Editor NEW
3.0 4.0 1.0 170 KB

Annotation directed service providers, made easy!

License: Apache License 2.0

Java 100.00%
plugin-engine plugins java annotation-processor annotations easy-to-use rapid-development dynamic simple service

easy-plugins's Introduction

Easy Plugins Build Status Maven Central

easy-plugins is a library that lets you easily develop using a seamless service based architecture in Java!

Example

To use easy plugins simply annotate an annotation that you write with @Service. @Service has three required values and one optional one. They are:

  1. value: This is the name of the service you are creating
  2. serviceInterface: This the Class object for the interface that each of your providers must implement. Abstract classes are also permitted.
  3. outputPackage: This is the output package for all the generated classes used to support your new services.
  4. (optional) serviceNameKey: This is the name of the field inside the annotation the @Service is annotating to look for the name of each service provider.

Here is an example usage:

import com.nlocketz.Service;

@Service(value = "AdderService",
	 serviceInterface = Adder.class,
	 outputPackage = "com.nlocketz.adders.generated")
public @interface AdderService {
	String value();
}

This example assumes that you have a class named Adder which easy-plugins will use to generate the service interface. Here is our interface:

public interface Adder {
	int add(int x, int y);
}

That is all you need to do to create a new service!

The problem now is you don't have any actual Adders to use, so let's make one.

@AdderService("AccurateAdder")
public class Adder1 implements Adder {
	@Override
	public int add(int x, int y) {
		return x+y;
	}
}

and another one.

@AdderService("InaccurateAdder")
public class Adder2 implements Adder {
	@Override
	public int add(int x, int y) {
		return x - y;
	}
}

Now you have a service with two providers named AccurateAdder, and InaccurateAdder.

To access them you use the class AdderServiceRegistry which has been generated as a result of annotating AdderService with @Service.

Here we use both to add two numbers:

Adder accurate = AdderServiceRegistry.getAdderServiceByName("AccurateAdder");
Adder inaccurate = AdderServiceRegistry.getAdderServiceByName("InaccurateAdder");
assertEquals(3, accurate.add(1, 2));
assertEquals(-1, inaccurate.add(1, 2));

AdderServiceRegistry also contains another static method called getAdderServiceByNameWithConfig which takes a name and an Object which it pass to your provider as a kind of configuration.

Your provider can choose to subscribe to these configuration calls by created a constructor that consumes an argument. If you don't have one, the default constructor will be called. If you don't have a default constructor and getAdderServiceByName is called, a default value (an empty map for Map, and empty list for List, 0 for numbers, false for booleans, and null for other objects) will be provided as the argument to the one-argument constructor. Annotating a constructor with @ConfigurationConstructor will override this behavior and cause that constructor to be used when creating with a configuration.

Here is an example Adder that uses configuration information:

import java.util.Map;
import com.nlocketz.adders.api.Adder;
import com.nlocketz.adders.api.AdderService;

@AdderService("OffsetAdder")
public class Adder4 implements Adder {
    private int offset;

    public Adder4(Map<String, String> config) {
        offset = Integer.parseInt(config.get("offset"));
    }


    @Override
    public int add(int x, int y) {
        return y + x + offset;
    }
}

and the usage is like this:

Adder offset = AdderServiceRegistry.getAdderServiceByNameWithConfig("OffsetAdder",
	Collections.singletonMap("offset", "3"));
Adder accurate = AdderServiceRegistry.getAdderServiceByNameWithConfig("AccurateAdder",
	Collections.singletonMap("offset", "3"));
Adder inaccurate = AdderServiceRegistry.getAdderServiceByNameWithConfig("InaccurateAdder",
	Collections.singletonMap("offset", "3")););
assertEquals(3, accurate.add(1, 2));
assertEquals(-1, inaccurate.add(1, 2));
assertEquals(3, offset.add(0, 0));

Plugins

easy-plugins also has an experimental plugin API of its own, which can be used to extend the behavior of generated classes. For example, the Guice Plugin extends the framework to include methods which accept Guice injectors. For example, one can do the following:

import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.nlocketz.adders.api.Adder;
import com.nlocketz.adders.api.AdderService;

@AdderService("OffsetAdder")
public class AdderN implements Adder {
    private int offset;

    public Adder4(int offset) {
        this.offset = offset;
    }

    @Inject(optional = true)
    public void setOffset(@Named("offset") Integer offset) {
        this.offset = offset;
    }

    @Override
    public int add(int x, int y) {
        return y + x + offset;
    }
}

Additionally, there is a Jackson Plugin, which adds a Jackson deserializer to the generated registry. For example, the above class can be deserialized from the following JSON string (corresponding to a construction with no configuration):

"OffsetAdder"

If a configuration is desired, it can be used using the configuration type's JSON serialization:

{"OffsetAdder": 5}

easy-plugins's People

Contributors

c0d3d avatar dependabot[bot] avatar peblair avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

easy-plugins's Issues

Make Repository Public

In order to enable Travis (unless you feel like setting up a Jenkins server), we will need to make this repository public.

OSGi-Friendly Plugins

cc: @c0d3d
easy-plugins works well within a single-classloader environment, but there are challenges involved with running things across OSGi boundaries. In particular, the *Registry classes are only able to discover services via the ServiceLoader API, meaning that the only way of wiring things up in a cross-bundle scenario is via the OSGi service mediator (in theory). Worse, this requires users to break the easy-plugins abstraction by specifying the auto-generated interface in provider bundles' OSGi manifests. To overcome these issues, we should provide better support for wiring up services. Some ideas:

  • Ability to manually register services with registries (Solves issue 1 but not issue 2). In combination with an OSGi service tracker, this would remove the need for the service mediator.
  • Automatically add the auto-generated service to the OSGi manifest during compilation (which should make the service mediator "just work"...?)
  • Add a method to the registry which returns a service tracker for its associated interface. This has the following benefits: (a) it is a "robust" abstraction which still hides many details, (b) it can be implemented by a plugin.

Additionally, there may be good solutions not listed here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.