With RESTEasy I've implemented a subclass of Application to provide a list of singleton resources. Is there a way to add another singleton dynamically later on? I've not found a way to do it from the API docs.
I have not tried this myself, but I found a blog post where this is described:
http://sarbarian.wordpress.com/2010/03/07/resteasy-and-osgi-perfect-match/
During deployment, RESTEasy puts it's registry in the servlet context. The idea suggstested in the blog, is that you fetch the registry from the servlet context, and then add your resource class.
Something like this:
import org.jboss.resteasy.spi.Registry;
Object resource = new MyService();
Registry registry = (Registry) context.getAttribute(Registry.class.getName());
registry.addSingletonResource(resource);
I have tried this myself, and although it was nerve-wracking, it works great. We've got an infrastructure where several independent projects (plug-ins) are imported and loaded by our main application, and we wanted to include access to them in our existing RestEasy API (and Swagger documentation).
We originally hard-coded the class-loading into our main Rest Application class, which required all of the plug-ins to be displayed at all times in our API. To avoid that and to re-instate the independence of those projects (all, some, or none may be included at any given time), we created dynamic methods that return the feature/target class itself, which is loaded during the bootstrap phase of the RestEasy framework. It worked like a charm.
Within our Rest Application class, we implemented it as follows:
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(mainAPIStuff.class);
classes.add(plugin1.class);
classes.add(plugin2.class);
classes.add(plugin3.class);
.
.
.
return classes;
}
That was the old way. The new way was to loop through all of our plugins and return a hashSet of the classes that the plugins provide:
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(mainAPIStuff.class);
classes.addAll(pluginsHelper.getClasses());
return classes;
}
The pluginsHelper has sundry shared plugin methods and administrative tools, like getClasses(), which does basically this:
public static Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
// Probe plugins for their Rest Class implementation
List plugins = getCurrentlyInstalledPluginsFromOurSystem();
for (PluginObject plugin : plugins) {
Class<?> clazz = plugin.getPluginRestClass();
if (clazz != null) {
classes.add(clazz);
}
}
return classes;
}
RestEasy builds the Rest targets dynamically when any target is called, so all of the plugin installation, startup, config has already completed and the system is in the correct state before it is called. This is very valuable to understand, since then you can do all of the RestEasy class manipulation and loading at call time.
I use Reateasy with Undertow in embedded environment, where I manually instantiate ResteasyDeployment, and register Resource class is really easy, see code snippet blow:
UndertowJaxrsServer undertowJaxrsServer = new UndertowJaxrsServer();
ResteasyDeployment resteasyDeployment = new ResteasyDeployment();
undertowJaxrsServer.start();
resteasyDeployment.start();
final DeploymentInfo undertowDeployment =
undertowJaxrsServer
.undertowDeployment(resteasyDeployment)
.setContextPath("/abc")
.setDeploymentName("TEST")
.setClassLoader(Thread.currentThread().getContextClassLoader());
undertowJaxrsServer.deploy(undertowDeployment);
resteasyDeployment.getRegistry().addSingletonResource(new ResourceApiImpl());
Related
I'm currently writing an application that requires to operate on different type of devices. My approach would be to make a "modular" application that can dynamically load different classes according to the device they need to operate on.
To make the application easily extensible, my goal is to assign a specific path to the additional modules (either .jar or .class files) leaving the core program as it is. This would be crucial when having different customers requiring different modules (without having to compile a different application for each of them).
These modules would implement a common interface, while the "core" application can use these methods defined on the interface and let the single implementations do the work. What's the best way to load them on demand? I was considering the use of URLClassLoader but i don't know if this approach is up-to-date according to new patterns and Java trends, as I would like to avoid a poorly designed application and deprecated techniques. What's an alternative best approach to make a modular and easily extensible application with JDK 9 (that can be extended just by adding module files to a folder) ?
Additionnaly to the ServicerLoader usage given by #SeverityOne, you can use the module-info.java to declare the different instanciation of the interface, using "uses"/"provides" keywords.
Then you use a module path instead of a classpath, it loads all the directory containing your modules, don't need to create a specific classLoader
The serviceLoader usage:
public static void main(String[] args) {
ServiceLoader<IGreeting> sl = ServiceLoader.load(IGreeting.class);
IGreeting greeting = sl.findFirst().orElseThrow(NullPointerException::new);
System.out.println( greeting.regular("world"));
}
In the users project:
module pl.tfij.java9modules.app {
exports pl.tfij.java9modules.app;
uses pl.tfij.java9modules.app.IGreeting;
}
In the provider project:
module pl.tfij.java9modules.greetings {
requires pl.tfij.java9modules.app;
provides pl.tfij.java9modules.app.IGreeting
with pl.tfij.java9modules.greetings.Greeting;
}
And finally the CLI usage
java --module-path mods --module pl.tfij.java9modules.app
Here is an example; Github example (Thanks for "tfij/" repository initial exemple)
Edit, I realized the repository already provides decoupling examples:
https://github.com/tfij/Java-9-modules---reducing-coupling-of-modules
It sounds like you might want to use the ServicerLoader interface, which has been available since Java 6. However, bear in mind that, if you want to use Spring dependency injection, this is probably not what you want.
There are two scenarios.
Implementation jar's are on classpath
In this scenario you can simply use ServiceLoader API (refer to #pdem answer)
Implementation jar's not on classpath
Lets Assume BankController is your interface and CoreController is your implementation.
If you want to load its implementation dynamically from dynamic path,c create a new module layer and load class.
Refer to the following piece of code:
private final BankController loadController(final BankConfig config) {
System.out.println("Loading bank with config : " + JSON.toJson(config));
try {
//Curent ModuleLayer is usually boot layer. but it can be different if you are using multiple layers
ModuleLayer currentModuleLayer = this.getClass().getModule().getLayer(); //ModuleLayer.boot();
final Set<Path> modulePathSet = Set.of(new File("path of implementation").toPath());
//ModuleFinder to find modules
final ModuleFinder moduleFinder = ModuleFinder.of(modulePathSet.toArray(new Path[0]));
//I really dont know why does it requires empty finder.
final ModuleFinder emptyFinder = ModuleFinder.of(new Path[0]);
//ModuleNames to be loaded
final Set<String> moduleNames = moduleFinder.findAll().stream().map(moduleRef -> moduleRef.descriptor().name()).collect(Collectors.toSet());
// Unless you want to use URLClassloader for tomcat like situation, use Current Class Loader
final ClassLoader loader = this.getClass().getClassLoader();
//Derive new configuration from current module layer configuration
final Configuration configuration = currentModuleLayer.configuration().resolveAndBind(moduleFinder, emptyFinder, moduleNames);
//New Module layer derived from current modulee layer
final ModuleLayer moduleLayer = currentModuleLayer.defineModulesWithOneLoader(configuration, loader);
//find module and load class Load class
final Class<?> controllerClass = moduleLayer.findModule("org.util.npci.coreconnect").get().getClassLoader().loadClass("org.util.npci.coreconnect.CoreController");
//create new instance of Implementation, in this case org.util.npci.coreconnect.CoreController implements org.util.npci.api.BankController
final BankController bankController = (BankController) controllerClass.getConstructors()[0].newInstance(config);
return bankController;
} catch (Exception e) {BootLogger.info(e);}
return null;
}
Reference : https://docs.oracle.com/javase/9/docs/api/java/lang/module/Configuration.html
So I've been making some kind of plugins API for a Java project (to load JAR files externally) and well, I wanted to be able to add any Guice module inside any plugin to my project's dependency graph.
What I did was have a PluginsModule and in the configure method scan for other modules in plugins and install them using Java's ServiceLoader.
I made a test plugin and made a module for it, I confirmed it did get installed. No problems at this point. The problems appear when I do anything inside that module, for example I bound some interface to an implementation in that plugin (just to clear this up, I did the same thing without the plugin and it worked so it's not a binding problem) and tried to inject it, configuration errors saying there was no implementation for that interface appear.
public enum StandardGuiceModuleScanningStrategy implements GuiceModuleScanningStrategy {
INSTANCE;
#Override
public Set<Module> scan(Path directory) throws IOException {
File directoryAsFile = directory.toFile();
File[] childrenFiles = directoryAsFile.listFiles();
if (!directoryAsFile.isDirectory()
|| childrenFiles == null
|| childrenFiles.length == 0) {
return Collections.emptySet();
}
Set<Module> modules = new HashSet<>();
for (File childrenFile : childrenFiles) {
ClassLoader directoryClassLoader = new URLClassLoader(
new URL[]{childrenFile.toURI().toURL()});
ServiceLoader<Module> moduleServiceLoader = ServiceLoader.load(
Module.class, directoryClassLoader);
moduleServiceLoader.forEach(modules::add);
}
return modules;
}
In that implementation of my GuiceModuleScanningStrategy, as I mentioned before, I did use ServiceLoader. Anyways, I also tried other stuff, like scanning the JAR file and checking for a Module, and seeing if it has a specific annotation.
All Guice Modules annotated with #GuiceModule, will be installed into a child Injector. All classes annotated with #AutoBind will be bound to all inherited interfaces. You can also name it, which would lead to a named binding and overwrite the interfaces, which should be used. And if you don't want to use all Features, just overwrite the StartupModule and bind only the Features you want or your own.
I have a project with multiple modules in Android Studio. A module may have a dependency on another module, for example:
Module PhoneApp -> Module FeatureOne -> Module Services
I've included my annotation processing in the root module but the android-apt annotation processing occurs only at the top most level (PhoneApp) so that it should theoretically have access to all the modules at compile time. However, what I'm seeing in the generated java file is only the classes annotated in PhoneApp and none from the other modules.
PhoneApp/build/generated/source/apt/debug/.../GeneratedClass.java
In the other modules, I am finding a generated file in the intermediates directory that contains only the annotated files from that module.
FeatureOne/build/intermediates/classes/debug/.../GeneratedClass.class
FeatureOne/build/intermediates/classes/debug/.../GeneratedClass.java
My goal is to have a single generated file in PhoneApp that allows me to access the annotated files from all modules. Not entirely sure why the code generation process is running for each and failing to aggregate all annotations at PhoneApp. Any help appreciated.
Code is fairly simple and straight forward so far, checkIsValid() omitted as it works correctly:
Annotation Processor:
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
try {
for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(GuiceModule.class)) {
if (checkIsValid(annotatedElement)) {
AnnotatedClass annotatedClass = new AnnotatedClass((TypeElement) annotatedElement);
if (!annotatedClasses.containsKey(annotatedClass.getSimpleTypeName())) {
annotatedClasses.put(annotatedClass.getSimpleTypeName(), annotatedClass);
}
}
}
if (roundEnv.processingOver()) {
generateCode();
}
} catch (ProcessingException e) {
error(e.getElement(), e.getMessage());
} catch (IOException e) {
error(null, e.getMessage());
}
return true;
}
private void generateCode() throws IOException {
PackageElement packageElement = elementUtils.getPackageElement(getClass().getPackage().getName());
String packageName = packageElement.isUnnamed() ? null : packageElement.getQualifiedName().toString();
ClassName moduleClass = ClassName.get("com.google.inject", "Module");
ClassName contextClass = ClassName.get("android.content", "Context");
TypeName arrayOfModules = ArrayTypeName.of(moduleClass);
MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("juice")
.addParameter(contextClass, "context")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(arrayOfModules);
methodBuilder.addStatement("$T<$T> collection = new $T<>()", List.class, moduleClass, ArrayList.class);
for (String key : annotatedClasses.keySet()) {
AnnotatedClass annotatedClass = annotatedClasses.get(key);
ClassName className = ClassName.get(annotatedClass.getElement().getEnclosingElement().toString(),
annotatedClass.getElement().getSimpleName().toString());
if (annotatedClass.isContextRequired()) {
methodBuilder.addStatement("collection.add(new $T(context))", className);
} else {
methodBuilder.addStatement("collection.add(new $T())", className);
}
}
methodBuilder.addStatement("return collection.toArray(new $T[collection.size()])", moduleClass);
TypeSpec classTypeSpec = TypeSpec.classBuilder("FreshlySqueezed")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(methodBuilder.build())
.build();
JavaFile.builder(packageName, classTypeSpec)
.build()
.writeTo(filer);
}
This is just for a demo of annotation processing that works with Guice, if anyone is curious.
So how can I get all the annotated classes to be included in the generated PhoneApp .java file from all modules?
It's never too late to answer a question on SO, so...
I have faced a very similar complication during one of tasks at work.
And I was able to resolve it.
Short version
All you need to know about generated classes from moduleB in moduleA is package and class name. That can be stored in some kind of MyClassesRegistrar generated class placed in known package. Use suffixes to avoid names clashing, get registrars by package. Instantiate them and use data from them.
Lond version
First of all - you will NOT be able to include your compile-time-only dependency ONLY at topmost module (lets call it "app" module as your typical android project structure does). Annotation processing just does not work that way and, as far as I could find out - nothing can be done about this.
Now to the details. My task was this:
I have human-written annotated classes. I'll name them "events". At compile time I need to generate helper-classes for those events to incorporate their structure and content (both statically-available (annotation values, consts, etc) and runtime available (I am passing event objects to those helpers when using latter). Helper class name depends on event class name with a suffix so I don't know it until code generation finished.
So after helpers are generated I create a factory and generate code to provide new helper instance based on MyEvent.class provided. Here's the problem: I only needed one factory in app module, but it should be able to provide helpers for events from library module - this can't be done straightforward.
What I did was:
skip generating factory for modules that my app module depends upon;
in non-app modules generate a so-called HelpersRegistrar implementation(s):
– they all share same package (you'll know why later);
– their names don't clash because of suffix (see below);
– differentiation between app module and library-module is done via javac "-Amylib.suffix=MyModuleName" param, that user MUST set - this is a limitation, but a minor one. No suffix must be specified for app module;
– HelpersRegistrar generated implementation can provide all I need for future factory code generating: event class name, helper class name, package (these two share package for package-visibility between helper and event) - all Strings, incorporated in POJO;
in app module I generate helpers - as usual, then I obtain HelperRegistrars by their package, instantiate them, run through their content to enrich my factory with code that provides helpers from other modules. All I needed for this was class names and a package.
Voilà! My factory can provide instances of helpers both from app module and from other modules.
The only uncertainty left is order of creating and running processor-class instances in app module and in other modules. I have not found any solid info on this, but running my example shows that compiler (and, therefore, code generation) first runs in module that we depend upon, and then - in app module (otherwise compilation of app module will be f..cked). This gives us reason to expect known order of code processor executions in different modules.
Another, slightly similar, approach is this: skip registrars, generate factories in all modules and write factory in app module to use other factories, that you get and name same way as registrars above.
Example can be seen here: https://github.com/techery/janet-analytics - this is a library where I applied this approach (the one without registrars since I have factories, but that can be not the case for you).
P. S.: suffix param can be switched to simpler "-Amylibraryname.library=true" and factories/registrars names can be autogenerated/incremented
Instead of using Filer to save generated file, use regular java file writing instead. You will need to serialize objects to temp files when processing because even static variables won't save in between modules. Configure gradle to delete the temp files before compilation.
I'm using Reflections to find classes that have an specific annotation. My project structure is the following
One WAR package:
WEB-INF/classes/...packages.../ClassAnnoted1.class
One JAR package that is included by the war that has a class that executes this code:
Reflections reflections= new Reflections(ClasspathHelper.forWebInfClasses(servletContext))
Set set= reflections.getTypesAnnotatedWith(CustomAnnotation.class)
CustomAnnotation is also present on the JAR package.
the set size is correct (ie if I have 3 classes with the annotation in my WAR the jar, the set size comes back as 3), but all elements inside it are null instead of Class. I need to get the class and check the annotation parameters inside the class of the JAR.
Anyone got any idea of why this is happening?
EDIT:
Reflections reflections= new Reflections("com.my.customAnnotededClasses"); //package that my annoted class is in
Set set= reflections.getTypesAnnotatedWith(CustomAnnotation.class);
Also does not work, in this case the set length is zero instead of the number of classes with the annotation.
EDIT 2:
Ok, the real problem was that I was packaging my whole application as an EAR so I had the following:
EAR
----> WAR
----> JAR
The jar was included in the EAR lib folder and not on the WAR lib folder. So the jar classes couldn't see the war classes, once i made the WAR depend on the JAR directly like this:
EAR
----> WAR
---------> JAR
It started working. But the original question still stands, there might be situations where I want the Jar classes included in the EAR instead of the WAR (if i have multiple wars that need to use my jar for instance).
I guess I can't do it using the reflections library. So I did it by hand:
public static List<Class<?>> getClassesAnnotatedWith(Class annotation, ServletContext servletContext) {
List<Class<?>> webClasses, jarClasses;
webClasses= getClassesAnnotedWithFromClassLoader(annotation, servletContext.getClassLoader());
jarClasses= getClassesAnnotedWithFromClassLoader(annotation, Thread.currentThread().getContextClassLoader());
for (Class<?> jarClass : jarClasses) {
Class<?> elementToAdd= null;
for (Class<?> webClass : webClasses) {
if ( ! jarClass.getName().equals(webClass.getName())) {
elementToAdd= jarClass;
}
}
if(elementToAdd != null) {
webClasses.add(elementToAdd);
}
}
return webClasses;
}
private static List<Class<?>> getClassesAnnotedWithFromClassLoader(Class annotation, ClassLoader classLoader) {
List<Class<?>> classes= new ArrayList<Class<?>>();
Class<?> classLoaderClass= classLoader.getClass();
while (! classLoaderClass.getName().equals("java.lang.ClassLoader")) {
classLoaderClass= classLoaderClass.getSuperclass();
}
try {
Field fldClasses= classLoaderClass.getDeclaredField("classes");
fldClasses.setAccessible(true);
Vector<Class<?>> classesVector= (Vector<Class<?>>) fldClasses.get(classLoader);
for (Class c : classesVector) {
if (c.isAnnotationPresent(annotation)) {
classes.add(c);
}
}
} catch (Exception e) { }
return classes;
}
I get the ClassLoader from my WAR package through the ServletContext object. There is also a protection in case a class is defined in both the WAR and the JAR with the annotation and same name (you should probably check if the packages are the same too though).
Note that you should probably never use this code in your own projects (maybe only for debugging). It involves reflecting the ClassLoader class to make the "classes" property public. This property might not exists in Java 9 for example, so beware. This might also have some security problems if you are interacting modules written by third parties.
i had one a similar problem. are you sure, you included the annotation-classes into your classpath? if they are not loaded, they will somehow be found but not really returned and without any exception or anything
The Reflections library gave me various problems. Now I am using the reflection part of the Guava library: until now, no unexpected behavior has occurred.
In any case, I think that it is very rare that the source of the problem is the Java classloader.
Maybe try to load the class CustomAnnotation.class before to use it in the Reflections API.
Your code should work on conventional environments.
However, in different environments, such as osgi, you get:
1) urls with different protocol (bundle/vfs/...)
2) different class loader.
In the first case, you should a) add the relevant UrlType (see the DefaultUrlTypes in Vfs for examples), or b) use different method to get the urls (see other methods in ClasspathHelper and examine the returned URL list)
In the second case, you should a) pass the customClassLoader to Reflections constructor or ConfigurationBuilder in order resolving will happen, or b) query the store directly reflections.getStore().get(TypeAnnotationsScanner.class)
see also #8339845, JbossIntegration
I have a java project I want to create which will be built on top of some vendor APIs. The APIs connect to servers running said vendor's software and perform various actions.
I have 2 different versions of the servers supported by 2 different API versions. Any changes to the API's are in internal implementation only. I.E. The classes, interfaces, methods, etc. available to me in the older version exist in the newer version. Therefore the code I write should compile and run with either API version. There is a version number in the API presented to the servers when using the API to connect that prevents you from using a different version API on that particular server.
Is there a way to switch JAR files on the fly at runtime? (something like a c/c++ DLL??)
If switching API versions at runtime isn't possible, what is the most elegant way to handle the problem. Build the code 2x (one for each api version)?
I hope I'm missing something but approach 2 doesn't seem ideal. Here's a more concrete example of why:
package org.myhypotheticalwrapper.analyzer;
import org.myhypothetical.worker;
import org.myhypothetical.comparator;
public class Analyzer {
Worker w1 = new Worker();
Worker w2 = new Worker();
Comparator c = new Comparator(w1.connectAndDoStuff(),w2.connectAndDoStuff());
c.generateReport();
}
This is my dilema. I want w1 to be built with the old API and w2 be built with the new API so they can connect to the appropriate servers. Other than the API's they sit on top of, they are the same (identical code). Do I have to create two uniquely named Class types for W1 and W2 even though their code is identical, simply to accommodate different API versions? It seems like that could get unwieldy fast, if I had many API versions that I wanted to interact with.
Any suggestions and comments greatly appreciated.
-new guy
The easiest is probably having a classloader loading in classes not in the default classpath.
From http://www.exampledepot.com/egs/java.lang/LoadClass.html
// Convert File to a URL
URL url = file.toURL(); // file:/c:/myclasses/
URL[] urls = new URL[]{url};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
// Load in the class; MyClass.class should be located in
// the directory file:/c:/myclasses/com/mycompany
Class cls = cl.loadClass("com.mycompany.MyClass");
You can't really change out a class file once it's been loaded, so there's really no way to replace a class at runtime. Note that projects like JavaRebel get around this with some clever use of instrumentation via the javaagent - but even what you can do with that is limited.
From the sounds of it you just need to have two parallel implementations in your environment at the same time, and don't need to reload classes at runtime. This can be accomplished pretty easily. Assume your runtime consists of the following files:
analyzer.jar - this contains the analyzer / test code from above
api.jar - this is the common forward-facing api code, e.g. interfaces
api-impl-v1.jar - this is the older version of the implementation
api-impl-v2.jar - this is the newer version of the implementation
Assume your worker interface code looks like this:
package com.example.api;
public interface Worker {
public Object connectAndDoStuff();
}
And that your implementations (both in v1 and v2) look like this:
package com.example.impl;
import com.example.api.Worker;
public class WorkerImpl implements Worker {
public Object connectAndDoStuff() {
// do stuff - this can be different in v1 and v2
}
}
Then you can write the analyzer like this:
package com.example.analyzer;
import com.example.api.Worker;
public class Analyzer {
// should narrow down exceptions as needed
public void analyze() throws Exception {
// change these paths as need be
File apiImplV1Jar = new File("api-impl-v1.jar");
File apiImplV2Jar = new File("api-impl-v2.jar");
ClassLoader apiImplV1Loader =
new URLClassLoader(new URL[] { apiImplV1Jar.toURL() });
ClassLoader apiImplV2Loader =
new URLClassLoader(new URL[] { apiImplV2Jar.toURL() });
Worker workerV1 =
(Worker) apiImplV1Loader.loadClass("com.example.impl.WorkerImpl")
.newInstance();
Worker workerV2 =
(Worker) apiImplV2Loader.loadClass("com.example.impl.WorkerImpl").
.newInstance();
Comparator c = new Comparator(workerV1.connectAndDoStuff(),
workerV2.connectAndDoStuff());
c.generateReport();
}
}
To run the analyzer you would then include analyzer.jar and api.jar in the classpath, but leave out the api-impl-v1.jar and api-impl-v2.jar.
You need to make sure that the classes are in different packages. You can't import two jar files with the same package listing and expect one to be recognized over the other. If they are in different packages you can use:
com.foo.Worker w1;
com.bar.Worker w2;
Your worker needs to have a delegate that implements an interface. You will have to write two delegates, one for the old api, one for the new. Choose which delegate to instantiate at runtime. Something like:
public class Worker {
private WorkerDelegate delegate;
public void foo() { delegate.foo(); }
public Object bar(){ return delegate.bar(); }
public Enum API{v1,v2};
public Worker(API api) {
try { switch(api){
case v1: delegate = Class.forName("my.v1.impl").newInstance(); break
case v2: delegate = Class.forName("my.v2.impl").newInstance(); break
}
}catch(...){throw new Error(e);}
}
}
More implementations can be added later with ease.