Is it possible to set the #ExtensionMethod for the whole project?
For example if I extend Object with a method isNull(), would it be possible to use that in the whole project with only define the #ExtensionMethod in for example a config class and use it everywhere without declaring it on each class?
Lombok contributor here:
No. The primary reason is 'surprises' - lombok would then be having an effect on a source file that has zero mentions of lombok, anywhere in side it (no types or imports; a CMD+F for lombok would produce zero results), and yet, doesn't compile without it.
Lombok does have a config system (lombok.config) and that would be the place to define some fully qualified class names to be treated as automatically considered sources of extension methods for all java source files in the directory where this config file appears and all subdirs of that directory. But, this feature doesn't exist right now, and won't, until I and other major contributors hold a bit of a debate on whether we want to open this can of worms.
Some meet-in-the-middle solution where there is a list of 'default extensions', but you still need to enable it by annotating the class with #DefaultExtensionMethods or what not (or just #ExtensionMethod and nothing more, with no arguments) might be where we draw the line. I'll keep it in mind.
Related
There is a way to use annotations to build a java class, based on the properties of multiple java classes?
I want to create a generic log history table for all operations and entities in a spring data jpa project, for this i was thinking if would be possible to get all properties of my entities at compilation time to generate this generic entity log class.
I don't know so much about annotations, but it is used to generate source files so i believe that isn't a impossible ideia.
Could someone give some direction? If it's possible would be nice to point me a good starting point. Or if there is something already done that match my intent.
Annotations themselves do not generate source files -- they signify pointcuts for other classes to enhance/enrich them, or as a marker interface.
However, you can definately use an annotation scanner to scan files and get all the fields.
Then what is left is generating a class from this.
(and then, compile it). Be aweare that this is a multi-step process, and it may seem a bit clunky: you create a file with name GenericEntity, make sure it's in the proper package (so start it with package my.fun.project, write the imports, and write the java class, all as strings which you send tot he file.
From you scan you have an annotated field / class and you can get the type and name (see the reflections library if necessary), and write that to your file as well. Then close the class properly with a }. Now it should a file which should not give compilation errors when loaded in your IDE.
This GenericEntityGenerator then has to be executed (using a maven plugin, probably) on your source code, probably during the generate-sources phase, after which your generated class will be compiled during the compile phase.... and bob's your uncle now.
In all, a fun project
I'm seeing an interesting behavior in some of the code I inherited where there's a project with a dependency jar in which there exists a class with the same name and in the same package as a class in the project:
Eclipse project:
src/com.abc.d.E
depends on XYZ.jar in which there exists a com.abc.d.E.class
Just curious if this setup is legal in Java. According to Eclipse, which doesn't mark it as an error and allows to create a new class that shadows an existing class in a dependency jar, except when one tries to rename such an existing class in the workspace - then it produces the following warning:
"Binary references to a refactored element have been found. They will
not be updated, which may lead to problems if you proceed."
And then this forward refactoring is allowed. However, if one would like to refactor backwards to a conflicting name, the following message appears in Eclipse:
"Type named 'E' already exists in package 'com.abc.d'
So is it
a correct Java
an allowed inconsistency in Eclipse's behavior or
.an Eclipse bug?
Thank you.
If both classes where accessible to the same classloader (for example if both jar files find themselves on the same classpath), then only one of them would be loaded (probably the one that's first in the list), which would lead to all kinds of nasty results, esepcially if they don't behave the same.
Assume library 1 uses E.frobnicate() while library 2 expects there to be a E.frob() method: one of those will get a NoSuchMethodError and generally just fail spectacularly.
Generally speaking, the package name and class name should uniquely identify a class. And if that's no longer the case then you'll get into trouble.
You can work around the issue as long as you never need those two libraries to be accessible from the same classloader, then the JVM can handle it just fine (because FQCN plus the classloader is used internally by the JVM to uniquely identify a class during runtime).
The only case where this is intentionally done is if some library re-implements classes of another one in a binary-compatible manner. See log4j-over-slf4j for an example.
There's nothing that states you cannot have two classes with the same package/class name.
This happens all the time when you accidentally have two versions of a library on your classpath, leading to things like IncompatibleClassChangeError or getting the wrong version because you're at the mercy of class load order.
Eclipse, OTOH, has to do something with those classes other than just loading the first one encountered on the classpath. You can't rename to something that already exists, because that wouldn't make any sense.
Renaming something out from under something that already exists is likely an error, but can't be guaranteed to be an error, because you might be fixing a naming problem, rather than just creating a new/different one.
I don't get to see the source code but the .class file.
Can I still find out the files that are imported?
Keep in mind that imports are simply a convenience mechanism that lets the Java developer refer to a class using it's simple name (Date) rather than it's Fully Qualified Name (FQN - java.util.Date or java.sql.Date).
So if you run the .class file through a decompiler, you'll likely see references using the FQN and possibly no import statements.
Well, if you just want to do it manually, I suggest you take a look at a decompiler such as JD GUI
Otherwise, you need to go the reflection way if you want this information programmatically.
If you need to do this in batch, and do not want to bother with all the other details that a decompiler would offer, you can inspect the constant pool for class references.
Beware that, as previously mentioned, source imports are merely a convenience and do not correspond directly to anything in class files. Scanning the constant pool will not show unused imports from the source file, and it will not show classes used only for compile-time constants (public static final String ... and the like). It will show FQNs even for classes in the same package, and it will show classes referred to using FQN without an import. It will show classes whose signatures are used implicitly:
URL loc = Something.class.getProtectionDomain().getCodeSource().getLocation();
will produce references to ProtectionDomain and CodeSource in bytecode even though the source did not explicitly mention them.
https://hg.netbeans.org/core-main/raw-file/default/nbbuild/antsrc/org/netbeans/nbbuild/VerifyClassLinkage.java is an example of how to do this scan (see the dependencies method).
Is there a way to modify .class files in order to add Java annotations to certain methods? Basically I want to traverse methods of each class file in a jar file and annotate certain ones. Note that this is not at run-time while using the jar file. Rather, after I'm done I want to have modified class files with the annotations.
I do have access to the source code, so if there's an automatic source code modifier, that would work as well...
I'm assuming I'll need a tool such as Javassist or ASM. If so, which one should I use and how would I go about it?
Actually, this is a classic use case for AspectJ:
declare #method : public * BankAccount+.*(..) : #Secured(role="supervisor")
While I will grant you that direct byte code manipulation is more powerful, AspectJ is much more user-friendly, and it immediately gives you compiler warnings when you are doing something wrong.
Also, if you use Load Time Weaving, you can leave the original library jar unchanged, because the weaving happens at class-load time.
Reference:
Declare Annotation
AspectJ in Action (book)
Googling for an hour or so turned this article up which seems to completely answer my question: use ASM. To write class files using the changed bytecode, use ClassWriter.
Well, time to get to work then, I guess. :)
I understand the purpose of class annotations, thanks to How and where are Annotations used in Java?. What is the purpose of package annotations, as described in this blog post and ยง7.4.1 of the Java Language Specification?
Why would you want to associate metadata with a package? What kinds of things could you do?
bnd tool (and maven-bundle-plugin) makes use of package annotations. Putting #Version and #Export annotation in package-info.java allows it to generate OSGi metadata.
javadoc uses package annotations.
JAXB uses package-level annotations, for example, to specify mapping of a Java type to XML Schema type package-wide. Package annotations are also used in JBoss's xml binding.
Struts 2 Convention plugin uses an annotation to specify a default interceptor for all actions in a package.
There are some package-level Hibernate Annotations. An example of those annotations' usage can be found here.
I suppose #Deprecated would make sense. And maybe something like #Generated if the whole package was generated by some tool from non-Java source. Or #Internal if this package is not part of a public API.
Maybe OSGi tools (where you need to declare the versions of your packages, and the packages you depend on) could make use of this, too.
Has anyone seen those in the wild?
Two reasons that I can think of:
Annotating special packages to let some aspects (for example using AspectJ) to weave the classes in them for specific functionality.
Annotating some packages that are to be read by some tools, for example for source, meta-data or other kinds of resource generation.
JAXB for example allows most annotations that are normally used on a type to be equally well applied to a package. The meaning in that case would be to specify the default for all classes in that package.
For example, if you want all properties of all classes in a package that are exposed via getter/setters to be mapped in XML you could specify #XmlAccessorType(XMLAccessType.PROPERTY) on each class or simply specify it on the package.
This is not the real purpose, but I'm using them as a workaround to avoid recompilation of the package-info.java files.
The problem is that javac (and the Ant task <javac>) creates no class file for the package-info.java if there is only documentation (the reason for their existence) and the package bla; statement, and that the ant task recompiles every file for which there is no (or an older) corresponding .class file.
Adding a dummy annotation there (like SuppressWarnings) had the effect that a package-info.class is produced and thus the file is not recompiled until changed again.
(Ant 1.8.0 solved this by creating an empty package-info.class, even if there was no annotation, but I'm using an older ant here.)
Test metadata -- that is metadata around test packages (unit tests or otherwise). You can ascribe various pieces of test metadata that are appropriate at the package level, such as: features, owners, versions, bugs/issues, etc. These could be refined at the class or method level, but having package-level definitions or defaults could be handy for brevity. I've utilized a variant of this approach (before the benefit of annotations).
A similar argument could be made for generalized code metadata around the same sorts of things: features, ownership, defects, historical information, etc.