diff options
author | wisberg <wisberg> | 2003-08-06 02:08:40 +0000 |
---|---|---|
committer | wisberg <wisberg> | 2003-08-06 02:08:40 +0000 |
commit | a63bc04fb1cb6e8d6d0bc2a509ab9658e3d78c43 (patch) | |
tree | cd4e827c60136df7bf9850591b0c64baff549d20 /docs | |
parent | b0d37c4b51a344bee94bb7f7cc1ecef1a233e3ab (diff) | |
download | aspectj-a63bc04fb1cb6e8d6d0bc2a509ab9658e3d78c43.tar.gz aspectj-a63bc04fb1cb6e8d6d0bc2a509ab9658e3d78c43.zip |
initial checkin of the sandbox.
The basic structure and examples of each type are there,
but I have more examples and the ones there are not
altogether validated. I'll make a few more changes
before emailing dev and users about usage, etc.
Diffstat (limited to 'docs')
33 files changed, 2114 insertions, 0 deletions
diff --git a/docs/sandbox/api-clients/org/aspectj/samples/JoinPointCollector.java b/docs/sandbox/api-clients/org/aspectj/samples/JoinPointCollector.java new file mode 100644 index 000000000..66f62386f --- /dev/null +++ b/docs/sandbox/api-clients/org/aspectj/samples/JoinPointCollector.java @@ -0,0 +1,69 @@ +/* @author Mik Kersten */ + +// START-SAMPLE api-ajde-modelWalker Walk model to collect join point information for advised methods and constructors +package org.aspectj.samples; + +import java.util.*; +import org.aspectj.tools.ajc.Main; + + +import org.aspectj.asm.*; + +/** + * Collects join point information for all advised methods and constructors. + * + * @author Mik Kersten + */ +public class JoinPointCollector extends Main { + + /** + * @param args + */ + public static void main(String[] args) { + String[] newArgs = new String[args.length +1]; + newArgs[0] = "-emacssym"; + for (int i = 0; i < args.length; i++) { + newArgs[i+1] = args[i]; + } + new JoinPointCollector().runMain(newArgs, false); + } + + public void runMain(String[] args, boolean useSystemExit) { + super.runMain(args, useSystemExit); + + ModelWalker walker = new ModelWalker() { + public void preProcess(StructureNode node) { + ProgramElementNode p = (ProgramElementNode)node; + + // first check if it is a method or constructor + if (p.getProgramElementKind().equals(ProgramElementNode.Kind.METHOD)) { + + // now check if it is advsied + for (Iterator it = p.getRelations().iterator(); it.hasNext(); ) { + + RelationNode relationNode = (RelationNode)it.next(); + Relation relation = relationNode.getRelation(); + if (relation == AdviceAssociation.METHOD_RELATION) { + System.out.println("method: " + p.toString() + ", advised by: " + relationNode.getChildren()); + } + } + } + + // code around the fact that constructor advice relationship is on the type + if (p.getProgramElementKind().equals(ProgramElementNode.Kind.CONSTRUCTOR)) { + for (Iterator it = ((ProgramElementNode)p.getParent()).getRelations().iterator(); it.hasNext(); ) { + RelationNode relationNode = (RelationNode)it.next(); + Relation relation = relationNode.getRelation(); + if (relation == AdviceAssociation.CONSTRUCTOR_RELATION) { + System.out.println("constructor: " + p.toString() + ", advised by: " + relationNode.getChildren()); + } + } + } + } + }; + + StructureModelManager.getDefault().getStructureModel().getRoot().walk(walker); + } +} +//END-SAMPLE api-ajde-modelWalker + diff --git a/docs/sandbox/common/caching/Caching.java b/docs/sandbox/common/caching/Caching.java new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/docs/sandbox/common/caching/Caching.java diff --git a/docs/sandbox/common/com/company/app/AppException.java b/docs/sandbox/common/com/company/app/AppException.java new file mode 100644 index 000000000..aa23eeff8 --- /dev/null +++ b/docs/sandbox/common/com/company/app/AppException.java @@ -0,0 +1,11 @@ + +package com.company.app; + +public class AppException extends Exception { + public AppException() { + super(); + } + public AppException(String s) { + super(s); + } +}
\ No newline at end of file diff --git a/docs/sandbox/common/com/company/app/Main.java b/docs/sandbox/common/com/company/app/Main.java new file mode 100644 index 000000000..8d04c0ec9 --- /dev/null +++ b/docs/sandbox/common/com/company/app/Main.java @@ -0,0 +1,40 @@ + +package com.company.app; + +import java.util.Arrays; +import org.aspectj.lang.SoftException; + +public class Main implements Runnable { + public static void main(String[] argList) { + new Main().runMain(argList); + } + + String[] input; + + void spawn() { + new Thread(this, toString()).start(); // KEEP CE 15 declares-factory + } + + public void runMain(String[] argList) { + this.input = argList; + run(); + } + + public void run() { + String[] input = this.input; + String s = ((null == input) || (0 == input.length)) + ? "[]" + : Arrays.asList(input).toString(); + System.out.println("input: " + s); + try { + doDangerousThings(); // KEEP CW 30 declares-exceptionSpelunking + } catch (AppException e) { // KEEP CW 31 declares-exceptionSpelunking + e.printStackTrace(System.err); + } + } + + private void doDangerousThings() throws AppException { // KEEP CW 38 + + } + +}
\ No newline at end of file diff --git a/docs/sandbox/common/com/company/lib/Factory.java b/docs/sandbox/common/com/company/lib/Factory.java new file mode 100644 index 000000000..3abb207c9 --- /dev/null +++ b/docs/sandbox/common/com/company/lib/Factory.java @@ -0,0 +1,14 @@ + +package com.company.lib; + +public class Factory { + + public static Thread makeThread(Runnable runnable, String name) { + class MyThread extends Thread { + MyThread(Runnable runnable, String name) { + super(runnable, name); + } + } + return new MyThread(runnable, name); + } +}
\ No newline at end of file diff --git a/docs/sandbox/common/company.lst b/docs/sandbox/common/company.lst new file mode 100644 index 000000000..64c4786f7 --- /dev/null +++ b/docs/sandbox/common/company.lst @@ -0,0 +1,2 @@ +com/company/app/*.java +com/company/lib/*.java diff --git a/docs/sandbox/common/declares/Declares.java b/docs/sandbox/common/declares/Declares.java new file mode 100644 index 000000000..4f763440b --- /dev/null +++ b/docs/sandbox/common/declares/Declares.java @@ -0,0 +1,140 @@ + +package declares; + +import com.company.app.*; +import java.rmi.RemoteException; +import java.io.IOException; + +/** + * @author Wes Isberg + */ +aspect A { + + // START-SAMPLE declares-threadFactoryRequired Error when not using Thread factory + /** signal error if Thread constructor called outside our Thread factory */ + declare error : call(Thread+.new(..)) && within(com.company..*) + && !withincode(Thread com.company.lib.Factory.makeThread(..)) : + "constructing threads prohibited - use Factory.makeThread(..)"; + // END-SAMPLE declares-threadFactoryRequired + +} + +/* @author Wes Isberg */ + +aspect TypeConstraints { + +// START-SAMPLE declares-typeConstraints Using declare to enforce type constraints +protected interface SoughtException {} +// XXX ajc broken here? +/** + * Require that any SoughtException implementation be + * a subclass of Throwable. This picks out the mistake + * of declaring SoughtException a parent of something + * that is not an exception at all. + */ +declare error : staticinitialization(SoughtException+) + && ! staticinitialization(SoughtException) + && ! staticinitialization(Throwable+) : + "all SoughtException must be subclasses of Throwable"; +// END-SAMPLE declares-typeConstraints Using declare to enforce type constraints +} + +// START-SAMPLE declares-exceptionSpelunking Using declare warning to find Exception-related code + +/** + * List AppException catch blocks and callers as a way + * of investigating a possibly-large code base. + */ +aspect SeekAppExceptions { + pointcut withinScope() : within(com.company..*); + + /** + * Find calls to stuff that throws AppException. + */ + declare warning : withinScope() && + (call(* *(..) throws AppException+) + || call(new(..) throws AppException+)) : + "fyi, another call to something that can throw IOException"; + + /** + * Find catch clauses handling AppException + */ + declare warning : withinScope() && handler(AppException+): + "fyi, code that handles AppException"; +} +// END-SAMPLE declares-exceptionSpelunking + + +/** @author Jim Hugunin, Wes Isberg */ + +class RuntimeRemoteException extends RuntimeException { + RuntimeRemoteException(RemoteException e) {} +} + +// XXX untested sample declares-softenRemoteException + +// START-SAMPLE declares-softenRemoteException + +/** + * Convert RemoteExceptions to RuntimeRemoteException + * and log them. Enable clients that don't handle + * RemoteException. + */ +aspect HandleRemoteException { + /** + * Declare RemoteException soft to enable use by clients + * that are not declared to handle RemoteException. + */ + declare soft: RemoteException: throwsRemoteException(); + + /** + * Pick out join points to convert RemoteException to + * RuntimeRemoteException. + * This implementation picks out + * execution of any method declared to throw RemoteException + * in our library. + */ + pointcut throwsRemoteException(): within(com.company.lib..*) + && execution(* *(..) throws RemoteException+); + + /** + * This around advice converts RemoteException to + * RuntimeRemoteException at all join points picked out + * by <code>throwsRemoteException()</code>. + * That means *no* RemoteException will be thrown from + * this join point, and thus that none will be converted + * by the AspectJ runtime to <code>SoftException</code>. + */ + Object around(): throwsRemoteException() { + try { + return proceed(); + } catch (RemoteException re) { + re.printStackTrace(System.err); + throw new RuntimeRemoteException(re); + } + } +} +//END-SAMPLE declares-softenRemoteException + +/* + XXX another declare-soft example from Jim: + +aspect A { + +pointcut check(): + within(com.foo.framework.persistence.*) && + executions(* *(..)); + +declare soft: SQLException: check(); + +after () throwing (SQLException sqlex): check() { + if (sql.getSQLCode().equals("SZ001")) { + throw new AppRuntimeException("Non-fatal Database error occurred.", + "cache refresh failure", sqlex); + } else { + throw new AppFatalRuntimeException( + "Database error occurred - contact support", sqlex); + } +} +} +*/ diff --git a/docs/sandbox/common/language/Context.java b/docs/sandbox/common/language/Context.java new file mode 100644 index 000000000..f0021a28a --- /dev/null +++ b/docs/sandbox/common/language/Context.java @@ -0,0 +1,70 @@ + +package language; + +public class Context { + public static void main(String[] argList) { + new C().run(); + } +} + +class C { + static int MAX = 2; + int i; + C() { + i = 1; + } + public void run() { + try { + more(); + } catch (MoreError e) { + // log but continue + System.out.println(e.getMessage()); + } + } + + private void more() { + i++; + if (i >= MAX) { + i = 0; + throw new MoreError(); + } + } + static class MoreError extends Error { + MoreError() { + super("was too much!"); + } + } +} + +/** @author Erik Hilsdale, Wes Isberg */ +aspect A { + + // START-SAMPLE language-fieldSetContext Check input and result for a field set. + /** + * Check input and result for a field set. + */ + void around(int input, C targ) : set(int C.i) + && args(input) && target(targ) { + String m = "setting C.i=" + targ.i + " to " + input; + System.out.println(m); + proceed(input, targ); + if (targ.i != input) { + throw new Error("expected " + input); + } + } + // END-SAMPLE language-fieldSetContext + + // START-SAMPLE language-handlerContext Log exception being handled + /** + * Log exception being handled + */ + before (C.MoreError e) : handler(C.MoreError) + && args(e) && within(C) { + System.out.println("handling " + e); + } + // END-SAMPLE language-handlerContext + + // See Initialization.java for constructor call, + // constructor execution, and {pre}-initialization + +}
\ No newline at end of file diff --git a/docs/sandbox/common/language/ControlFlow.java b/docs/sandbox/common/language/ControlFlow.java new file mode 100644 index 000000000..d832259db --- /dev/null +++ b/docs/sandbox/common/language/ControlFlow.java @@ -0,0 +1,43 @@ + +package language; + +public class ControlFlow { + public static void main(String[] argList) { + Fact.factorial(6); + } +} + +class Fact { + static int factorial(int i) { + if (i < 0) { + throw new IllegalArgumentException("negative: " + i); + } + if (i > 100) { + throw new IllegalArgumentException("big: " + i); + } + return (i == 0 ? 1 : i * factorial(i-1)); + } +} + +/** + * Demonstrate recursive calls. + * @author Erik Hilsdale + */ +aspect LogFactorial { + // START-SAMPLE language-cflowRecursionBasic Pick out latest and original recursive call + /** call to factorial, with argument */ + pointcut f(int i) : call(int Fact.factorial(int)) && args(i); + + /** print most-recent recursive call */ + before(int i, final int j) : f(i) && cflowbelow(f(j)) { + System.err.println(i + "-" + j); + } + + /** print initial/topmost recursive call */ + before(int i, final int j) : f(i) + && cflowbelow(cflow(f(j)) && !cflowbelow(f(int))) { + System.err.println(i + "@" + j); + } + // END-SAMPLE language-cflowRecursionBasic +} + diff --git a/docs/sandbox/common/language/Initialization.java b/docs/sandbox/common/language/Initialization.java new file mode 100644 index 000000000..46d3ec872 --- /dev/null +++ b/docs/sandbox/common/language/Initialization.java @@ -0,0 +1,95 @@ + +package language; + +public class Initialization { + public static void main(String[] argList) { + Thing thing = new Thing(); + if (12 != thing.counter) { + System.err.println("expected 12, got " + thing.counter); + } + thing = new Thing(20); + if (32 != thing.counter) { + System.err.println("expected 32, got " + thing.counter); + } + thing = new AnotherThing(); + if (2 != thing.counter) { + System.err.println("expected 2, got " + thing.counter); + } + thing = new AnotherThing(20); + if (23 != thing.counter) { + System.err.println("expected 23, got " + thing.counter); + } + } +} +/** @author Erik Hilsdale, Wes Isberg */ + +// START-SAMPLE language-initialization Understanding object creation join points +/* + * To work with an object right when it is constructed, + * understand the differences between the join points for + * constructor call, constructor execution, and initialization. + */ +class Thing { + int counter; + Thing() { + this(1); + } + Thing(int value) { + counter = value; + } +} + +class AnotherThing extends Thing { + AnotherThing() { + super(); + } + + AnotherThing(int i) { + super(++i); + } +} + +aspect A { + /** + * After any call to any constructor, fix up the thing. + * In AspectJ 1.1, this only affects callers in the input + * classes or source files, but not super calls. + */ + after() returning (Thing thing): + call(Thing.new(..)) { + postInitialize(thing); + } + + /** + * After executing the int constructor, fix up the thing. + * This works regardless of how the constructor was called + * (by outside code or by super), but only for the + * specified constructors. + */ + after() returning (Thing thing): execution(Thing.new(int)) { + thing.counter++; + } + + /** + * DANGER -- BAD!! Before executing the int constructor, + * this uses the target object, which is not constructed. + */ + before (Thing thing): this(thing) && execution(Thing.new(int)) { + // thing.counter++; // DANGER!! thing not constructed yet. + } + + /** + * This advises all Thing constructors in one join point, + * even if they call each other with this(). + */ + after(Thing thing) returning: this(thing) + && initialization(Thing.new(..)) { + thing.counter++; + } + + protected void postInitialize(Thing thing) { + thing.counter += 10; + } +} +//END-SAMPLE language-initialization + diff --git a/docs/sandbox/common/libraries/PointcutLibraryTest.java b/docs/sandbox/common/libraries/PointcutLibraryTest.java new file mode 100644 index 000000000..e110c7ced --- /dev/null +++ b/docs/sandbox/common/libraries/PointcutLibraryTest.java @@ -0,0 +1,95 @@ +package libraries; + +/** @author Wes Isberg */ +public class PointcutLibraryTest { + public static void main(String[] a) { + new Test().run(); + } +} + +class Test { + public Test() {} + public void run(){ prun(); } + private void prun() { + System.out.println("Test.prun()"); + } +} + +// START-SAMPLE library-classPointcutLibrary Defining library pointcuts in a class +/** private default implementation of library */ +class PrivatePointcutLibrary { + pointcut adviceCflow() : cflow(adviceexecution()); + pointcut publicCalls() : call(public * *(..)) + && !adviceCflow() + ; +} + +/** public interface for library */ +class PointcutLibrary extends PrivatePointcutLibrary { +} + +// ---- different clients of the library + +/** client by external reference to library */ +aspect ExternalClientOfLibrary { + before() : PointcutLibrary.publicCalls() { + System.out.println("XCL: " + + thisJoinPointStaticPart); + } +} + +/** use library by inheriting scope in aspect */ +aspect AEL extends PointcutLibrary { + before() : publicCalls() { + System.out.println("AEL: " + + thisJoinPointStaticPart); + } +} + +/** use library by inheriting scope in class */ +class CEL extends PointcutLibrary { + static aspect A { + before() : publicCalls() { + System.out.println("CEL: " + + thisJoinPointStaticPart); + } + } +} + +/** more clients by inheritance */ +aspect CELSubAspect extends CEL { + before() : publicCalls() { + System.out.println("CSA: " + + thisJoinPointStaticPart); + } +} + + +// ---- redefining library pointcuts + +//-- affect all clients of PointcutLibrary +// test: XCL advises Test() +class VendorPointcutLibrary extends PrivatePointcutLibrary { + /** add calls to public constructors */ + pointcut publicCalls() : PrivatePointcutLibrary.publicCalls() + || (call(public new(..)) && !adviceCflow()); + static aspect A { + declare parents: + PointcutLibrary extends VendorPointcutLibrary; + } +} + +//-- only affect CEL, subtypes, & references thereto +// test: CSA does not advise call(* println(String)) +// test: CSA advises call(* prun()) +class CPlus extends PointcutLibrary { + /** add calls to private methods, remove calls to java..* */ + pointcut publicCalls() : (PointcutLibrary.publicCalls() + || (call(private * *(..)) && !adviceCflow())) + && (!(call(* java..*.*(..)) || call(java..*.new(..)))); + static aspect A { + declare parents: CEL extends CPlus; + } +} +// END-SAMPLE library-classPointcutLibrary + diff --git a/docs/sandbox/common/tracing/Logging.java b/docs/sandbox/common/tracing/Logging.java new file mode 100644 index 000000000..349abb235 --- /dev/null +++ b/docs/sandbox/common/tracing/Logging.java @@ -0,0 +1,28 @@ + +package tracing; + +import org.aspectj.lang.Signature; + +/** + * @author Wes Isberg + */ +aspect A { + // START-SAMPLE tracing-simpleTiming Record time to execute public methods + /** record time to execute my public methods */ + Object around() : execution(public * com.company..*.* (..)) { + long start = System.currentTimeMillis(); + try { + return proceed(); + } finally { + long end = System.currentTimeMillis(); + recordTime(start, end, + thisJoinPointStaticPart.getSignature()); + } + } + // implement recordTime... + // END-SAMPLE tracing-simpleTiming + + void recordTime(long start, long end, Signature sig) { + // to implement... + } +} diff --git a/docs/sandbox/common/tracing/TraceJoinPoints.java b/docs/sandbox/common/tracing/TraceJoinPoints.java new file mode 100644 index 000000000..fd42a0fde --- /dev/null +++ b/docs/sandbox/common/tracing/TraceJoinPoints.java @@ -0,0 +1,132 @@ + +// START-SAMPLE tracing-traceJoinPoints Trace join points executed to log +/* TraceJoinPoints.java */ + +package tracing; + +import org.aspectj.lang.*; +import org.aspectj.lang.reflect.*; +import java.io.*; + +/** + * Print join points being executed in context to a log.xml file. + * To use this, define the abstract pointcuts in a subaspect. + * @author Jim Hugunin, Wes Isberg + */ +public abstract aspect TraceJoinPoints + extends TraceJoinPointsBase { + + // abstract protected pointcut entry(); + + PrintStream out; + int logs = 0; + int depth = 0; + boolean terminal = false; + + /** + * Emit a message in the log, e.g., + * <pre>TraceJoinPoints tjp = TraceJoinPoints.aspectOf(); + * if (null != tjp) tjp.message("Hello, World!");</pre> + */ + public void message(String s) { + out.println("<message>" + prepareMessage(s) + "</message>"); + } + + protected void startLog() { + makeLogStream(); + } + + protected void completeLog() { + closeLogStream(); + } + + protected void logEnter(JoinPoint.StaticPart jp) { + if (terminal) out.println(">"); + indent(depth); + out.print("<" + jp.getKind()); + writeSig(jp); + writePos(jp); + + depth += 1; + terminal = true; + } + + protected void logExit(JoinPoint.StaticPart jp) { + depth -= 1; + if (terminal) { + getOut().println("/>"); + } else { + indent(depth); + getOut().println("</" + jp.getKind() + ">"); + } + terminal = false; + } + + protected PrintStream getOut() { + if (null == out) { + String m = "not in the control flow of entry()"; + throw new IllegalStateException(m); + } + return out; + } + + protected void makeLogStream() { + try { + String name = "log" + logs++ + ".xml"; + out = new PrintStream(new FileOutputStream(name)); + } catch (IOException ioe) { + out = System.err; + } + } + + protected void closeLogStream() { + PrintStream out = this.out; + if (null != out) { + out.close(); + // this.out = null; + } + } + + /** @return input String formatted for XML */ + protected String prepareMessage(String s) { // XXX unimplemented + return s; + } + + void message(String sink, String s) { + if (null == sink) { + message(s); + } else { + getOut().println("<message sink=" + quoteXml(sink) + + " >" + prepareMessage(s) + "</message>"); + } + } + + void writeSig(JoinPoint.StaticPart jp) { + PrintStream out = getOut(); + out.print(" sig="); + out.print(quoteXml(jp.getSignature().toShortString())); + } + + void writePos(JoinPoint.StaticPart jp) { + SourceLocation loc = jp.getSourceLocation(); + if (loc == null) return; + PrintStream out = getOut(); + + out.print(" pos="); + out.print(quoteXml(loc.getFileName() + + ":" + loc.getLine() + + ":" + loc.getColumn())); + } + + protected String quoteXml(String s) { // XXX weak + return "\"" + s.replace('<', '_').replace('>', '_') + "\""; + } + + protected void indent(int i) { + PrintStream out = getOut(); + while (i-- > 0) out.print(" "); + } +} +// END-SAMPLE tracing-traceJoinPoints + +
\ No newline at end of file diff --git a/docs/sandbox/common/tracing/TraceJoinPointsBase.java b/docs/sandbox/common/tracing/TraceJoinPointsBase.java new file mode 100644 index 000000000..d06423001 --- /dev/null +++ b/docs/sandbox/common/tracing/TraceJoinPointsBase.java @@ -0,0 +1,53 @@ + +// START-SAMPLE tracing-traceJoinPoints Trace join points executed +/* TraceJoinPointsBase.java */ + +package tracing; + +import org.aspectj.lang.JoinPoint; + +/** + * Trace join points being executed in context. + * To use this, define the abstract members in a subaspect. + * <b>Warning</b>: this does not trace join points that do not + * support after advice. + * @author Jim Hugunin, Wes Isberg + */ +abstract aspect TraceJoinPointsBase { + // this line is for AspectJ 1.1 + // for Aspectj 1.0, use "TraceJoinPointsBase dominates * {" + declare precedence : TraceJoinPointsBase, *; + + abstract protected pointcut entry(); + + protected pointcut exit(): call(* java..*.*(..)); + + final pointcut start(): entry() && !cflowbelow(entry()); + + final pointcut trace(): cflow(entry()) + && !cflowbelow(exit()) && !within(TraceJoinPointsBase+); + + private pointcut supportsAfterAdvice() : !handler(*) + && !preinitialization(new(..)); + + before(): start() { startLog(); } + + before(): trace() && supportsAfterAdvice(){ + logEnter(thisJoinPointStaticPart); + } + + after(): trace() && supportsAfterAdvice() { + logExit(thisJoinPointStaticPart); + } + + after(): start() { completeLog(); } + + abstract protected void logEnter(JoinPoint.StaticPart jp); + abstract protected void logExit(JoinPoint.StaticPart jp); + abstract protected void startLog(); + abstract protected void completeLog(); +} + +// END-SAMPLE tracing-traceJoinPoints + +
\ No newline at end of file diff --git a/docs/sandbox/common/tracing/TraceMyJoinPoints.java b/docs/sandbox/common/tracing/TraceMyJoinPoints.java new file mode 100644 index 000000000..a5aa686d6 --- /dev/null +++ b/docs/sandbox/common/tracing/TraceMyJoinPoints.java @@ -0,0 +1,17 @@ + + +// START-SAMPLE tracing-traceJoinPoints Trace to log join points executed by main method +/* TraceMyJoinPoints.java */ + +package tracing; + +import com.company.app.Main; + +/** + * Trace all join points in company application. + * @author Jim Hugunin, Wes Isberg + */ +aspect TraceMyJoinPoints extends TraceJoinPoints { + protected pointcut entry() : execution(void Main.runMain(String[])); +} +// END-SAMPLE tracing-traceJoinPoints diff --git a/docs/sandbox/inoculated/buildRun.sh b/docs/sandbox/inoculated/buildRun.sh new file mode 100644 index 000000000..8f3c283c3 --- /dev/null +++ b/docs/sandbox/inoculated/buildRun.sh @@ -0,0 +1,20 @@ +#!/bin/sh
+
+JDKDIR="${JDKDIR:-${JAVA_HOME:-`setjdk.sh`}}"
+AJ_HOME="${AJ_HOME:-`setajhome.sh`}"
+PS="${PS:-;}"
+ajrt=`pathtojava.sh "$AJ_HOME/lib/aspectjrt.jar"`
+mkdir -p ../classes
+
+for i in *.java; do
+ pack=`sed -n '/package/s|.*package *\([^ ][^ ]*\)[ ;].*|\1|p' "$i"`
+ [ -n "$pack" ] && pack="${pack}."
+ rm -rf classes/*
+ cname=$pack`basename $i .java`
+ echo ""
+ echo "########## $cname"
+ $AJ_HOME/bin/ajc -d ../classes -classpath "$ajrt" "$i"
+ && $JDKDIR/bin/java -classpath "../classes${PS}$ajrt" $cname
+done
+
+rm -rf ../classes
diff --git a/docs/sandbox/inoculated/readme.internal.txt b/docs/sandbox/inoculated/readme.internal.txt new file mode 100644 index 000000000..61d69591a --- /dev/null +++ b/docs/sandbox/inoculated/readme.internal.txt @@ -0,0 +1,54 @@ +// XXX do not distribute + +------ contents +05sd.Isberg40-43,76.pdf # not for distribution +BufferTest.java +CompileTime.java +Injection.java +MainFailure.java +RecordingInput.java +RoundTrip.java +RunTime.java +RuntimeWrites.java +StubReplace.java +buildRun.sh +readme.internal.txt # not for distribution +readme.txt + +------ summary of todo's +- consider moving to packages, combining PrinterStream, etc. +- use DOS linefeeds - check throughout (also line length) +- see XXX + - assess handling of one style mistake + - see if second mistake was actually in article - corrected in code + +------ fyi +- standard of care: show language, not problem +- formatting: lineation, line width, DOS linefeeds, etc. +- organization: + - code currently compiles/runs one at a time + and does not compile all at once b/c of + common fixtures (PrinterStream...) + - currently packages (com.xerox.printing) in base dir + +- Copyright/license: examples, ,but PARC Inc. +- article code unit flagged with "article page #" + +------ style fyi +- flagging style mistake in StubReplace.java: + + // XXX style mistake in article code + //pointcut printerStreamTestCalls() : call(* PrinterStream+.write()); + +- leaving CompileTime.java use of + in call for factory pointcut: + + call(Point+ SubPoint+.create(..)) + + - for static methods where the method name specification + involves no * but does reflect a factory naming convention + (and not polymorphism) + (though not restricting factory methods to being static) + + - for referring to the return value when I want to pick out + the type and all subtypes + diff --git a/docs/sandbox/inoculated/readme.txt b/docs/sandbox/inoculated/readme.txt new file mode 100644 index 000000000..04f05e65a --- /dev/null +++ b/docs/sandbox/inoculated/readme.txt @@ -0,0 +1,36 @@ + +This contains demonstration source code for the article +"Get Inoculated!" in the May 2002 issue of Software Development +magazine. + +To use it you will need the AspectJ tools available from +http://eclipse.org/aspectj. We also recommend you download the +documentation bundle and support for the IDE of your choice. + +Each file has a snippet for a section of the article. To find +one in particular, see the back-references to "article page #": + + CompileTime.java: // article page 40 - warning + CompileTime.java: // article page 41 - error + RunTime.java: // article page 41 - runtime NPE + RuntimeWrites.java: // article page 42 - field writes + RecordingInput.java: // article page 42 - recording input + MainFailure.java: // article page 42 - recording failures from main + BufferTest.java: // article page 43 - input driver + Injection.java: // article page 43 - fault injection + StubReplace.java: // article page 76 - stubs + RoundTrip.java: // article page 76 - round trip + +Compile and run as usual: + + > set AJ_HOME=c:\aspectj1.0 + > set PATH=%AJ_HOME%\bin;%PATH% + > ajc -classpath "$AJ_HOME/lib/aspectjrt.jar" {file} + > java -classpath ".;$AJ_HOME/lib/aspectjrt.jar" {class} + +For email discussions and support, see http://eclipse.org/aspectj. + + +Enjoy! + +the AspectJ team diff --git a/docs/sandbox/inoculated/src/BufferTest.java b/docs/sandbox/inoculated/src/BufferTest.java new file mode 100644 index 000000000..f2cc479e8 --- /dev/null +++ b/docs/sandbox/inoculated/src/BufferTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1998-2002 PARC Inc. All rights reserved. + * + * Use and copying of this software and preparation of derivative works based + * upon this software are permitted. Any distribution of this software or + * derivative works must comply with all applicable United States export + * control laws. + * + * This software is made available AS IS, and PARC Inc. makes no + * warranty about the software, its performance or its conformity to any + * specification. + */ + +import java.util.*; +import java.io.*; + +import org.aspectj.lang.*; + +/** @author Wes Isberg */ +public aspect BufferTest { + + // article page 43 - input driver + // START-SAMPLE testing-inoculated-proceedVariants Using around for integration testing + /** + * When PrinterBuffer.capacity(int) is called, + * test it with repeatedly with a set of input + * (validating the result) and then continue with + * the original call. + * + * This assumes that the capacity method causes no + * relevant state changes in the buffer. + */ + int around(int original, PrinterBuffer buffer) : + call(int PrinterBuffer.capacity(int)) && args(original) && target(buffer) { + int[] input = new int[] { 0, 1, 10, 1000, -1, 4096 }; + for (int i = 0; i < input.length; i++) { + int result = proceed(input[i], buffer); // invoke test + validateResult(buffer, input[i], result); + } + return proceed(original, buffer); // continue with original processing + } + // END-SAMPLE testing-inoculated-proceedVariants + + void validateResult(PrinterBuffer buffer, int input, int result) { + System.err.println("validating input=" + input + " result=" + result + + " buffer=" + buffer); + } + + public static void main(String[] args) { + PrinterBuffer p = new PrinterBuffer(); + int i = p.capacity(0); + System.err.println("main - result " + i); + } +} + +class PrinterBuffer { + int capacity(int i) { + System.err.println("capacity " + i); + return i; + } +} diff --git a/docs/sandbox/inoculated/src/Injection.java b/docs/sandbox/inoculated/src/Injection.java new file mode 100644 index 000000000..6a857ef7a --- /dev/null +++ b/docs/sandbox/inoculated/src/Injection.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 1998-2002 PARC Inc. All rights reserved. + * + * Use and copying of this software and preparation of derivative works based + * upon this software are permitted. Any distribution of this software or + * derivative works must comply with all applicable United States export + * control laws. + * + * This software is made available AS IS, and PARC Inc. makes no + * warranty about the software, its performance or its conformity to any + * specification. + */ + +import java.io.*; + +/** + * Demonstrate technique of fault-injection + * as coordinated by test driver. + * @author Wes Isberg + */ +aspect InjectingIOException { + + // article page 43 - fault injection + // START-SAMPLE testing-inoculated-injectIOException Inject IOException on test driver command + /** the test starts when the driver starts executing */ + pointcut testEntryPoint(TestDriver driver) : + target(driver) && execution(* TestDriver.startTest()); + + /** + * The fault may be injected at the execution of any + * (non-static) PrinterStream method that throws an IOException + */ + pointcut testCheckPoint(PrinterStream stream) : target(stream) + && execution(public * PrinterStream+.*(..) throws IOException); + + /** + * After the method returns normally, query the + * test driver to see if we should instead throw + * an exception ("inject" the fault). + */ + after (TestDriver driver, PrinterStream stream) returning + throws IOException : + cflowbelow(testEntryPoint(driver)) + && testCheckPoint(stream) { + IOException e = driver.createException(stream); + if (null != e) { + System.out.println("InjectingIOException - injecting " + e); + throw e; + } + } + /* Comment on the after advice IOException declaration: + + "throws IOException" is a declaration of the advice, + not the pointcut. + + Since the advice might throw the injected fault, it + must declare that it throws IOException. When advice declares + exceptions thrown, the compiler will emit an error if any + join point is not also declared to throw an IOException. + + In this case, the testCheckPoint pointcut only picks out + methods that throw IOException, so the compile will not + signal any errors. + */ + // END-SAMPLE testing-inoculated-injectIOException +} + +/** this runs the test case */ +public class Injection { + /** Run three print jobs, two as a test and one normally */ + public static void main(String[] args) throws Exception { + Runnable r = new Runnable() { + public void run() { + try { new TestDriver().startTest(); } + catch (IOException e) { + System.err.println("got expected injected error " + e.getMessage()); + } + } + }; + + System.out.println("Injection.main() - starting separate test thread"); + Thread t = new Thread(r); + t.start(); + + System.out.println("Injection.main() - running test in this thread"); + r.run(); + t.join(); + + System.out.println("Injection.main() - running job normally, not by TestDriver"); + new PrintJob().runPrintJob(); + } +} + +/** handle starting of test and determining whether to inject failure */ +class TestDriver { + + /** start a new test */ + public void startTest() throws IOException { + new PrintJob().runPrintJob(); + } + + /** this implementation always injects a failure */ + public IOException createException(PrinterStream p) { + return new IOException(""+p); + } +} + +//--------------------------------------- target classes + +class PrintJob { + /** this job writes to the printer stream */ + void runPrintJob() throws IOException { + new PrinterStream().write(); + } +} + +class PrinterStream { + /** this printer stream writes without exception */ + public void write() throws IOException { + System.err.println("PrinterStream.write() - not throwing exception"); + } +} + diff --git a/docs/sandbox/inoculated/src/MainFailure.java b/docs/sandbox/inoculated/src/MainFailure.java new file mode 100644 index 000000000..6d5a62a35 --- /dev/null +++ b/docs/sandbox/inoculated/src/MainFailure.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 1998-2002 PARC Inc. All rights reserved. + * + * Use and copying of this software and preparation of derivative works based + * upon this software are permitted. Any distribution of this software or + * derivative works must comply with all applicable United States export + * control laws. + * + * This software is made available AS IS, and PARC Inc. makes no + * warranty about the software, its performance or its conformity to any + * specification. + */ + +import java.util.*; +import java.io.*; +import org.aspectj.lang.*; + +/** @author Wes Isberg */ + +public aspect MainFailure { + + public static void main (String[] args) { TargetClass.main(args); } + + pointcut main(String[] args) : + args(args) && execution(public static void main(String[])); + + // article page 42 - recording failures from main + // START-SAMPLE testing-inoculated-failureCapture Log failures + /** log after failure, but do not affect exception */ + after(String[] args) throwing (Throwable t) : main(args) { + logFailureCase(args, t, thisJoinPoint); + } + // END-SAMPLE testing-inoculated-failureCapture + + // alternate to swallow exception +// /** log after failure and swallow exception */ +// Object around() : main(String[]) { +// try { +// return proceed(); +// } catch (Error e) { // ignore +// logFailureCase(args, t, thisJoinPoint); +// // can log here instead +// } +// return null; +// } + + public static void logFailureCase(String[] args, Throwable t, Object jp) { + System.err.println("failure case: args " + Arrays.asList(args)); + } +} + +class TargetClass { + static Thread thread; + /** will throw error if exactly one argument */ + public static void main (String[] args) { + // make sure to do at least one failure + if (thread == null) { + Runnable r = new Runnable() { + public void run() { + main(new String[] {"throwError" }); + } + }; + thread = new Thread(r); + thread.start(); + } + if (1 == args.length) { + throw new Error("hello"); + } + try { thread.join(); } + catch (InterruptedException ie) { } + } +} + + diff --git a/docs/sandbox/inoculated/src/RunTime.java b/docs/sandbox/inoculated/src/RunTime.java new file mode 100644 index 000000000..e3e93e9d0 --- /dev/null +++ b/docs/sandbox/inoculated/src/RunTime.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 1998-2002 PARC Inc. All rights reserved. + * + * Use and copying of this software and preparation of derivative works based + * upon this software are permitted. Any distribution of this software or + * derivative works must comply with all applicable United States export + * control laws. + * + * This software is made available AS IS, and PARC Inc. makes no + * warranty about the software, its performance or its conformity to any + * specification. + */ + +import java.util.*; +import java.awt.Point; + +import org.aspectj.lang.JoinPoint; + +public class RunTime { + public static void main(String[] a) { + AnotherPoint.create(); + SubPoint.create(); + } +} + +/** @author Wes Isberg */ +aspect FactoryValidation { + + // START-SAMPLE declares-inoculated-prohibitNonprivateConstructors Error to have accessible sub-Point constructors + /** We make it an error for any Point subclasses to have non-private constructors */ + declare error : execution(!private Point+.new(..)) + && !within(java*..*) : + "non-private Point subclass constructor"; + // END-SAMPLE declares-inoculated-prohibitNonprivateConstructors + + // article page 41 - runtime NPE + // START-SAMPLE testing-inoculated-runtimeErrorWhenNullReturnedFromFactory Throw Error when factory returns null + /** Throw Error if a factory method for creating a Point returns null */ + after () returning (Point p) : + call(Point+ SubPoint+.create(..)) { + if (null == p) { + String err = "Null Point constructed when this (" + + thisJoinPoint.getThis() + + ") called target (" + + thisJoinPoint.getTarget() + + ") at join point (" + + thisJoinPoint.getSignature() + + ") from source location (" + + thisJoinPoint.getSourceLocation() + + ") with args (" + + Arrays.asList(thisJoinPoint.getArgs()) + + ")"; + throw new Error(err); + } + } + // END-SAMPLE testing-inoculated-runtimeErrorWhenNullReturnedFromFactory +} + +class SubPoint extends Point { + public static SubPoint create() { return null; } // will cause Error + private SubPoint(){} +} + +class AnotherPoint extends Point { + public static Point create() { return new Point(); } + + // to see that default constructor is picked out by declare error + // comment out this constructor + private AnotherPoint(){} +} + + diff --git a/docs/sandbox/inoculated/src/RuntimeWrites.java b/docs/sandbox/inoculated/src/RuntimeWrites.java new file mode 100644 index 000000000..0f49247cb --- /dev/null +++ b/docs/sandbox/inoculated/src/RuntimeWrites.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 1998-2002 PARC Inc. All rights reserved. + * + * Use and copying of this software and preparation of derivative works based + * upon this software are permitted. Any distribution of this software or + * derivative works must comply with all applicable United States export + * control laws. + * + * This software is made available AS IS, and PARC Inc. makes no + * warranty about the software, its performance or its conformity to any + * specification. + */ + +import org.aspectj.lang.*; + +/** test cases for controlling field writes */ +public class RuntimeWrites { + + public static void main(String[] args) { + System.err.println("---- setup: valid write"); + final SubPrinterStream me = new SubPrinterStream(); + + System.err.println("---- setup: invalid write, outside initialization - nonstatic"); + me.setOne(1); + + System.err.println("---- setup: invalid write, outside initialization - static"); + me.one = 0; + + System.err.println("---- setup: invalid write, caller is not same as target"); + PrinterStream other = new PrinterStream(me); + } +} + + +/** + * Control field writes. + * This implementation restricts writes to the same object during initialization. + * This is like having field values be final, except that + * they may be set set outside the constructor. + * @author Wes Isberg + */ +aspect ControlFieldWrites { + public static boolean throwError; + + // article page 42 - field writes + + // START-SAMPLE testing-inoculated-permitWritesDuringConstruction Constructor execution + /** execution of any constructor for PrinterStream */ + pointcut init() : execution(PrinterStream+.new(..)); + // END-SAMPLE testing-inoculated-permitWritesDuringConstruction + + // START-SAMPLE testing-inoculated-prohibitWritesExceptWhenConstructing Prohibit field writes after construction + /** any write to a non-static field in PrinterStream itself */ + pointcut fieldWrites() : set(!static * PrinterStream.*); + + + /** + * Handle any situation where fields are written + * outside of the control flow of initialization + */ + before() : fieldWrites() && !cflow(init()) { + handle("field set outside of init", thisJoinPointStaticPart); + } + // END-SAMPLE testing-inoculated-prohibitWritesExceptWhenConstructing + + // START-SAMPLE testing-inoculated-prohibitWritesByOthers Prohibit field writes by other instances + /** + * Handle any situation where fields are written + * by another object. + */ + before(Object caller, PrinterStream targ) : this(caller) + && target(targ) && fieldWrites() { + if (caller != targ) { + String err = "variation 1: caller (" + caller + + ") setting fields in targ (" + targ + ")"; + handle(err, thisJoinPointStaticPart); + } + } + // END-SAMPLE testing-inoculated-prohibitWritesByOthers + + //---- variations to pick out subclasses as well + // START-SAMPLE testing-inoculated-prohibitWritesEvenBySubclasses Prohibit writes by subclasses + /** any write to a non-static field in PrinterStream or any subclasses */ + //pointcut fieldWrites() : set(!static * PrinterStream+.*); + + /** execution of any constructor for PrinterStream or any subclasses */ + //pointcut init() : execution(PrinterStream+.new(..)); + // END-SAMPLE testing-inoculated-prohibitWritesEvenBySubclasses + + //---- variation to pick out static callers as well + // START-SAMPLE testing-inoculated-prohibitWritesEvenByStaticOthers Prohibit writes by other instances and static methods + /** + * Handle any situation where fields are written + * other than by the same object. + */ + before(PrinterStream targ) : target(targ) && fieldWrites() { + Object caller = thisJoinPoint.getThis(); + if (targ != caller) { + String err = "variation 2: caller (" + caller + + ") setting fields in targ (" + targ + ")"; + handle(err, thisJoinPointStaticPart); + } + } + // END-SAMPLE testing-inoculated-prohibitWritesEvenByStaticOthers + + //-------------- utility method + void handle(String error, JoinPoint.StaticPart jpsp) { + error += " - " + jpsp; + if (throwError) { + throw new Error(error); + } else { + System.err.println(error); + } + } +} + + +class PrinterStream { + int one; + private int another; + + PrinterStream() { setOne(1); } + + PrinterStream(PrinterStream other) { + other.another = 3; + } + + public void setOne(int i) { + one = i; + } +} + +class SubPrinterStream extends PrinterStream { + private int two; + + SubPrinterStream() { + setOne(2); + setTwo(); + } + + public void setTwo() { + two = 2; + } +} + diff --git a/docs/sandbox/inoculated/src/StubReplace.java b/docs/sandbox/inoculated/src/StubReplace.java new file mode 100644 index 000000000..56bda0367 --- /dev/null +++ b/docs/sandbox/inoculated/src/StubReplace.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1998-2002 PARC Inc. All rights reserved. + * + * Use and copying of this software and preparation of derivative works based + * upon this software are permitted. Any distribution of this software or + * derivative works must comply with all applicable United States export + * control laws. + * + * This software is made available AS IS, and PARC Inc. makes no + * warranty about the software, its performance or its conformity to any + * specification. + */ + +public class StubReplace { + public static void main(String[] args) { + new PrintJob().run(); + } +} + +/** @author Wes Isberg */ +aspect Stubs { + + // article page 76 - stubs + + // START-SAMPLE testing-inoculated-replaceWithProxy Replace object with proxy on constructiono + /** + * Replace all PrintStream with our StubStream + * by replacing the call to any constructor of + * PrinterStream or any subclasses. + */ + PrinterStream around () : within(PrintJob) + && call (PrinterStream+.new(..)) && !call (StubStream+.new(..)) { + return new StubStream(thisJoinPoint.getArgs()); + } + // END-SAMPLE testing-inoculated-replaceWithProxy + + // START-SAMPLE testing-inoculated-adviseProxyCallsOnly Advise calls to the proxy object only + pointcut stubWrite() : printerStreamTestCalls() && target(StubStream); + + pointcut printerStreamTestCalls() : call(* PrinterStream.write()); + + before() : stubWrite() { + System.err.println("picking out stubWrite" ); + } + // END-SAMPLE testing-inoculated-adviseProxyCallsOnly +} + +class PrinterStream { + public void write() {} +} + +class StubStream extends PrinterStream { + public StubStream(Object[] args) {} +} + +class PrintJob { + public void run() { + PrinterStream p = new PrinterStream(); + System.err.println("not PrinterStream: " + p); + System.err.println("now trying call..."); + p.write(); + } +} + diff --git a/docs/sandbox/inoculated/src/buildRun.sh b/docs/sandbox/inoculated/src/buildRun.sh new file mode 100644 index 000000000..8f3c283c3 --- /dev/null +++ b/docs/sandbox/inoculated/src/buildRun.sh @@ -0,0 +1,20 @@ +#!/bin/sh
+
+JDKDIR="${JDKDIR:-${JAVA_HOME:-`setjdk.sh`}}"
+AJ_HOME="${AJ_HOME:-`setajhome.sh`}"
+PS="${PS:-;}"
+ajrt=`pathtojava.sh "$AJ_HOME/lib/aspectjrt.jar"`
+mkdir -p ../classes
+
+for i in *.java; do
+ pack=`sed -n '/package/s|.*package *\([^ ][^ ]*\)[ ;].*|\1|p' "$i"`
+ [ -n "$pack" ] && pack="${pack}."
+ rm -rf classes/*
+ cname=$pack`basename $i .java`
+ echo ""
+ echo "########## $cname"
+ $AJ_HOME/bin/ajc -d ../classes -classpath "$ajrt" "$i"
+ && $JDKDIR/bin/java -classpath "../classes${PS}$ajrt" $cname
+done
+
+rm -rf ../classes
diff --git a/docs/sandbox/inoculated/src/com/xerox/printing/CompileTime.java b/docs/sandbox/inoculated/src/com/xerox/printing/CompileTime.java new file mode 100644 index 000000000..f7af344e0 --- /dev/null +++ b/docs/sandbox/inoculated/src/com/xerox/printing/CompileTime.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1998-2002 PARC Inc. All rights reserved. + * + * Use and copying of this software and preparation of derivative works based + * upon this software are permitted. Any distribution of this software or + * derivative works must comply with all applicable United States export + * control laws. + * + * This software is made available AS IS, and PARC Inc. makes no + * warranty about the software, its performance or its conformity to any + * specification. + */ + +package com.xerox.printing; + +import java.awt.Point; +import java.io.IOException; + +class ClassOne { + int i = 1 ; // 20 expect warning +} + +class ClassError { + int i = 1 ; // 24 expect warning +} + +class PrinterStream {} + +class SubPrinterStream extends PrinterStream { + public void delegate() { + try { + throw new IOException(""); + } catch (IOException e) {} // 33 expect error + } +} + +class SubPoint extends Point { + Point create() { return new Point(); } // no error + Point another() { return new Point(); } // 39 expect error +} + +/** @author Wes Isberg */ +aspect CompileTime { + + // article page 40 - warning + // START-SAMPLE declares-inoculated-nonSetterWrites Warn when setting non-public field + /** warn if setting non-public field outside a setter */ + declare warning : + within(com.xerox.printing..*) + && set(!public * *) && !withincode(* set*(..)) + : "writing field outside setter" ; + // END-SAMPLE declares-inoculated-nonSetterWrites + + // article page 41 - error + // START-SAMPLE declares-inoculated-validExceptionHandlingMethod Error when subclass method handles exception + declare error : handler(IOException+) + && withincode(* PrinterStream+.delegate(..)) + : "do not handle IOException in this method"; + // END-SAMPLE declares-inoculated-validExceptionHandlingMethod + + // START-SAMPLE declares-inoculated-validPointConstruction Error when factory not used + declare error : !withincode(Point+ SubPoint+.create(..)) + && within(com.xerox..*) + && call(Point+.new(..)) + : "use SubPoint.create() to create Point"; + // END-SAMPLE declares-inoculated-validPointConstruction +} + + diff --git a/docs/sandbox/inoculated/src/com/xerox/printing/RecordingInput.java b/docs/sandbox/inoculated/src/com/xerox/printing/RecordingInput.java new file mode 100644 index 000000000..aab1d2076 --- /dev/null +++ b/docs/sandbox/inoculated/src/com/xerox/printing/RecordingInput.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1998-2002 PARC Inc. All rights reserved. + * + * Use and copying of this software and preparation of derivative works based + * upon this software are permitted. Any distribution of this software or + * derivative works must comply with all applicable United States export + * control laws. + * + * This software is made available AS IS, and PARC Inc. makes no + * warranty about the software, its performance or its conformity to any + * specification. + */ + +package com.xerox.printing; + +class PrinterBuffer { + public int capacity(int i) { return i; } +} + +public aspect RecordingInput { + + // @author Wes Isberg + // article page 42 - recording input + pointcut capacityCall (int i) : + within(com.xerox..*) && args(i) + && call(public * PrinterBuffer.capacity(int)) ; + // XXX style error - + not needed + // call(public * PrinterBuffer+.capacity(int)) + + before (int i) : capacityCall(i) { + log.print("<capacityCall tjp=\"" + thisJoinPoint + + "\" input=\"" + i + "\"/>"); + } + + Log log = new Log(); + class Log { + void print(String s) { System.out.println(s); } + } + public static void main(String[] args) { + PrinterBuffer p = new PrinterBuffer(); + p.capacity(1); + p.capacity(2); + } +} diff --git a/docs/sandbox/inoculated/src/com/xerox/printing/RoundTrip.java b/docs/sandbox/inoculated/src/com/xerox/printing/RoundTrip.java new file mode 100644 index 000000000..4aeb0892b --- /dev/null +++ b/docs/sandbox/inoculated/src/com/xerox/printing/RoundTrip.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1998-2002 PARC Inc. All rights reserved. + * + * Use and copying of this software and preparation of derivative works based + * upon this software are permitted. Any distribution of this software or + * derivative works must comply with all applicable United States export + * control laws. + * + * This software is made available AS IS, and PARC Inc. makes no + * warranty about the software, its performance or its conformity to any + * specification. + */ + +package com.xerox.printing; + +public class RoundTrip { + public static void main(String[] args) { + PrinterStream p = testing(); + System.err.println(" got "+ p.number); + } + static PrinterStream testing() { return new PrinterStream(1); } + +} + +/** @author Wes Isberg */ +aspect VerifyPrinterStreamIntegrity { + // article page 76 - round trip + // START-SAMPLE testing-inoculated-roundTrip Round-trip integration testing + /** + * After returning a PrinterStream from any call in our + * packages, verify it by doing a round-trip between + * PrinterStream and BufferedPrinterStream. + * This uses a round-trip as a way to verify the + * integrity of PrinterStream, but one could also use + * a self-test (built-in or otherwise) coded specifically + * for validating the object (without changing state). + */ + after () returning (PrinterStream stream) : + call (PrinterStream+ com.xerox.printing..*(..)) + && !call (PrinterStream PrinterStream.make(BufferedPrinterStream)) { + BufferedPrinterStream bufferStream = new BufferedPrinterStream(stream); + PrinterStream newStream = PrinterStream.make(bufferStream); + if (!stream.equals(newStream)) { + throw new Error("round-trip failed for " + stream); + } else { + System.err.println("round-trip passed for " + stream); + } + } + // END-SAMPLE testing-inoculated-roundTrip +} + +class BufferedPrinterStream { + int num; + BufferedPrinterStream(int i) { this.num = i; } + BufferedPrinterStream(PrinterStream p) { this(p.number); } +} + +class PrinterStream { + int number; + static PrinterStream make(BufferedPrinterStream p) { + return new PrinterStream(p.num); + } + PrinterStream(int i) { this.number = i; } + void write() {} + // XXX hashcode + public boolean equals(Object o) { + if (o == this) { + return true; + } else if (null == o) { + return false; + } else if (o instanceof PrinterStream) { + return ((PrinterStream)o).number == number; + } else { + return o.equals(this); + } + } +} diff --git a/docs/sandbox/readme-sandbox.html b/docs/sandbox/readme-sandbox.html new file mode 100644 index 000000000..d8c874399 --- /dev/null +++ b/docs/sandbox/readme-sandbox.html @@ -0,0 +1,192 @@ +<html> +<title>README - AspectJ sandbox for sample code and instructions</title> +<body> +<h2>README - AspectJ sandbox for sample code and instructions</h2> +This directory is a place to put some scraps that might end up in the +AspectJ documentation or on the web site: +<ul> +<li>sample code for AspectJ programs, + </li> +<li>sample code for extensions to AspectJ tools and API's, + </li> +<li>sample scripts for invoking AspectJ tools, and + </li> +<li>documentation trails showing how to do given tasks + using AspectJ, AJDT, or various IDE or deployment + environments. + </li> +</ul> +In the past, we found it tedious to keep and verify +sample code used in documentation because it involves +copying the code from the documentation +into a form that can be tested (or vice versa). +Further, there are a lot of tips and sample code +contributed on the mailing lists, but they can be +difficult to find when needed. This aims to be +a place where such contributions can be gathered, +kept in good order, and extracted for use in docs, +without too much overhead. + +<p> +Code in the sandbox is kept in a running state; +it's tested by running the harness on the sandbox +test suite file + <a href="sandbox-test.xml">sandbox-test.xml</a>. +To extract the code for documentation, we use a tool +that recognizes +a sample within a source file if it has comments +signalling the start and end of the sample. +Keeping sample code in sync with the documentation +and with the test suite specification only takes +a bit of care with formatting and comments. +The rest of this document tells how. + +<p><code>org.aspectj.internal.tools.build.SampleGatherer</code> +(in the build module) extracts samples +of the following form from any "source" file +(currently source, html, text, and shell scripts): + +<pre> + ... some text, possibly including @author tags + {comment} START-<skipParse/>SAMPLE [anchorName] [anchor title] {end-comment} + ... sample code ... + {comment} END-<skipParse/>SAMPLE [anchorName] {end-comment} + ... more text ... +</pre> + +<p> +Each sample extracted consists of the code and associated +attributes: the source location, the anchor name +and title, any text flagged with XXX, +and the author pulled from the closest-preceding +<code>@author</code> tag. (If there is no author, +the AspectJ team is presumed.) + +<code>SampleGatherer</code> can render the collected +samples back out to HTML (todo: or DocBook) for +inclusion in the FAQ, the online +samples, the Programming Guide, etc. +An editor might reorder or annotate the samples +but the code should not be edited +to avoid introducing mistakes. + +<p>To help keep the sample code in sync with the docs... +<ul> +<li>Use comments in the sample to explain the code, + rather than describing it in the documentation. + </li> +<li>Preformat samples to be included without modification + in docs: + <ul> + <li>Use 4 spaces rather than a tab for each indent. + </li> + <li>Keep lines short - 60 characters or less. + </li> + <li>Code snippets taken out of context of the defining type + can be indented only once in the source code, + even though they might normally be indented more. + </li> + <li>Indent advice pointcuts once beyond the block code: +<pre> + before() : call(!public * com.company.library..*.*(String,..)) + && within(Runnable+) { // indent once more than code + // code here + } +</pre> + </li> + </ul> + </li> +</ul> + +<p> +Where you put the sample code depends on how big it is +and what it's for. Any code intended as an extension to +the AspectJ tools goes in the <a href="api-clients">api-clients/</a> +directory. +Most code will instead be samples of AspectJ programs. +Subdirectories of this directory should be the base directories of +different source sets. + +The <a href="common">common/</a> directory should work for +most code snippets, but standalone, self-consistent code +belongs in its own directory, +as do sets pertaining to particular publications or tutorials. +An example of this are the sources for the "Test Inoculated" +article in the <a href="inoculated">inoculated/</a> directory. + +<p> +When adding code, add a corresponding test case in +<a href="sandbox-test.xml">sandbox-test.xml</a>. +This file has the same format as other harness test suites; +for more information, +see <a href="../../tests/readme-writing-compiler-tests.html"> +../../tests/readme-writing-compiler-tests.html</a>. + +The test suite should run and pass after new code is added +and before samples are extracted. + +<p>To keep sample code in sync with the tests: +<ul> +<li>The test title should be prefixed with the anchor name and + have any suffixes necessary for clarity and to make sure + there are unique titles for each test. + E.g., for a sample with the anchor + "<code>language-initialization</code>", + <pre> + <ajc-test + dir="common" + title="language-initialization constructor-call example"> + ... + </ajc-test> + </pre> + (Someday we'll be able to compare the test titles with + the anchor names to verify that the titles contain + all the anchor names.) + <p></li> +<li>Avoid mixing compiler-error tests with ordinary code, + so others can reuse the target code in their samples. + Most of the sample code should compile. + <p></li> +<li>Any code that is supposed to trigger a compiler error + should be tested by verifying that the error message + is produced, checking either or both of the line number + and the message text. E.g., +<pre> + <compile files="declares/Declares.java, {others}" + <message kind="error" line="15" text="Factory"/> + </compile> +</pre> + Where a test case refers to a line number, + we have to keep the expected message + and the target code in sync. + You can help with this by adding a comment in the target code + so people editing the code know not to fix + or move the code. E.g., +<pre> + void spawn() { + new Thread(this, toString()).start(); // KEEP CE 15 declares-factory + } +</pre> + Any good comment could work, but here are some optional conventions: + <p> + <ul> + <li>Use "CE" or "CW" for compiler error or warning, respectively. + <p></li> + <li>Specify the line number, if one is expected. + <p></li> + <li>Specify the affected test title(s) or sample code anchor label + to make it easier to find the test that will break if the + code is modified. (The editor can also run the tests + to find any broken ones.) + <p> + </li> + </ul> + </li> +</ul> +<p> +Happy coding! +<hr> + +</body> +</html> + diff --git a/docs/sandbox/sandbox-test.xml b/docs/sandbox/sandbox-test.xml new file mode 100644 index 000000000..494fa5672 --- /dev/null +++ b/docs/sandbox/sandbox-test.xml @@ -0,0 +1,141 @@ +<!DOCTYPE suite SYSTEM "../tests/ajcTestSuite.dtd"> +<!-- ../ path when running, ../../ when editing? --> + + +<!-- Notes + + - test titles should be prefixed with the corresponding + sample anchor/label, and suffixed to be unique. + + - take care to keep error line numbers in sync with sources + + TODO + - verify that RunTime and RuntimeWrites throw Errors + - harness bug: errStreamIsError not false when not forked; fork? + - check output/error stream against expected + - verify api-clients, using access to aspectjtools.jar... + +--> +<suite> + + <ajc-test dir="common" title="declares-* default declares"> + <compile + argfiles="company.lst" + files="declares/Declares.java"> + <message + kind="error" + file="Main.java" + line="15" + text="Factory"/> + <message + kind="warning" + file="Main.java" + line="30" + text="throw"/> + <message + kind="warning" + file="Main.java" + line="31" + text="handles"/> + </compile> + </ajc-test> + + <ajc-test dir="common" + title="ensure company compiles and runs without aspects"> + <compile argfiles="company.lst"/> + <run class="com.company.app.Main"/> + </ajc-test> + + <ajc-test dir="common" title="language-initialization"> + <compile files="language/Initialization.java"/> + <run class="language.Initialization" + errStreamIsError="true"/> + </ajc-test> + + <ajc-test dir="common" title="language-*Context"> + <compile files="language/Context.java"/> + <run class="language.Context" /> + </ajc-test> + + <ajc-test dir="common" title="language-cflowRecursionBasic"> + <compile files="language/ControlFlow.java"/> + <run class="language.ControlFlow"/> + </ajc-test> + + + <ajc-test dir="common" title="tracing-simpleLogging"> + <compile + argfiles="company.lst" + files="tracing/Logging.java"/> + <run class="com.company.app.Main"/> + </ajc-test> + + <ajc-test dir="common" title="tracing-traceJoinPoints"> + <compile + argfiles="company.lst" + files="tracing/TraceJoinPoints.java, + tracing/TraceJoinPointsBase.java, + tracing/TraceMyJoinPoints.java"/> + <run class="com.company.app.Main"/> + </ajc-test> + + <ajc-test dir="inoculated/src" title="declares-inoculated-*"> + <compile files="com/xerox/printing/CompileTime.java"> + <message line="20" kind="warning"/> + <message line="24" kind="warning"/> + <message line="33" kind="error"/> + <message line="39" kind="error"/> + </compile> + </ajc-test> + + <ajc-test dir="inoculated/src" title="testing-inoculated-roundTrip"> + <compile files="com/xerox/printing/RoundTrip.java"/> + <run class="com.xerox.printing.RoundTrip"/> + </ajc-test> + + <ajc-test dir="inoculated/src" title="testing-inoculated-roundTrip"> + <compile files="BufferTest.java"/> + <run class="BufferTest"/> + </ajc-test> + + <ajc-test dir="inoculated/src" title="testing-inoculated-roundTrip"> + <compile files="BufferTest.java"/> + <run class="BufferTest"/> + </ajc-test> + + <ajc-test dir="inoculated/src" title="testing-inoculated-injectIOException"> + <compile files="Injection.java"/> + <run class="Injection"/> + </ajc-test> + + <ajc-test dir="inoculated/src" title="testing-inoculated-injectIOException"> + <compile files="MainFailure.java"/> + <run class="MainFailure"/> + </ajc-test> + + <ajc-test dir="inoculated/src" title="testing-inoculated-runtimeFactories"> + <compile files="RunTime.java"/> + </ajc-test> + + <ajc-test dir="inoculated/src" title="testing-inoculated-runtimeWrites"> + <compile files="RuntimeWrites.java"/> + </ajc-test> + + <ajc-test dir="inoculated/src" title="testing-inoculated-{proxies}"> + <compile files="StubReplace.java"/> + <run class="StubReplace"/> + </ajc-test> + +</suite> + +<!-- + TODO XXX verify api-clients using aspectjtools.jar? + + <ajc-test dir="api-clients" title="api-ajde-modelWalker"> + <compile files="org/aspectj/samples/JoinPointCollector.java" + classpath="../aj-build/dist/tools/lib/aspectjtools.jar;"/> + <run ... /> + </ajc-test> + +--> +
\ No newline at end of file diff --git a/docs/sandbox/scripts/snippets.sh b/docs/sandbox/scripts/snippets.sh new file mode 100644 index 000000000..76c60ca94 --- /dev/null +++ b/docs/sandbox/scripts/snippets.sh @@ -0,0 +1,21 @@ +#!/bin/sh
+# shell script snippets for AspectJ
+
+
+# @author Wes Isberg
+# START-SAMPLE scripts-weaveLibraries
+ASPECTJ_HOME="${ASPECTJ_HOME:-c:/aspectj-1.1.0}"
+ajc="$ASPECTJ_HOME/bin/ajc"
+
+# make system.jar by weaving aspects.jar into lib.jar and app.jar
+$ajc -classpath "$ASPECTJ_HOME/lib/aspectjrt.jar" \
+ -aspectpath aspects.jar" \
+ -injars "app.jar;lib.jar" \
+ -outjar system.jar
+
+# XXX copy any resources from input jars to output jars
+
+# run it
+java -classpath "aspects.jar;system.jar" com.company.app.Main
+
+# END-SAMPLE scripts-weaveLibraries
diff --git a/docs/sandbox/trails/debugging.html b/docs/sandbox/trails/debugging.html new file mode 100644 index 000000000..3c6bbcfab --- /dev/null +++ b/docs/sandbox/trails/debugging.html @@ -0,0 +1,58 @@ +<html> +<title>Debugging AspectJ Programs +</title> +<body> +<h2>Debugging AspectJ Programs +</h2> +<!-- @author Wes Isberg --> +<!-- START-SAMPLE trails-debugging-aspectj10 --> +<h3>Debugging AspectJ 1.0 Programs +</h3> +The AspectJ 1.0 compiler produces .class files that have the +normal source file attribute and line information as well +as the information specified by JSR-045 required to debug +.class files composed of multiple source files. +This permits the compiler to inline advice code +into the .class files of the target classes; +the advice code in the target class can have source +attributes that point back to the aspect file. + +<p> +Support for JSR-45 varies. +At the time of this writing, Sun's VM supports it, +but not some others, which means that the Sun VM +must be used as the runtime VM. + +Because the VM does all the work of associating the +source line with the code being debugged, +debuggers should be able to operate normally with +AspectJ 1.0 source files even if they weren't written +with that in mind, if they use the correct +API's to the debugger. Unfortunately, some debuggers +take shortcuts based on the default case of one file +per class in order to avoid having the VM calculate +the file suffix or package prefix. These debuggers +do not support JSR-45 and thus AspectJ. + +<!-- END-SAMPLE trails-debugging-aspectj10 --> + +<!-- @author Wes Isberg --> +<!-- START-SAMPLE trails-debugging-aspectj11 --> +<h3>Debugging AspectJ 1.1 Programs +</h3> +The AspectJ 1.1 compiler usually implements advice as +call-backs to the aspect, which means that most +AspectJ programs do not require JSR-45 support in +order to be debugged. However, it does do a limited +amount of advice inlining; to avoid this, use the +<code>-XnoInline</code> flag. +<p> +Because inlined advice can be more efficient, we +plan to support JSR-45 as soon as feasible. +This will require upgrading the BCEL library we +use to pass around the correct source attributes. + +<!-- END-SAMPLE trails-debugging-aspectj11 --> + +</body> +</html> diff --git a/docs/sandbox/trails/j2ee.txt b/docs/sandbox/trails/j2ee.txt new file mode 100644 index 000000000..8685939b1 --- /dev/null +++ b/docs/sandbox/trails/j2ee.txt @@ -0,0 +1,34 @@ + +This contains short notes on using AspectJ with various J2EE +servers and deployment tools. + +// @author Wes Isberg + +-------- START-SAMPLE j2ee-servlets-generally Using AspectJ in servlets +AspectJ programs work if run in the same namespace and with aspectjrt.jar. +Servlet runners and J2EE web containers should run AspectJ programs fine +if the classes and required libraries are deployed as usual. The runtime +classes and shared aspects might need to be deployed into a common +directory to work properly across applications, especially in containers +that use different class loaders for different applications or use +different class-loading schemes. + +As with any shared library, if more than one application is using AspectJ, +then the aspectjrt.jar should be deployed where it will be loaded by a +common classloader. + +Aspects which are used by two applications might be independent or shared. +Independent aspects can be deployed in each application-specific archive. +Aspects might be shared explicitly or implicitly, as when they are stateful +or staticly bound (however indirectly) to common classes. When in doubt, +it is safest to deploy the aspects in the common namespace. + +-------- END-SAMPLE j2ee-servlets-generally + +-------- START-SAMPLE j2ee-servlets-tomcat4 Running AspectJ servlets in Tomcat 4.x +To deploy an AspectJ program as a Tomcat servlet, +place aspectjrt.jar in shared/lib and deploy the required libraries +and AspectJ-compiled servlet classes as usual. + +-------- END-SAMPLE j2ee-servlets-tomcat4 + |