Quantcast

Introduce static members into several classes at once?

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Introduce static members into several classes at once?

Martin Schafföner-3
This time it's not a compiler bug, but a plain old user question.

I'm currently investigating how useful AspectJ is in converting/extending a
legacy code basis. I'm trying to roll out new logging mechanisms with
minimal code changes, especially to avoid boiler-plate code.

What I want to do is to inject static members into a multitude of classes
selected by a type pattern. Basically I want to have a marker annotation
@MakeLoggable, and all types on which this annotation is declared should get
a private static member. I dream of something like this:

public @interface MakeLoggable {}

Public aspect AutoLogging {
  Private static Logger (@MakeLoggable *).LOGGER /* = init code */;
}

This doesn't compile, it seems I can only declare the LOGGER member on one
type exactly, not on several at once. And I cannot find from the
documentation how I can express my wish in valid AspectJ code :-)

Is it possible at all, and if so, how?

Martin




_______________________________________________
aspectj-users mailing list
[hidden email]
https://dev.eclipse.org/mailman/listinfo/aspectj-users
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: Introduce static members into several classes at once?

Simone Gianni
Hi Martin,
you can declare fields on multiple classes at once. You need to declare an interface, then declare the field/methods/whatever on that interface, then inject the interface on the target types.

For example :

public aspect InjectInt {

  public interface WithInt {
  }

  // a Field
  public int WithInt.intField = 1;
 
  // a Method
  public void WithInt.doSomething() {
     System.out.println(intfield);
  }

  // Inject the interface to all classes in the "domain" package
  declare parents : com.mycompany.domain.* implements WithInt;

}

It should work with static fields as well, but I've never used static ITDs so try playing with it a bit. Obviously, where i wrote "com.mycompany.domain.*" you can place any "Type Pattern", so you can inject the interface on all classes annotated with an annotation, every class that extends another class or implements one or more interfaces, and any other combination you can think of.

Simone

2010/4/22 Martin Schafföner <[hidden email]>
This time it's not a compiler bug, but a plain old user question.

I'm currently investigating how useful AspectJ is in converting/extending a
legacy code basis. I'm trying to roll out new logging mechanisms with
minimal code changes, especially to avoid boiler-plate code.

What I want to do is to inject static members into a multitude of classes
selected by a type pattern. Basically I want to have a marker annotation
@MakeLoggable, and all types on which this annotation is declared should get
a private static member. I dream of something like this:

public @interface MakeLoggable {}

Public aspect AutoLogging {
 Private static Logger (@MakeLoggable *).LOGGER /* = init code */;
}

This doesn't compile, it seems I can only declare the LOGGER member on one
type exactly, not on several at once. And I cannot find from the
documentation how I can express my wish in valid AspectJ code :-)

Is it possible at all, and if so, how?

Martin




_______________________________________________
aspectj-users mailing list
[hidden email]
https://dev.eclipse.org/mailman/listinfo/aspectj-users


_______________________________________________
aspectj-users mailing list
[hidden email]
https://dev.eclipse.org/mailman/listinfo/aspectj-users
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

RE: Introduce static members into several classes at once?

Martin Schafföner-3
> you can declare fields on multiple classes at once. You need to declare
> an interface, then declare the field/methods/whatever on that
> interface, then inject the interface on the target types.

That's what I tried and what worked:

public @interface MakeLoggable {}

public interface Loggable{
  Logger getLogger();
}

public aspect AutoLogging {
 declare parents: (@MakeLoggable *) implements Loggable;

 public Logger Loggable.mLogger;

 public Logger Loggable.getLogger()
{
  return mLogger; // okay, lazy initialization here
}
}

> It should work with static fields as well, but I've never used static
> ITDs so try playing with it a bit. Obviously, where i wrote

However, if I switch the mLogger injection to

public static Logger Loggable.mLogger;

I get an error that I cannot introduce a static member into an interface.

So, can I get this done at all? If so, how?

Martin


_______________________________________________
aspectj-users mailing list
[hidden email]
https://dev.eclipse.org/mailman/listinfo/aspectj-users
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: Introduce static members into several classes at once?

Simone Gianni
Hi Martin,
yes, unfortunately you can declare a static ITD only directly on a class, not on an interface. You could change Loggable to be a class (you can use declare parents to alter also superclasses and not only interfaces), but still it will not work as expected, cause there will be only one static logger in Loggable, and not one in each class as it would happen when injecting an interface. Moreover, also when injecting an interface, if there is a hierarchy the method/field will be injected in the topmost class and not in subclasses.

You can "simulate" a static field using pertypewithin ( http://www.eclipse.org/aspectj/doc/next/adk15notebook/pertypewithin.html ), something like this :

public @interface MakeLoggable {}

public interface Loggable{
 Logger getLogger();
}


// pertypewithin : one aspect instance per type affected
// you could use Loggable+, cause all @MakeLoggable will implement Loggable as declared in next aspect
public aspect LoggerHolder pertypewithin(@MakeLoggable *) {
  // Ths is private to the aspect instance
  private Logger logger;
  public Logger getLogger() {
    return mLogger; // okay, lazy initialization here
  }
}

public aspect LoggableImpl {
 declare parents: (@MakeLoggable *) implements Loggable;

 public Logger Loggable.getLogger()
{
   Loggerholder holder = LoggerHolder.aspectOf(this.getClass());
   // Maybe check for holder != null?  just in case :D
   return holder.getLogger();
}
}

I have separated in two aspects to make it easy to read.

Another approach could be to inject the (same) Logger into each instance :

public interface Loggable{
 Logger getLogger();
 void setLogger(Logger l);
}

public aspect LoggableImpl {
 declare parents: (@MakeLoggable *) implements Loggable;
 
 private Logger Loggable.logger = null;

 public Logger Loggable.getLogger()
{
   return logger;
}
 
 public void Loggable.setLogger(Logger l) {
  logger = l;
}
}

public aspect LoggerHolder pertypewithin(Loggable+) {
  private Logger staticLogger = null;
  after(Loggable instance) : execution(* Loggable+.new(..)) && this(instance) {
    if (staticLogger == null) // init the logger
    instance.setLogger(staticLogger);
  }
}


However, this is okay if you are trying to understand how AspectJ can be used, but to produce a logging/tracing aspect there are simpler ways that don't require injecting a logger or a static logger :

public aspect LogAllCalls pertypewithin(@Logme *) {
   private Logger logger = null;
   private void initLogger(Class c) {
      // lazy init
   }

   after() : staticinitialization((@Logme *)) {
      initLogger(thisJoinPointStaticPart.getSignature().getDeclaringType());
   }

   pointcut logmethod() : execution(* (@MakeLoggable *).*(..));

   before() : logmethod() {
      logger.debug("Start of " + thisJoinPoint.toLongString() + " with parameters " + thisJoinPoint.getArgs());
   }
   after() : logmethod() {
      logger.debug("End of " + thisJoinPoint.toLongString());
   }
  
   // etc...
}


Again, I haven't tested any code snippet in this mail (so they will contain errors and typos :)) , but should give you an idea.

Hope this helps,
Simone

2010/4/22 Martin Schafföner <[hidden email]>
> you can declare fields on multiple classes at once. You need to declare
> an interface, then declare the field/methods/whatever on that
> interface, then inject the interface on the target types.

That's what I tried and what worked:

public @interface MakeLoggable {}

public interface Loggable{
 Logger getLogger();
}

public aspect AutoLogging {
 declare parents: (@MakeLoggable *) implements Loggable;

 public Logger Loggable.mLogger;

 public Logger Loggable.getLogger()
{
 return mLogger; // okay, lazy initialization here
}
}

> It should work with static fields as well, but I've never used static
> ITDs so try playing with it a bit. Obviously, where i wrote

However, if I switch the mLogger injection to

public static Logger Loggable.mLogger;

I get an error that I cannot introduce a static member into an interface.

So, can I get this done at all? If so, how?

Martin


_______________________________________________
aspectj-users mailing list
[hidden email]
https://dev.eclipse.org/mailman/listinfo/aspectj-users


_______________________________________________
aspectj-users mailing list
[hidden email]
https://dev.eclipse.org/mailman/listinfo/aspectj-users
Loading...