Bug 533199 - AnnotationFormatError: duplicate annotation for class when using annotation container
Summary: AnnotationFormatError: duplicate annotation for class when using annotation c...
Status: NEW
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.7.3   Edit
Hardware: PC Linux
: P3 normal with 3 votes (vote)
Target Milestone: ---   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact: Srikanth Sankaran CLA
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2018-04-04 05:29 EDT by Guillaume Smet CLA
Modified: 2023-06-01 05:48 EDT (History)
9 users (show)

See Also:


Attachments
Test case (6.60 KB, application/gzip)
2018-04-04 05:29 EDT, Guillaume Smet CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Guillaume Smet CLA 2018-04-04 05:29:19 EDT
Created attachment 273414 [details]
Test case

When using the Eclipse compiler on this sort of things:

	@Pattern.List({
			@Pattern(regexp = "[0-9]+"),
			@Pattern(regexp = "[A-Z]+"),
	})
	private String nameWithContainer;

And calling:

	YourAnnotatedBean.class.getDeclaredField( "nameWithContainer" ).getAnnotatedType()

Java fails with the following exception:

java.lang.annotation.AnnotationFormatError: Duplicate annotation for class: interface org.hibernate.validator.bugs.Pattern: @org.hibernate.validator.bugs.Pattern(message={javax.validation.constraints.Pattern.message}, groups=[], regexp=[A-Z]+)
	at sun.reflect.annotation.TypeAnnotationParser.mapTypeAnnotations(TypeAnnotationParser.java:360)
	at sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl.<init>(AnnotatedTypeFactory.java:139)
	at sun.reflect.annotation.AnnotatedTypeFactory.buildAnnotatedType(AnnotatedTypeFactory.java:65)
	at sun.reflect.annotation.TypeAnnotationParser.buildAnnotatedType(TypeAnnotationParser.java:79)
	at java.lang.reflect.Field.getAnnotatedType(Field.java:1170)
	at org.hibernate.validator.bugs.YourTestCase.testWithContainer(YourTestCase.java:13)

Whereas the same project compiled with Javac works flawlessly.

Attached is a simple Maven project reproducing the issue. Import it as Maven project in Eclipse, and run YourTestCase as a JUnit test.

You should have one test passing (the one without the container) and one failing with the aforementioned exception (the one with the container).

This got initially reported as an Hibernate Validator issue on SO: https://stackoverflow.com/questions/49640244/bean-validation-duplicate-annotation-exception/49647126#49647126 .
Comment 1 Olivier Thomann CLA 2018-04-10 14:16:54 EDT
The problem comes from the way we collect type annotations. For @Pattern.List({
			@Pattern(regexp = "[0-9]+"),
			@Pattern(regexp = "[A-Z]+"),
	}), we end up with three type annotations. @Pattern.List with two inner annotations and @Pattern(regexp = "[0-9]+") with @Pattern(regexp = "[A-Z]+") as the same level. This is wrong. I'll see if I can find some time to investigate this. I modified the collector of TypeAnnotation to not proceed inner annotations and this seems to work. Need more reviews.
Comment 2 Eclipse Genie CLA 2020-10-18 19:04:17 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.
Comment 3 Daniel Brent CLA 2021-05-27 12:32:04 EDT
As of release 2020-06, I have started to encounter this same issue (though on a method location rather than a field). I have tried updating to the current latest  version 4.19.0.20210311-1200 with the same results, but reverting my installation to 4.15.0.20200313-1200 resolves the issue. This is effectively preventing any ability to update Eclipse beyond 2020-03.
Comment 4 Davide Malpassini CLA 2021-12-28 09:15:25 EST
We having the same issue. Is there some workaround. The classic javac doesn't have the same issue.
Comment 5 Andrejus Afanasjevas CLA 2022-04-26 09:01:10 EDT
Got the same issue with Spring Tool Suite 4. v4.14.0.RELEASE.
Changing Hibernate Validator does not help. Any known workarounds?
Comment 6 Mauro Molinari CLA 2022-08-09 11:56:01 EDT
I've encountered this problem today with Eclipse 2021-12. Very nasty bug, it prevents smooth upgrading from Bean Validation API 1.1 to 2.0.
Comment 7 Philippe Gioseffi CLA 2022-08-23 06:53:35 EDT
Using Eclipe 22-06 in a project with Spring Boot 2.7.3, all the dependencies managed by Spring Boot and Java 17 and got the very same problem today. Isn't eclipse compiler using the one we set in Installed JREs and then check it in Environmet Variables? This would prevent the problem until the bug in Eclipse compiler is not resolved.
Comment 8 Mauro Molinari CLA 2022-08-23 07:12:56 EDT
(In reply to Philippe Gioseffi from comment #7)
> Isn't eclipse compiler using the one we set in Installed JREs and then check
> it in Environmet Variables? This would prevent the problem until the bug in
> Eclipse compiler is not resolved.

Eclipse uses ECJ, the Eclipse compiler, which is a different implementation compared to OpenJDK javac. This is why compilation may fail in one compiler and not in the other or (like in this case) the produced byte code may differ, independently of what you select as the JRE to run the program with, which indeed controls just the VM to use at runtime.
Historically committers tried to keep ECJ behaviour as much close as possible to javac one, even when javac clearly exhibited a bug, in order to avoid surprises at runtime, but in this case it seems like there's a bug in ECJ byte code generation which has not yet been fixed.

At least, this is my understanding of this problem :-)
Comment 9 Srikanth Sankaran CLA 2023-06-01 05:46:23 EDT
Simplified single file test case:

package org.hibernate.validator.bugs;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.TYPE_USE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Field;

import org.hibernate.validator.bugs.Pattern.List;

class YourAnnotatedBean {

	@Pattern.List({
			@Pattern(regexp = "[0-9]+"),
			@Pattern(regexp = "[A-Z]+"),
	})
	private String nameWithContainer;
	
	@Pattern(regexp = "[0-9]+")
	@Pattern(regexp = "[A-Z]+")
	private String name;
}


@Target({ FIELD, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)

 @interface Pattern 
 {
	String regexp();

	@Target({  FIELD, TYPE_USE })
	@Retention(RUNTIME)
	@interface List {

		Pattern[] value();
	}
}

public class YourTestCase {

	public void testWithContainer() throws NoSuchFieldException, SecurityException {
		Field field = YourAnnotatedBean.class.getDeclaredField( "nameWithContainer" );
		if (1 != field.getAnnotatedType().getAnnotations().length ) {
			throw new Error("Broken");
		}
	}
	
	
	public void testWithoutContainer() throws NoSuchFieldException, SecurityException {
		Field field = YourAnnotatedBean.class.getDeclaredField( "name" );
		if (1 != field.getAnnotatedType().getAnnotations().length) {
			throw new Error("Broken");
		}
	}
	
	public static void main(String[] args) throws NoSuchFieldException, SecurityException {
		new YourTestCase().testWithContainer();
		new YourTestCase().testWithoutContainer();
	}
}