MyFaces CODI
  1. MyFaces CODI
  2. EXTCDI-127

Injection in FacesConverter does not work

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Minor Minor
    • Resolution: Fixed
    • Affects Version/s: 0.9.2
    • Fix Version/s: 0.9.3
    • Labels:
      None
    • Environment:
      Myfaces 2.0.3, CODI 0.9.2, ExtVal 2.0.4, OWB 1.0.0, PrimeFaces 2.2-RC2

      Description

      The bean which sould be injected in my converter is always null.

      I also added some logging to CODI, to see where the problem is.
      The bean was created and injected into the converter but it seems as the used converter is not the converter which was created by CODI.

      Is there something wrong in my setup or is this really a bug?

      -----------------------------------------------------------

      @Advanced
      @FacesConverter("localeConverter")
      public class LocaleConverter implements Converter {

      @Inject private LocaleService localeService;

      -----------------------------------------------------------

      <h:form>
      <h:selectOneMenu
      id="selectLocaleMenu"
      value="#

      {localeController.selectedLocale}

      "
      onchange="this.form.submit()"
      converter="localeConverter">
      <f:selectItems
      value="#

      {allLocalesController.locales}

      "
      var="locale"
      itemLabel="#

      {locale.name}

      "
      itemValue="#

      {locale}

      "/>
      </h:selectOneMenu>
      </h:form>

        Issue Links

          Activity

          Hide
          Jakob Korherr added a comment -

          Actually this should work.

          CODI wraps the FacesContext in a CodiFacesContextWrapper and there (in getApplication()) it wraps the Application in a InjectionAwareApplicationWrapper. Then, in this application wrapper, it performs dependency injection on the converter instances before returning them.

          However, maybe primefaces or extval override the JSF Application too and thus cause some problems here. Could you please set a break point in InjectionAwareApplicationWrapper (package org.apache.myfaces.extensions.cdi.jsf2.impl.listener.request) in the two createConverter() methods and check if dependency injection really is performed? ..and if yes, if this converter object really is used.

          Thanks!

          Show
          Jakob Korherr added a comment - Actually this should work. CODI wraps the FacesContext in a CodiFacesContextWrapper and there (in getApplication()) it wraps the Application in a InjectionAwareApplicationWrapper. Then, in this application wrapper, it performs dependency injection on the converter instances before returning them. However, maybe primefaces or extval override the JSF Application too and thus cause some problems here. Could you please set a break point in InjectionAwareApplicationWrapper (package org.apache.myfaces.extensions.cdi.jsf2.impl.listener.request) in the two createConverter() methods and check if dependency injection really is performed? ..and if yes, if this converter object really is used. Thanks!
          Hide
          Jakob Korherr added a comment -

          OK, I created a quick sample for this one and I see the issue too. I'm working on it!

          Show
          Jakob Korherr added a comment - OK, I created a quick sample for this one and I see the issue too. I'm working on it!
          Hide
          Jakob Korherr added a comment -

          The problem is that the <h:selectOneMenu /> component (like any input or output component) tries to save the converter in its state (see UIOutput.saveState() on line 199 in MyFaces core 2.0.3). However, for a normal (non-serializable) converter only the Class of the converter is saved in the state. Later, on the next request, the state is restored and the converter is created via converterClass.newInstance(). Unfortunately this "custom" converter instance is used to process the data and thus you get an instance of your converter without CDI injection.

          For CODI it seems like we have to do some custom state saving of @Advanced converters...

          Show
          Jakob Korherr added a comment - The problem is that the <h:selectOneMenu /> component (like any input or output component) tries to save the converter in its state (see UIOutput.saveState() on line 199 in MyFaces core 2.0.3). However, for a normal (non-serializable) converter only the Class of the converter is saved in the state. Later, on the next request, the state is restored and the converter is created via converterClass.newInstance(). Unfortunately this "custom" converter instance is used to process the data and thus you get an instance of your converter without CDI injection. For CODI it seems like we have to do some custom state saving of @Advanced converters...
          Hide
          Jakob Korherr added a comment -

          Workaround for this problem:

          create this method in your localeController:

          public Converter getConverter()

          { return FacesContext.getCurrentInstance().getApplication().createConverter("localeConverter"); }

          and use converter="#

          {localeController.converter}

          " in your h:selectOneMenu.

          Show
          Jakob Korherr added a comment - Workaround for this problem: create this method in your localeController: public Converter getConverter() { return FacesContext.getCurrentInstance().getApplication().createConverter("localeConverter"); } and use converter="# {localeController.converter} " in your h:selectOneMenu.
          Hide
          Leonardo Uribe added a comment -

          I think it is possible to use PostRestoreStateEvent to inject the required field on the converter. The other alternative is use a delegate pattern over converters that has inject fields, and put some code on converter wrapper restoreState() method.

          Show
          Leonardo Uribe added a comment - I think it is possible to use PostRestoreStateEvent to inject the required field on the converter. The other alternative is use a delegate pattern over converters that has inject fields, and put some code on converter wrapper restoreState() method.
          Hide
          Jakob Korherr added a comment -

          PostRestoreStateEvent sounds like a good idea!

          In addition I have already thought of wrapping the Converter (maybe via proxy) in order to perform a custom state saving algorithm. However, if PostRestoreStateEvent works, it should be preferred IMO.

          Thanks for the input, Leo!

          Show
          Jakob Korherr added a comment - PostRestoreStateEvent sounds like a good idea! In addition I have already thought of wrapping the Converter (maybe via proxy) in order to perform a custom state saving algorithm. However, if PostRestoreStateEvent works, it should be preferred IMO. Thanks for the input, Leo!
          Hide
          Jakob Korherr added a comment -

          created sample for this one in hello_myfaces-codi-jsf20.

          Show
          Jakob Korherr added a comment - created sample for this one in hello_myfaces-codi-jsf20.
          Hide
          Thomas Andraschko added a comment -

          thanks for your effort, Jakob!

          Show
          Thomas Andraschko added a comment - thanks for your effort, Jakob!
          Hide
          Gerhard Petracek added a comment -

          changed to minor because there are 2 workarounds

          the 2nd one is the implementation of the StateHolder interface.
          we have to document it.

          Show
          Gerhard Petracek added a comment - changed to minor because there are 2 workarounds the 2nd one is the implementation of the StateHolder interface. we have to document it.
          Hide
          Gerhard Petracek added a comment - - edited

          fyi: PostRestoreStateEvent is broken (currently)

          Show
          Gerhard Petracek added a comment - - edited fyi: PostRestoreStateEvent is broken (currently)
          Hide
          Gerhard Petracek added a comment -

          if this feature isn't used it's possible to reduce the overhead via:

          public void vetoRestoreInjectionPointsObserver(@Observes ProcessAnnotatedType processAnnotatedType)
          {
          if(processAnnotatedType.getAnnotatedType().getJavaClass().equals(RestoreInjectionPointsObserver.class))

          { processAnnotatedType.veto(); }

          }

          (in a cdi extension - see javax.enterprise.inject.spi.Extension)

          Show
          Gerhard Petracek added a comment - if this feature isn't used it's possible to reduce the overhead via: public void vetoRestoreInjectionPointsObserver(@Observes ProcessAnnotatedType processAnnotatedType) { if(processAnnotatedType.getAnnotatedType().getJavaClass().equals(RestoreInjectionPointsObserver.class)) { processAnnotatedType.veto(); } } (in a cdi extension - see javax.enterprise.inject.spi.Extension)

            People

            • Assignee:
              Gerhard Petracek
              Reporter:
              Thomas Andraschko
            • Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Development