2 Replies Latest reply on Jul 8, 2004 10:59 PM by brett_s_r

    Extending Reflection to intercept Constructors

    brett_s_r

      I am using the Javassist Loader and Reflection classes from javassist.reflect, to stub-out and mock classes transparently, especially when performing unit tests.

      I have found that when used with the reflective Loader, Reflection can stub-out and intercept everything (i.e. methods, fields, statics) with the exception of Constructors. In order to be able to transparently mock certain classes, I need to be able to intercept their constructors, as these may throw Exceptions, e.g. due to resource dependencies that we are trying to mock-out, such as database connections etc.

      I have started adding constructor interception as an optional feature of javassist.reflect.Reflection, and the purpose of this posting is to seek community feedback on whether others might find this sensible/useful. Depending on what is possible through the rest of the API, particularly CtNewConstructor, I am thinking that the best design approach might be to copy the way Method interception works, by "moving" the real Methods and introducing a delegating stub Constructor. I'm guess that this will delegate to a Method in a subclass of Metaobject. As the real classes' Constructor has been intercepted, it would then be up to the Metaobject to instantiate a suitable replacement underlying object to delegate to.

      Any other approaches?

      Any thoughts/comments, however brief, most welcome ... I need to code this now ... may need some help from interested parties ...

      Brett Randall

        • 1. Re: Extending Reflection to intercept Constructors
          brett_s_r

          OK, that approach won't quite work, as it doesn't make sense to try and rename the Constructors as is done with the Methods.

          I have succeeded in "hard intercepting" the construction by calling (on CtConstructor):

          insertBeforeBody("return;");
          , which is handy as it retains the correct (implicit or explicit) super(..) call to the superclass constructor, and blocks the remainder of the constructor from executing. It does however prevent the real original code in the Constructor body from being executed later if required.

          Since renaming is probably not on, I see a couple of other options:

          1) Before inserting the "return;", copy the Constructor body (after any super(...)), converting it into a factory method.
          2) Try and make the execution of "return;" conditional in some way, based on a field.

          I'm thinking that the second one may be the simplest.

          Brett Randall


          • 2. Re: Extending Reflection to intercept Constructors
            brett_s_r

            I now have a working version based on 2) above, by adding a boolean field to the class, which is set during the instantiation of the embedded Metaobject (this is an instance field and is instantiated straight after the super(...) constructor runs and prior to the execution of the remainder of the constructor). The field determines whether the full constructor (after any explicit or implicit call to super(...)) should be intercepted (effectively stopped).

            The constructor is enhanced through the following code in Reflection:

            CtConstructor c;
            ...
            c.insertBeforeBody("if (" + interceptConstructorField + ") {return;}");


            ... where interceptConstructorField contains the name of the new boolean field added to the class. This field is set in the constructor of the Metaobject based on an equivalent new (static) boolean field in Metaobject. So, if:

            Metaobject.setInterceptConstructor(true)


            ... is called, when the next instance of the reflected class is instantiated, only its superclass constructor will run, as the inserted return will be reached. Set it back to false, and the full constructor will run for the next instance.

            The design uses static fields and methods in Metaobject (should these live instead in ClassMetaobject?), as the instance of Metaobject is not instantiated until during the instantiation of the reflected object. This made me realise that the additional field within the reflected class is probably not needed, rather:

            c.insertBeforeBody("if (" + metaobjectGetter + "().isInterceptConstructor()) {return;}");


            ... is just as effective, with the slight disadvantage being that there is no way down the track to tell whether a particular reflective object's constructor was intercepted. So maybe the field should stay.

            I have a full set of jUnit tests. Could someone please advise me on how best to contribute this enhancement?

            Brett Randall