6 Replies Latest reply on Feb 26, 2016 2:59 AM by mkouba

    Weld injects a proxy instead of real instance - why?

    dermoritz

      In my application i have "@RequestScoped" converter classes that convert jpa entities to jaxb classes (dto serialized to json) and vice versa. All of those classes inject Provider<JaxbClass>. I need to use the provider because those Jaxb-classes extend a class that itself injects some stuff. This all works fine: the provider produces real instances. The class hierarchy looks like that:

       

      interface Identifiable (getId) -> abstract class Linkable (injects some stuff) -> jaxb-class (annotated with xmlroot)

       

      Some classes also extend an additional abstract class (createBy) that provides an additional field, also here no problem.

      The problem begins if i create an Jaxb-classes from an generic abstract class (don't know if this is related to problem). The hierarchy is:

       

      [hierarchy as before]->abstract class<T>->jaxb-class

       

      if i try to get an instance via a provider of those classes the instance is a weld proxy. This proxy has additional fields and the json (un)marshaller moans about unknown field (handler), it can't handle it. My question is why in the latter case i get an proxy?

       

      Here is a working example:

      Converter

      @RequestScoped
      public class VendorRepresentationConverter extends RepresentationConverter.Base<VendorRepresentation,Vendor> {
      
          @Inject
          @CurrentUser
          private User user;
      
          @Inject
          private Provider<VendorRepresentation> vrProv;
      
          @Inject
          private UserRepresentationConverter userReprsentationConverter;
          @Override
          protected Vendor createNew(VendorRepresentation representation) {
              return new Vendor(representation.getName(), representation.getHomePage(), user);
          }
      
          @Override
          public VendorRepresentation from(Vendor vendor) {
              VendorRepresentation vendorRepresentation = vrProv.get(); //returns real instance
              vendorRepresentation.setName(vendor.getName());
              vendorRepresentation.setHomePage(vendor.getHomePage());
              vendorRepresentation.setCreatedBy(userReprsentationConverter.from(vendor.getCreatedBy()));
              vendorRepresentation.setId(vendor.getId());
              return vendorRepresentation;
          }
      
        @Override
          public Vendor update(VendorRepresentation representation, Vendor target) {
              target.setName(representation.getName());
              target.setHomePage(representation.getHomePage());
              return target;
          }
      }
      
      
      

       

      jaxb class:

      @XmlRootElement(name = "vendor", namespace = "urn:problems:vendor")
      public class VendorRepresentation extends CreatedByUserRepresentation{
      
        @NotNull
          private String name;
      
          @NotNull
          private String homePage;
      
          @Override
          protected Class<?> getResourceClass() {
              return VendorResource.class;
          }
      
      
          public String getName() {
              return name;
          }
      
        public void setName(String name) {
              if(Strings.isNullOrEmpty(name)){
                  throw new IllegalArgumentException("Name must neither null nor empty.");
              }
              this.name = name;
          }
      
          public String getHomePage() {
              return homePage;
          }
      
          public void setHomePage(String homePage) {
              if(Strings.isNullOrEmpty(homePage)){
                  throw new IllegalArgumentException("Home page must neither null nor empty.");
              }
              this.homePage = homePage;
          }
      
      
      
      

      now the non working example

       

      converter

      @RequestScoped
      public class ProblemVoteRepresentationConverter extends VoteRepresentationConverter<ProblemVoteRepresentation, ProblemVote> {
      
          @Inject
          private ProblemRepresentationConverter problemConverter;
      
          @Inject
          private Provider<ProblemVoteRepresentation> problemVoteRepresentationProvider;
      
          @Override
          protected ProblemVote createNew( ProblemVoteRepresentation representation ) {
              return new ProblemVote( problemConverter.to( representation.getVoteOf() ), representation.getValue()==1, user );
          }
      
          @Override
          public ProblemVoteRepresentation from( ProblemVote problemVote ) {
              ProblemVoteRepresentation problemVoteRepresentation = problemVoteRepresentationProvider.get(); //proxy is provided!!!!!!!!!!!!!!!
              problemVoteRepresentation.setVoteOf(problemConverter.from(problemVote.getEntity()));
              problemVoteRepresentation.setValue(problemVote.getValue());
              problemVoteRepresentation.setCreatedBy(userConverter.from( problemVote.getCreatedBy()));
              problemVoteRepresentation.setId( problemVote.getId());
              return problemVoteRepresentation;
          }
      }
      
      
      

      based on

      @RequestScoped
      public abstract class VoteRepresentationConverter<REST extends Identifiable, SOURCE> extends RepresentationConverter.Base<REST, SOURCE>{
      
          @Inject
          @CurrentUser
          protected User user;
      
          @Inject
          protected UserRepresentationConverter userConverter;
      
          @Override
          public SOURCE update(REST representation, SOURCE target) {
              throw new UnsupportedOperationException("Votes can't be updated.");
          }
      }
      
      
      

       

      jaxb:

      @XmlRootElement(name = "problemvote", namespace = "urn:problems:problemvote")
      public class ProblemVoteRepresentation extends VoteRepresentation<ProblemRepresentation> {
      
          @Override
          protected Class<?> getResourceClass() {
              return ProblemVoteResource.class;
          }
      }
      
      
      

      based on:

      public abstract class VoteRepresentation<T extends CreatedByUserRepresentation> extends CreatedByUserRepresentation{
      
          @NotNull
          private T voteOf;
      
          @Min(-1) // down vote
          @Max(1) // up vote
          private int value;
      
          @AssertTrue
          public boolean valueIsNotZero() {
              return value != 0;
          }
      
          public T getVoteOf() {
              return voteOf;
          }
      
          public int getValue() {
              return value;
          }
      
          public void setVoteOf(T voteOf) {
              this.voteOf = voteOf;
          }
      
          public void setValue(int value) {
              this.value = value;
          }
      
      }
      
      
      

       

      now the hierarchy continues like in the working example. So what could possibly cause weld to provide a proxy instead of real instance of "ProblemVoteRepresentation" ?

        • 1. Re: Weld injects a proxy instead of real instance - why?
          mkouba

          Hi Moritz,

          that's odd. What Weld version in which environment (WildFly, GlassFish, Weld SE...) do you use? Also a simple reproducer or test case would be great..

          • 2. Re: Weld injects a proxy instead of real instance - why?
            dermoritz

            I am using wildfly 9.0.2 but i also tried with wildfly 10.

             

            my next step would be to burn down to the problem (first with a real simple dta and then step by step add classes to hierarchy) - hopefully i find the cause and then i can create a simple test for it. I hoped someone could provide an idea where to look first.

            • 3. Re: Weld injects a proxy instead of real instance - why?
              dermoritz

              I found the cause why weld is creating the proxy!: It is because of the method annotated with "@AssertTrue"!

              As soon as i remove the annotation all works fine.

              I guess this is a bug isn't it? I can't see any reason why this should cause to use a proxy. The main problem is using a proxy breaks serializability.

               

              What do you think?

              • 4. Re: Weld injects a proxy instead of real instance - why?
                mkouba

                Hi Moritz,

                I think I found the problem. If valueIsNotZero() is annotated with @AssertTrue then it's a constrained method (see also Bean Validation spec, 10.1.2. Method and constructor validation). And this method validation is implemented as a CDI interceptor. As a result, Weld has to create a subclass "proxy" even for @Dependent bean.

                • 5. Re: Weld injects a proxy instead of real instance - why?
                  dermoritz

                  thanks for the information!

                   

                  so what can i do about it? i want to serialize and validate those objects. I searched ways (Java EE7) to exclude the "handler" field on class but i only found implementation specific annotations. Do you know a way to get those proxy through the wire (as json)?

                   

                  On the other hand i could implement the validation manually but this would become ugly: instead of "@Valid jsonFromOutside" i have to implement validate(jsonFromOutside) right?

                  • 6. Re: Weld injects a proxy instead of real instance - why?
                    mkouba

                    Hm, I don't understand the meaning of valueIsNotZero() method. If the value should never equal to zero then you should probably create a custom BV constraint which only accepts 1 and -1 values. Otherwise you have to invoke this method manually, right?