EMMA Coverage Report (generated Fri Jan 30 13:20:29 EST 2009)
[all classes][org.springframework.batch.repeat.interceptor]

COVERAGE SUMMARY FOR SOURCE FILE [RepeatOperationsInterceptor.java]

nameclass, %method, %block, %line, %
RepeatOperationsInterceptor.java100% (4/4)100% (12/12)100% (187/187)100% (48/48)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class RepeatOperationsInterceptor100% (1/1)100% (4/4)100% (81/81)100% (18/18)
RepeatOperationsInterceptor (): void 100% (1/1)100% (8/8)100% (3/3)
invoke (MethodInvocation): Object 100% (1/1)100% (53/53)100% (11/11)
isComplete (Object): boolean 100% (1/1)100% (13/13)100% (1/1)
setRepeatOperations (RepeatOperations): void 100% (1/1)100% (7/7)100% (3/3)
     
class RepeatOperationsInterceptor$1100% (1/1)100% (2/2)100% (71/71)100% (17/17)
RepeatOperationsInterceptor$1 (RepeatOperationsInterceptor, MethodInvocation,... 100% (1/1)100% (15/15)100% (1/1)
doInIteration (RepeatContext): ExitStatus 100% (1/1)100% (56/56)100% (16/16)
     
class RepeatOperationsInterceptor$RepeatOperationsInterceptorException100% (1/1)100% (1/1)100% (5/5)100% (2/2)
RepeatOperationsInterceptor$RepeatOperationsInterceptorException (String, Thr... 100% (1/1)100% (5/5)100% (2/2)
     
class RepeatOperationsInterceptor$ResultHolder100% (1/1)100% (5/5)100% (30/30)100% (12/12)
RepeatOperationsInterceptor$ResultHolder (): void 100% (1/1)100% (9/9)100% (3/3)
getValue (): Object 100% (1/1)100% (3/3)100% (1/1)
isReady (): boolean 100% (1/1)100% (3/3)100% (1/1)
setFinalValue (Object): void 100% (1/1)100% (8/8)100% (4/4)
setValue (Object): void 100% (1/1)100% (7/7)100% (3/3)

1/*
2 * Copyright 2006-2007 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 
17package org.springframework.batch.repeat.interceptor;
18 
19import org.aopalliance.intercept.MethodInterceptor;
20import org.aopalliance.intercept.MethodInvocation;
21import org.springframework.aop.ProxyMethodInvocation;
22import org.springframework.batch.repeat.ExitStatus;
23import org.springframework.batch.repeat.RepeatCallback;
24import org.springframework.batch.repeat.RepeatContext;
25import org.springframework.batch.repeat.RepeatException;
26import org.springframework.batch.repeat.RepeatOperations;
27import org.springframework.batch.repeat.support.RepeatTemplate;
28import org.springframework.util.Assert;
29 
30/**
31 * A {@link MethodInterceptor} that can be used to automatically repeat calls to
32 * a method on a service. The injected {@link RepeatOperations} is used to
33 * control the completion of the loop. Independent of the completion policy in
34 * the {@link RepeatOperations} the loop will repeat until the target method
35 * returns null or false. Be careful when injecting a bespoke
36 * {@link RepeatOperations} that the loop will actually terminate, because the
37 * default policy for a vanilla {@link RepeatTemplate} will never complete if
38 * the return type of the target method is void (the value returned is always
39 * not-null, representing the {@link Void#TYPE}).
40 * 
41 * @author Dave Syer
42 */
43public class RepeatOperationsInterceptor implements MethodInterceptor {
44 
45        private RepeatOperations repeatOperations = new RepeatTemplate();
46 
47        /**
48         * Setter for the {@link RepeatOperations}.
49         * 
50         * @param batchTempate
51         * @throws IllegalArgumentException if the argument is null.
52         */
53        public void setRepeatOperations(RepeatOperations batchTempate) {
54                Assert.notNull(batchTempate, "'repeatOperations' cannot be null.");
55                this.repeatOperations = batchTempate;
56        }
57 
58        /**
59         * Invoke the proceeding method call repeatedly, according to the properties
60         * of the injected {@link RepeatOperations}.
61         * 
62         * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
63         */
64        public Object invoke(final MethodInvocation invocation) throws Throwable {
65 
66                final ResultHolder result = new ResultHolder();
67                // Cache void return value if intercepted method returns void
68                final boolean voidReturnType = Void.TYPE.equals(invocation.getMethod().getReturnType());
69                if (voidReturnType) {
70                        // This will be ignored anyway, but we want it to be non-null for
71                        // convenience of checking that there is a result.
72                        result.setValue(new Object());
73                }
74 
75                try {
76                        repeatOperations.iterate(new RepeatCallback() {
77 
78                                public ExitStatus doInIteration(RepeatContext context) throws Exception {
79                                        try {
80 
81                                                MethodInvocation clone = invocation;
82                                                if (invocation instanceof ProxyMethodInvocation) {
83                                                        clone = ((ProxyMethodInvocation) invocation).invocableClone();
84                                                }
85                                                else {
86                                                        throw new IllegalStateException(
87                                                                        "MethodInvocation of the wrong type detected - this should not happen with Spring AOP, so please raise an issue if you see this exception");
88                                                }
89 
90                                                Object value = clone.proceed();
91                                                if (voidReturnType) {
92                                                        return ExitStatus.CONTINUABLE;
93                                                }
94                                                if (!isComplete(value)) {
95                                                        // Save the last result
96                                                        result.setValue(value);
97                                                        return ExitStatus.CONTINUABLE;
98                                                }
99                                                else {
100                                                        result.setFinalValue(value);
101                                                        return ExitStatus.FINISHED;
102                                                }
103                                        }
104                                        catch (Throwable e) {
105                                                if (e instanceof Exception) {
106                                                        throw (Exception) e;
107                                                }
108                                                else {
109                                                        throw new RepeatOperationsInterceptorException("Unexpected error in batch interceptor", e);
110                                                }
111                                        }
112                                }
113 
114                        });
115                }
116                catch (Throwable t) {
117                        // The repeat exception should be unwrapped by the template
118                        throw t;
119                }
120 
121                if (result.isReady()) {
122                        return result.getValue();
123                }
124 
125                // No result means something weird happened
126                throw new IllegalStateException("No result available for attempted repeat call to " + invocation
127                                + ".  The invocation was never called, so maybe there is a problem with the completion policy?");
128        }
129 
130        /**
131         * @param result
132         * @return
133         */
134        private boolean isComplete(Object result) {
135                return result == null || (result instanceof Boolean) && !((Boolean) result).booleanValue();
136        }
137 
138        /**
139         * Simple wrapper exception class to enable nasty errors to be passed out of
140         * the scope of the repeat operations and handled by the caller.
141         * 
142         * @author Dave Syer
143         * 
144         */
145        private static class RepeatOperationsInterceptorException extends RepeatException {
146                /**
147                 * @param message
148                 * @param e
149                 */
150                public RepeatOperationsInterceptorException(String message, Throwable e) {
151                        super(message, e);
152                }
153        }
154 
155        /**
156         * Simple wrapper object for the result from a method invocation.
157         * 
158         * @author Dave Syer
159         * 
160         */
161        private static class ResultHolder {
162                private Object value = null;
163 
164                private boolean ready = false;
165 
166                /**
167                 * Public setter for the Object.
168                 * @param value the value to set
169                 */
170                public void setValue(Object value) {
171                        this.ready = true;
172                        this.value = value;
173                }
174 
175                /**
176                 * @param value
177                 */
178                public void setFinalValue(Object value) {
179                        if (ready) {
180                                // Only set the value the last time if the last time was also
181                                // the first time
182                                return;
183                        }
184                        setValue(value);
185                }
186 
187                /**
188                 * Public getter for the Object.
189                 * @return the value
190                 */
191                public Object getValue() {
192                        return value;
193                }
194 
195                /**
196                 * @return true if a value has been set
197                 */
198                public boolean isReady() {
199                        return ready;
200                }
201        }
202 
203}

[all classes][org.springframework.batch.repeat.interceptor]
EMMA 2.0.5312 (C) Vladimir Roubtsov