I have a multimodule project with two projects: Core and A. The idea is to launch/ run A whenever Core is launched.
How can I customize the ServiceLoader to look up and call the modules in the Plugins folder from core?
plugin-Project
+ Core
+ src\main\java
Core.java
+ A
+ src\main\java
A.java
+ Plugins
Core
public class Core extends Application {
private ServiceLoader<View> views;
private BorderPane mainBorderPane;
@Override
public void init() {
loadViews();
}
private void loadViews() {
views = ServiceLoader.load(View.class);
}
@Override
public void start(Stage stage) throws Exception {
stage.setTitle("Ui Application");
mainBorderPane = new BorderPane();
mainBorderPane.setTop(createMenuBar());
Scene scene = new Scene(new Group(), 800, 605);
scene.setRoot(mainBorderPane);
stage.setScene(scene);
stage.show();
}
private MenuBar createMenuBar() {
MenuBar menuBar = new MenuBar();
Menu viewMenu = new Menu("Views");
menuBar.getMenus().add(viewMenu);
ToggleGroup toggleGroup = new ToggleGroup();
views.forEach(v -> {
RadioMenuItem item = new RadioMenuItem(v.getName());
item.setToggleGroup(toggleGroup);
item.setOnAction(event -> {
Label label = new Label(v.getName());
mainBorderPane.setLeft(label);
mainBorderPane.setCenter(v.getView());
});
viewMenu.getItems().add(item);
});
return menuBar;
}
public static void main(String[] args) {
launch(args);
}
}
View.java
public interface View {
String getName();
Node getView();
}
Scenario
The application I'm working on is an multi-module stand-alone desktop application. For example, Core would hold a pane on the left (left-pane). left-pane will accept nodes
from any module that implements an interface called LeftPane
. A implements the LeftPane
interface. Whenever Core is launched, it should scan through a folder, plugins in this case and automatically start all the bundles there, including A, which would go on to populate the left pane.
The easiest way of course would be to have the plugins already on the classpath. Then you can simply access the interfaces through the
ServiceLoader
.Or you provide a mechanism, that will detect your plugin jar files at a specific location and adds them to the class path. This is the tricky part. One way to do this is by using a custom
ClassLoader
for your application that allows adding jar files to the classpath.I have chosen a different approach, that access the non public API of the
ClassLoader
that is in use for my application:When the plugin jar file is on the classpath you can access an exposed interface through the
ServiceLoader
. Let me illustrate this with an example. The interface that is exposed can look like this:The interface (which can also be a base class) is part of your core application. The plugin has a class that implements this interface.
Next you do the lookup for all implementations of that interface:
The
ServiceLoader
implementsIterable
, and therefore you can loop over all the implementations:Also take a look at Oracles documentation on this as well as this question