View Javadoc
1   /*
2    * Copyright 2002-2016 the original author or authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.github.springtestdbunit;
18  
19  import java.util.ArrayList;
20  import java.util.Arrays;
21  import java.util.Collections;
22  import java.util.Iterator;
23  import java.util.List;
24  
25  import org.springframework.test.context.TestContext;
26  import org.springframework.test.context.TestExecutionListener;
27  
28  /**
29   * A {@link TestExecutionListener} implementation that works by chaining together other {@link TestExecutionListener}s
30   * and ensures that methods are called in the correct order. The {@link #prepareTestInstance}, {@link #beforeTestClass}
31   * and {@link #afterTestClass} methods will delegate to the chain in the order that it is defined. The
32   * {@link #afterTestClass} and {@link #afterTestMethod} methods will delegate in reverse order. methods.
33   * <p>
34   * For example, a typical call on a chain containing items "a" and "b" would be: <code>a.beforeTestMethod</code>,
35   * <code>b.beforeTestMethod</code>, <code>b.afterTestMethod</code>, <code>a.afterTestMethod</code>.
36   *
37   * @author Phillip Webb
38   */
39  public abstract class TestExecutionListenerChain implements TestExecutionListener {
40  
41  	private List<TestExecutionListener> chain;
42  
43  	private List<TestExecutionListener> reverseChain;
44  
45  	public TestExecutionListenerChain() {
46  		this.chain = createChain();
47  		this.reverseChain = new ArrayList<TestExecutionListener>(this.chain);
48  		Collections.reverse(this.reverseChain);
49  	}
50  
51  	/**
52  	 * Returns the chain of {@link TestExecutionListener} classes in the correct order.
53  	 * @return The chain
54  	 */
55  	protected abstract Class<?>[] getChain();
56  
57  	/**
58  	 * Factory method used to create the chain. By default this method will construct the chain using the classes from
59  	 * {@link #getChain()}.
60  	 * @return The chain
61  	 */
62  	protected List<TestExecutionListener> createChain() {
63  		Class<?>[] chainClasses = getChain();
64  		try {
65  			List<TestExecutionListener> chain = new ArrayList<TestExecutionListener>(chainClasses.length);
66  			for (int i = 0; i < chainClasses.length; i++) {
67  				chain.add((TestExecutionListener) chainClasses[i].newInstance());
68  			}
69  			return chain;
70  		} catch (Exception ex) {
71  			throw new IllegalStateException("Unable to create chain for classes " + Arrays.asList(chainClasses), ex);
72  		}
73  	}
74  
75  	public void beforeTestClass(final TestContext testContext) throws Exception {
76  		forwards(new Call() {
77  			public void call(TestExecutionListener listener) throws Exception {
78  				listener.beforeTestClass(testContext);
79  			}
80  		});
81  	}
82  
83  	public void prepareTestInstance(final TestContext testContext) throws Exception {
84  		forwards(new Call() {
85  			public void call(TestExecutionListener listener) throws Exception {
86  				listener.prepareTestInstance(testContext);
87  			}
88  		});
89  	}
90  
91  	public void beforeTestMethod(final TestContext testContext) throws Exception {
92  		forwards(new Call() {
93  			public void call(TestExecutionListener listener) throws Exception {
94  				listener.beforeTestMethod(testContext);
95  			}
96  		});
97  	}
98  
99  	public void afterTestMethod(final TestContext testContext) throws Exception {
100 		backwards(new Call() {
101 			public void call(TestExecutionListener listener) throws Exception {
102 				listener.afterTestMethod(testContext);
103 			}
104 		});
105 	}
106 
107 	public void afterTestClass(final TestContext testContext) throws Exception {
108 		backwards(new Call() {
109 			public void call(TestExecutionListener listener) throws Exception {
110 				listener.afterTestClass(testContext);
111 			}
112 		});
113 	}
114 
115 	private void forwards(Call call) throws Exception {
116 		runChain(this.chain.iterator(), call);
117 	}
118 
119 	private void backwards(Call call) throws Exception {
120 		runChain(this.reverseChain.iterator(), call);
121 	}
122 
123 	private void runChain(Iterator<TestExecutionListener> iterator, Call call) throws Exception {
124 		Throwable lastException = null;
125 		while (iterator.hasNext()) {
126 			try {
127 				call.call(iterator.next());
128 			} catch (Throwable ex) {
129 				lastException = ex;
130 			}
131 		}
132 		if (lastException != null) {
133 			if (lastException instanceof Exception) {
134 				throw (Exception) lastException;
135 			}
136 			throw new Exception(lastException);
137 		}
138 	}
139 
140 	private static interface Call {
141 		public void call(TestExecutionListener listener) throws Exception;
142 	}
143 
144 }