1 2 Previous Next 29 Replies Latest reply on Jan 15, 2003 10:25 PM by belaban Go to original post
      • 15. Re: Reopening getters/setters vs. field interception
        belaban


        > /**
        > * Gets something.
        > *
        > * @read-only
        > */


        No, I don't think this is acceptable, for the following reasons:

        1. People will want to add objects to the cache that were not instrumented with Javadoc like this.

        2. Also, what are you going to do about objects that sit in libraries, and to which you don't have the source code ?

        What's wrong about adding some additional synthesized methods to a class in the classloader when the class is loaded ?
        E.g:

        void foo() {
        // bla
        a=23;
        modified = true; // <--- synthesized code
        // bla
        }


        Bela

        • 16. Re: Reopening getters/setters vs. field interception
          marc.fleury

          > When will this be ready ? Is it already ? Because I
          > can use these interceptors also to *implement* my
          > cache.

          Bill has committed all of it already only he doesn't call the Stack a "Stack" it has this fancy name probably from his CORBA days....

          You should be able to seriously start working on the caches :). Let us know what you require, it seems you will need the ability to specify that this interceptor only applies to getters and setters.

          The importance of detyping is that we can add this behavior *dynamically* to the object. We only instrument the object for indirection, what is the indirection is runtime.

          • 17. Re: Reopening getters/setters vs. field interception
            marc.fleury

            > We're talking reflection calls on fields not methods.
            > so, java.lang.reflect.Field.set(obj, val) will not be

            remember that in JDK1.4 we can specify our classloaders as System class loader. Then we specify instrumentation of the Field class with the implementation of setters to generate a

            ((AOPState)o).invoke("set", field, value);

            and the implementation to do a

            invoke(...) {

            mark field as dirty within transaction, keep track of it;

            }


            All the getters on the other side can be made optimistic transactional with a getter that returns the version you are in according to your transactional identifier. That in effect enables us to create INSTANTANEOUS SNAPSHOTS OF STATE IN VM. The getter simply returns the latest commited version unless you are associated with the transaction. It occurs to me that the versions should be numbered in a unique identifier in VM (that needs to be fast and grow big ). You declared your state domain to be fully optimistic/full-undo

            You can associate a time to a thread and that will "Snapshot" any state you access at a given time. FINALLY REPORTING IS POSSIBLE ON LARGE JAVA SYSTEMS EVEN EJB. Simply do

            Snapshot.set();

            This associates the time with the current thread and whenever the thread accesss the get we return the field value whose 'timestamp' is equal or lower to the one specified. Snapshot is easy.

            hmmmm it is another aspect. Clearly.

            • 18. Re: Reopening getters/setters vs. field interception
              aloubyansky

              knock knock... may i come in?
              (the first phrase i learned in the school being late on the English lessons)

              >> /**
              >> * Gets something.
              >> *
              >> * @read-only
              >> */
              >
              > No, I don't think this is acceptable, for the following reasons:


              As for me, it would be ideal. The problem is it requires a "special" compilation. Do we accept it?


              >1. People will want to add objects to the cache that were not instrumented with Javadoc like this.
              >2. Also, what are you going to do about objects that sit in libraries, and to which you don't have the source code ?

              >What's wrong about adding some additional synthesized methods to a class in the classloader when the class is loaded ?
              >E.g:
              >
              >void foo() {
              >// bla
              >a=23;
              >modified = true; // <--- synthesized code
              >// bla
              >}

              Ok, but this is just a flag. How do you know when to set it to true? Did i miss something?
              There are 3 choices coming to my mind:
              1. follow some naming convention like get/set
              2. configure with xml/attributes what methods change the data. BTW, did you think about marking some fields as cacheable and some not, like transient? Or is it too much?
              3. introduce the notion of object's state. Before each method is executed the current state is recorded and after the method excution new state is calculated and compared with the recorded one. If the state is changed then modified = true.

              Am I in sync?
              alex

              • 19. Re: Reopening getters/setters vs. field interception
                aloubyansky

                >void foo() {
                >// bla
                >a=23;
                >modified = true; // <--- synthesized code
                >// bla
                >}
                now I got it, sorry. But doubt whether it's possible with existing frameworks.

                • 20. Re: Reopening getters/setters vs. field interception
                  marc.fleury

                  > knock knock... may i come in?
                  > (the first phrase i learned in the school being late
                  > on the English lessons)

                  Don't be late next time or you will miss the fun again. Yes, you may come in as I really want you to get this stuff.

                  > >> /**
                  > >> * Gets something.
                  > >> *
                  > >> * @read-only
                  > >> */

                  > As for me, it would be ideal. The problem is it
                  > requires a "special" compilation. Do we accept it?

                  you need a special compilation because the bytecode doesn't carry the metadata. If only the bytecode could carry some metadata then you could give hints as to what needs to be instrumented.

                  we don't like as pointed out anything that has to have separate metadata than its class is doomed to administrative failure. Maybe we can infer a lot of it from its structure and its usage. Instrumentation at run time is that.

                  That is the trick, to leverage the system at runtime to determine what classes make it into cache by listening for inserts.


                  And anything that requires compilation? If we can do without it, we will :) We are at the javassist level where we use that API to instrument the loaded bytecode. Expert help required to dig in the bytecode.

                  > >1. People will want to add objects to the cache that
                  > were not instrumented with Javadoc like this.
                  > >2. Also, what are you going to do about objects that
                  > sit in libraries, and to which you don't have the
                  > source code ?
                  >
                  > >What's wrong about adding some additional
                  > synthesized methods to a class in the classloader
                  > when the class is loaded ?
                  > >E.g:
                  > >
                  > >void foo() {
                  > >// bla
                  > >a=23;
                  > >modified = true; // <--- synthesized code
                  > >// bla
                  > >}
                  >
                  > Ok, but this is just a flag.

                  yes

                  > Am I in sync?

                  almost, get here early next time...

                  • 21. Re: Reopening getters/setters vs. field interception
                    aloubyansky

                    So, we really need to read the bytecode and say whether it changes the object's/field's state. Is anybody working on it? I'll delve in it anyway but if someone could share some useful info it'd be great.
                    Thanks in advance,
                    alex

                    • 22. Re: Reopening getters/setters vs. field interception
                      crazybob

                      > > As for me, it would be ideal. The problem is it
                      > > requires a "special" compilation. Do we accept
                      > it?
                      >
                      > you need a special compilation because the bytecode
                      > doesn't carry the metadata. If only the bytecode
                      > could carry some metadata then you could give hints
                      > as to what needs to be instrumented.

                      If the problem is more with maintaining the meta data than generating it, we can store the meta data in the class file (http://java.sun.com/docs/books/vmspec/html/ClassFile.doc.html#1442).

                      We could have a utility class that parses the data out of the class file (this doesn't even require a custom class loader).

                      The utility would input the meta data and the original class file and output a new class file with the meta data embedded. The inputted meta data could come from the source code or from an XML file (in the case when we don't have the source code--comparable to an EJB descriptor).

                      Using it would just require an extra Ant target that points at the source, the compiled classes, and a destination directory.

                      The actual meta data values could be text or serialized objects.

                      Am I treading old ground?

                      • 23. Re: Reopening getters/setters vs. field interception
                        ehenne

                        Hi,

                        I have watched this forum and other AOP resources for some time and feel now confident to add some cents (in EURO! :-).

                        Before talking about fields I would like to extend Marc's interceptor diagram which I think is quite useful for discussion.

                        Marc uses this

                        [=-|||-=o

                        where "[" is the interface "o" is the target object and "|||" are some interceptors. "=" is typed access and "-" is "untyped" access (via invoke). I hope I got this right.

                        This picture works very well for methods, but for field access I would like to extend it with the caller object "c":

                        c=[=-|||-=o

                        For a normal field access the picture would then be:

                        c=[o

                        and with field interceptors:

                        c=-|||-=[o

                        Here the instrumentation has to be done on the caller side because with field access the interface is tightly coupled to the object, there is now place to insert any code in between.

                        The plan to implement field interception is to use Javassist, but by using byte code manipulation at class load time we could get much more!

                        With the help of Javassist you could have for fields something like this:

                        ciii=[o

                        where "i" stands for "inline code injection" and it could be any statement sequence accepted by the Javassist compiler.

                        This would give you more efficient alternatives to the somewhat expensive field interceptor chains.

                        Now you could easily specify Bela's proposal somewhere above:
                        [pre]>void foo() {
                        > // bla
                        > a=23;
                        > modified = true; // <--- synthesized code
                        > // bla
                        >}
                        [/pre]You would just define for field "a" a code injection like:
                        [pre] { $proceed; modified = true; }
                        [/pre]The above is in Javassist syntax, $proceed stands for the original field access. With chained injections $proceed would stand for the previous injections.

                        This is a just a basic idea without all the details, e.g. one would need a more specific specification for the injection pointcuts than just class level and may be the injected code should be more restricted than within Javassist.

                        The above is not meant as a substitution for the inspector chain, so the most general field access diagram would look like:

                        ciii=-|||-=[o

                        The same could be also done with methods, where we would get:

                        ciii=[=-|||-=iiio

                        Here the code injections "iii" could be done on the caller side as well as around the method body.

                        I am not sure if all this should be really done, but my point is that either a simple way with only method interception and getters/setters should be chosen or on the other hand when the Javassist route is taken much more than currently proposed could be provided.

                        Elmar

                        • 24. 3767260
                          belaban

                          Hi Elmar,

                          good idea, but can we reify this ? For example, setting {@proceed; modified=true} is too static.

                          Let's step back for a moment and think about what we are trying to achieve.

                          1. We want to intercept *method invocations*.
                          2. We want to intercept state changes

                          #2 seems to be a specialization of #1: we can see a field change as an invocation of a setter method.

                          So could we do the following ?

                          - Inject code for each field (write) access:
                          {$proceed; interceptor_chain.first().stateChanged($fieldname, $newValue, $oldValue}.

                          - So for field interceptors, we can either (a) add a stateChanged() method (besides the invoke()) to the Interceptor interface or (b) make every field access to X a method invocation of setX().

                          This way, we could use Elmar's suggestion of code injection and still have interceptor chains for field access.

                          Am I making sense ?

                          Bela

                          • 25. Re: Reopening getters/setters vs. field interception
                            ehenne

                            Hi Bela,

                            you certainly make sense!

                            I agree that "modified = true" might be often much too static, I used it just as an "extreme" example of what could be done with the above inlining. But you could also do something like {$proceed; MyIntereceptor.fieldChanged(this, $fieldname, $newValue, $oldValue} and much more.

                            I just browsed a little through the current source code of AOP and looked especially at the implementation of field access. I found there a lot of code which will be executed between the field access and the final invoke code of the user interceptor. I guess the performance penalty of this might be acceptable only in very limited cases.

                            With the inline approach you can do most of the filtering statically and you can jump directly to the user interceptor, so it can be much more efficient. You just do not have a standard way to build an interceptor chain dynamically at runtime. But as I said above, this inlining should not be viewed as a replacement of the invoke chain, just as an addition.

                            Elmar

                            • 26. Re: Reopening getters/setters vs. field interception
                              belaban


                              > With the inline approach you can do most of the
                              > filtering statically and you can jump directly to the
                              > user interceptor, so it can be much more efficient.
                              > You just do not have a standard way to build an
                              > interceptor chain dynamically at runtime. But as I
                              > said above, this inlining should not be viewed as a
                              > replacement of the invoke chain, just as an
                              > addition.

                              What I want to achieve is:

                              Person p=new Person("Bela Ban", 38);
                              Cache c;
                              c.put("bela", p); // replicates the new entry

                              p.age=p.age++; // <---

                              I want to be able to detect any state change in p, in the above case I want to know about the change of field 'age'.

                              So what I'd like to do is to dynamically add myself (as cache) to the interceptor chain of p when Cache.put() is called for the first time.

                              At this point I may be interested in *all* field changes (and record the old values for versioning) or I might just be interested in the *first* state change, internally kepp track of the dirty objects, and *remove* myself from the interceptor chain, because I already know that this object is dirty for the rest of the transaction.

                              Once a new transaction starts, I'll add myself again to p's interceptor chain.

                              My point here is that field interception should be very efficient if the interceptor chain is empty.


                              With static code injection (like "modified=true") the above dynamicity could not be achieved.

                              So, I guess, my argument is that method interception and field interception should be treated the same.

                              Bela

                              • 27. Re: Reopening getters/setters vs. field interception
                                ehenne

                                > My point here is that field interception should be very efficient
                                > if the interceptor chain is empty.

                                Yes, I agree totally that this a very important requirement when you want to use the interceptors for caching. But the current implementation generates a lot of overhead even for empty interceptor chains (there are several method calls and object instantiations with every field access before the empty chain is detected).

                                A very efficient alternative would be the following inline code:
                                {$proceed; if (isCached) { CacheInterceptor.fieldChanged(this, $fieldname, $newValue, $oldValue)}}
                                isCached would be a new object field which has to be set to true as long as the object resides in the cache. So the overhead for field access outside the cache would be only a simple additional boolean test. Adding new fields at class load time is no problem for Javassist.

                                Elmar

                                • 28. Re: Reopening getters/setters vs. field interception
                                  aloubyansky

                                  > Adding new fields at class load time is no problem
                                  > for Javassist.

                                  Yes, but we should keep in mind that instrumentation can be done only once, right? And before finishing it to add all the stuff from all the realms, i.e. cache, persistence, etc.

                                  alex

                                  • 29. Re: Reopening getters/setters vs. field interception
                                    ehenne

                                    > Yes, but we should keep in mind that instrumentation
                                    > can be done only once, right? And before finishing it
                                    > to add all the stuff from all the realms, i.e. cache,
                                    > persistence, etc.

                                    Ok, my idea is to have a general way to provide the users with the power of Javassist. So the above case of caching is just an example of how this could be used.

                                    To make it a little clearer let me show some XML which could be used to specify the above example:
                                    [pre]
                                    <field-interceptor name="StateChanges">

                                    {$proceed; if (isCached) {
                                    CacheInterceptor.fieldChanged(this, $fieldname, $newValue, $oldValue)
                                    }}

                                    </field-interceptor>

                                    <interceptor-pointcut name="Caching" class="com.acme.POJO.*">

                                    <field-declaration name="isCached" type="boolean" modifiers="private"/>
                                    <field-interceptor-ref name="StateChanges" fields="*"/>

                                    </interceptor-pointcut>

                                    [/pre]This is just a quick sketch for the according XML, but I hope this is sufficient to show the general idea.

                                    Elmar

                                    1 2 Previous Next