[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [aspectj-dev] pertypewithin() vs. pertype() association...



All,

As we know the primary use case for pertype()/pertypewithin() is
logging/tracing and support for the common idiom of using one logger per
traced class. Setting aside the desire for consistency with other
per-clauses and the usual behaviour of lazy aspect instantiation on first
reference my primary concern is simplicity for the user when writing a
concrete aspect that extends a tracing library aspect. This should include
all three possible syntaxes: AspectJ, annotation and XML (for load-time
weaving). Any implementation should also be very efficient because of the
performance sensitivity of tracing (see also -XlazyTjp). To this end the
implementation we have with pertypewithin() _is_ sufficient as the example
below shows.

First we start with a simple tracing aspect and no "consumer":

package tracing;

import org.aspectj.lang.*;

public abstract aspect Tracing {

      private pointcut staticContext () : !this(Object);
      private pointcut nonStaticContext (Object obj) : this(obj);
      private pointcut toStringMethod () : execution(String toString());

      private pointcut excluded () :
            within(Tracing+)
            || toStringMethod();

      pointcut tracedMethod () :
            execution(public * *(..))
            && !excluded();

      protected abstract pointcut shouldTrace ();

      before (Object obj) : tracedMethod() && nonStaticContext(obj) && shouldTrace() {
            enter(thisJoinPoint,obj);
      }

      before () : tracedMethod() && staticContext() && shouldTrace() {
            enter(thisJoinPoint);
      }

      after() returning(Object ret) : tracedMethod() && shouldTrace() {
            exit(thisJoinPointStaticPart,ret);
      }

      after() throwing(Exception ex) : tracedMethod() && shouldTrace() {
            exception(thisJoinPointStaticPart,ex);
      }

      protected abstract void enter (JoinPoint jp, Object obj);

      protected abstract void enter (JoinPoint jp);

      protected abstract void exit (JoinPoint.StaticPart sjp, Object ret);

      protected abstract void exception (JoinPoint.StaticPart sjp, Exception ex);

}

Next we define a consumer-specific abstract aspect that overrides the
template methods in this case JDK 1.4 logging. The use of an "if()"
pointcut allows us to exploit -XlazyTjp:

package tracing;

import java.util.logging.*;

import org.aspectj.lang.*;

public abstract aspect JDK14Tracing extends Tracing {

      protected abstract pointcut tracingScope ();

      protected pointcut shouldTrace () :
            if(tracingEnabled) && tracingScope();

      public final static Level ENABLED = Level.FINER;
      public final static Level DISABLED = Level.OFF;

      private static boolean tracingEnabled = false;
      private Logger logger;

      protected void initLogger (String name) {
            logger = Logger.getLogger(name);
            if (!tracingEnabled) {
                  tracingEnabled = isTracingEnabled(logger);
            }
      }

      public static boolean isTracingEnabled () {
            return tracingEnabled;
      }


      private static boolean isTracingEnabled (Logger logger) {
            return logger.isLoggable(ENABLED);
      }

      protected void enter (JoinPoint jp) {
            if (isTracingEnabled(logger)) {
                  Signature signature = jp.getSignature();
                  logger.entering(signature.getDeclaringTypeName(),signature.getName(),jp.getArgs());
            }
      }

      protected void enter (JoinPoint jp, Object obj) {
            if (isTracingEnabled(logger)) {
                  Signature signature = jp.getSignature();
                  logger.entering(signature.getDeclaringTypeName(),signature.getName(),jp.getArgs());
            }
      }

      protected void exit (JoinPoint.StaticPart sjp, Object ret) {
            if (isTracingEnabled(logger)) {
                  Signature signature = sjp.getSignature();
                  logger.exiting(signature.getDeclaringTypeName(),signature.getName(),ret);
            }
      }

      protected void exception (JoinPoint.StaticPart sjp, Exception ex) {
            if (isTracingEnabled(logger)) {
                  Signature signature = sjp.getSignature();
                  logger.exiting(signature.getDeclaringTypeName(),signature.getName(),ex);
            }
      }
}

Next we choose our granularity and logger name (based on class name).
Alternative implementations could use a singleton. The fact that we have
used "pertypewithin(*)" means that all the classes in our application (or
at least those exposed to the weaver) that are initialized will have an
aspect associated with them but this is a small cost. The important thing
is that only those within our tracing scope will get a logger object.

package tracing;

import java.util.logging.*;

import org.aspectj.lang.*;

public abstract aspect PTWJDK14Tracing extends JDK14Tracing pertypewithin(*) {

      before(): staticinitialization(*) && tracingScope() {
            String name = thisJoinPointStaticPart.getSignature().getDeclaringTypeName();
            initLogger(name);
      }

}

Finally, and most importantly, the user writes a simple concrete aspect. Of
course all the parent aspects could be collapsed into a single aspect but
that is transparent to the user:

package tracing;

import java.util.logging.*;

import org.aspectj.lang.*;

public aspect TestTracing extends PTWJDK14Tracing {

      protected pointcut tracingScope () :
          within(test..*) && !within(test.Grandchild);

}

We can also do the same using XML:

<aspectj>
      <aspects>
            <concrete-aspect name="tracing.TestTracing" extends="tracing.PTWJDK14Tracing">
                  <pointcut name="tracingScope" expression="within(test..*) && !within(test.Grandchild)"/>
            </concrete-aspect>
      </aspects>

      <weaver options="-showWeaveInfo -XlazyTjp">
            <include within="test..*"/>
    </weaver>
</aspectj>

Matthew Webster
AOSD Project
Java Technology Centre, MP146
IBM Hursley Park, Winchester,  SO21 2JN, England
Telephone: +44 196 2816139 (external) 246139 (internal)
Email: Matthew Webster/UK/IBM @ IBMGB, matthew_webster@xxxxxxxxxx
http://w3.hursley.ibm.com/~websterm/

"Wes Isberg" <wes@xxxxxxxxxxxxxx>@eclipse.org on 25/01/2005 19:39:30

Please respond to aspectj-dev@xxxxxxxxxxx

Sent by:    aspectj-dev-admin@xxxxxxxxxxx


To:    aspectj-dev@xxxxxxxxxxx
cc:
Subject:    Re: [aspectj-dev] pertypewithin() vs. pertype() association...


Hi All -

Ramnivas, re:

> - The first join point encountered is the one matched by
> staticinitialization(TypePattern)

As Jim points out in the bug, this is not always the case :(

Adrian, re:
> >...the enclosing type of a join point shadow is
> >probably the right answer as Jim suggested

which is to say

  thisEnclosingJoinPointStaticPart.getSignature()
    .getDeclaringType()

, right?

Adrian, is it fair (or helpful?) to say that

  pertypewithin(TypePattern)

is syntactic sugar for

  pertype(within(TypePattern)
      && (staticinitialization(*) || execution(static * *(..))))

(the only form we'd like to support now)?

Eric, re:
> The problem arises
> for me, as soon as people start reasoning about expressions like
> pertype(call(* Foo.bar())). It's not quite straightforward here, what
> type
> is meant: Do we get an instance for the calling type or the caller type
> - or
> maybe even for both.

Using the pertype(Pointcut) form might enable developers to
also associate aspects with the declaring types of references
used to call, get, or set.  e.g.,

   aspect A pertype(target(SecurityManager))
       Logger logger = ...;
       before() : call(* *(..)) {
           // log all calls to any SecurityManager to same log
       }
   }

This would *not* be implemented using the enclosing type but
using the declaring type of the signature itself.  (The enclosing
type and declaring type are the same for the join points that
don't roam ({static|pre}initialization, {advice}execution, handler).
They differ for the roaming join points (call, get, set).)

As with call join points, developers would have to remember that
it's the declaring type of the reference, not the referent. And
since the reference/declaring type here can be an interface, it
raises the question how those would be implemented, and whether
developers would again have to factor in limitations due to code
the implementation controls.

Personally, I'm strongly biased initially to keep the pointcut
form (partly in anticipation of better tools for correlating
and simplifying pointcuts directly), but these issues lead me
down the path proposed by Adrian, et al, which I think focuses
more on usability and the common use-cases, and allows us to
later generalize pertypewithin(TypePattern) to pertype(Pointcut).

Wes

P.S. - Eric, re:
> The major drawback as you
> mentioned turns out to be "sub-aspecting".

This could be a whole topic unto itself: when is narrowing,
widening, or otherwise changing instantiation sensible,
and when should it provoke warnings or be restricted.
In some cases, superaspects might rely upon the scopes
they define and wish to restrict the ability of subaspects
to change the scope. hmm:

  abstract aspect A requires percflow(pc()) { .. }

P.P.S. - Eric, re:
> I have actually found
> quite some use cases where you want to associate aspect instances with
> a certain object which is *not* necessarily exposed through a given
> pointcut.

It would be useful to post about these cases, at least to
see if there are any workarounds.

> ------------Original Message------------
> From: Eric Bodden <eric@xxxxxxxxx>
> To: aspectj-dev@xxxxxxxxxxx
> Date: Tue, Jan-25-2005 9:50 AM
> Subject: Re: [aspectj-dev] pertypewithin() vs. pertype() association...
>
> ----- Original Message -----
> From: "Adrian Colyer" <adrian_colyer@xxxxxxxxxx>
> > You make a good case for the pros of the PointcutDesignator form. The
> > design as documented (and mostly implemented in the tree) was a
> compromise
> > between the (A) and (B) designs in the bug report.
>
> > I'd like to hear the opinions of others on this point too, so please
> jump
> > in if you have an opinion one way or the other...
> Well, since you asked for it...
>
> I certainly would not very much go for option (B), which would allow
> *any*
> pointcut in a pertype-clause if I understood correctly. The problem
> arises
> for me, as soon as people start reasoning about expressions like
> pertype(call(* Foo.bar())). It's not quite straightforward here, what
> type
> is meant: Do we get an instance for the calling type or the caller type
> - or
> maybe even for both.
>
> >From the simplicity point of view, my personal opinion is that
> pertypewithin(<TypePattern>) would be best. The major drawback as you
> mentioned turns out to be "sub-aspecting". But if it was possible to
> have
> subclasses define their own pertype-instantiation, I guess that would
> be a
> good compromise.
>
> Another question I had in mind was if you also considered *perobject*
> instantiation rather than pertype, as proposed in the "association
> aspects"
> paper (http://portal.acm.org/citation.cfm?id=976275). I have actually
> found
> quite some use cases where you want to associate aspect instances with
> a
> certain object which is *not* necessarily exposed through a given
> pointcut.
> One such use case is for example where you want to track the state of a
> certain object in order to perform validation on it or something
> similar.
> percflow/perthis/pertarget sometimes turn out to be "too weak" for
> this.
>
> Hope that helps,
>
> Eric
>
> --
> Eric Bodden
> RWTH Aachen University
>
>
> _______________________________________________
> aspectj-dev mailing list
> aspectj-dev@xxxxxxxxxxx
> http://dev.eclipse.org/mailman/listinfo/aspectj-dev
>


_______________________________________________
aspectj-dev mailing list
aspectj-dev@xxxxxxxxxxx
http://dev.eclipse.org/mailman/listinfo/aspectj-dev