Java CodeModel: annotation with class as value - class not on classpath - java

I need to generate code and in this case annotations using suns CodeModel library. The annotations value is a Class object. However that class is not known at compile time. The solution I have now is:
JAnnotationUse oneToMany = field.annotate(OneToMany.class)
.param("targetEntity", Class.forName(className);
However this obviously requires that the according class is on the classpath. I want to avoid the user having to deal with such issues.
Of course the other option would be to generate the java source code file and manipulate it afterwards (as pure string) but that seems very messy.
Is there any way with CodeModel to use the plain string className + ".class" instead?

You can handle the case where the class it not in the classpath by catching the ClassNotFoundException:
try {
JAnnotationUse oneToMany = field.annotate(OneToMany.class)
.param("targetEntity", Class.forName(className);
} catch (ClassNotFoundException e) {
// handle the exception
}
The part where you handle the exception really depends on what you're trying to achieve, depending on the situation you might want to log the error/add another anotation/throw an exception/...

Ok. Here my solution. Use javaassist to generate the class and the according Class object at runtime. Since the end result will be
#OneToMany(targetEntity = com.foo.Bar.class)
as normal text in a java source code file, it does not matter that this generated "dummy class" is not actually equivalent to the real actual class that was referenced.
So we adjust the creation of the annotation as Guillaume indicated and catch the ClassNotfoundExeption. In the exception handler we call the generateDummyClass method that uses javaassist to create a Class object:
JAnnotationUse oneToMany = field.annotate(OneToMany.class);
//...snipped...
try {
oneToMany.param("targetEntity", Class.forName(jpaProperty.getTargetEntity()));
} catch (ClassNotFoundException ex) {
try {
Class targetEntityClass = generateDummyClass(jpaProperty.getTargetEntity());
oneToMany.param("targetEntity", targetEntityClass);
} catch (CannotCompileException compileEx) {
throw ex;
}
}
}
Method generateDummyClass that create a class object at runtime using javaassist:
private Class<?> generateDummyClass(String fullQualifedClassName)
throws IOException, CannotCompileException {
ClassPool pool = ClassPool.getDefault();
pool.insertClassPath(new ClassClassPath(this.getClass()));
CtClass ctClass = pool.makeClass(fullQualifedClassName);
Class<?> clazz = ctClass.toClass();
return clazz;
}
And we get a java source code file with:
#OneToMany(targetEntity = org.foo.Bar.class)
private Set<Bar> bars = new HashSet<Bar>();

Related

Dynamically loading method from external class

I am trying to load methods Customer.cypher and Customer.cypherCBC method from my class Configuration. Customer class is rendering from different environments so few environmets are having cypherCBC() and cypher() method and few are having only cypher() method.
Now i want to check if cypherCBC if not there in Customer class then load cypher() method. My function is so far;
try {
Class<?> customerClass = Class.forName("com.myapp.impl.service.Customer");
Object obj = customerClass.newInstance();
//here getting "NoSuchMethodException" exception
Method methodCBC = customerClass.getDeclaredMethod("cypherCBC", String.class); //line - 7
if(methodCBC.getName().equals("cypherCBC")){
methodCBC.invoke(obj, new String(dbshPass));
System.out.println("CYPHER_CBC: "
+ methodCBC.invoke(obj, new String(dbshPass)));
}else{
Method method = customerClass.getDeclaredMethod("cypher", String.class);
method.invoke(obj, new String(dbshPass));
System.out.println("CYPHER: " + method.invoke(obj, new String(dbshPass)));
}
}catch (Exception e){
e.printStackTrace();
}
Getting an error at line 7.
NoSuchMethodException:
com.myapp.impl.service.Customer.cypherCBC(java.lang.String)
that means for particular environment class Customer doesn't having cypherCBC() method, but ideally it should come in else part and execute cypher() method.
Class<?> client = null;
Object obj = null;
try{
client = Class.forName("com.myapp.impl.service.Client");
obj = client.newInstance();
}catch (InstantiationException ex) {
System.err.println("Not able to create Instance of Class");
} catch (IllegalAccessException ex) {
System.err.println("Not able to access Class");
} catch (ClassNotFoundException ex) {
System.err.println("Not able to find Class");
}
try {
Method methodCBC = client.getDeclaredMethod("cypherCBC", String.class);
System.out.println("CYPHER_CBC: " + methodCBC.invoke(obj, new String(dbshPass)));
}catch (NoSuchMethodException ex) {
System.err.println("Not able to find Method on class");
ex.printStackTrace();
} catch (Exception e){
e.printStackTrace();
}
That is exactly what is to be expected: getDeclaredMethod() throws that exception when no method exists that meets your specification. And you are wondering that it throws an exception if the required method is missing? Hint: better read the javadoc next time. Don't assume that something does something, but verify your assumptions!
Besides: read your code again. What is it doing? You are asking "give me the method named 'foo'". And then, your next step is to ask that method "is your name 'foo'". So even without reading javadoc, it should become clear that your logic is flawed.
As solution, you can implement a non-throwing lookup yourself, like
private Method lookupCypher(Class<?> client, String methodName) {
for (Method declaredMethod : client.getDeclardMethods()) {
if (declaredMethod.getName().equals(methodName)) {
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
if (parameterTypes.length == 1 && parameterTypes[0].equals(String.class)) {
// so declaredMethod has the given name, and takes one string as argument!
return declaredMethod;
}
}
// our search didn't reveal any matching method!
return null;
}
Using that helper method, you can rewrite your code to:
Method toInvoke = lookupCypher(client, "cypherCBC");
if (toInvoke == null) {
toInvoke = lookupCypher(client, "cypher");
}
toInvoke(obj, new String ...
Or, with the idea from hunter in mind; a much more "OO like" version:
interface CustomerCypherWrapper {
void cypher(String phrase);
}
class NewCustomerWrapper() implements CustomerCypherWrapper {
#Override
void cypher(String phrase) {
new Customer.cypherCBC(phrase);
}
}
class oldCustomerWrapper() implements CustomerCypherWrapper {
#Override
void cypher(String phrase) {
new Customer.cypher(phrase);
}
}
And your client code boils down to:
CustomerCypherWrapper wrapper =
(lookupCypher(..., "cypherCBC") == null)
? new NewCustomerWrapper()
: new OldCustomerWrapper();
wrapper.cypher();
[ I hope you notice that my version A) is easier to read and B) doesn't contain any duplicated code any more. ]
And yes, an alternative implementation of the lookup method could just go like
private Method lookupCyper(Client<?>, String methodName) {
try {
return client.getDeclaredMethod(methodName, String.class);
} catch ....
and return null;
}
... return your public cypherCBC method
But that is an "uncommon practice" in Java. In Java, we ask for permission; instead of forgiveness. Unlike other languages
if you compile the application with a Customer class which has both method,you can use reflection once to check whether the cypherCBC method available or not at runtime, then you can keep that status, you can call the method without using reflection
if(newVersion)
{
customer.cypherCBC(arg);
}
else
{
customer.cypher(arg);
}
But to write a better application,you should use two version baselines.
even though this is a small code fragment you should setup a another module to hide this Customer class and its interactions,that module should have two versions. but your main module has only single version.Now when you you deliver the application , product should be packaged with right version baseline based on compatibility for the target environment.
Although reflection works (as explained in the other answers). if you have control over the Customer class, you can try a non-reflection approach.
interface CBCCypherable {
public String cypherCBC(String pass);
}
You can now have two versions of Customer class, one that implements CBCCypherable and one that doesn't. And when you call it, it looks like this:
Customer c = new Customer();
if (c instanceof CBCCypherable) {
((CBCCypherable)c).cypherCBC(pass);
} else {
c.cypher(pass);
}
What you get with this solution is much simpler code, and as a bonus the compiler will check that you use the correct method name and parameter types. Unlike with reflection, where that's all your job, and you have to run the code to find out if something's wrong.
P.s.: I don't know if this is just sample code or you are really encrypting/hashing passwords here, but it's generally considered a bad idea to roll your own security code.

Extending an Enum by copying

I wish to "extend" enum XResult in enum XStatus, by copying the values of XResult into XStatus.
Here is how I'm doing it. If item is not found in XResult, description is left null:
private final String description;
private final Whatever funStuff;
private XStatus(){
String d=null;
Whatever f=null;
try{
XResult xResult = XResult.valueOf(this.name());
d = XResult.toString();
f = XResult.getWhatever();
}
catch (Exception e){
}
this.description = d;
this.funStuff = f;
}
The issue is that if XResult does not contain such an item, it would throw IllegalArgumentException.
Question:
Is this the best way to copy values of one enum into another? The reason for the question is, I am deeply troubled with having the expense of try-catch to define the enum.
Is the try-catch worthwhile for its expense, if indeed this is the only way.
What are my alternatives, besides unreliable manual copying (which does not track changes in XResult)?
Rest of the code:
For the curious, this is the rest of the code that is inconsequential to the question:
private XStatus(final String d){
this.description = d;
}
public String toString(){
if (description==null || description.length()==0)
return doSomethingTo( this.name() );
return description;
}
public getWhatever(){ /*similar to toString */ }
Well, enums are in principle just Java objects so you could use reflection to test if XResult contains the desired value or even add new values at runtime to your enum class.
Just for reference, and in case someone suggests this as answer, my initial way was to do it this way:
private final String description;
private final Whatever funStuff;
private XStatus(XResult xResult){
this.description = xResult.toString();
this.funStuff = xResult.getWhatever();
}
Which means, instead of declaring
enum XStatus {
//from XResult
NOT_FOUND,
DELETED,
PROCESSED,
TBD,
blah, blah ...
}
I would have to declare
enum XStatus {
//from XResult
NOT_FOUND(XResult.NOT_FOUND),
DELETED(XResult.DELETED),
PROCESSED(XResult.PROCESSED),
TBD(XResult.TBD),
blah, blah ...
}
A further technique which might solve your problem to dynamically change the XResult class on loading it to your application is to simply manipulate its content at load-time using a byte manipulation framework like f.e. Javassist.
This requires however that your application hasn't loaded the enum before and you run your own classloader which will load the enum (and all the classes that access the enum) in that classloader (or in a child classloader of the classloader that loaded the bytes of the enum you have modified).
On using javassist you could do something like that (haven't done it actually with enums yet)
public byte[] modifyClass(String className)
{
try
{
// load the bytes from the .class file that actually contains the original definition of the XResult enum
byte[] xResultBytes = ...
// define a classpool javassist will use to find the definition for classes
ClassPool cp = ClassPool.getDefault();
// add a new search path for class definitions to the class path
cp = cp.insertClassPath(new ClassClassPath(this.getClass()));
// add the jar file containing java classes needed to the classpath
cp = cp.insertClassPath(jarFile.getAbsolutePath());
// as you do not have loaded XResult with any classloader yet you do not have a Class file of it
// so you need to provide the loaded bytes and the fully-qualified class name of the enum
cp = cp.appendClassPath(new ByteArrayClassPath(className, xResultBytes));
// now you are good to go with modifying the class
// first create a javassist classrepresentation of the bytes loaded
CtClass cc = cp.get(className);
// you can't modify frozen classes
if (!cc.isFrozen())
{
// you only want to instrument the XResult.class
if (className.endsWith("XResult")) // better replace it with the full name
{
// find, add, remove or rename annotations, fields and methods to your liking here
}
}
return cc.toBytecode();
}
catch (Exception e)
{
// handle error
}
}
In a custom classloader you can override findClass to actually define the modified bytes as XResult instead of the original bytes:
#Override
protected Class<?> findClass(String className) throws ClassNotFoundException
{
// instead of using the original bytes use the modified byte of the class
byte[] classBytes = modifyClass(className);
if (classBytes != null)
{
// this will actually create the Class<XResult> enum representation
return defineClass(className, classBytes, 0, classBytes.length);
}
throw new ClassNotFoundException(...);
}
Instead of a custom classloader you could also use the loader javassist provides:
ClassPool pool = ClassPool.getDefault();
Loader cl = new Loader(pool);
CtClass ct = pool.get("test.Rectangle");
ct.setSuperclass(pool.get("test.Point"));
Class c = cl.loadClass("test.Rectangle");
Object rect = c.newInstance();
Probably you should decompile an enum class and see what an enum class actually contains and what part is you want to get rid of. F.e if the thrown exception does bother you you could simply remove the method and replace it with one that don't throws the exception but returns simply null.
Have a look at the very explanatory tutorials javassist have:
Reading and writing bytecode, ClassPool, ClassLoader
Introspection and customization
Bytecode level API, Generics, Varargs, J2ME
This is how to do it without valueOf in generally O(1):
public enum XStatus {
;
private XStatus() {
XResult xr = Helper.NAMES.get(this.name()); // do whatever with
}
static {
Helper.NAMES = null; // get rid of the map
} // after the constants are instantiated
private static class Helper {
static Map<String, XResult> NAMES = new HashMap<String, XResult>();
static {
for(XResult xr : XResult.values()) {
NAMES.put(xr.name(), xr);
}
}
}
}
Note that this is basically the same as valueOf but you don't have to try-catch. Of course valueOf only throws RuntimeExceptions so you only have to catch if you have constants that do not parallel XResult.
Also, just throwing this out there, a more automatic solution would be something like this:
public final class XStatus {
private XStatus(XResult xr) {
//
}
private static final XStatus[] TABLE; // or an EnumMap
// with Collections.unmodifiableMap
static {
XResult[] xrValues = XResult.values();
TABLE = new XStatus[xrValues.length];
for(XResult xr : xrValues) {
TABLE[xr.ordinal()] = new XStatus(xr);
}
}
public static XStatus xStatusFor(XResult xr) {
return TABLE[xr.ordinal()];
}
}
That will mirror XResult 1:1 without relying on any kind of String evaluation. You need an XResult to retrieve its corresponding constant but that's the point. Also if for some reason XResult were to change outside of your control you don't need to change XStatus.

Check if class exists without running into ClassNotFoundException

In order to schedule the execution of a job, i get the name of a class as a string-input.
This class may be in one of two packages, but i don't know which one so i have to check this.
By now, i have two try-catch-blocks
Class<Job> clazz;
String className; //the above mentioned string, will be initialized
try {
clazz = (Class<Job>) Class.forName("package.one." + className);
} catch (ClassNotFoundException ex) {
try {
clazz = (Class<Job>) Class.forName("package.two." + className);
} catch (ClassNotFoundException ex1) {
//some error handling code here, as the class is
//in neither of the two packages
}
}
For more packages this will get uglier and more unreadable. Furthermore, it is - for me - against the concept of exceptions, as exceptions should'nt be expected/used for flow-control!
Is there any way to rewrite this without the utilization of the ClassNotFoundException?
I'd stick to the Class.forName method for that.
You can store the class and package names in Collections or Sets and loop through those elements. When you get a ClassNotFoundException, you just continue your search. If you don't get an exception, you exit the loop using break, as you have found the class you were looking for.
The reason I'd go for Class.forName is that it loads the class for you if it had not been already loaded by the VM. This is quite a powerful side effect.
It basically relieves you of the trouble of digging through the whole CLASSPATH and looking for class files to load in the VM and dealing with issues such as whether the class has already been loaded or not by the VM.
EDIT:
Jakob Jenkov has written some really great articles/tutorials on Java. I've found them extremely useful when dealing with reflection, class loaders and concurrency (some of the "hardest" aspects of Java).
Here's a good article on the Java class loader by him, for if you still decide not to use Class.forName.
public Class<Job> getClass(String className) {
String packages[] = { "package.one.", "package.two." };
for (int j = 0; j < packages.length; j++) {
try {
return (Class<Job>) Class.forName(packages[j] + className);
} catch (ClassNotFoundException ex) {
System.out.println("Package "+packages[j]+" is not worked");
}
}
return null;
}
You can use Guava's Reflection utilities to get the ClassInfo of every class loaded in the classpath.
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
ClassPath classPath = ClassPath.from(classLoader);
ImmutableSet<ClassInfo> set = classPath.getTopLevelClasses();
for (ClassInfo ci : set) {
System.out.println(ci.getName());
}
In the loop you can implement your custom logic to load the class with the className you're providing.
In this case, I wouldn't worry about using the ClassNotFoundException. While in general a case can be made to not use exceptions for flow control, here it hardly counts as such.
I'd probably wrap it in a function, like so
public static String getPackageForClass(String className, String... packageNames) {
for (String packageName : packageNames) {
try {
Class.forName(packageName + className);
return packageName;
} catch (ClassNotFoundException ignored) {
}
}
return "";
}
or return the class directly, if you so wish
public static Class getPackageForClass(String className, String... packageNames) {
for (String packageName : packageNames) {
try {
return Class.forName(packageName + className);
} catch (ClassNotFoundException ignored) {
}
}
return null;
}

Java - How to find all direct subclasses of a class with reflection? [duplicate]

How does one go about and try to find all subclasses of a given class (or all implementors of a given interface) in Java?
As of now, I have a method to do this, but I find it quite inefficient (to say the least).
The method is:
Get a list of all class names that exist on the class path
Load each class and test to see if it is a subclass or implementor of the desired class or interface
In Eclipse, there is a nice feature called the Type Hierarchy that manages to show this quite efficiently.
How does one go about and do it programmatically?
Scanning for classes is not easy with pure Java.
The spring framework offers a class called ClassPathScanningCandidateComponentProvider that can do what you need. The following example would find all subclasses of MyClass in the package org.example.package
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AssignableTypeFilter(MyClass.class));
// scan in org.example.package
Set<BeanDefinition> components = provider.findCandidateComponents("org/example/package");
for (BeanDefinition component : components)
{
Class cls = Class.forName(component.getBeanClassName());
// use class cls found
}
This method has the additional benefit of using a bytecode analyzer to find the candidates which means it will not load all classes it scans.
There is no other way to do it other than what you described. Think about it - how can anyone know what classes extend ClassX without scanning each class on the classpath?
Eclipse can only tell you about the super and subclasses in what seems to be an "efficient" amount of time because it already has all of the type data loaded at the point where you press the "Display in Type Hierarchy" button (since it is constantly compiling your classes, knows about everything on the classpath, etc).
This is not possible to do using only the built-in Java Reflections API.
A project exists that does the necessary scanning and indexing of your classpath so you can get access this information...
Reflections
A Java runtime metadata analysis, in the spirit of Scannotations
Reflections scans your classpath, indexes the metadata, allows you to query it on runtime and may save and collect that information for many modules within your project.
Using Reflections you can query your metadata for:
get all subtypes of some type
get all types annotated with some annotation
get all types annotated with some annotation, including annotation parameters matching
get all methods annotated with some
(disclaimer: I have not used it, but the project's description seems to be an exact fit for your needs.)
Try ClassGraph. (Disclaimer, I am the author). ClassGraph supports scanning for subclasses of a given class, either at runtime or at build time, but also much more. ClassGraph can build an abstract representation of the entire class graph (all classes, annotations, methods, method parameters, and fields) in memory, for all classes on the classpath, or for classes in selected packages, and you can query this class graph however you want. ClassGraph supports more classpath specification mechanisms and classloaders than any other scanner, and also works seamlessly with the new JPMS module system, so if you base your code on ClassGraph, your code will be maximally portable. See the API here.
Don't forget that the generated Javadoc for a class will include a list of known subclasses (and for interfaces, known implementing classes).
I know I'm a few years late to this party, but I came across this question trying to solve the same problem. You can use Eclipse's internal searching programatically, if you're writing an Eclipse Plugin (and thus take advantage of their caching, etc), to find classes which implement an interface. Here's my (very rough) first cut:
protected void listImplementingClasses( String iface ) throws CoreException
{
final IJavaProject project = <get your project here>;
try
{
final IType ifaceType = project.findType( iface );
final SearchPattern ifacePattern = SearchPattern.createPattern( ifaceType, IJavaSearchConstants.IMPLEMENTORS );
final IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
final SearchEngine searchEngine = new SearchEngine();
final LinkedList<SearchMatch> results = new LinkedList<SearchMatch>();
searchEngine.search( ifacePattern,
new SearchParticipant[]{ SearchEngine.getDefaultSearchParticipant() }, scope, new SearchRequestor() {
#Override
public void acceptSearchMatch( SearchMatch match ) throws CoreException
{
results.add( match );
}
}, new IProgressMonitor() {
#Override
public void beginTask( String name, int totalWork )
{
}
#Override
public void done()
{
System.out.println( results );
}
#Override
public void internalWorked( double work )
{
}
#Override
public boolean isCanceled()
{
return false;
}
#Override
public void setCanceled( boolean value )
{
}
#Override
public void setTaskName( String name )
{
}
#Override
public void subTask( String name )
{
}
#Override
public void worked( int work )
{
}
});
} catch( JavaModelException e )
{
e.printStackTrace();
}
}
The first problem I see so far is that I'm only catching classes which directly implement the interface, not all their subclasses - but a little recursion never hurt anyone.
I did this several years ago. The most reliable way to do this (i.e. with official Java APIs and no external dependencies) is to write a custom doclet to produce a list that can be read at runtime.
You can run it from the command line like this:
javadoc -d build -doclet com.example.ObjectListDoclet -sourcepath java/src -subpackages com.example
or run it from ant like this:
<javadoc sourcepath="${src}" packagenames="*" >
<doclet name="com.example.ObjectListDoclet" path="${build}"/>
</javadoc>
Here's the basic code:
public final class ObjectListDoclet {
public static final String TOP_CLASS_NAME = "com.example.MyClass";
/** Doclet entry point. */
public static boolean start(RootDoc root) throws Exception {
try {
ClassDoc topClassDoc = root.classNamed(TOP_CLASS_NAME);
for (ClassDoc classDoc : root.classes()) {
if (classDoc.subclassOf(topClassDoc)) {
System.out.println(classDoc);
}
}
return true;
}
catch (Exception ex) {
ex.printStackTrace();
return false;
}
}
}
For simplicity, I've removed command line argument parsing and I'm writing to System.out rather than a file.
Keeping in mind the limitations mentioned in the other answers, you can also use openpojo's PojoClassFactory (available on Maven) in the following manner:
for(PojoClass pojoClass : PojoClassFactory.enumerateClassesByExtendingType(packageRoot, Superclass.class, null)) {
System.out.println(pojoClass.getClazz());
}
Where packageRoot is the root String of the packages you wish to search in (e.g. "com.mycompany" or even just "com"), and Superclass is your supertype (this works on interfaces as well).
Depending on your particular requirements, in some cases Java's service loader mechanism might achieve what you're after.
In short, it allows developers to explicitly declare that a class subclasses some other class (or implements some interface) by listing it in a file in the JAR/WAR file's META-INF/services directory. It can then be discovered using the java.util.ServiceLoader class which, when given a Class object, will generate instances of all the declared subclasses of that class (or, if the Class represents an interface, all the classes implementing that interface).
The main advantage of this approach is that there is no need to manually scan the entire classpath for subclasses - all the discovery logic is contained within the ServiceLoader class, and it only loads the classes explicitly declared in the META-INF/services directory (not every class on the classpath).
There are, however, some disadvantages:
It won't find all subclasses, only those that are explicitly declared. As such, if you need to truly find all subclasses, this approach may be insufficient.
It requires the developer to explicitly declare the class under the META-INF/services directory. This is an additional burden on the developer, and can be error-prone.
The ServiceLoader.iterator() generates subclass instances, not their Class objects. This causes two issues:
You don't get any say on how the subclasses are constructed - the no-arg constructor is used to create the instances.
As such, the subclasses must have a default constructor, or must explicity declare a no-arg constructor.
Apparently Java 9 will be addressing some of these shortcomings (in particular, the ones regarding instantiation of subclasses).
An Example
Suppose you're interested in finding classes that implement an interface com.example.Example:
package com.example;
public interface Example {
public String getStr();
}
The class com.example.ExampleImpl implements that interface:
package com.example;
public class ExampleImpl implements Example {
public String getStr() {
return "ExampleImpl's string.";
}
}
You would declare the class ExampleImpl is an implementation of Example by creating a file META-INF/services/com.example.Example containing the text com.example.ExampleImpl.
Then, you could obtain an instance of each implementation of Example (including an instance of ExampleImpl) as follows:
ServiceLoader<Example> loader = ServiceLoader.load(Example.class)
for (Example example : loader) {
System.out.println(example.getStr());
}
// Prints "ExampleImpl's string.", plus whatever is returned
// by other declared implementations of com.example.Example.
It should be noted as well that this will of course only find all those subclasses that exist on your current classpath. Presumably this is OK for what you are currently looking at, and chances are you did consider this, but if you have at any point released a non-final class into the wild (for varying levels of "wild") then it is entirely feasible that someone else has written their own subclass that you will not know about.
Thus if you happened to be wanting to see all subclasses because you want to make a change and are going to see how it affects subclasses' behaviour - then bear in mind the subclasses that you can't see. Ideally all of your non-private methods, and the class itself should be well-documented; make changes according to this documentation without changing the semantics of methods/non-private fields and your changes should be backwards-compatible, for any subclass that followed your definition of the superclass at least.
The reason you see a difference between your implementation and Eclipse is because you scan each time, while Eclipse (and other tools) scan only once (during project load most of the times) and create an index. Next time you ask for the data it doesn't scan again, but look at the index.
I'm using a reflection lib, which scans your classpath for all subclasses: https://github.com/ronmamo/reflections
This is how it would be done:
Reflections reflections = new Reflections("my.project");
Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
You can use org.reflections library and then, create an object of Reflections class. Using this object, you can get list of all subclasses of given class.
https://www.javadoc.io/doc/org.reflections/reflections/0.9.10/org/reflections/Reflections.html
Reflections reflections = new Reflections("my.project.prefix");
System.out.println(reflections.getSubTypesOf(A.class)));
Add them to a static map inside (this.getClass().getName()) the parent classes constructor (or create a default one) but this will get updated in runtime. If lazy initialization is an option you can try this approach.
I just write a simple demo to use the org.reflections.Reflections to get subclasses of abstract class:
https://github.com/xmeng1/ReflectionsDemo
I needed to do this as a test case, to see if new classes had been added to the code. This is what I did
final static File rootFolder = new File(SuperClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
private static ArrayList<String> files = new ArrayList<String>();
listFilesForFolder(rootFolder);
#Test(timeout = 1000)
public void testNumberOfSubclasses(){
ArrayList<String> listSubclasses = new ArrayList<>(files);
listSubclasses.removeIf(s -> !s.contains("Superclass.class"));
for(String subclass : listSubclasses){
System.out.println(subclass);
}
assertTrue("You did not create a new subclass!", listSubclasses.size() >1);
}
public static void listFilesForFolder(final File folder) {
for (final File fileEntry : folder.listFiles()) {
if (fileEntry.isDirectory()) {
listFilesForFolder(fileEntry);
} else {
files.add(fileEntry.getName().toString());
}
}
}
If you intend to load all subclassess of given class which are in the same package, you can do so:
public static List<Class> loadAllSubClasses(Class pClazz) throws IOException, ClassNotFoundException {
ClassLoader classLoader = pClazz.getClassLoader();
assert classLoader != null;
String packageName = pClazz.getPackage().getName();
String dirPath = packageName.replace(".", "/");
Enumeration<URL> srcList = classLoader.getResources(dirPath);
List<Class> subClassList = new ArrayList<>();
while (srcList.hasMoreElements()) {
File dirFile = new File(srcList.nextElement().getFile());
File[] files = dirFile.listFiles();
if (files != null) {
for (File file : files) {
String subClassName = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
if (! subClassName.equals(pClazz.getName())) {
subClassList.add(Class.forName(subClassName));
}
}
}
}
return subClassList;
}
find all classes in classpath
public static List<String> getClasses() {
URLClassLoader urlClassLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
List<String> classes = new ArrayList<>();
for (URL url : urlClassLoader.getURLs()) {
try {
if (url.toURI().getScheme().equals("file")) {
File file = new File(url.toURI());
if (file.exists()) {
try {
if (file.isDirectory()) {
for (File listFile : FileUtils.listFiles(file, new String[]{"class"}, true)) {
String classFile = listFile.getAbsolutePath().replace(file.getAbsolutePath(), "").replace(".class", "");
if (classFile.startsWith(File.separator)) {
classFile = classFile.substring(1);
}
classes.add(classFile.replace(File.separator, "."));
}
} else {
JarFile jarFile = new JarFile(file);
if (url.getFile().endsWith(".jar")) {
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
if (jarEntry.getName().endsWith(".class")) {
classes.add(jarEntry.getName().replace(".class", "").replace("/", "."));
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
return classes;
}
enter link description hereService Manager in java will get all implementing classes for an interface in J

Get Class by part of name in Java

Okay, so I am trying to do my network packet handling in Java using classes. For reading data from my stream I use a DataInputStream. My reading thread for my server looks like this:
public void run()
{
while(client.isActive())
{
try{
handle(is.readShort());
}catch (IOException e){
client.stop(e);
break;
}
}
}
Now I've got a method handle:
public void handle(short id) throws IOException
{
InPacket packet = null;
try {
packet = ClassUtils.newInstance(InPacket.class, "server.client.InPacket"+id);
}catch (Exception e){
e.printStackTrace();
}
if (packet!=null){
packet.handle(this);
}
else{
throw new IOException("Invalid packet");
}
}
I try to instantiate a new class using the
packet = ClassUtils.newInstance(InPacket.class, "server.client.InPacket"+id);
line.
In ClassUtils this is that function:
public static <T> T newInstance(Class<? extends T> type, String className) throws
ClassNotFoundException,
InstantiationException,
IllegalAccessException {
Class<?> clazz = Class.forName(className);
Class<? extends T> targetClass = clazz.asSubclass(type);
T result = targetClass.newInstance();
return result;
}
My problem is: when I try to get that class with only part of the name (I try to get it by "InPacket1", while the class is called "InPacket1Connect"), it can't find it. Is it possible to do this in Java, and if so how? If not, what method do you recommend for handling my network packets?
An alternative approach would be to use a map (or enum) which maps the id to the full class name.
Pulling in the stuff from the comments, ensure that this mapping class is available as a jar (or may be in the same jar which contains the implementations of the packet handlers) as your "messaging layer" jar.
Like this, maybe?
packet = ClassUtils.newInstance(InPacket.class, "server.client.InPacket"+id+"Connect");
^^^^^^^^^^
(but I may have misunderstood your question)
Why can you not create the full class name?
packet = ClassUtils.newInstance(InPacket.class, "server.client.InPacket"+id + "Connect"
You could implement the functionality you seem to be asking for - a kind of "fuzzy" classname matching - by writing your own classloader. The classloader could search some directories for class files partially matching the type.
My feeling is that this is potentially a brittle solution, there's a danger of loading unexpected classes. I prefer Nim's suggestion of explicitly having a mapping table if you can't use an algorthimic classname generator.

Categories