Uploaded image for project: 'OpenWebBeans'
  1. OpenWebBeans
  2. OWB-893

OpenWebBeans 1.2.1 fails when injecting generic value holder

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: 1.2.1
    • Fix Version/s: 1.2.1
    • Component/s: Injection and Lookup
    • Labels:
      None

      Description

      In OmniFaces we're using a producer method with the following signature:

      @Produces
      @Param
      public <V> ParamValue<V> produce(InjectionPoint injectionPoint)
      

      Injection then takes place into a bean as follows:

      @Inject @Param
      private ParamValue<String> text1;
      

      @Param is a qualifier with only non-binding attributes.

      See RequestParameterProducer and Param

      This works in all versions of Weld that we tested on and in OpenWebBeans 1.1.8 (TomEE 1.5.2.). Unfortunately it does not work with OpenWebBeans 1.2.1-SNAPSHOT (TomEE 1.6.0-SNAPSHOT).

      The problem seems to be that 1.2.1 has added an additional check in org.apache.webbeans.util.GenericsUtil.satisfiesDependency that wasn't there before:

      return ClassUtil.isSame(injectionPointRawType, beanRawType)? isAssignableFrom(injectionPointType, beanType): false;
      

      The problem is with isAssignableFrom, because the producer is always seen as producing a ParamValue<Object. The actual values at the point of evaluation with the above given injection example where:

      injectionPoint = ParamValue<class java.lang.String> 
      beanType = ParamValue<class java.lang.Object>
      

      Those are not directly assignable so the injection fails.

      Is OmniFaces doing something wrong here, is this check to strict, or should the producer type not be seen as ParamValue<Object>?

        Activity

        Hide
        romain.manni-bucau Romain Manni-Bucau added a comment -

        Hi,

        Arne worked on it with CDI expert group and trunk should be aligned on CDI spec. You can easily workaround it using a custom extension and registering these objects as beans (see deltaspike which can help too with its BeanBuilder)

        Show
        romain.manni-bucau Romain Manni-Bucau added a comment - Hi, Arne worked on it with CDI expert group and trunk should be aligned on CDI spec. You can easily workaround it using a custom extension and registering these objects as beans (see deltaspike which can help too with its BeanBuilder)
        Hide
        arjan.tijms Arjan Tijms added a comment -

        Thanks for the very quick reply Romain!

        Arne worked on it with CDI expert group and trunk should be aligned on CDI spec.

        In other words, OmniFaces is doing it wrong and it should not have a generic producer method?

        You can easily workaround it using a custom extension and registering these objects as beans (see deltaspike which can help too with its BeanBuilder)

        I'm not exactly sure how that would work, but I think I just found another workaround by making the producer method use raw types. Typically I consider raw types something to be avoided, but they seem to do the trick here:

         
        @Produces 
        @Param 
        public ParamValue produce(InjectionPoint injectionPoint) 
        

        The following spec issue inspired this solution: https://issues.jboss.org/browse/CDI-304

        If you're absolutely sure that OWB is doing the right thing here and generic producer methods are simply not legal, then you can close this issue

        (p.s. for some reason text formatting doesn't work in the description of an issue here, but it does work in comments)

        Show
        arjan.tijms Arjan Tijms added a comment - Thanks for the very quick reply Romain! Arne worked on it with CDI expert group and trunk should be aligned on CDI spec. In other words, OmniFaces is doing it wrong and it should not have a generic producer method? You can easily workaround it using a custom extension and registering these objects as beans (see deltaspike which can help too with its BeanBuilder) I'm not exactly sure how that would work, but I think I just found another workaround by making the producer method use raw types. Typically I consider raw types something to be avoided, but they seem to do the trick here: @Produces @Param public ParamValue produce(InjectionPoint injectionPoint) The following spec issue inspired this solution: https://issues.jboss.org/browse/CDI-304 If you're absolutely sure that OWB is doing the right thing here and generic producer methods are simply not legal, then you can close this issue (p.s. for some reason text formatting doesn't work in the description of an issue here, but it does work in comments)
        Hide
        romain.manni-bucau Romain Manni-Bucau added a comment -

        i'll wait Arne confirm it but i got the same issue.

        personally i added custom beans through AfterBeanDiscovery with type implementing ParameterizedType

        Show
        romain.manni-bucau Romain Manni-Bucau added a comment - i'll wait Arne confirm it but i got the same issue. personally i added custom beans through AfterBeanDiscovery with type implementing ParameterizedType
        Hide
        arne Arne Limburg added a comment -

        The result of the discussion was, that the spec is broken. So either we implement it spec conform or we implement it the way that it works for you
        I just stopped the work on that issue to wait, which way will be gone by the spec.

        Show
        arne Arne Limburg added a comment - The result of the discussion was, that the spec is broken. So either we implement it spec conform or we implement it the way that it works for you I just stopped the work on that issue to wait, which way will be gone by the spec.
        Hide
        romain.manni-bucau Romain Manni-Bucau added a comment -
        Show
        romain.manni-bucau Romain Manni-Bucau added a comment - FYI: here is an extension https://github.com/rmannibucau/cdi-converters/blob/master/src/main/java/com/github/rmannibucau/converter/internals/ConverterExtension.java registering ParameterizedType which could match your need.
        Hide
        arjan.tijms Arjan Tijms added a comment -

        ParameterizedType which could match your need.

        Cool, thanks a lot for the pointer Romain! I'm sure going to study that thoroughly!

        The result of the discussion was, that the spec is broken. So either we implement it spec conform or we implement it the way that it works for you

        Hmmm... tricky indeed. As I mentioned, by letting the producer return a raw type I got it working for me, but others might stumble upon the same issue. Especially since it did work on older TomEE releases and on the current JBoss etc ones. Since I'm still a CDI novice I can't say for sure how common a generic producer method really is in practice.

        Show
        arjan.tijms Arjan Tijms added a comment - ParameterizedType which could match your need. Cool, thanks a lot for the pointer Romain! I'm sure going to study that thoroughly! The result of the discussion was, that the spec is broken. So either we implement it spec conform or we implement it the way that it works for you Hmmm... tricky indeed. As I mentioned, by letting the producer return a raw type I got it working for me, but others might stumble upon the same issue. Especially since it did work on older TomEE releases and on the current JBoss etc ones. Since I'm still a CDI novice I can't say for sure how common a generic producer method really is in practice.
        Hide
        arjan.tijms Arjan Tijms added a comment -

        After some digging I found the following method in Weld 2.0.0sp1 in the class org.jboss.weld.resolution.BeanTypeAssignabilityRules:

            @Override
            protected boolean areActualTypeArgumentsMatching(ActualTypeHolder requiredType, Type[] otherActualTypeArguments) {
                if (requiredType.getActualTypeArguments().length == 0) {
                    /*
                     * A parameterized bean type is considered assignable to a raw required type if the raw types are identical and
                     * all type parameters of the bean type are either unbounded type variables or java.lang.Object.
                     */
                    return isArrayOfUnboundedTypeVariablesOrObjects(otherActualTypeArguments);
                } else if (otherActualTypeArguments.length == 0) {
                    /*
                     * A raw bean type is considered assignable to a parameterized required type if the raw types are identical and all
                     * type parameters of the required type are either unbounded type variables or java.lang.Object.
                     */
                    return isArrayOfUnboundedTypeVariablesOrObjects(requiredType.getActualTypeArguments());
                } else {
                    return super.areActualTypeArgumentsMatching(requiredType, otherActualTypeArguments);
                }
            }
        

        Because of the second if the workaround with the raw return type of the producer doesn't work on this particular version of Weld; it only works if the injection point in my example is declared as ParamValue<Object> or ParamValue<?>, but it was declared as ParamValue<String> and thus it fails.

        So unfortunately, this is again a different rule than Weld 1.x, OWB 1.8.1 and OWB 1.2.1-SNAPSHOT use.

        Show
        arjan.tijms Arjan Tijms added a comment - After some digging I found the following method in Weld 2.0.0sp1 in the class org.jboss.weld.resolution.BeanTypeAssignabilityRules : @Override protected boolean areActualTypeArgumentsMatching(ActualTypeHolder requiredType, Type[] otherActualTypeArguments) { if (requiredType.getActualTypeArguments().length == 0) { /* * A parameterized bean type is considered assignable to a raw required type if the raw types are identical and * all type parameters of the bean type are either unbounded type variables or java.lang. Object . */ return isArrayOfUnboundedTypeVariablesOrObjects(otherActualTypeArguments); } else if (otherActualTypeArguments.length == 0) { /* * A raw bean type is considered assignable to a parameterized required type if the raw types are identical and all * type parameters of the required type are either unbounded type variables or java.lang. Object . */ return isArrayOfUnboundedTypeVariablesOrObjects(requiredType.getActualTypeArguments()); } else { return super .areActualTypeArgumentsMatching(requiredType, otherActualTypeArguments); } } Because of the second if the workaround with the raw return type of the producer doesn't work on this particular version of Weld; it only works if the injection point in my example is declared as ParamValue<Object> or ParamValue<?> , but it was declared as ParamValue<String> and thus it fails. So unfortunately, this is again a different rule than Weld 1.x, OWB 1.8.1 and OWB 1.2.1-SNAPSHOT use.
        Hide
        arjan.tijms Arjan Tijms added a comment -

        p.s. this last rule was actually mentioned in the spec issue that I mentioned above (https://issues.jboss.org/browse/CDI-304).

        Show
        arjan.tijms Arjan Tijms added a comment - p.s. this last rule was actually mentioned in the spec issue that I mentioned above ( https://issues.jboss.org/browse/CDI-304 ).
        Hide
        arjan.tijms Arjan Tijms added a comment -

        Sorry, also forgot to mention that the generic producer method public <V> ParamValue<V> produce(InjectionPoint injectionPoint) works in Weld 2.0.0SP1. The super method areActualTypeArgumentsMatching in above code fragment is called then which eventually calls org.jboss.weld.resolution.EventTypeAssignabilityRules.isAssignableFrom(TypeVariable<?>, Type), where beanType is "V":

        protected boolean isAssignableFrom(TypeVariable<?> requiredType, Type beanType) {
            return isTypeInsideBounds(beanType, EMPTY_TYPES, requiredType.getBounds());
        }
        

        This will return true.

        As Weld 2.0.0SP1 represents the spec final version (passed the TCK, which is never complete of course), will the behavior still change here following the spec discussion?

        At the moment I'm not 100% sure whether I should change OmniFaces back to the generic producer (which worked on OWB 1.8.1 and Weld 1.x + 2.x) before I look into Romain's suggestion.

        Show
        arjan.tijms Arjan Tijms added a comment - Sorry, also forgot to mention that the generic producer method public <V> ParamValue<V> produce(InjectionPoint injectionPoint) works in Weld 2.0.0SP1. The super method areActualTypeArgumentsMatching in above code fragment is called then which eventually calls org.jboss.weld.resolution.EventTypeAssignabilityRules.isAssignableFrom(TypeVariable<?>, Type) , where beanType is "V": protected boolean isAssignableFrom(TypeVariable<?> requiredType, Type beanType) { return isTypeInsideBounds(beanType, EMPTY_TYPES, requiredType.getBounds()); } This will return true. As Weld 2.0.0SP1 represents the spec final version (passed the TCK, which is never complete of course), will the behavior still change here following the spec discussion? At the moment I'm not 100% sure whether I should change OmniFaces back to the generic producer (which worked on OWB 1.8.1 and Weld 1.x + 2.x) before I look into Romain's suggestion.
        Hide
        arjan.tijms Arjan Tijms added a comment -

        So either we implement it spec conform or we implement it the way that it works for you

        Just wondering, is there already a decision made about this?

        Show
        arjan.tijms Arjan Tijms added a comment - So either we implement it spec conform or we implement it the way that it works for you Just wondering, is there already a decision made about this?
        Hide
        arjan.tijms Arjan Tijms added a comment -

        To make matters even more complicated, CanDI (Resin 4.0.36) does not allow generics in producer methods at all. Period. It gave me the following exception:

        classpath:META-INF/caucho/app-default.xml:55: javax.enterprise.inject.InjectionException:
        'public org.omnifaces.cdi.param.ParamValue org.omnifaces.cdi.param.RequestParameterProducer.produce(javax.enterprise.inject.spi.InjectionPoint)'
        is an invalid @Produces method because it returns a generic type class
        org.omnifaces.cdi.param.ParamValue<produce_T_0>
        

        Given the spec and above referenced CDI issue I don't think this is quite right, but it does make things really difficult at this point.

        Show
        arjan.tijms Arjan Tijms added a comment - To make matters even more complicated, CanDI (Resin 4.0.36) does not allow generics in producer methods at all. Period. It gave me the following exception: classpath:META-INF/caucho/app-default.xml:55: javax.enterprise.inject.InjectionException: 'public org.omnifaces.cdi.param.ParamValue org.omnifaces.cdi.param.RequestParameterProducer.produce(javax.enterprise.inject.spi.InjectionPoint)' is an invalid @Produces method because it returns a generic type class org.omnifaces.cdi.param.ParamValue<produce_T_0> Given the spec and above referenced CDI issue I don't think this is quite right, but it does make things really difficult at this point.
        Hide
        struberg Mark Struberg added a comment -

        I'm currently working on a few tests in this area.

        The CDI-1.0 spec is defined for

        • generic bean type is assigned to generic injection point type
        • generic bean type is assigned to raw or unbound (erased) injection point type

        but it does say nothing abou the other way around: what does happen if the injection point type is generic but the bean type is raw or unbound. In OWB-1.1.x we just allowed this scenario and did the cast as the JVM would do it native: just assign the value user and if the generic type does mismatch a ClassCastException would be thrown at runtime anyway. But in this case the producer method has at least the possibility to evaluate the InjectionPoint and perform the right thing.

        Now the bad part: in CDI-1.1 some folks (who didn't like to listen to me when I explained it to them) introduced that additional part:
        "A raw bean type is considered assignable to a parameterized required type if the raw types are identical and all type parameters of the required type are either unbounded type variables or java.lang.Object."

        With other words:

        @Inject Stable<?> stable; would work if there is a @Produces Stable<Horse> method.
        But there is no way with the current wording to do it the other way around:
        @Inject Stable<Horse> horseStable;
        @Inject Stable<Pig> pigStable;
        with a single producer method @Produces <PET> Stable<PET>

        got me?

        If it would be only me, then I'd really ditch this paragraph again and do it properly, which means we need to introduce a multi-pass approach as this imo prevents us from providing such a 'generic' producer method which takes the type info from the InjectionPoint.
        This could be done by introducing a measuring approach as it's implemented in the JVM at compile time (resp the bridge method stuff at runtime): if there is a @Produces Stable<Pig> then this would take precedence over the unbound @Produces <PET> Stable<PET>.

        Show
        struberg Mark Struberg added a comment - I'm currently working on a few tests in this area. The CDI-1.0 spec is defined for generic bean type is assigned to generic injection point type generic bean type is assigned to raw or unbound (erased) injection point type but it does say nothing abou the other way around: what does happen if the injection point type is generic but the bean type is raw or unbound. In OWB-1 .1.x we just allowed this scenario and did the cast as the JVM would do it native: just assign the value user and if the generic type does mismatch a ClassCastException would be thrown at runtime anyway. But in this case the producer method has at least the possibility to evaluate the InjectionPoint and perform the right thing. Now the bad part: in CDI-1.1 some folks (who didn't like to listen to me when I explained it to them) introduced that additional part: "A raw bean type is considered assignable to a parameterized required type if the raw types are identical and all type parameters of the required type are either unbounded type variables or java.lang.Object." With other words: @Inject Stable<?> stable; would work if there is a @Produces Stable<Horse> method. But there is no way with the current wording to do it the other way around: @Inject Stable<Horse> horseStable; @Inject Stable<Pig> pigStable; with a single producer method @Produces <PET> Stable<PET> got me? If it would be only me, then I'd really ditch this paragraph again and do it properly, which means we need to introduce a multi-pass approach as this imo prevents us from providing such a 'generic' producer method which takes the type info from the InjectionPoint. This could be done by introducing a measuring approach as it's implemented in the JVM at compile time (resp the bridge method stuff at runtime): if there is a @Produces Stable<Pig> then this would take precedence over the unbound @Produces <PET> Stable<PET>.
        Hide
        arjan.tijms Arjan Tijms added a comment -

        Mark, thanks a lot for your thorough reply.

        The CDI-1.0 spec is defined for [...] what does happen if the injection point type is generic but the bean type is raw or unbound.

        And there's the generic method producer of course. So for the producer there are three options:

        Raw:

        @Produces
        public ParamValue produce(InjectionPoint injectionPoint)
        

        Unbound:

        @Produces
        public ParamValue<?> produce(InjectionPoint injectionPoint)
        

        Generic method with type parameter:

         
        @Produces 
        public <V> ParamValue<V> produce(InjectionPoint injectionPoint) 
        

        But there is no way with the current wording to do it the other way around:

        @Inject Stable<Horse> horseStable;
        @Inject Stable<Pig> pigStable;
        

        with a single producer method @Produces <PET> Stable<PET>

        Indeed, with both CDI 1.0 and CDI 1.1 this seemingly obvious thing seems to be impossible. Despite not being specified, for CDI 1.0 implementations using the "hack" where the producer returns a raw type seems to work with Weld, OpenWebBeans and CanDI. Unfortunately for CDI 1.1 implementations this doesn't work, as Weld 2.0 doesn't work with it.

        If it would be only me, then I'd really ditch this paragraph again and do it properly, which means we need to introduce a multi-pass approach as this imo prevents us from providing such a 'generic' producer method which takes the type info from the InjectionPoint.

        There are probably some intricate details I'm not aware of, but that sounds really good to me. Unfortunately Java EE 7 was just released and it thus will be a long time for a new CDI 1.2 will see the light of day. For utility libraries or applications that need to be compatible with CDI 1.0, 1.1 and the upcoming 1.2 it will be really hard if not impossible to have a single producer method that works on all 3 versions.

        Is OWB 1.2.1 btw going to be a CDI 1.1 implementation, or still a CDI 1.0 one but with some 1.1 semantics?

        Show
        arjan.tijms Arjan Tijms added a comment - Mark, thanks a lot for your thorough reply. The CDI-1.0 spec is defined for [...] what does happen if the injection point type is generic but the bean type is raw or unbound. And there's the generic method producer of course. So for the producer there are three options: Raw: @Produces public ParamValue produce(InjectionPoint injectionPoint) Unbound: @Produces public ParamValue<?> produce(InjectionPoint injectionPoint) Generic method with type parameter: @Produces public <V> ParamValue<V> produce(InjectionPoint injectionPoint) But there is no way with the current wording to do it the other way around: @Inject Stable<Horse> horseStable; @Inject Stable<Pig> pigStable; with a single producer method @Produces <PET> Stable<PET> Indeed, with both CDI 1.0 and CDI 1.1 this seemingly obvious thing seems to be impossible. Despite not being specified, for CDI 1.0 implementations using the "hack" where the producer returns a raw type seems to work with Weld, OpenWebBeans and CanDI. Unfortunately for CDI 1.1 implementations this doesn't work, as Weld 2.0 doesn't work with it. If it would be only me, then I'd really ditch this paragraph again and do it properly, which means we need to introduce a multi-pass approach as this imo prevents us from providing such a 'generic' producer method which takes the type info from the InjectionPoint. There are probably some intricate details I'm not aware of, but that sounds really good to me. Unfortunately Java EE 7 was just released and it thus will be a long time for a new CDI 1.2 will see the light of day. For utility libraries or applications that need to be compatible with CDI 1.0, 1.1 and the upcoming 1.2 it will be really hard if not impossible to have a single producer method that works on all 3 versions. Is OWB 1.2.1 btw going to be a CDI 1.1 implementation, or still a CDI 1.0 one but with some 1.1 semantics?
        Hide
        struberg Mark Struberg added a comment -

        Arjan, thanks for your feedback as well! It is really important for us to learn which use cases do not yet work well with OWB.

        OWB-1.2.1 is CDI 1.0 with all the stuff of CDI-1.1 internally. There is a cdi11 module which repackages OWB to provide CDI 1.1 APIs for allowing us to run the CDI 1.1 TCK.
        There will be an owb-2.0 branch once we find more time to work on it. As has been with CDI 1.0: OWB will provide a meaningful implementation which satisfies the users needs. And I bet the Weld team will have open ears if our approach is working.

        @Produces
        public ParamValue<?> produce(InjectionPoint injectionPoint)
        

        This is imo actually disallowed by the CDI spec (both 1.0 and 1.1). There are a few things which are easily overlooked:
        3.1: "If the managed bean class is a generic type, it must have scope @Dependent. If a managed bean with a parameterized bean class declares any scope other than
        @Dependent, the container automatically detects the problem and treats it as a definition error."
        3.3: "If the producer method return type is a parameterized type, it must specify an actual type parameter or type variable for each type parameter.
        If a producer method return type contains a wildcard type parameter the container automatically detects the problem and treats it as a definition error."

        We face exactly the same issues with Weld 2.x over in DeltaSpike. See DELTASPIKE-405.

        Show
        struberg Mark Struberg added a comment - Arjan, thanks for your feedback as well! It is really important for us to learn which use cases do not yet work well with OWB. OWB-1 .2.1 is CDI 1.0 with all the stuff of CDI-1.1 internally. There is a cdi11 module which repackages OWB to provide CDI 1.1 APIs for allowing us to run the CDI 1.1 TCK. There will be an owb-2.0 branch once we find more time to work on it. As has been with CDI 1.0: OWB will provide a meaningful implementation which satisfies the users needs. And I bet the Weld team will have open ears if our approach is working. @Produces public ParamValue<?> produce(InjectionPoint injectionPoint) This is imo actually disallowed by the CDI spec (both 1.0 and 1.1). There are a few things which are easily overlooked: 3.1: "If the managed bean class is a generic type, it must have scope @Dependent. If a managed bean with a parameterized bean class declares any scope other than @Dependent, the container automatically detects the problem and treats it as a definition error." 3.3: "If the producer method return type is a parameterized type, it must specify an actual type parameter or type variable for each type parameter. If a producer method return type contains a wildcard type parameter the container automatically detects the problem and treats it as a definition error." We face exactly the same issues with Weld 2.x over in DeltaSpike. See DELTASPIKE-405 .
        Hide
        struberg Mark Struberg added a comment -

        We've now switched back to the CDI-1.0 approach which is also the intended one. An error was introduced in the CDI 1.1 spec which is to be reverted in the future. See https://issues.jboss.org/browse/CDI-389 for more information.

        The valid way to write this should be

        @Produces 
        public <V> ParamValue<V> produce(InjectionPoint injectionPoint)
        

        I've just need to also fix the generics rules for Delegates.
        Can you please verify the latest version from trunk?

        Show
        struberg Mark Struberg added a comment - We've now switched back to the CDI-1.0 approach which is also the intended one. An error was introduced in the CDI 1.1 spec which is to be reverted in the future. See https://issues.jboss.org/browse/CDI-389 for more information. The valid way to write this should be @Produces public <V> ParamValue<V> produce(InjectionPoint injectionPoint) I've just need to also fix the generics rules for Delegates. Can you please verify the latest version from trunk?
        Hide
        arjan.tijms Arjan Tijms added a comment -

        Thanks Mark! I'll make sure to check it out soon.

        Show
        arjan.tijms Arjan Tijms added a comment - Thanks Mark! I'll make sure to check it out soon.
        Hide
        struberg Mark Struberg added a comment -

        still not fully ok - working on it

        Show
        struberg Mark Struberg added a comment - still not fully ok - working on it
        Hide
        arjan.tijms Arjan Tijms added a comment -

        Okay, I'll wait with testing then till it's fully ok.

        Do you by chance have any idea how long it typically takes for a new TomEE 1.6-SNAPSHOT to incorporate the latest OWB 1.2.1-SNAPSHOT? We have some users who are very eager to run OmniFaces on the TomEE 1.6 snapshots and if possible we would like to hold off our release until a TomEE 1.6-SNAPSHOT is available that is capable or running OmniFaces 1.6.

        Show
        arjan.tijms Arjan Tijms added a comment - Okay, I'll wait with testing then till it's fully ok. Do you by chance have any idea how long it typically takes for a new TomEE 1.6-SNAPSHOT to incorporate the latest OWB 1.2.1-SNAPSHOT? We have some users who are very eager to run OmniFaces on the TomEE 1.6 snapshots and if possible we would like to hold off our release until a TomEE 1.6-SNAPSHOT is available that is capable or running OmniFaces 1.6.
        Hide
        struberg Mark Struberg added a comment -

        should work already. usially we trigger an owb + tomee deploy immediately after such Bugfixes.

        Show
        struberg Mark Struberg added a comment - should work already. usially we trigger an owb + tomee deploy immediately after such Bugfixes.

          People

          • Assignee:
            struberg Mark Struberg
            Reporter:
            arjan.tijms Arjan Tijms
          • Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development