I tried to use the Java ServiceLoader to find all classes that implement a specific interface like so:
loader = ServiceLoader.load(Operation.class);
try {
for (Operation o : loader) {
operations.add(o);
}
} catch (ServiceConfigurationError e) {
LOGGER.log(Level.SEVERE, "Uncaught exception", e);
}
Unfortunately, when I run Eclipse in debug mode the ServiceLoader doesn't find any classes. I feel like I'm missing a trivial point...
ServiceLoader cannot do it.
In order to expose class as a service that can be discovered by ServiceLoader you need to put its name into provider configuration file, as described in Creating Extensible Applications With the Java Platform
.
There are no built-in ways find all classes that implement a particular interface. Frameworks that can do something similar use their own classpath scanning solutions (and even with custom classpath scanning it's not easy because .class files only store information about interfaces implemented directly, not transitively).
If the implementations are ones that you wrote yourself, you could use AutoService to make them available through the ServiceLoader interface, eg
#AutoService(Operation.class)
class Foo implements FooInterface {
}
#AutoService(Operation.class)
class Bar extends Foo {
}
In order to scan your classpath at runtime for implementations of specific interface you would need to use different solution eg. Reflections (notice s on the end, this is not java's Reflection API)
Related
Is that possible implement the same code but only enabled when adding a dependency to SpringBoot project?
If possible, how to achieve it?
I want to implement the code like this:
DoSomethingUtil doSomethingUtil = new DoSomethingUtil();
doSomethingUtil.send("API URL", "System A", "Hello");
It would do nothing when project didn't add the implement of the DoSomethingUtil.java.
After adding to pom.xml that which would implement the DoSomethingUtil.java, it would really do something.
Given that you don't need to know about DoSomethingUtil anywhere else in your code, you can run something on it only if it's present in your classpath (without importing it) if you use reflection all the way:
try {
Class<?> dsuClass = Class.forName("do.something.util.DoSomethingUtil");
Object dsuInstance = dsyClass.getConstructor().newInstance();
Method sendMethod = dsuClass.getDecaredMethod("send", String.class, String.class, String.class);
sendMethod.invoke(dsuInstance, "API URL", "System A", "Hello");
} catch (Exception ignored) {}
You may want to revisit the poor error handling above to distinguish (at least) between class not being present in the classpath and send() method invocation failure.
What you appear to be describing is adding a dependency, not "importing" something.
Will it work?
Sort of. What you could do is overlay the definition of the.pkg.DoSomethingUtil with another version of the.pkg.DoSomethingUtil in a different JAR file. It can work, but it makes your application sensitive to the order of the JARs on the runtime classpath. That makes your application fragile ... to say the least.
You can probably make this work with classic Java if you have full control of the runtime classpath. However:
I'm not sure if it will work with SpringBoot.
If you tried this sort of thing on Android, the APK builder would protest. It treats the scenario of two classes with the same full name as an error.
I think there is a better solution:
Refactor the code so that there is a DoSomethingUtil interface and two classes; e.g. RealDoSomethingUtil and DummyDoSomethingUtil.
Replace new DoSomethingUtil() with a call to a factory method.
Implement the factory method something like this:
private static Class<?> doSomethingClass;
public static synchronized DoSomethingUtil makeDoSomethingUtil() {
if (doSomethingClass == null) {
try {
doSomethingClass = Class.forName("the.pkg.RealDoSomethingUtil");
} catch (Exception ex) {
doSomethingClass = the.pkg.DummyDoSomethingUtil.class;
}
}
return (DoSomethingUtil) (doSomethingClass.newInstance());
}
Put RealDoSomethingUtil into the add-on JAR file, and DoSomethingUtil, RealDoSomethingUtil and the factory method into the main JAR file.
You should probably make the exception handling more selective so that it deals with different classloader errors differently. For example, if RealDoSomethingUtil exists but can't be loaded, you probably should log that ... or maybe let the exception crash the application.
You could also make use of ServiceLoader, but I don't know if it would be simpler ...
The java Service Provide API (SPI) is there to detect wether implementation(s) of an interface exists.
You have a jar with an interface DoSomethingUtil in your application.
Possibly on the class path an implementation jar (MyDoSomethingUtilImpl implements DoSomethingUtil), with an entry in META-INF/services.
You must check whether the interface is implemented.
One could make a fallback implementation.
Consider the following interface
// src/MyInterface.java
interface MyInterface {
public void quack();
}
which is used in the following application dynamically; i.e. its implementation is loaded dynamically—for demonstration purposes we'll just use the implementing class' name to determine which implementation to load.
// src/Main.java
class Main {
public static void main(String[] args) {
try {
MyInterface obj = (MyInterface) Class.forName("Implementation")
.getDeclaredConstructor()
.newInstance();
obj.quack();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
The following implementation of the interface is available:
// src/Implementation.java
class Implementation implements MyInterface {
public void quack() {
System.out.println("This is a sample implementation!");
}
}
As I would intuitively think, MyInterface provides information that is only relevant at compile-time, such as which methods can be invoked on objects that implement it, but it shouldn't be needed at runtime, since it doesn't provide any "executable code". But this is not the case: if I try to run the compiled Main.class without MyInterface.class, it complains:
$ javac -d bin/ src/*
$ rm bin/MyInterface.class
$ java -cp bin/ Main
Exception in thread "main" java.lang.NoClassDefFoundError: MyInterface
[...]
I guess it makes sense because it needs access to the MyInterface's Class object to perform the cast to MyInterface, so it needs to load MyInterface. But I feel there should be a way to make it a compile-time only dependency. How?
Some context
This question arose when I learned that there can be compile-time only dependencies, an example of which is the servlet api. I read that when compiling servlet code, you need to have the servlet-api (in Tomcat's case) jar, but at runtime it is not needed because the server provides an implementation. Since I didn't understand exactly how that could work, I tried setting up the little experiment above. Did I misunderstand what that means?
Edit: this Gradle page mentions that a compile-time only dependency could be
Dependencies whose API is required at compile time but whose implementation is to be provided by a consuming library, application or runtime environment.
What would be an example for that? I find that sentence a bit confusing, because it seems to imply that the API is not needed at runtime, and only the implementation is. From the answers, I gather that's not possible, right? (Unless somehow implementing a custom classloader?)
Yes, looks like you misunderstood example with servlet-api.jar. You need it in your project as a compile time dependency because Tomcat comes itself with that jar and that jar will be added to runtime classpath by Tomcat.
if you use classes/interfaces in your code they should be somehow added to classpath since your code depends on them.
And starting Java 8 interfaces can have default implementations for methods ("executable code") and interfaces also can have constants.
Maybe it is possible to run application without interface declaration but in that case you need to develop your custom Classloader which will check for interface implementation and load it instead of interface itself.
Did I misunderstand what that means?
Yes.
You're talking about "provided" dependencies (at least, that's what Maven calls them). Such a dependency still must be present on the classpath/modulepath at both compile-time and runtime. However, you don't have to include the provided dependency with your application when deploying your application, because the target container/framework already includes the dependency.
I am trying to figure out the ServiceLoader of Java.
I set up a VERY basic test-implementation:
public class BaseThingy {
public BaseThingy(){
Iterator<WriteService> iter = ServiceLoader.load(WriteService.class).iterator();
while (iter.hasNext()) {
WriteService plugin = iter.next();
System.out.print(plugin.getText());
}
}}
Interface:
public interface WriteService {
String getText();}
Now, as far as I understood things, I write an implementation, and put the implementing class (with no further files or manifest??) into a jar.
The project itself requires a file: META-INF\services\experimental.plugin.WriteService
In this file, I write the full name of the implementation (in my case. that would be experimental.plugin.WriteHello).
Now, I am working within Intellij as IDE.
Where should I put the file, and where should I put the jar with the implementing class?
I am not getting any errors, but neither is ANY implementation being found.
Or does the jar-file need anything additional after all?
The META-INF/services/experimental.plugin.WriteService must be in the classpath as well as the JAR with the implementation.
I have written this project and already use it in other libraries of mine.
However, I find something amiss. Namely, in each user of this library, I create a utility class whose only role is to provide one or more MessageBundles. And this sucks.
I'd like to have, built into the library, a mechanism in order to have library users be able to register/recall bundles.
My first idea would be to have a singleton factory with a .register() and .get() method (with appropriate checks for duplicate keys etc) and call these from within static initialization blocks...
... But there is a problem: there is no guarantee as to which static initialization block will be called first.
Knowing that I'd like to keep the dependencies of this library "intact" (which is to mean, no external dependency at all), what solution would you recommend?
(note: this is Java 6+)
You could use the standard support for service providers: ServiceLoader. You would simply require each user of your library to provide an implementation of some interface, for example
public interface MessageBundleProvider {
List<MessageBundle> getBundles();
}
The name of the class implementing this interface would have to be specified in a file of the jar file of the user library named META-INF/services/com.example.MessageBundleProvider.
At runtime, your library would automatically discover all the message bundle providers using the following code:
private static final ServiceLoader<MessageBundleProvider> LOADER
= ServiceLoader.load(MessageBundleProvider.class);
private static final List<MessageBundle> BUNDLES;
static {
BUNDLES = new ArrayList<MessageBundle>();
for (MessageBundleProvider provider : loader) {
for (MessageBundle bundle : provider.getBundles()) {
BUNDLES.add(bundle);
}
}
}
Disclaimer: I know that ServiceLoader exists, but I've never used it before. It's how all the standard Java service providers are discovered, though (like JDBC drivers, charset providers, etc.).
I'm attempting to implement a package-scanning feature, similar to Spring's component-scan, for the Android framework I'm developing. Basically, I would like to be able to specify a base package, e.g. com.foo.bar and retrieve all Class instances that have a particular annotation. I don't want to have to register every component with my framework as that would defeat the purpose of the auto scanning.
Based on my research, it seems that it's not possible with Java to retrieve resources given a package name using reflection. However, I briefly looked into the Reflections framework, and I'm wondering if there is an Android-compatible equivalent. If not, perhaps there is a slightly less obvious way to accomplish what I want to do.
I looked into the Spring source a bit to see how they achieved this, but I don't think what they are doing would work within the Dalvik runtime.
Update
Currently, the below code has been the best I can do to retrieve all classes that contain a specific annotation, but frankly it's a pretty poor solution. It makes some really unsafe assumptions about the ClassLoader plus it scans (and loads) all application classes.
public Set<Class<?>> getClassesWithAnnotation(Class<? extends Annotation> annotation) {
Set<Class<?>> classes = new HashSet<Class<?>>();
Field dexField = PathClassLoader.class.getDeclaredField("mDexs");
dexField.setAccessible(true);
PathClassLoader classLoader = (PathClassLoader) Thread.currentThread().getContextClassLoader();
DexFile[] dexs = (DexFile[]) dexField.get(classLoader);
for (DexFile dex : dexs) {
Enumeration<String> entries = dex.entries();
while (entries.hasMoreElements()) {
String entry = entries.nextElement();
Class<?> entryClass = dex.loadClass(entry, classLoader);
if (entryClass != null && entryClass.isAnnotationPresent(annotation)) {
classes.add(entryClass);
}
}
}
return classes;
}
I wanted to find all the subclass at runtime.
So I've been looking for android class scanning.
This is my final code from what I gathered in web.
You will get the idea.
public static void findSubClasses(Context context, Class parent) {
ApplicationInfo ai = context.getApplicationInfo();
String classPath = ai.sourceDir;
DexFile dex = null;
try {
dex = new DexFile(classPath);
Enumeration<String> apkClassNames = dex.entries();
while (apkClassNames.hasMoreElements()) {
String className = apkClassNames.nextElement();
try {
Class c = context.getClassLoader().loadClass(className);
if (parent.isAssignableFrom(c)) {
android.util.Log.i("nora", className);
}
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// android.util.Log.i("nora", className);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
dex.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
I share the opinion of Joop Eggen and find his approach a good one. In Android I try to avoid the usual web app features which lead to a long lasting application start. I do not use reflection or package scanning.
But if you want to .... if I understand it correctly you want to have an annotation for a class. Instead of using annotations you could also use marker interfaces (to just have more possibilites).
1) Look at
Annotation: Java custom annotation and dynamic loading
Has an implementation in the question which just answers your question.
Annotation: Scanning Java annotations at runtime
Interface: Find Java classes implementing an interface
Interface: Is something similar to ServiceLoader in Java 1.5?
Interface: How can I get a list of all the implementations of an interface programmatically in Java?
Interface: Since the approach is expensive, maybe the ServiceLoader is a compromise between execution time and comfort, since it loads only the classes given in the services file. On the other hand if only classes with a certain interface are in your package then the ServiceLoader isn't that faster.
2) AndroidAnnotations
I would prefer the way AndroidAnnotations work (maybe an integration in AndroidAnnotations is the preferable way): It automatically adds an extra compilation step that generates source code, using the standard Java Annotation Processing Tool. So instead of runtime scanning you execute code based on the annotations generated during compile time.
I think the Bean/EBean annotation could work for you (only single class): https://github.com/excilys/androidannotations/wiki/Enhance%20custom%20classes
A scan-feature is not available, see this thread
3) Writing your own annotation processor
See APT (Annotation Processing Tool). The idea would be to generate a static function which returns a list of classes which are annotated, so that no class scanning is needed.
A very good ressource is http://javadude.com/articles/annotations/index.html
Take a look at Vogar's ClassPathScanner. It uses it to find test cases on the class path.
EDIT:
I found this issue in the Android issue tracker. It appears that ClassLoader.getResource(String) is 'working as expected', in that it returns null. This is expected because the DalvikVM does not keep the resources around after compiling. There are workarounds listed in the issue, but there may be another way to access the classes you desire.
Use the PackageManager to get a hold of an instance of ApplicationInfo. ApplicationInfo has a public field called sourceDir which is the full path (a String) to the location of the source directory for that application. Create a File from this String, and you should be able to navigate to your package within the source directory. Once there, you can use the method from my original answer to find the classes you are looking for.
String applicationSourceDir =
getPackageManager().getApplicationInfo(androidPackageName, 0).sourceDir;
/EDIT
You should be able to use the ClassLoader.getResource(String) to get a URL to your specific package (the passed in String being the package name you are interested in delimited by path separators rather than periods). With this URL you can then call getFile(), from which you can create a Java File to the package folder. Call packageFile.listFiles() from there, and you have your classes/subpackages.
Be recursive with the subpackages, and with the classes find the Class object using the static Class.forName(String) method.
In your java build process incorporate the class path scanning, generating injection data/code. This could then be ported too to Dalvik. It is even more efficient that dynamic scanning.