Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Composable Annotations #2294

Closed
xenoterracide opened this issue Nov 21, 2019 · 14 comments
Closed

[FEATURE] Composable Annotations #2294

xenoterracide opened this issue Nov 21, 2019 · 14 comments

Comments

@xenoterracide
Copy link

xenoterracide commented Nov 21, 2019

from spring documentation

A composed annotation is an annotation that is meta-annotated with one or more annotations with the intent of combining the behavior associated with those meta-annotations into a single custom annotation. For example, an annotation named @TransactionalService that is meta-annotated with Spring's @transactional and @service annotations is a composed annotation that combines the semantics of @transactional and @service. @TransactionalService is technically also a custom stereotype annotation

So what I want is the ability to write my own annotation, that is composed of lombok annotations, say for example @Immutable

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURC)
@Documented
@Value
@Builder
public @interface Immutable {}

now I could add

@Immutable
class MyClass {

and it would behave the same as if I had annotated it with @Value @Builder, @Value @Data could even be implemented in this way and would prevent requests for more of these (well maybe not prevent)

Additional context
would solve #2124

@nimo23
Copy link

nimo23 commented Dec 9, 2019

I also need this feature:

Actually I have a lombok dependency hell on my entities:

// lombok dependency hell
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Entity {
..
}

which could be simplified by

// create lombok shortcut
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
@Documented
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public @interface LombokEntity {}
@LombokEntity
public class Entity {
..
}

With such a feature, the following issues could be resolved at once by the user instead of provided by lombok:

@rzwitserloot Please make such a shortcut annotation (=stereotype=composable annotation) possible and relieve us from the lombok dependency hell.

@janrieke
Copy link
Contributor

This has been proposed and discussed several times. Although it would be nice, it is very complicated to implement because it requires resolution.

@Maaartinus
Copy link
Contributor

@janrieke

... very complicated to implement because it requires resolution.

... unless we forget about defining the shortcut using annotations (which is ugly and pretends that every annotation can target an annotation). With lombok.config, it'd be probably much simpler (I'd still require the composed annotation to be defined). WDYT?

@ariskycode
Copy link

Composable annotations would be very useful in the case of enterprise applications. We have a lot of boiler-plate code that could be reduced to a single annotation if we could implement our own combination. It can get hellish annotating every class with the same 3 annotations and taking care of those annotations.
Almost every service class could be annotated like

@ServiceClass
public class Service {

rather than

@Log4j2
@AllArgsConstructor
@NoArgsCconstructor
@RequiredArgsConstructor
public class Service {

which takes care of constructors that are required for constructor autowiring and bean initialization.

@rzwitserloot
Copy link
Collaborator

@nimo23 Why don't you just use @Data here? The tostring impl that bad?

@nimo23
Copy link

nimo23 commented Jan 15, 2020

@rzwitserloot because @Data is not a shortcut for:

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor

Lombok should provide composable annotations (for example: @Stereotype) to combinate any lombok annotations at will.

The benefit is shorter, uncluttered code, DRY (dont repeat yourself) and also that I only need to change one source instead of multiple classes if I decide to change lombok annotations for each of the class.

@sidian123
Copy link

期待这个特性的完成, 等的我心慌....

very expect this feature to complete

@t1
Copy link

t1 commented Dec 26, 2020

It would be nice to be able to compose several Lombok annotations into a custom shortcut annotation, which many people request again and again. It could also be reused for the built-in shortcut annotations like @Data or @Value.

In a second step, this feature would get real super powers, if it could be combined with arbitrary annotations from other packages, i.e. a custom @DomainEntity annotation to combine @Getter @Setter @Builder @NoArgsConstructor @AllArgsConstructor with @javax.persistence.Entity and more!

@rzwitserloot
Copy link
Collaborator

@t1 @sidian123 @ariskycode – please tone down the entitled borderline insulting behaviour.

@t1: It would be nice to be able to compose several Lombok annotations into a custom shortcut annotation, which many people request again and again.

Good that you searched through a bunch of issues and found that this feature has been requested again and again. But evidently you didn't bother to read these issues, because we shoot it down again and again. With reasons.

Thus, I'm not sure how I am to take a comment like yours. 'again and again' is a bit judgemental. Perhaps you think Project Lombok owes you this feature?

It doesn't, and that kind of entitled attitude is insulting to your friendly neighbourhood open source maintainer.

Incredulity that the feature isn't being added? Well, you are your own answer. I did not notice a pull request stapled to your 'again and again' passive aggressive note, and that explains why this feature isn't around yet. It's complicated (feel free to peruse these issues as to why this is quite complicated, it's clear you didn't bother to do this and/or did not understand the issues this feature request needs to solve first), and we lost our magic wands that make features just appear out of thin air.

Repeatedly asking for complicated features just clutters the tracker and ticks off the maintainers, so cut that out, not just here, but for all open source projects, and read more of an issue first before chiming in with another me too!!!. Again, that advice goes for all FOSS projects you care to request features for. Input is usually quite welcome, but assume the project maintainers are generally aware of common feature requests, and thus, entertain the thought there might be reasons for why a rather obvious request isn't implemented.

@nimo23 wrote: Lombok should provide composable annotations

I believe you have a typo there. You wrote "Lombok should", but surely you meant to type "I want".

@nimo23: Bolded text extolling the virtues of shorter code.

You're talking to the Project Lombok maintainers. You decided to write this in bold? Jiminy Christmas. Think about that for a moment. That was either a pointless waste of bold or quite rude. I'll give you the benefit of the doubt and consider it a momentary lapse of common sense on your part. Put please, when posting on FOSS lists, take a moment. Hit the 'preview' button if available. Read over what you wrote and think about how it might come across.

@rzwitserloot
Copy link
Collaborator

rzwitserloot commented Dec 26, 2020

These meta-annotation ideas are, as contributor @janrieke has highlighted, borderline unworkable, but the alternate
idea (as explained by @Maaartinus) of setting up a non-annotation based meta-annotation system might be able to bridge the divide, and sidesteps the resolution issue.

But, what would it look like? lombok.config is bad at multiline statements, and piling a bunch of lombok annotations on one line seems a bit ugly. Also, I don't think import statements have any place in there, and yet without them, are we to consider any annotation in any lombok package, be it @Getter -> lombok.Getter, @WithBy -> lombok.experimental.WithBy, @Jacksonized -> lombok.extern.jackson.Jacksonized?

Perhaps we need a completely new aspect to lombok config files, for meta 'annotations'. What if we allow something like this:

>cat lombok.config
lombok.toString.callSuper = true
lombok.fieldNameConstants.innerTypeName = Field

@com.foo.customcompanypkg.ServiceClass {
@Log4j2("myTopic")
@AllArgsConstructor(onConstructor=@javax.annotation.Inject)
@NoArgsConstructor(access = PRIVATE)
}

This will be a complex parsing bonanza. Laying down some basic ideas on what seems reasonable:

  • All lombok packages are assumed to be imported by default. Any conflicts (I can foresee a world where the same annotation exists in multiple packages someday) can only be resolved by fully qualifying the name, lombok. and all.
  • Anything else, excluding java.lang, cannot be imported and must be written FQN (Fully Qualified Name).
  • The lombok.config parser recognizes such as meta-block because the entire node starts with an @, followed by optional whitespace, followed by the FQN of some meta-annotation that you must define yourself and which cannot ever have any arguments (yet, maybe we can add a templating system to this at some point?) Lombok will then look for this annotation anywhere, even if it isn't on the classpath / doesn't exist. Goes for absolutely anywhere, including types, methods, fields, parameters, and local variables.
  • This avoids the problem that all lombok annotations need to then be specced to be legal on annotations, and avoids the problems with resolution.
  • annoparams are allowed, but lombok is only capable of parsing a limited subset: Only actual constants. In java, @Foo(5+2) is legal, but it won't be here. @Foo(SomeClass.SOME_CONSTANT_FIELD) also won't work here. Legal annoparams are ints, longs (5L, or really large numbers), doubles, strings, booleans, fully qualified classrefs (java.lang.String.class, no generics of course), limited enum support (only lombok's own enums, such as AccessLevel), very limited annotation support such as onConstructor, without support for the workarounds needed for onMethod/onConstructor and friends, and let's start with only marker annotations to keep things simple for now. As far as I know, it's markers like @Inject and @AutoWired that usually need to be added. Also, one-dim arrays of any of that is also allowed, to match what annotations allow.
  • No multipass. You can't put a meta-annotation in your definition, only lombok annotations.
  • You can't put non-lombok annotations in here, because lombok probably cannot add them in time for the general Annotation Processor system to pick them up. Therefore lombok won't actually add these annotations, these merely mean that lombok itself will act as if these annotations were present. To protect against confusion on this front, any inclusion of annotations lombok doesn't recognize are considered an error. To support custom forks of lombok with additional annotations, lombok will check its own jar for the annotations that are supported, by checking a specific set of packages within itself. This list is powered by a text file, thus, custom forks that use a custom package can just add that package to the list.
  • The error checking is only performed once a meta annotation is actually used someplace, so that lombok can place any errors in your definition on a relevant AST node (that meta-annotation usage somewhere in your project). If the definition has errors, lombok won't process that file at all, just put error markers explaining what's wrong on the meta-annotation.

big problem with this idea:

  • This creates the problem that the lombok.config really, really needs to be part of the project source control, and it becomes difficult to transfer a meta-'annotation' via established dependency distribution systems: If you have an in-house base project that would have been the obvious home for such a meta-annotation, and all projects the company creates always have that base project in their dep list, then if lombok had a 'meta annotation' concept (it doesn't and it won't, to be clear), then you can just start using the meta-annotation immediately. But with lombok.config, you'd have to copy over the meta-annotation definition first. Failure to do so produces no obvious errors; without the lombok config, and without resolution, lombok has no idea that a given annotation is intended to be a meta annotation, and if the meaning of the meta-annotation is not defined in a lombok.config, then an error should be produced. That'd be swell, but lombok can't do that , as it wouldn't know it needs to.
  • The fact that lombok.config doesn't quite match up with dependency distribution is a more general problem. Yes, you can put a lombok.config in your 'workspace' (which most developers have; a common root dir for all your projects), but unless you have a monorepo source control (admittedly, a popular concept, but nowhere near 90%+ in my experience), a workspace-level lombok config isn't in source control, complicating distribution of it. We already have some initial work on the concept of importing your lombok config, maybe we can expand on that. Projects would still need to explicitly add a lombok.config file, but perhaps each lombok.config file can at least start out with a simple one-liner that imports another lombok.config from elsewhere. At least now it's a single file you can add to your 'make new project' script easily, and if you want to change a meta-annotation, you no longer have to change 500 config files.
  • Open question: Should lombok support package-level annotations? I think no (the idea being that you put a meta-anno on your package in package-info.java, and that lombok, because it has no resolution, figures this out by explicitly checking for the existence of package-info.java and parsing it out.
  • Should lombok support defaulting meta-annotations? Where a lombok.config file doesn't just explain what some meta-annotation means, but also has a mechanism to say: Assume this meta-annotation is present on every class processed. This opens up multiple cans of worms, so I'm 99% certain: Not for the first release of this feature.

@nimo23
Copy link

nimo23 commented Dec 26, 2020

Another alternative is to have a @Lombok-Annotation which provides simple on/off-switches for common lombok annotations. If some annotations needs to be configured, then the user can use the standard annotation instead (For example, instead of "allArgsConstructor="true" use @AllArgsConstructor(staticName = "name")

The following

@Lombok(getter="true", setter="true", noArgsConstructor="true", "allArgsConstructor="true", builder="true")
class User {
}
@Lombok(builder="true")
class User {
}

is the same as

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
class User {
}

@Rawi01
Copy link
Collaborator

Rawi01 commented Dec 26, 2020

@rzwitserloot In #557 I have linked a proof of concept version. I recently fixed a bunch of problems and my current local version also features default values copying values of the meta annotation to the new added annotation.

Before:

//conf: lombok.metaAnnotations += @SourceAnnotation(a2={"a2", "b2"}) @TargetAnnotation(a1=<a1>, a2=<a2>)

@SourceAnnotation(a1 = {"a", "b"})
class MetaAnnotationBoundArray {
	private String test;
}

After:

@SourceAnnotation(a1 = {"a", "b"})
@TargetAnnotation(a1 = {"a", "b"}, a2 = {"a2", "b2"})
class MetaAnnotationBoundArray {
	private String test;
}

I planned to continue it as a seperate project but it obviusly would be better to do it in lombok itself.

@rzwitserloot
Copy link
Collaborator

Another alternative is to have a @Lombok-Annotation which provides simple on/off-switches for common lombok annotations.

This does not solve any of the stated problems, and is ugly. Let's not dwell on this route any further.

@rzwitserloot
Copy link
Collaborator

Oof, yeah, okay, let's close this one and move all debate there.

Duplicate of #557.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants