Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[aspectj-dev] (Post 1.1!) AspectJ Language Extension Proposal: Named scopes


(Warning: long note).

Returning from last weeks AOSD conference, I was inspired to think
about what minimal changes could be made to the AspectJ language to
start to bring some of the benefits of other AOSD approaches to the
AspectJ language, without sacrificing any of AspectJ's simplicity,
directness and ease of use. I came up with the simple proposal below,
to add named scopes to the language. On initial investigation this
seems to add significant expressiveness to the language without
raising complexity.

Disclaimers: I am not a Language Designer (IANALD), and this concept
this not undergone any real review. I put it out as an alpha proposal
to start a discussion and gather feedback.

Motivation:
===========
Advice can be applied to anonymous pointcuts, or alternately pointcuts
may be named and be combined in expressions. Naming of pointcuts gives
additional and important expressive power to the language.

Pointcuts (named or anonymous) refer to static scopes (by name
patterns). These scopes are always anonymous (explicitly
enumerated). Naming of scopes gives additional and important
expressive power to the language - allowing me to express my intent at
a higher level of abstraction. As well as the power gained from naming
and operating on scopes, this proposal comes from the recognition that
the same collection of type specifications is often scattered across
several pointcut declarations causing redundancy and maintenance overhead.

For the examples that follow, I will use a J2EE Application Server,
since most people have an intuitive idea of what the "components"
within such a server might be (EJB container, web container, ORB
etc.).

Using Named Scopes: Pointcut Designators
========================================

Suppose we declare a scope "ejbContainer"

scope ejbContainer: .... ;

(rules for declaring scope are given later), then the following rules apply:

1) The literal text "ejbContainer" when used in a pointcut where a type
   pattern is expected refers to the ejbContainer scope.

    1a) If immediately followed or preceded by "*" wildcard
    character, then "ejbContainer" instead refers to the literal
    characters "ejbContainer" preceded or followed by any other
    characters, and not to the scope defined by "ejbContainer".

    1b) "ejbContainer+" means any type declared in the ejbContainer
    scope, or any of their subtypes (whether those subtypes are within
    the scope or not).


2)call pointcut designators

     2a) call([visibility] <return-type>
              ejbContainer.<name-pattern>(<arg-pattern>))

        means any call to any method in the ejbContainer matching the
        given name and arguments (with the given visibility and
        return-type).

     2b) call([visibility] ejbContainer.new(<arg-pattern>))

        means any call to any constructor in the ejbContainer matching
        the given arguments (with the given visibility if specified).

3) execution pointcut designators

     3a) execution([visibility] <return-type>
              ejbContainer.<name-pattern>(<arg-pattern>))

        means execution of any method in the ejbContainer matching the
        given name and arguments (with the given visibility and
        return-type).

     3b) execution([visibility] ejbContainer.new(<arg-pattern>))

        means execution of any constructor in the ejbContainer
        matching the given arguments (with the given visibility if
        specified).


4) initialization & prenitialization pointcut designators

   4a) [pre]initialization(ejbContainer.new(<arg-pattern>))

       means [pre]initialization of any object whose type is defined
       in the ejbContainer where the first constructor called in the
       type matches the given signature.

5) get pointcut designators

   5a) get([visibility] <field-type> ejbContainer.<name-pattern>)

       means any reference to any field in the ejbContainer matching
       the given name pattern (with the given visibility and type).

6) set pointcut designators

   6a) set([visibility] <field-type> ejbContainer.<name-pattern>)

       means any reference to any field in the ejbContainer matching
       the given name pattern (with the given visibility and type).

7) staticinitialization pointcut designators

   7a) staticinitialization(ejbContainer)

       means the staticinitialization of any type in the ejbContainer.

8) handler pointcut designators

   8a) handler(ejbContainer)

       means any handler handling a type defined in the ejbContainer

9) within pointcut designators

   9a) within(ejbContainer)

       means all join points where the code executing is defined in
       the types in ejbContainer

10)withincode pointcut designators

   10a) withincode([visibility] <return-type>
                        ejbContainer.<name-pattern>(arg-pattern))

        means all join points where the code executing is defined in a
        method within the ejbContainer matching the given name and
        argument pattern (and also visibility and return-type).

   10a) withincode([visibility] ejbContainer.new(arg-pattern))

        means all join points where the code executing is defined in a
        constructor within the ejbContainer matching the argument
        pattern (and also visibility).


11) this, target, args, cflow, cflowbelow and if

    named scopes cannot be used with these pointcut designators.


Using Named Scopes: Declarations
================================

1) declare parents: ejbContainer extends <typeList>

   All types defined in the ejbContainer scope are to extend the types
   of typeList. I think this form is likely to be seldom used, but is
   included for completeness since the "implements" form seems powerful.

2) declare parents: ejbContainer implements <typeList>

   All types defined in the ejbContainer implement the types in
   typeList.

3) declare warning/error

   I speculate a couple of new forms for declare warning/error that seem
   powerful and work with scopes

   3a) declare warning: isEmptyScope(ejbContainer)

       declares a warning if the ejbContainer scope is empty - likely to
       be rarely used in this form, but the negation is more useful:

       declare warning: !isEmptyScope(ejbContainer && orb)

       - this says that the intersection of the orb and ejbContainer
       should be empty. (Still seems better than
       nonEmptyScope(<scope>) and !nonEmptyScope(scope>) which
       introduces a double negative).

   3b) declare warning: connection(ejbContainer, webContainer)

       this is equivalent to:
       declare warning:
          ( call(* webContainer.*(..)) || call(webContainer.new(..))
              get(* webContainer.*) || set(* webContainer.*)
          ) & within(ejbContainer);

       it raises a warning if there is any direct connection between
       two components (scopes).


Declaring Named Scopes:
=======================

1) informal grammar of scope declarations:

   scope_decl := "scope" scope_name ":" scope ";"

   scope := simple_scope | scope_expression

   scope_expression := "(" scope ")" |
                       "!" scope |
                          scope "&&" scope |
                        scope "||" scope

   scope := scope_name |
            "packages(" package_pattern ")" |
            "types(" type_pattern ")" |
            "methods(" method_pattern ")" |
            "constructors(" constructor_pattern ")" |
            "fields(" field_pattern ")"

         
* Adding a package to a scope adds all of the types, methods, fields and
  constructors defined in the package.

* Adding a type to a scope adds all of the (inner) types, methods,
  fields and constructors defined in the type.

* Scopes are declared in the same namespace as types, so it is an
  error to declare a scope and a type with the same (fully-qualified) name.

2) sample scope declarations

  2a) scope ejbContainer: packages(com.xyz.ejb.container..*);

      the ejbContainer comprises all packages com.xyz.ejb.container
      and sub-packages - together with all the types, methods,
      constructors and fields defined in those packages.

  2b) scope ejbFeature: ejbContainer || methods(* *.ejb*(..));

      the ejb feature comprises the ejbContainer plus any method whose
      name begins with "ejb", wherever it may be declared.

  2c) scope persistentObjects: types(<type-pattern1>) ||
                  types(<type-pattern2>)

      persistentObjects comprises all the types in type-pattern1 or
      type-pattern2, together with all the methods, constructors and
      fields declared in those types.

  2d) scope personnel: packages(personnelpayroll..*) &&
                       !methods(* *.*pay*(..)) &&
                           !methods(* *.*salary*(..)) &&
                           !fields(* *pay*) &&
                       !fields(* *salary*);

      personnel comprises all the types, methods, constructors and
      fields defined in the personnelpayroll package and subpackages,
      except for methods and fields that contain either "pay" or
      "salary" in their name.


Examples of Usage:
==================

1) call(ejbContainer.*(..)) && !within(ejbContainer)
   any call to an ejbContainer method that  originates outside the container.

2) handler(ejbContainer)
   exception handling for any exceptions defined within the ejbContainer

3) handler(*) && within(ejbContainer)
   exception handling for any exceptions caught within the ejbContainer

4) cflow(execution(ejbContainer.*(..)) || execution(ejbContainer.new(..)))
   everything downstream of an ejbContainer method or constructor

5) after(Object o) returning:
     execution(persistentObjects.new(..)) && target(o) {
      ...
   }
   after construction of a persistent object...

6) after(Object o) returning: set(* persistentObjects.*) && target(o) {
     ...
   }
   after setting a field of a persistent object...

7) declare parents: peristentObjects implements Serializable;
   all persistentObjects implement the Serializable interface

8) declare error: connection( kernel, middleware );
   the kernel should not make any "upcalls" to the middleware layer

Note in the examples how we can refer repeatedly to (e.g.) the
ejbContainer without having to duplicate in each place the set of
patterns that define it. The code also reads more clearly and
specifies our intent more precisely because we have been able to name
the scope.

IDE Support
===========

A scope declaration should appear in the outline view of the file in
which it is declared. Expansion of the scope declaration node shows
the contents of the scope in a tree view. Since scopes may be "slices"
that crosscut the dominant decomposition (i.e. may contain only parts
of a type or package), then double clicking on an entity in the scope
tree should ideally open up an editor showing just the parts of the
entity defined within the scope.

This could also integrate nicely with a tool like FEAT that enables you
to build up scope (concern) declarations interactively by exploration.

Implementation
==============

From an implementation perspective this appears on first glance to be
a reasonably well contained extension to the language: a scope name
can be used in place of a package/type name pattern and is easily
resolved statically to obtain to all the
packages/types/methods/constructors/fields defined within the scope as
needed.

-- Adrian.
Adrian_Colyer@xxxxxxxxxx

Back to the top