U60 change makes annotations visible in synthesized methods, causes CDI to see lambdas as observer methods

arjan tijms arjan.tijms at gmail.com
Wed Aug 26 14:04:22 UTC 2015


Hi,

I'm with the JSF EG/Mojarra team and we're seeing what looks like a
problematic regression in JDK8 u60.

In a CDI extension we have the following observer method:

    public <T> void processBean(@Observes ProcessBean<T> event,
BeanManager beanManager) {
        getAnnotation(beanManager, event.getAnnotated(), FacesDataModel.class)
           .ifPresent(d ->
               forClassToDataModelClass.put(
                   d.forClass(),
                   (Class<? extends DataModel<?>>)
event.getBean().getBeanClass()
               )
           );
    }

With u45 and below this will generate the following (decompiled the
class with javap -v -p -s -c)

  private void lambda$processBean$4(javax.enterprise.inject.spi.ProcessBean,
javax.faces.model.FacesDataModel);
    descriptor:
(Ljavax/enterprise/inject/spi/ProcessBean;Ljavax/faces/model/FacesDataModel;)V
    flags: ACC_PRIVATE, ACC_SYNTHETIC
    Code:
      stack=3, locals=3, args_size=3
         0: aload_0
         1: getfield      #4                  // Field
forClassToDataModelClass:Ljava/util/Map;
         4: aload_2
         5: invokeinterface #57,  1           // InterfaceMethod
javax/faces/model/FacesDataModel.forClass:()Ljava/lang/Class;
        10: aload_1
        11: invokeinterface #58,  1           // InterfaceMethod
javax/enterprise/inject/spi/ProcessBean.getBean:()Ljavax/enterprise/inject/spi/Bean;
        16: invokeinterface #59,  1           // InterfaceMethod
javax/enterprise/inject/spi/Bean.getBeanClass:()Ljava/lang/Class;
        21: invokeinterface #55,  3           // InterfaceMethod
java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
        26: checkcast     #45                 // class java/lang/Class
        29: pop
        30: return
      LineNumberTable:
        line 110: 0
        line 111: 5
        line 112: 11
        line 110: 21
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      31     0  this   Lcom/sun/faces/cdi/CdiExtension;
            0      31     2     d   Ljavax/faces/model/FacesDataModel;


U60 however generates the following:

  private void lambda$processBean$4(javax.enterprise.inject.spi.ProcessBean,
javax.faces.model.FacesDataModel);
    descriptor:
(Ljavax/enterprise/inject/spi/ProcessBean;Ljavax/faces/model/FacesDataModel;)V
    flags: ACC_PRIVATE, ACC_SYNTHETIC
    Code:
      stack=3, locals=3, args_size=3
         0: aload_0
         1: getfield      #4                  // Field
forClassToDataModelClass:Ljava/util/Map;
         4: aload_2
         5: invokeinterface #57,  1           // InterfaceMethod
javax/faces/model/FacesDataModel.forClass:()Ljava/lang/Class;
        10: aload_1
        11: invokeinterface #58,  1           // InterfaceMethod
javax/enterprise/inject/spi/ProcessBean.getBean:()Ljavax/enterprise/inject/spi/Bean;
        16: invokeinterface #59,  1           // InterfaceMethod
javax/enterprise/inject/spi/Bean.getBeanClass:()Ljava/lang/Class;
        21: invokeinterface #55,  3           // InterfaceMethod
java/util/Map.put:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
        26: checkcast     #45                 // class java/lang/Class
        29: pop
        30: return
      LineNumberTable:
        line 110: 0
        line 111: 5
        line 112: 11
        line 110: 21
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      31     0  this   Lcom/sun/faces/cdi/CdiExtension;
            0      31     1 event   Ljavax/enterprise/inject/spi/ProcessBean;
            0      31     2     d   Ljavax/faces/model/FacesDataModel;
    RuntimeVisibleParameterAnnotations:
      parameter 0:
        0: #79()
      parameter 1:

The difference is that the parameter annotation is made visible for
this synthesized method. The result is that CDI subsequently scans
this class and sees the synthesized method for the lambda as an
observer method, which is of course not the intention. In this
particular case it will result in a definition error:

"Error occurred during deployment: Exception while loading the app :

CDI definition failure:

WELD-000409: Observer method for container lifecycle event
[[BackedAnnotatedMethod]

private com.sun.faces.cdi.CdiExtension.lambda$processBean$4(@Observes
ProcessBean<Object>, FacesDataModel)] can only inject BeanManager.."

I think that if the synthesized method happened to be a correct
observer method, e.g. for a no-parameter lambda or one that happens to
have the BeanManager as its first and only parameter, then CDI would
silently start to call this method in response to events, which too is
highly undesirable.

Kind regards,
Arjan Tijms


More information about the lambda-dev mailing list