-
Notifications
You must be signed in to change notification settings - Fork 5
/
BasilTestBase.java
331 lines (307 loc) · 12.1 KB
/
BasilTestBase.java
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
package uk.ac.open.kmi.basil.it;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.TreeSet;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.shiro.config.Ini;
import org.apache.stanbol.commons.testing.http.RequestBuilder;
import org.apache.stanbol.commons.testing.http.RequestExecutor;
import org.apache.stanbol.commons.testing.jarexec.JarExecutor;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.init.ScriptUtils;
/**
*
* Most of the initial code has been learnt from Apache Stanbol's
* integration-tests module.
*
* https://svn.apache.org/repos/asf/stanbol/trunk/commons/testing/stanbol/src/
* main/java/org/apache/stanbol/commons/testing/stanbol/StanbolTestBase.java
*
* @author enridaga
*
*/
public class BasilTestBase {
public static final String TEST_SERVER_URL_PROP = "test.server.url";
public static final String SERVER_READY_TIMEOUT_PROP = "server.ready.timeout.seconds";
public static final String SERVER_READY_PROP_PREFIX = "server.ready.path";
public static final String KEEP_JAR_RUNNING_PROP = "keepJarRunning";
public static final String BASIL_CONFIGURATION_FILE = "basil.configurationFile";
public static final String TEST_DB_INIT = "test.db.init";
public static final String TEST_DB_INIT_SCRIPT = "test.db.init.script";
protected static String serverBaseUrl;
protected static String dbInit = "false";
protected static String jdbcConnectionUrl = null;
protected static String databaseName = null;
protected static String user = null;
protected static String password = null;
private static final Logger log = LoggerFactory.getLogger(BasilTestBase.class);
protected boolean serverReady = false;
protected RequestBuilder builder;
protected CloseableHttpClient httpClient = null;
protected RequestExecutor executor;
/**
* Overwrite to enable authentication for requests
*
* @return the <code>username:password</code> or <code>null</code> to
* deactivate authentication (default)
*/
protected String getCredentials() {
return null;
}
@BeforeClass
public static void initialize() throws Exception {
dbInit = System.getProperty(TEST_DB_INIT);
String conf = System.getProperty(BASIL_CONFIGURATION_FILE);
log.info("Configuration: {}", conf);
log.info("Init a test db: {}", dbInit);
if ("true".equals(dbInit)) {
log.debug("{} is true", TEST_DB_INIT);
conf = createTestDb();
}
startServer(conf);
}
@AfterClass
public static void cleanup() {
if(!dbInit.equals("true")){
return;
}
log.info("Cleaning up: dropping db {}", databaseName);
try {
// Delete the test Database
Class.forName("com.mysql.jdbc.Driver");
try (Connection conn = DriverManager.getConnection(jdbcConnectionUrl, user, password)) {
try (Statement create = conn.createStatement()) {
create.executeUpdate("DROP DATABASE `" + databaseName + "`");
log.info("Database {} dropped", databaseName);
}
}
} catch (Exception e) {
log.error("IGNORED (cannot cleanup test db!)", e);
}
}
/**
* Returns the location of the new configuration file
*
* @return
* @throws Exception
*/
private static String createTestDb() throws Exception {
log.info("Creating test db");
final String inputConfigurationFile = System.getProperty(BASIL_CONFIGURATION_FILE);
String str = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";
final String dbPostfix = '_' + RandomStringUtils.random(8, str.toCharArray());
File f = new File(inputConfigurationFile);
if (!f.exists() || !f.canRead()) {
throw new Exception("Cannot use basil configuration file: " + f);
}
Ini ini = Ini.fromResourcePath(f.getAbsolutePath());
jdbcConnectionUrl = new StringBuilder().append("jdbc:mysql://").append(ini.get("").get("ds.serverName"))
.append(":").append(ini.get("").get("ds.port")).append("/").toString();
databaseName = ini.get("").get("ds.databaseName") + dbPostfix;
user = ini.get("").get("ds.user");
password = ini.get("").get("ds.password");
log.info("Attemp to init a test db {}", databaseName);
Class.forName("com.mysql.jdbc.Driver");
try (Connection conn = DriverManager.getConnection(jdbcConnectionUrl, user, password)) {
try (Statement create = conn.createStatement()) {
create.executeUpdate("CREATE DATABASE `" + databaseName + "`");
log.info("Database {} created", databaseName);
}
try (Statement create = conn.createStatement()) {
create.executeQuery("USE `" + databaseName + "`");
}
// Now init db
Resource r = new FileSystemResource(System.getProperty(TEST_DB_INIT_SCRIPT));
log.info("Running init script: {}", r);
ScriptUtils.executeSqlScript(conn, r);
}
String newConfigurationFile = inputConfigurationFile + dbPostfix;
log.info("Write configuration file for test server instance: {}", newConfigurationFile);
Path from = Paths.get(inputConfigurationFile);
Path to = Paths.get(newConfigurationFile);
Charset charset = StandardCharsets.UTF_8;
String content = new String(Files.readAllBytes(from), charset);
content = content.replaceAll(ini.get("").get("ds.databaseName"), databaseName);
Files.write(to, content.getBytes(charset));
// Configuration file
return newConfigurationFile;
}
public static synchronized void startServer(String configurationFile) throws Exception {
log.info("Starting testing server");
if (serverBaseUrl != null) {
// concurrent initialization by loading subclasses
return;
}
final String configuredUrl = System.getProperty(TEST_SERVER_URL_PROP);
if (configuredUrl != null && !configuredUrl.trim().isEmpty()) {
serverBaseUrl = configuredUrl;
try {
new URL(configuredUrl);
} catch (MalformedURLException e) {
log.error("Configured " + TEST_SERVER_URL_PROP + " = " + configuredUrl + "is not a valid URL!");
throw e;
}
log.info(TEST_SERVER_URL_PROP + " is set: not starting server jar (" + serverBaseUrl + ")");
} else {
Properties properties = System.getProperties();
// Add jvm option for basil configuration file
String opts = properties.getProperty("jar.executor.vm.options");
opts += " -Dbasil.configurationFile=" + configurationFile;
properties.setProperty("jar.executor.vm.options", opts);
final JarExecutor j = JarExecutor.getInstance(properties);
j.start();
serverBaseUrl = "http://localhost:" + j.getServerPort();
log.info("Forked subprocess server listening to: " + serverBaseUrl);
// Optionally block here so that the runnable jar stays up - we can
// then run tests against it from another VM
if ("true".equals(System.getProperty(KEEP_JAR_RUNNING_PROP))) {
log.info(KEEP_JAR_RUNNING_PROP + " set to true - entering infinite loop"
+ " so that runnable jar stays up. Kill this process to exit.");
while (true) {
Thread.sleep(1000L);
}
}
}
}
@Before
public void waitForServerReady() throws Exception {
log.debug("> before {}#waitForServerReady()", getClass().getSimpleName());
// initialize instance request builder and HTTP client
builder = new RequestBuilder(serverBaseUrl);
// TODO:user name and pwd
String credentials = getCredentials();
if (credentials != null && !credentials.isEmpty()) {
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(HttpHost.create(serverBaseUrl)),
new UsernamePasswordCredentials(credentials));
httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
} else {
httpClient = HttpClients.createDefault();
}
executor = new RequestExecutor(httpClient);
if (serverReady) {
log.debug(" ... server already marked as ready!");
return;
}
// Timeout for readiness test
final String sec = System.getProperty(SERVER_READY_TIMEOUT_PROP);
final int timeoutSec = sec == null ? 60 : Integer.valueOf(sec);
log.info("Will wait up to " + timeoutSec + " seconds for server to become ready");
final long endTime = System.currentTimeMillis() + timeoutSec * 1000L;
// Get the list of paths to test and expected content regexps
final List<String> testPaths = new ArrayList<String>();
final TreeSet<Object> propertyNames = new TreeSet<Object>();
propertyNames.addAll(System.getProperties().keySet());
for (Object o : propertyNames) {
final String key = (String) o;
if (key.startsWith(SERVER_READY_PROP_PREFIX)) {
testPaths.add(System.getProperty(key));
}
}
// Consider the server ready if it responds to a GET on each of
// our configured request paths with a 200 result and content
// that matches the regexp supplied with the path
long sleepTime = 100;
readyLoop: while (!serverReady && System.currentTimeMillis() < endTime) {
// Wait a bit between checks, to let the server come up
Thread.sleep(sleepTime);
sleepTime = Math.min(5000L, sleepTime * 2);
// A test path is in the form path:substring or just path, in which
// case
// we don't check that the content contains the substring
log.debug(" - check serverReady Paths");
for (String p : testPaths) {
final String[] s = p.split(":");
final String path = s[0];
final String substring = (s.length > 1 ? s[1] : null);
final String url = serverBaseUrl + path;
log.debug(" > url: {}", url);
log.debug(" > content: {}", substring);
final HttpGet get = new HttpGet(url);
// authenticate as admin with password admin
// XXX let it use the default credentials, if any
// get.setHeader("Authorization", "Basic YWRtaW46YWRtaW4=");
for (int i = 2; i + 1 < s.length; i = i + 2) {
log.debug(" > header: {}:{}", s[i], s[i + 1]);
if (s[i] != null && !s[i].isEmpty() && s[i + 1] != null && !s[i + 1].isEmpty()) {
get.setHeader(s[i], s[i + 1]);
}
}
CloseableHttpResponse response = null;
HttpEntity entity = null;
try {
log.debug(" > execute: {}", get);
response = httpClient.execute(get);
log.debug(" > response: {}", response);
entity = response.getEntity();
final int status = response.getStatusLine().getStatusCode();
if (status != 200) {
log.info("Got {} at {} - will retry", status, url);
continue readyLoop;
} else {
log.debug("Got {} at {} - will retry", status, url);
}
if (substring != null) {
if (entity == null) {
log.info("No entity returned for {} - will retry", url);
continue readyLoop;
}
final String content = EntityUtils.toString(entity);
if (!content.contains(substring)) {
log.info("Returned content for {} does not contain " + "{} - will retry", url, substring);
continue readyLoop;
}
}
} catch (HttpHostConnectException e) {
log.info("Got HttpHostConnectException at " + url + " - will retry");
continue readyLoop;
} finally {
EntityUtils.consumeQuietly(entity);
if (response != null) {
response.close();
}
}
}
serverReady = true;
log.info("Got expected content for all configured requests, server is ready");
}
if (!serverReady) {
throw new Exception("Server not ready after " + timeoutSec + " seconds");
}
}
@After
public void closeExecutor() {
executor.close();
}
}