aboutsummaryrefslogtreecommitdiffstats
path: root/tests/bugs161/pr230134/Tracing.java
blob: 1f3b76753c4f68937de2a913d3364e8924d82e4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/*******************************************************************************
 * Copyright (c) 2005 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
 *
 * Contributors:
 *     Matthew Webster - initial implementation
 *     Sian January
 *******************************************************************************/
package org.aspectj.lib.tracing;

import org.aspectj.lang.*;

/**
 * This root abstract aspect determines the basic tracing behaviour
 * i.e. entry/exit/exception using the method/constructor execution() pointcut
 * and before/after returning/after throwing advice. Determining what
 * methods and constructors belonging to which classes is delegated to a
 * user-supplied concrete aspect using an abstract pointcut. When tracing
 * occurs and what is done with the captured data is delegated to an abstract,
 * infrastructure-specific sub-aspect through template methods.
 */
public abstract aspect Tracing {

	/**
	 * Sub-aspects <b>must</b> implement this pointcut to determine what and when to
	 * trace
	 */
	protected abstract pointcut shouldTrace ();

	private pointcut staticContext () : !this(Object);
    private pointcut nonStaticContext (Object obj) : this(obj);
    private pointcut voidMethod () : execution(void *(..));

	public final static pointcut methodExecution () : execution(* *(..));
	public final static pointcut constructorExecution () : execution(new(..));
	public final static pointcut objectMethod () : execution(* Object.*(..));

	/**
	 * Sub-aspects <b>may</b> override this point to determine which methods if any
	 * are traced. By default include only public methods and those not inherited
	 * from java.lang.Object e.g. toString().
	 */
	protected pointcut includedMethod () :
		execution(public * *(..))
		&& !objectMethod();


	/**
	 * Sub-aspects <b>may</b> override this point to determine which constructors if any
	 * are traced. By default include only public constructors.
	 */
	protected pointcut includedConstructor () :
		execution(public new(..));

	/*
	 * Exclude methods and constructors in Tracing and sub-aspects as well as
	 * those in the control flow of Tracing advice or constructors to avoid recursion.
	 */
	private pointcut excluded () :
		within(Tracing+)
//		|| cflow((adviceexecution() || execution(new(..))) && within(Tracing+))
		|| cflow((adviceexecution() && within(Tracing+)))
		 ;

	/*
	 * Trace only method execution included by the user but excluded by the aspect e.g. itself
	 */
	private pointcut tracedMethod () :
		methodExecution()
		&& includedMethod()
		&& !excluded()
		;

	/*
	 * Trace only constructor execution included by the user but excluded by the aspect e.g. itself
	 */
	private pointcut tracedConstructor (Object obj) :
		constructorExecution()
		&& includedConstructor()
		&& !excluded()
		&& this(obj)
		;

	/*
	 * Trace entry to instance methods
	 *
	 * Tracing pattern 1: Only use thisJoinPoint in before()
	 */
	before (Object obj) : tracedMethod() && nonStaticContext(obj) && shouldTrace() {
		enter(thisJoinPoint,obj);
	}

	/*
	 * Trace entry to static methods
	 *
	 * Tracing pattern 1: Only use thisJoinPoint in before()
	 */
	before () : tracedMethod() && staticContext() && shouldTrace() {
		enter(thisJoinPoint);
	}

	/*
	 * Trace exit from void methods
	 *
	 * Tracing pattern 1: Use thisJoinPointStaticPart in after()
	 */
	after() returning() : tracedMethod() && voidMethod() && shouldTrace() {
		exit(thisJoinPointStaticPart);
	}

	/*
	 * Trace exit from non-void methods including return value
	 *
	 * Tracing pattern 1: Use thisJoinPointStaticPart in after()
	 */
	after() returning(Object ret) : tracedMethod() && !voidMethod() && shouldTrace() {
		exit(thisJoinPointStaticPart,ret);
	}

	/*
	 * Trace exceptions thrown from methods and constructors
	 *
	 * Tracing pattern 1: Use thisJoinPointStaticPart in after()
	 */
	after() throwing(Throwable th) : (tracedMethod() || tracedConstructor(Object)) && shouldTrace() {
		if (shouldTrace(th)) exception(thisJoinPointStaticPart,th);
	}

	/*
	 * Trace entry to constructors
	 *
	 * Tracing pattern 1: Only use thisJoinPoint in before()
	 */
	before () : tracedConstructor(Object) && shouldTrace() {
		enter(thisJoinPoint);
	}

	/*
	 * Trace exit from constructors including new object
	 *
	 * Tracing pattern 1: Only use thisJoinPoint in before()
	 */
	after (Object obj) : tracedConstructor(obj) && shouldTrace() {
		exit(thisJoinPointStaticPart,obj);
	}

	/*
	 * Template methods to log data implemented by infrastructure-specific sub-aspects
	 * e.g. java.util.logging.Logger
	 */
	protected abstract void enter (JoinPoint jp, Object obj);

	protected abstract void enter (JoinPoint jp);

	protected abstract void exit (JoinPoint.StaticPart sjp);

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

	protected abstract void exception (JoinPoint.StaticPart sjp, Throwable th);

	/**
	 * Format arguments into a comma separated list
	 *
	 * @param names array of argument names
	 * @param args array of arguments
	 * @return the formated list
	 */
	protected String formatArgs (String[] names, Object[] args) {
		StringBuffer sb = new StringBuffer();

		for (int i = 0; i < args.length; i++) {
			sb.append(formatParam(names[i],args[i]));
			if (i < args.length-1) sb.append(", ");
		}

		return sb.toString();
	}

	/**
	 * Format objects safely avoiding toString which can cause recursion,
	 * NullPointerExceptions or highly verbose results.
	 *
	 * @param obj parameter to be formatted
	 * @return the formated parameter
	 */
	protected Object formatObj (Object obj) {
		if (obj == null
				|| obj instanceof String
			    || obj instanceof Number
			    || obj instanceof Boolean
			    || obj instanceof Character
			    || obj instanceof Class
			    || obj instanceof StringBuffer
		    ) return obj;
		else try {
			return obj.getClass().getName() + "@" + Integer.toString(obj.hashCode(),16);
		} catch (Exception ex) {
			return obj.getClass().getName();
		}
	}

	/**
	 * Format parameter into name=value pair
	 *
	 * @param name parameter name
	 * @param arg parameted to be formatted
	 * @return the formated parameter
	 */
	protected String formatParam (String name, Object arg) {
		return name + "=" + formatObj(arg);
	}

	/**
	 * By default we do not trace errors e.g. OutOfMemoryError because the
	 * system my be in an inconsistent state. However users may override this
	 *
	 * @param th excpeption or error to be traced
	 * @return whether it should be traced
	 */
	protected boolean shouldTrace (Throwable th) {
		return !(th instanceof Error);
	}
}