I want to use Runtime Reflection with Scala annotations (could also be a Java annoations if necessary, but I would prefer to limit pure Java code)
I want to implement something like:
/**
print all methods that implement a specific annotation
*/
def getAllAnnotated(): Unit {...}
For example, if I have:
class Foo {
#printme
def foo(args: A): R
def oof(args: A): R
}
class Bar {
#printme
def bar(): Unit
}
The result of running getAllAnnotated() would be something like:
Foo.foo
Bar.bar
Note that I don't want to look in a specific class, but instead any available method
Try one of classpath scanners based on Java reflection (e.g. Reflections) + scala-reflect. Since we use Java reflection only to look for classes and scala-reflect to look for annotated methods, annotations can be written in Scala.
import org.reflections.Reflections
import org.reflections.scanners.SubTypesScanner
import org.reflections.util.{ClasspathHelper, ConfigurationBuilder}
import scala.annotation.StaticAnnotation
import scala.jdk.CollectionConverters._
import scala.reflect.runtime.currentMirror
import scala.reflect.runtime.universe._
class printme extends StaticAnnotation
val reflections = new Reflections(
(new ConfigurationBuilder)
.setUrls(ClasspathHelper.forPackage(""))
.setScanners(new SubTypesScanner(false))
)
def getAllAnnotated(): Unit =
reflections.getAllTypes.asScala
.flatMap(className =>
currentMirror.classSymbol(Class.forName(className))
.toType
.decls
.filter(symbol =>
symbol.isMethod && symbol.annotations.exists(_.tree.tpe =:= typeOf[printme])
)
.map(method => s"$className.${method.name}")
).foreach(println)
Alternatives to Reflections library are for example ClassGraph and Burningwave. If we replace scala-reflect with Java reflection then annotation will have to be written in Java because only annotations written in Java are visible at runtime with Java reflection.
In Java you can scan for the classes in the package using reflection Using Reflections to get all classes of the package and then recursively go inside all the classes to find the annotations.
Related
Lets say i have a method in some class in my application's package NetBeans project:
package wuzzle.woozle;
import org.contoso.frobber.grob.Whiztactular;
#Whiztactular
public void testFizBuzz() {
if (1 != 0)
throw new Exception("Whiztactular failed");
}
package frob;
import org.contoso.frobber.grob.Whiztactular;
#Whiztactular
public void testfrobFizBuzz() {
if (1 != 0)
throw new Exception("Whiztactular failed");
}
package grob;
import org.contoso.frobber.grob.Whiztactular;
#Whiztactular
public void testGrobZoom() {
if (1 != 0)
throw new Exception("Whiztactular failed");
}
package contoso.gurundy;
import org.contoso.frobber.grob.Whiztactular;
#Whiztactular
public void testDingbatWoozle() {
if (1 != 0)
throw new Exception("Whiztactular failed");
throw new Exception("Whiztactular failed");
}
I want to:
enumerate all classes/methods
find methods tagged with a specified #Annotation
construct the class
call the (parameterless) method
How can i do this in Java?
In .NET it's easy
Here's how you do it in .NET (in pseudo-Java):
//Find all methods in all classes tagged with #Test annotation,
//and add them to a list.
List<MethodInfo> whiztactularMethods = new ArrayList<>();
//Enumerate all assemblies in the current application domain
for (Assembly a : AppDomain.currentDomain.getAssemblies()) {
//Look at each type (i.e. class) in the assembly
for (Type t : a.getTypes()) {
//Look at all methods in the class.
for (MethodInfo m : t.getMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly)) {
//If the method has our #Whiztactular annotation defined: add it
if (m.IsDefined(typeof(org.contoso.frobber.grob.Whiztactular), true))
whiztactularMethods .add(m);
}
}
}
And now that we have a List of all methods with the #Whiztactular annotation, it's just a matter of calling them:
//Call every test method found above
for (MethodInfo m : whiztactularMethods) {
Object o = Activator.CreateInstance(m.DeclaringType); //Construct the test object
m.Invoke(o, null); //call the parameterless Whiztactular method
}
What is the JRE equivalent of the above?
In Delphi it's easy to
When a Delphi application starts, the initializer of each unit is called:
initialization
WhiztactularRunner.registerWhiztactularClass(TWuzzleWoozle);
So then i can have all my test code register itself.
But Java doesn't have .java file initialization; nor does it have static constructors.
The Journey
I want JUnit to run tests
↓
JUnit requires tests to be in a special separate project
↓
Use reflection to find the test methods
↓
Reflection requires you to know the name of the packages that all developers have put their tests in
↓
Use Reflections library
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create my own Test Annotation, and use reflections to find all methods that are tagged with it
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create my own TestCase annotation, and use reflections to find all classes that are tagged with it
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create my own TestCase interface, and use reflections to find all classes that implement it
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create my own TestCase class, and use reflections to find all classes that extend it
↓
Reflections requires you to know the name of the packages that all developers have put their tests in
↓
Create a static list, and use a static class constructor to register the class with the my TestRunner
↓
Java doesn't have static class constructors
↓
Create a static list, and use the package initializer to register the class with the my TestRunner
↓
Java doesn't have package initializers
↓
Create a static list, and use the events to listen for when a package is loaded, and then register the package with my static list
↓
Java doesn't have package load events
↓
Enumerate all packages
↓
Reflection has no way to enumerate all packages
↓
Ask the class loader that loaded my current class for any other classes it has loaded
↓
Class loader won't know about classes until someone has actually needed them, and you might not even be using the same class loader instance
↓
Enumerate all packages in the current class path ⇐ in progress
↓
Enumerate all jar files on the local PC, use a custom class loader to load each one, then get a list of all packages in each one ⇐ in progress
↓
Spent 4 days so far trying to solve this problem that was solvable in .NET with 5 lines of code, and in Delphi with 3 lines of code
↓
Investigate converting 409 jsp, and 498 java code files to ASP.net and C# ⇐ in progress
↓
Give up on having automated unit, functional, and integration tests ⇐ in progress
Research Effort
Get all methods with a particular annotation in a package (Question isn't about the current package. Accepted answer uses 3rd party library.)
Java seek a method with specific annotation and its annotation element (Question is about a specific class, rather than finding the classes)
How to find annotated methods in a given package?
How to run all methods with a given annotation?
getConstructor with no parameters
Call Methods at Runtime Using Java Reflection
JavaDocs - Invoking Methods
Default access modifier for a Java constructor
Can you find all classes in a package using reflection?
Get all methods with a particular annotation in a package (explains what a package is)
How to find annotated methods in a given package? (explains what a package is)
Additional research effort
java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory even though I have the right dependencies
Oracle: Handling Initialization Status With Event Handlers
how to register a java class if the static initializer isn't called till the class is referenced
Dynamic object registration in Java
getConstructor with no parameters
Load Jar dynamically and register class(es) in applicationContext at runtime
Is it possible to determine descendants solely through Java reflection API?
Call Methods at Runtime Using Java Reflection
JavaDocs - Invoking Methods
At runtime, find all classes in a Java application that extend a base class
Default access modifier for a Java constructor
Find Java classes implementing an interface
Finding all classes implementing a specific interface
How does JUnit find tests?
Book: Unit Testing in Java
2/28/1998: JUnit 1.0
JUnit Cookbook
How can I get a list of all the implementations of an interface programmatically in Java?
How can I get all Class files in a specific package in Java?
Class Loaders in Java
How can I enumerate all classes in a package and add them to a List?
Java Reflection - Get List of Packages
Getting the list of packages in a java project
Tool to convert java to c# code
Package Initialization in Java
How to write a package-level static initializer in Kotlin?
https://stackoverflow.com/questions/72795950/java-initialize-all-classes-in-package-without-knowing-names
https://github.com/classgraph/classgraph
What is an initialization block?
Package Initialization in Java
Java and .Net are fundamentally different. The reason this task is not supported in the JVM out of the box is because of JVM's intrinsic lazy class loading; the JVM is not fully aware of every possible class on the classpath and loading all of them would be extraordinarily intensive CPU and Memory wise.
Essentially: What you are asking for is not possible without extensive writing of code that can scan avaialable classes on the classpath without loading them and examine their contents. You could implement this yourself, but it will be quite intensive. If you do wish to go that route, the answer you mentioned How to find annotated methods in a given package? is likely the easiest method, but will be non-optimal.
All is not lost though: I think the easiest way to accomplish the task you're wanting is to use a library like the following: https://github.com/ronmamo/reflections
The Reflections library is able to scan the classpath without actually performing a classload and is quite quick. I use this lib personally for writing frameworks and scanning for developer extensions on startup.
Example:
// MethodsAnnotated with #GetMapping
Set<Method> resources = reflections.get(MethodsAnnotated.with(GetMapping.class).as(Method.class));
Use ClassGraph. Unlike Reflections, it's (exceptionally) actively maintained and works in many more scenarios (e.g. Reflections will keel over if you try to use it with modules).
try (ScanResult scanResult = new ClassGraph()
.enableAllInfo() // Scan classes, methods, fields, annotations
.scan()) { // Start the scan
for (ClassInfo clazz : scanResult.getClassesWithMethodAnnotation(annotation)) {
//Just your run-of-the-mill reflection from here
Class<?> loaded = clazz.loadClass(); //SEE THE NOTE
//Assumes the default constructor exists. Do what's appropariate if it doesn't.
Object instance = loaded.getConstructor().newInstance(...);
//Use the usual clazz.getMethods() and filter by annotation, or use ClassInfo to get closer
Method annotatedMethod = ...;
annotatedMethod.invoke(instance);
}
}
NOTE: ClassGraph tries its best to figure out what classloader to use when loading classes, but you can still end up in bizarre situations. So, if you know already the correct classloader to use, I'd recommned going with the regular Java reflection here instead, e.g. Class.forName(clazz.getName(), loader).
I have noticed i can call from eXistdb XQuery processor any Java classes, i need just to declare a namespace like the following:
declare namespace string="java:java.lang.String";
I've imported all the methods from the class String.
Now comes the question: is there a way to import my own classes? For example, if i made that class:
package example.model.A
class A1 {
public static example() { ... }
}
Is there a way to use the method example inside my xquery? Can i point the namespace to my personal class?
Thank you very much.
Given the following snippet:
Set<Class<? extends MyClass>> allClasses =
new Reflections("mypackage").getSubTypesOf(MyClass.class);
The library recursively search for all the classes extending MyClass in the given package and subpackages. I need to do this in the given package only, i.e., not in the subpackages. Is there a way to do so?
You can use the input filter (a Predicate), so that types in sub packages would not be scan:
new Reflections(new ConfigurationBuilder().
addUrls(ClasspathHelper.forPackage("my.pack")).
filterInputsBy(new FilterBuilder().
include("my\\.pack\\..*\\.class").
exclude("my\\.pack\\..*\\..*\\.class")));
And of course, you can always (post) filter the results, for example:
import static org.reflections.ReflectionUtils.*;
getAll(reflections.getSubTypesOf(A.class),
withPattern(".*my\\.pack\\.(?!.*\\..*).*")); //ouch...
This is brute force, but:
1) Get subclasses of MyClass. Put in a set.
2) Iterate over classes in the set; remove classes which belong to other packages (i.e. those for which _someClass.getPackage().equals(MyClass.class.getPackage()) is false)
I'm working on a project which has both scala and java code. I want to use a class written in scala in java code. Problem I'm having is that scala class has some self type dependencies. I don't know how to give them when creating new instance of that class from java.
trait Deps1 {
def dep1 = println("dep1")
}
trait Deps2 {
def dep2 = println("dep2")
}
class TestClass {
this: Deps1 with Deps2 =>
def test = {
dep1
dep2
}
}
In scala if I'm to create instance of TestClass I can use new TestClass with Deps1 with Deps2 but I don't know how to do that in java code.
I'm using scala 2.9.2. Can anyone help me on this?
If the traits are at all complicated, it's best to let Scala handle them. Write a stub in Scala that the Java can instantitate:
class TestWithDeps extends TestClass with Deps1 with Deps2
Scala traits are compiled to Java interfaces. So, in your Java code you are implementing the interface Deps1 for example. See this answer for example and details
For your code the the following Java code works:
class Test extends TestClass implements Deps1, Deps2{
public void dep1(){
Deps1$class.dep1(this);
}
public void dep2(){
Deps2$class.dep2(this);
}
public static void main(String []args){
Test test = new Test();
test.dep1();
test.dep2();
}
}
Running this gives:
dep1
dep2
Compile and run with the compiled Deps1, Deps2, and TestClass on the classpath and the scala-library jar like so javac -classpath .:scala-library.jar Test.java
You are asking on how to create anonymous classes in Java mixing traits. This is simply not possible because in Java you cannot mix a trait, you can implement an interface, which contains no code.
If you reason a little bit about the concept of Scala Trait and how it could be implemented on the top of JVM valid bytecote keeping the compatibility with Java, you figure it out by yourself (I am not saying this this the exact way Scala treats traits, but is very similar)
A java interface is created
When the trait is mixed into a class, at compile time the "concrete" functions are physically copied by the compiler into the class.
This is the trick that allows Scala trait to contain concrete functions, but of course you need the source file to be a Scala one. Remember furthermore that when you create anonymous classes like :
val a = new MyClass extends A with B
The scala compiler physically generates an anonymous class for that (and apply the necessary transformation to mix traits). So, if you need to create an instance of MyClass extends A with B from Java, as Rex suggested the best way is to create a stub in Scala, and instanciate that from Java. The stub will be compiled by Scalac, which will correctly handle trait.
I have downloaded a third party library and they have classes I need to refer to in the default package? How do I import these classes?
It's not possible directly with the compiler. Sun removed this capability. If something is in the default namespace, everything must be in the default namespace.
However, you can do it using the ClassLoader. Assuming the class is called Thirdparty, and it has a static method call doSomething(), you can execute it like this:
Class clazz = ClassLoader.getSystemClassLoader().loadClass("Thirdparty");
java.lang.reflect.Method method = clazz.getMethod("doSomething");
method.invoke(null);
This is tedious to say the least...
Long ago, sometime before Java 1.5, you used to be able to import Thirdparty; (a class from the unnamed/default namespace), but no longer. See this Java bug report. A bug report asking for a workaround to not being able to use classes from the default namespace suggests to use the JDK 1.3.1 compiler.
To avoid the tedious method.invoke() calls, I adapted the above solution:
Write an interface for the desired functionality in your desired my.package
package my.package;
public interface MyAdaptorInterface{
public void function1();
...
}
Write an adaptor in the default package:
public class MyAdaptor implements my.package.MyAdaptorInterface{
public void function1(){thirdparty.function1();}
...
}
Use ClassLoader/Typecast to access object from my.package
package my.package;
Class clazz = ClassLoader.getSystemClassLoader().loadClass("MyAdaptor");
MyAdaptorInterface myObj = (MyAdaptorInterface)clazz.newInstance();
myObj.function1();