What are some examples of „public annotation types“?

The Java Library Plugin documentation refers to „public annotation types“ in its guidelines for recognizing api and implementation dependencies:

I searched pretty hard. But I have not been able to find any such annotations anywhere.

I’m assuming the docs mean annotations that —when used on a particular library’s public members— consumers of that library would be required to include in their class path, when the consumer compiles their code to use the library.

My understanding is that the very nature of annotations means that if I implement my library using whatever annotations, then those annotations would always be invisible to consumers of my library.

So what is the Gradle documentation talking about then? I’m not seeing how an annotation could be an api dependency. One where a consumer of my library would be required to transitively depend on the same annotation in order for that consumer to compile their code against my library.

I guess I have completely misunderstood annotations then, reading the Gradle docs as they are currently written.

Please, can anybody point me to any examples of those „public annotation types“ that the Gradle docs are talking about? Thanks.

I believe the intention here is to capture annotations that are used in similar ways to the other’s listed. @CedricChampeau can confirm this interpretation.

To reword it a bit…

  • types used in super classes or interfaces => your library requires consumers to extend a particular class or implement a particular interface from the dependency
  • types used in public method parameters, including generic parameter types => your library requires a consumer to use particular types to call library methods or use the results of library methods
  • types used in public fields => similar to above, only for a field
  • public annotation types => your library requires a consumer to use a particular annotation
1 Like

Ahh! OK. I think I get it now! Thanks :+1:

So say there are three organizations: a, b and c. Org a specializes in developing annotations. Org b, a separate library developer, thinks org a’s annotations are the best thing since sliced bread. Org c is me.

Even though org b might not necessarily be using org a’s annotations anywhere on b’s own public members, b nevertheless might want to, for whatever reason, specify in their build script: api 'org.a:best-annotations-ever:18.3' to indicate their requirement for consumers of b’s library — me — to use a particular annotation developed by the unrelated third-party organization, a.

I could see that now. Thanks again.

Maybe it’s because I’ve never had to specify anything like it myself in real life. But that particular meaning didn’t jump right out at me as the most obvious interpretation of that one particular bullet point.

Real-life example: JSR 380 - Bean Validation 2.0

org.hibernate.validator:hibernate-validator is a reference implementation for validating beans using the annotations in javax.validation:validation-api. The validation API should be an API dependency (if they weren’t using Maven) because the entire point of the hibernate validator is to validate beans using the annotations from the validation API, so the consumer needs the validation API for the hibernate validator to be useful.

1 Like

That is an excellent example! Thanks.

I’m assuming you’re referring to Hibernate Validator being built with javax.validation:validation.api configured with Maven’s default compile scope. Right?

To make that particular part of your reply concrete for myself, I knocked together an experimental org c consumer project with implementation group: 'org.hibernate.validator:hibernate-validator:6.1.0.Alpha4'.

Running gradle dependencies on it reports:

compileClasspath - Compile classpath for source set 'main'.
\--- org.hibernate.validator:hibernate-validator:6.1.0.Alpha4
     +--- javax.validation:validation-api:2.0.1.Final
     +--- org.jboss.logging:jboss-logging:3.3.2.Final
     \--- com.fasterxml:classmate:1.3.4

So say Hibernate did use Gradle instead of Maven. And say they did specify api javax.validation:validation-api:2.0.1.Final in their Gradle build script. If nothing changes in my consumer project and I run gradle dependencies again, I expect that Gradle would report effectively the same as it does above. Or no?

My experiments weren’t all that thorough. I confess :disappointed: But I did find that an artifact built with Maven’s compile scope results in the same effective compilation-time class path as the same artifact built with Gradle’s api configuration.

For example, I can import javax.validation.constraints.*; in my Hibernate Validator-consuming application. And I can apply the JSR-380 annotations to elements of the consumer application. And my consumer app compiles and runs successfully.

I’m sure I haven’t told you anything that you don’t already know. And, of course, I agree with everything you’ve shared. It’s just that putting something I’ve never done before into words helps to make it concrete for myself :slight_smile:

Actually, I should correct myself.

If Hibernate used Gradle instead of Maven, then they could do something like:

dependencies {
        ...
        api  javax.validation:validation-api:2.0.1.Final
        implementation org.jboss.logging:jboss-logging:3.3.2.Final
        ...
}

Couldn’t they? And then the compile class path wouldn’t leak unwanted transitive dependencies. Resulting in a much more concise class path on the consumer’s side:

compileClasspath - Compile classpath for source set 'main'.
\--- org.hibernate.validator:hibernate-validator:6.1.0.Alpha4
     +--- javax.validation:validation-api:2.0.1.Final

+1 for Gradle.


Hi @CedricChampeau. I assume that you had at least one particular real-life annotation module in mind when you included „public annotation types“ in the Java Library Plugin docs. Yes?

@jjustinic gave an excellent example with Hibernate. But it’s built with Maven’s compile scope, not Gradle.

Please, are you able to share the one example library project that you had in mind that is built with Gradle and that configures a public annotation types module with Gradle’s api configuration?

I think I’ve found a second example of public annotation types. It’s from: „the OpenMUC project, a software framework based on Java and OSGi that simplifies the development of customized monitoring, logging and control systems“.

Because applications built to it must comply with that framework’s OSGi architecture, it makes the necessary OSGi annotations available on the class path of systems that extend it (consumers):

dependencies {
  ...
  api group: 'org.osgi', name: 'org.osgi.service.component.annotations', version: '1.4.0'
  ...
}

Since that project is built with Gradle’s api configuration, this example makes the doc’s public annotation types line even more concrete still for me.

In case anybody’s wondering why I’m like a dog with a bone on this question, I’m developing an application that programmatically recognizes api and implementation in libraries. So I’m interested in finding a bunch of different Gradle-built projects that I can run through it to train it — so to speak.

Any additional examples would be appreciated. Thanks.