Uploaded image for project: 'Weld'
  1. Weld
  2. WELD-2185

ArraySet.hashCode() breaks java.util.Set.hashCode() contract

    Details

    • Type: Bug
    • Status: Resolved (View Workflow)
    • Priority: Major
    • Resolution: Done
    • Affects Version/s: 1.1.33.Final, 2.3.4.Final
    • Fix Version/s: 2.3.5.Final
    • Component/s: None
    • Labels:
      None
    • Environment:

      Portable Extensions

    • Steps to Reproduce:
      Hide

      This code stores discovered Bean s in a Map Map<Set<Annotation>, Bean> and performs later a lookup by constructing a HashSet containing the matching annotations.

      Show
      This code stores discovered Bean s in a Map Map<Set<Annotation>, Bean> and performs later a lookup by constructing a HashSet containing the matching annotations.

      Description

      Weld uses ArraySet to store qualifiers in a Bean. Extensions that store qualifiers to a Bean and reguster custom Bean instances based on ProcessAnnotatedType with qualifiers are no longer able to use simple matching to obtain the Bean by its qualifiers.

      Exceprt:

      	Map<Set<Annotation>, Bean<CouchbaseOperations>> couchbaseOperationsMap = new HashMap<Set<Annotation>, Bean<CouchbaseOperations>>();
       
      	<T> void processBean(@Observes ProcessBean<T> processBean) {
      		Bean<T> bean = processBean.getBean();
      		couchbaseOperationsMap.put(bean.getQualifiers(), ((Bean<CouchbaseOperations>) bean));
      	}
      	 // later ...
      	
      	void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) {
      		
      		Bean<CouchbaseOperations> couchbaseOperationsBean = this.couchbaseOperationsMap.get(new HashSet<>(Arrays.asList(Default.class, Any.class)));
      		// couchbaseOperationsBean is null
      	}
      	
      

      This code stores discovered Bean s in a Map Map<Set<Annotation>, Bean> and performs later a lookup by constructing a HashSet containing the matching annotations.

      Example with more context:

      	private final Map<Set<Annotation>, Bean<CouchbaseOperations>> couchbaseOperationsMap = new HashMap<Set<Annotation>, Bean<CouchbaseOperations>>();
       
      	<T> void processBean(@Observes ProcessBean<T> processBean) {
      		Bean<T> bean = processBean.getBean();
      		for (Type type : bean.getTypes()) {
      			if (type instanceof Class<?> && CouchbaseOperations.class.isAssignableFrom((Class<?>) type)) {
      				couchbaseOperationsMap.put(bean.getQualifiers(), ((Bean<CouchbaseOperations>) bean));
      			}
      		}
      	}
       
      	protected <X> void processAnnotatedType(@Observes ProcessAnnotatedType<X> processAnnotatedType) {
       
      		AnnotatedType<X> annotatedType = processAnnotatedType.getAnnotatedType();
      		Class<X> repositoryType = annotatedType.getJavaClass();
       
      		if (isRepository(repositoryType)) {
       
      			Set<Annotation> qualifiers = getQualifiers(repositoryType);
       
      			// Store the repository type using its qualifiers.
      			repositoryTypes.put(repositoryType, qualifiers);
      		}
      	}
       
      	void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) {
      		for (Map.Entry<Class<?>, Set<Annotation>> entry : getRepositoryTypes()) {
      			Class<?> repositoryType = entry.getKey();
      			Set<Annotation> qualifiers = entry.getValue();
      			Bean<CouchbaseOperations> couchbaseOperationsBean = this.couchbaseOperationsMap.get(qualifiers);
      			
      			// couchbaseOperationsBean is null
      		}
      	}
      	
      	/**
      	 * Determines the qualifiers of the given type.
      	 */
      	private Set<Annotation> getQualifiers(final Class<?> type) {
       
      		Set<Annotation> qualifiers = new HashSet<Annotation>();
      		Annotation[] annotations = type.getAnnotations();
      		for (Annotation annotation : annotations) {
      			Class<? extends Annotation> annotationType = annotation.annotationType();
      			if (annotationType.isAnnotationPresent(Qualifier.class)) {
      				qualifiers.add(annotation);
      			}
      		}
       
      		// Add @Default qualifier if no qualifier is specified.
      		if (qualifiers.isEmpty()) {
      			qualifiers.add(DefaultAnnotationLiteral.INSTANCE);
      		}
       
      		// Add @Any qualifier.
      		qualifiers.add(AnyAnnotationLiteral.INSTANCE);
      		return qualifiers;
      	}
      

      References:

        Gliffy Diagrams

          Attachments

            Issue Links

              Activity

                People

                • Assignee:
                  mkouba Martin Kouba
                  Reporter:
                  mp911de Mark Paluch
                • Votes:
                  0 Vote for this issue
                  Watchers:
                  2 Start watching this issue

                  Dates

                  • Created:
                    Updated:
                    Resolved: