Project

General

Profile

Download (13.9 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* From spring-security-oauth
3
*
4
* Apache License
5
* Version 2.0, January 2004
6
*/
7
package eu.etaxonomy.cdm.remote.server;
8

    
9

    
10
import java.io.IOException;
11
import java.io.UnsupportedEncodingException;
12
import java.net.HttpURLConnection;
13
import java.net.URISyntaxException;
14
import java.net.URLDecoder;
15
import java.util.Arrays;
16
import java.util.HashMap;
17
import java.util.Map;
18

    
19
import org.apache.commons.logging.Log;
20
import org.apache.commons.logging.LogFactory;
21
import org.junit.Assume;
22
import org.junit.internal.AssumptionViolatedException;
23
import org.junit.rules.MethodRule;
24
import org.junit.runners.model.FrameworkMethod;
25
import org.junit.runners.model.Statement;
26
import org.springframework.http.HttpEntity;
27
import org.springframework.http.HttpHeaders;
28
import org.springframework.http.HttpMethod;
29
import org.springframework.http.HttpStatus;
30
import org.springframework.http.MediaType;
31
import org.springframework.http.ResponseEntity;
32
import org.springframework.http.client.ClientHttpRequest;
33
import org.springframework.http.client.ClientHttpResponse;
34
import org.springframework.http.client.SimpleClientHttpRequestFactory;
35
import org.springframework.util.FileCopyUtils;
36
import org.springframework.util.LinkedMultiValueMap;
37
import org.springframework.util.MultiValueMap;
38
import org.springframework.web.client.RequestCallback;
39
import org.springframework.web.client.ResponseErrorHandler;
40
import org.springframework.web.client.ResponseExtractor;
41
import org.springframework.web.client.RestClientException;
42
import org.springframework.web.client.RestTemplate;
43
import org.springframework.web.util.UriTemplate;
44
import org.springframework.web.util.UriUtils;
45

    
46
import eu.etaxonomy.cdm.common.URI;
47

    
48
/**
49
 * <p>
50
 * A rule that prevents integration tests from failing if the server application is not running or not accessible. If
51
 * the server is not running in the background all the tests here will simply be skipped because of a violated
52
 * assumption (showing as successful). Usage:
53
 * </p>
54
 *
55
 * <pre>
56
 * &#064;Rule public static BrokerRunning brokerIsRunning = BrokerRunning.isRunning();
57
 *
58
 * &#064;Test public void testSendAndReceive() throws Exception { // ... test using RabbitTemplate etc. }
59
 * </pre>
60
 * <p>
61
 * The rule can be declared as static so that it only has to check once for all tests in the enclosing test case, but
62
 * there isn't a lot of overhead in making it non-static.
63
 * </p>
64
 *
65
 * @see Assume
66
 * @see AssumptionViolatedException
67
 *
68
 * @author Dave Syer
69
 *
70
 */
71
public class ServerRunning implements MethodRule {
72

    
73
    /**
74
     *
75
     */
76
    private static final String CONNECT_TEST_PATH = "/classification.json";
77

    
78
    private static Log logger = LogFactory.getLog(ServerRunning.class);
79

    
80
    // Static so that we only test once on failure: speeds up test suite
81
    private static Map<Integer, Boolean> serverOnline = new HashMap<Integer, Boolean>();
82

    
83
    // Static so that we only test once on failure
84
    private static Map<Integer, Boolean> serverOffline = new HashMap<Integer, Boolean>();
85

    
86
    private final boolean assumeOnline;
87

    
88
    private static int DEFAULT_PORT = 8080;
89

    
90
    private static String DEFAULT_HOST = "localhost";
91

    
92
    private int port;
93

    
94
    private String hostName = DEFAULT_HOST;
95

    
96
    private RestTemplate client;
97

    
98
    /**
99
     * @return a new rule that assumes an existing running broker
100
     */
101
    public static ServerRunning isRunning() {
102
        return new ServerRunning(true);
103
    }
104

    
105
    /**
106
     * @return a new rule that assumes there is no existing broker
107
     */
108
    public static ServerRunning isNotRunning() {
109
        return new ServerRunning(false);
110
    }
111

    
112
    private ServerRunning(boolean assumeOnline) {
113
        this.assumeOnline = assumeOnline;
114
        setPort(DEFAULT_PORT);
115
    }
116

    
117
    /**
118
     * @param port the port to set
119
     */
120
    public void setPort(int port) {
121
        this.port = port;
122
        if (!serverOffline.containsKey(port)) {
123
            serverOffline.put(port, true);
124
        }
125
        if (!serverOnline.containsKey(port)) {
126
            serverOnline.put(port, true);
127
        }
128
        client = getRestTemplate();
129
    }
130

    
131
    /**
132
     * @param hostName the hostName to set
133
     */
134
    public void setHostName(String hostName) {
135
        this.hostName = hostName;
136
    }
137

    
138
    @Override
139
    public Statement apply(final Statement base, FrameworkMethod method, Object target) {
140

    
141
        // Check at the beginning, so this can be used as a static field
142
        if (assumeOnline) {
143
            Assume.assumeTrue(serverOnline.get(port));
144
        } else {
145
            Assume.assumeTrue(serverOffline.get(port));
146
        }
147

    
148
        RestTemplate client = new RestTemplate();
149
        boolean followRedirects = HttpURLConnection.getFollowRedirects();
150
        HttpURLConnection.setFollowRedirects(false);
151
        boolean online = false;
152
        try {
153
            client.getForEntity(new UriTemplate(getUrl(CONNECT_TEST_PATH)).toString(), String.class);
154
            online = true;
155
            logger.info("Basic connectivity test passed");
156
        } catch (RestClientException e) {
157
            logger.warn(String.format(
158
                    "Not executing tests because basic connectivity test failed for hostName=%s, port=%d", hostName,
159
                    port), e);
160
            if (assumeOnline) {
161
                Assume.assumeNoException(e);
162
            }
163
        } finally {
164
            HttpURLConnection.setFollowRedirects(followRedirects);
165
            if (online) {
166
                serverOffline.put(port, false);
167
                if (!assumeOnline) {
168
                    Assume.assumeTrue(serverOffline.get(port));
169
                }
170

    
171
            } else {
172
                serverOnline.put(port, false);
173
            }
174
        }
175

    
176
        return new Statement() {
177

    
178
            @Override
179
            public void evaluate() throws Throwable {
180
                try {
181
                    postForStatus("/oauth/uncache_approvals", new LinkedMultiValueMap<String, String>());
182
                    base.evaluate();
183
                } finally {
184
                    postForStatus("/oauth/cache_approvals", new LinkedMultiValueMap<String, String>());
185
                }
186

    
187
            }
188
        };
189

    
190
    }
191

    
192
    public String getBaseUrl() {
193
        return "http://" + hostName + ":" + port;
194
    }
195

    
196
    public String getUrl(String path) {
197
        if (path.startsWith("http:")) {
198
            return path;
199
        }
200
        if (!path.startsWith("/")) {
201
            path = "/" + path;
202
        }
203
        return "http://" + hostName + ":" + port + path;
204
    }
205

    
206
    public ResponseEntity<String> postForString(String path, MultiValueMap<String, String> formData) {
207
        HttpHeaders headers = new HttpHeaders();
208
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_FORM_URLENCODED));
209
        return client.exchange(getUrl(path), HttpMethod.POST, new HttpEntity<MultiValueMap<String, String>>(formData,
210
                headers), String.class);
211
    }
212

    
213
    public ResponseEntity<Void> postForStatus(String path, MultiValueMap<String, String> formData) {
214
        return postForStatus(path, new HttpHeaders(), formData);
215
    }
216

    
217
    public ResponseEntity<Void> postForStatus(String path, HttpHeaders headers, MultiValueMap<String, String> formData) {
218
        HttpHeaders actualHeaders = new HttpHeaders();
219
        actualHeaders.putAll(headers);
220
        actualHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
221
        return client.exchange(getUrl(path), HttpMethod.POST, new HttpEntity<MultiValueMap<String, String>>(formData,
222
                actualHeaders), (Class<Void>)null);
223
    }
224

    
225
    public HttpHeaders postForHeaders(String path, MultiValueMap<String, String> formData) {
226
        return postForHeaders(path, formData, null);
227
    }
228

    
229
    public HttpHeaders postForHeaders(String path, MultiValueMap<String, String> formData, final HttpHeaders headers) {
230

    
231
        RequestCallback requestCallback = new NullRequestCallback();
232
        if (headers != null) {
233
            requestCallback = new RequestCallback() {
234
                @Override
235
                public void doWithRequest(ClientHttpRequest request) throws IOException {
236
                    request.getHeaders().putAll(headers);
237
                }
238
            };
239
        }
240

    
241
        StringBuilder builder = new StringBuilder(getUrl(path));
242
        if (!path.contains("?")) {
243
            builder.append("?");
244
        } else {
245
            builder.append("&");
246
        }
247
        for (String key : formData.keySet()) {
248
            for (String value : formData.get(key)) {
249
                builder.append(key + "=" + value);
250
                builder.append("&");
251
            }
252
        }
253
        builder.deleteCharAt(builder.length() - 1);
254
        return client.execute(builder.toString(), HttpMethod.POST, requestCallback,
255
                new ResponseExtractor<HttpHeaders>() {
256
                    @Override
257
                    public HttpHeaders extractData(ClientHttpResponse response) throws IOException {
258
                        return response.getHeaders();
259
                    }
260
                });
261
    }
262

    
263
    public ResponseEntity<String> postForString(String path, HttpHeaders headers, MultiValueMap<String, String> formData) {
264
        HttpHeaders actualHeaders = new HttpHeaders();
265
        actualHeaders.putAll(headers);
266
        headers.setAccept(Arrays.asList(MediaType.APPLICATION_FORM_URLENCODED));
267
        return client.exchange(getUrl(path), HttpMethod.POST, new HttpEntity<MultiValueMap<String, String>>(formData,
268
                headers), String.class);
269
    }
270

    
271
    public ResponseEntity<String> getForString(String path, final HttpHeaders headers) {
272
        return client.exchange(getUrl(path), HttpMethod.GET, new HttpEntity<Void>((Void) null, headers), String.class);
273
    }
274

    
275
    public ResponseEntity<String> getForString(String path) {
276
        return getForString(path, new HttpHeaders());
277
    }
278

    
279
    public String getForRedirect(String path, final HttpHeaders headers) {
280
        ResponseEntity<Void> response = client.exchange(getUrl(path), HttpMethod.GET, new HttpEntity<Void>((Void) null,
281
                headers), Void.class);
282
        URI location = new URI(response.getHeaders().getLocation());
283
        try {
284
            return URLDecoder.decode(location.toString(), "UTF-8");
285
        } catch (UnsupportedEncodingException e) {
286
            throw new IllegalStateException("Could not decode URL", e);
287
        }
288
    }
289

    
290
    public HttpStatus getStatusCode(String path, final HttpHeaders headers) {
291
        RequestCallback requestCallback = new NullRequestCallback();
292
        if (headers != null) {
293
            requestCallback = new RequestCallback() {
294
                @Override
295
                public void doWithRequest(ClientHttpRequest request) throws IOException {
296
                    request.getHeaders().putAll(headers);
297
                }
298
            };
299
        }
300
        return client.execute(getUrl(path), HttpMethod.GET, requestCallback,
301
                new ResponseExtractor<ResponseEntity<Void>>() {
302
                    @Override
303
                    public ResponseEntity<Void> extractData(ClientHttpResponse response) throws IOException {
304
                        FileCopyUtils.copyToByteArray(response.getBody());
305
                        return new ResponseEntity<Void>(response.getStatusCode());
306
                    }
307
                }).getStatusCode();
308
    }
309

    
310
    public HttpStatus getStatusCode(String path) {
311
        return getStatusCode(getUrl(path), null);
312
    }
313

    
314
    public RestTemplate getRestTemplate() {
315
        RestTemplate client = new RestTemplate();
316
        client.setRequestFactory(new SimpleClientHttpRequestFactory() {
317
            @Override
318
            protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
319
                super.prepareConnection(connection, httpMethod);
320
                connection.setInstanceFollowRedirects(false);
321
            }
322
        });
323
        client.setErrorHandler(new ResponseErrorHandler() {
324
            // Pass errors through in response entity for status code analysis
325
            @Override
326
            public boolean hasError(ClientHttpResponse response) throws IOException {
327
                return false;
328
            }
329

    
330
            @Override
331
            public void handleError(ClientHttpResponse response) throws IOException {
332
            }
333
        });
334
        return client;
335
    }
336

    
337
    public UriBuilder buildUri(String url) {
338
        return UriBuilder.fromUri(url.startsWith("http:") ? url : getUrl(url));
339
    }
340

    
341
    private static final class NullRequestCallback implements RequestCallback {
342
        @Override
343
        public void doWithRequest(ClientHttpRequest request) throws IOException {
344
        }
345
    }
346

    
347
    public static class UriBuilder {
348

    
349
        private final String url;
350
        private MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>();
351

    
352
        public UriBuilder(String url) {
353
            this.url = url;
354
        }
355

    
356
        public static UriBuilder fromUri(String url) {
357
            return new UriBuilder(url);
358
        }
359

    
360
        public UriBuilder queryParam(String key, String value) {
361
            params.add(key, value);
362
            return this;
363
        }
364

    
365
        public URI build() {
366
            StringBuilder builder = new StringBuilder(url);
367
            try {
368
                if (!params.isEmpty()) {
369
                    builder.append("?");
370
                    boolean first = true;
371
                    for (String key : params.keySet()) {
372
                        if (!first) {
373
                            builder.append("&");
374
                        } else {
375
                            first = false;
376
                        }
377
                        for (String value : params.get(key)) {
378
                            builder.append(key + "=" + UriUtils.encodeQueryParam(value, "UTF-8"));
379
                        }
380
                    }
381
                }
382
                return new URI(builder.toString());
383
            } catch (UnsupportedEncodingException ex) {
384
                // should not happen, UTF-8 is always supported
385
                throw new IllegalStateException(ex);
386
            } catch (URISyntaxException ex) {
387
                throw new IllegalArgumentException("Could not create URI from [" + builder + "]: " + ex, ex);
388
            }
389
        }
390
    }
391
}
(1-1/2)