Skip to content

Commit 856607a

Browse files
authored
Merge pull request #131 from utPLSQL/bugfix/timeout_does_not_throw_exception
bugfix/Handling Timeout and Oracle CreateStatement stuck scenario
2 parents 5de50db + 8b7c845 commit 856607a

File tree

7 files changed

+295
-60
lines changed

7 files changed

+295
-60
lines changed

pom.xml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@
2929
<groupId>com.oracle.jdbc</groupId>
3030
<artifactId>ucp</artifactId>
3131
</exclusion>
32+
<exclusion>
33+
<groupId>com.oracle.jdbc</groupId>
34+
<artifactId>ojdbc8</artifactId>
35+
</exclusion>
36+
<exclusion>
37+
<groupId>com.oracle.jdbc</groupId>
38+
<artifactId>orai18n</artifactId>
39+
</exclusion>
3240
</exclusions>
3341
</dependency>
3442
<dependency>
@@ -53,6 +61,18 @@
5361
<artifactId>logback-classic</artifactId>
5462
<version>1.2.3</version>
5563
</dependency>
64+
<dependency>
65+
<groupId>com.oracle.jdbc</groupId>
66+
<artifactId>ojdbc8</artifactId>
67+
<version>12.2.0.1</version>
68+
<scope>compile</scope>
69+
</dependency>
70+
<dependency>
71+
<groupId>com.oracle.jdbc</groupId>
72+
<artifactId>orai18n</artifactId>
73+
<version>12.2.0.1</version>
74+
<scope>compile</scope>
75+
</dependency>
5676

5777
<!-- Test -->
5878
<dependency>
@@ -139,8 +159,26 @@
139159
<enabled>true</enabled>
140160
</snapshots>
141161
</repository>
162+
<repository>
163+
<id>maven.oracle.com</id>
164+
<releases>
165+
<enabled>true</enabled>
166+
</releases>
167+
<snapshots>
168+
<enabled>false</enabled>
169+
</snapshots>
170+
<url>https://maven.oracle.com</url>
171+
<layout>default</layout>
172+
</repository>
142173
</repositories>
143174

175+
<pluginRepositories>
176+
<pluginRepository>
177+
<id>maven.oracle.com</id>
178+
<url>https://maven.oracle.com</url>
179+
</pluginRepository>
180+
</pluginRepositories>
181+
144182
<profiles>
145183
<profile>
146184
<id>utPLSQL-local</id>

src/main/java/org/utplsql/cli/ReporterManager.java

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
class ReporterManager {
2020

2121
private List<ReporterOptions> reporterOptionsList;
22+
private List<Throwable> reporterGatherErrors;
23+
private ExecutorService executorService;
2224

2325
ReporterManager(List<String> reporterParams ) {
2426
initReporterOptionsList(reporterParams);
@@ -49,14 +51,33 @@ private void initReporterOptionsList( List<String> reporterParams ) {
4951
}
5052
}
5153

54+
private void abortGathering(Throwable e) {
55+
addGatherError(e);
56+
executorService.shutdownNow();
57+
}
58+
59+
private void addGatherError( Throwable e ) {
60+
if ( reporterGatherErrors == null ) {
61+
reporterGatherErrors = new ArrayList<>();
62+
}
63+
reporterGatherErrors.add(e);
64+
}
65+
66+
boolean haveGatherErrorsOccured() {
67+
return reporterGatherErrors != null && !reporterGatherErrors.isEmpty();
68+
}
69+
70+
List<Throwable> getGatherErrors() {
71+
return reporterGatherErrors;
72+
}
5273

5374
/** Initializes the reporters so we can use the id to gather results
5475
*
5576
* @param conn Active Connection
5677
* @return List of Reporters
5778
* @throws SQLException
5879
*/
59-
public List<Reporter> initReporters(Connection conn, ReporterFactory reporterFactory, CompatibilityProxy compatibilityProxy) throws SQLException
80+
List<Reporter> initReporters(Connection conn, ReporterFactory reporterFactory, CompatibilityProxy compatibilityProxy) throws SQLException
6081
{
6182
final List<Reporter> reporterList = new ArrayList<>();
6283

@@ -79,10 +100,14 @@ public List<Reporter> initReporters(Connection conn, ReporterFactory reporterFac
79100
*
80101
* @param executorService
81102
* @param dataSource
82-
* @param returnCode
83103
*/
84-
public void startReporterGatherers(ExecutorService executorService, final DataSource dataSource, final int[] returnCode)
104+
void startReporterGatherers(ExecutorService executorService, final DataSource dataSource)
85105
{
106+
if ( this.executorService != null && !this.executorService.isShutdown())
107+
throw new IllegalStateException("There is already a running executor service!");
108+
109+
this.executorService = executorService;
110+
86111
// TODO: Implement Init-check
87112
// Gather each reporter results on a separate thread.
88113
for (ReporterOptions ro : reporterOptionsList) {
@@ -103,9 +128,7 @@ public void startReporterGatherers(ExecutorService executorService, final DataSo
103128

104129
ro.getReporterObj().getOutputBuffer().printAvailable(conn, printStreams);
105130
} catch (SQLException | FileNotFoundException e) {
106-
System.out.println(e.getMessage());
107-
returnCode[0] = Cli.DEFAULT_ERROR_CODE;
108-
executorService.shutdownNow();
131+
abortGathering(e);
109132
} finally {
110133
if (fileOutStream != null)
111134
fileOutStream.close();
@@ -114,9 +137,9 @@ public void startReporterGatherers(ExecutorService executorService, final DataSo
114137
}
115138
}
116139

117-
public List<ReporterOptions> getReporterOptionsList() {
140+
List<ReporterOptions> getReporterOptionsList() {
118141
return reporterOptionsList;
119142
}
120143

121-
public int getNumberOfReporters() { return reporterOptionsList.size(); }
144+
int getNumberOfReporters() { return reporterOptionsList.size(); }
122145
}

src/main/java/org/utplsql/cli/RunCommand.java

Lines changed: 74 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@
22

33
import com.beust.jcommander.Parameter;
44
import com.beust.jcommander.Parameters;
5+
import com.zaxxer.hikari.HikariDataSource;
56
import org.slf4j.Logger;
67
import org.slf4j.LoggerFactory;
78
import org.utplsql.api.*;
89
import org.utplsql.api.compatibility.CompatibilityProxy;
910
import org.utplsql.api.db.DefaultDatabaseInformation;
1011
import org.utplsql.api.exception.DatabaseNotCompatibleException;
12+
import org.utplsql.api.exception.OracleCreateStatmenetStuckException;
1113
import org.utplsql.api.exception.SomeTestsFailedException;
1214
import org.utplsql.api.exception.UtPLSQLNotInstalledException;
1315
import org.utplsql.api.reporter.Reporter;
1416
import org.utplsql.api.reporter.ReporterFactory;
1517
import org.utplsql.cli.exception.DatabaseConnectionFailed;
18+
import org.utplsql.cli.exception.ReporterTimeoutException;
1619
import org.utplsql.cli.log.StringBlockFormatter;
1720

1821
import javax.sql.DataSource;
@@ -22,12 +25,12 @@
2225
import java.util.ArrayList;
2326
import java.util.Arrays;
2427
import java.util.List;
25-
import java.util.concurrent.ExecutorService;
26-
import java.util.concurrent.Executors;
27-
import java.util.concurrent.TimeUnit;
28+
import java.util.concurrent.*;
2829

2930
/**
30-
* Created by vinicius.moreira on 19/04/2017.
31+
* Issues a Run-Command with all the options
32+
*
33+
* Uses an executor to start a RunTestRunnerTask and one ReporterGatheringTask per Reporter requested.
3134
*
3235
* @author vinicious moreira
3336
* @author pesse
@@ -138,27 +141,17 @@ else if ( logDebug ) {
138141
LoggerConfiguration.configure(level);
139142
}
140143

141-
public int run() {
144+
public int doRun() throws OracleCreateStatmenetStuckException {
142145
init();
143146
outputMainInformation();
144147

148+
HikariDataSource dataSource = null;
149+
int returnCode = 0;
145150
try {
146151

147152
final List<Reporter> reporterList;
148153

149-
final File baseDir = new File("").getAbsoluteFile();
150-
final FileMapperOptions[] sourceMappingOptions = {null};
151-
final FileMapperOptions[] testMappingOptions = {null};
152-
153-
final int[] returnCode = {0};
154-
155-
sourceMappingOptions[0] = getFileMapperOptionsByParamListItem(this.sourcePathParams, baseDir);
156-
testMappingOptions[0] = getFileMapperOptionsByParamListItem(this.testPathParams, baseDir);
157-
158-
final List<String> finalIncludeObjectsList = getObjectList(includeObjects);
159-
final List<String> finalExcludeObjectsList = getObjectList(excludeObjects);
160-
161-
final DataSource dataSource = DataSourceProvider.getDataSource(getConnectionInfo(), getReporterManager().getNumberOfReporters() + 1);
154+
dataSource = (HikariDataSource) DataSourceProvider.getDataSource(getConnectionInfo(), getReporterManager().getNumberOfReporters() + 2);
162155

163156
initDatabase(dataSource);
164157
reporterList = initReporters(dataSource);
@@ -172,48 +165,77 @@ public int run() {
172165
ExecutorService executorService = Executors.newFixedThreadPool(1 + reporterList.size());
173166

174167
// Run tests.
175-
executorService.submit(() -> {
176-
try (Connection conn = dataSource.getConnection()) {
177-
TestRunner testRunner = new TestRunner()
178-
.addPathList(testPaths)
179-
.addReporterList(reporterList)
180-
.sourceMappingOptions(sourceMappingOptions[0])
181-
.testMappingOptions(testMappingOptions[0])
182-
.colorConsole(this.colorConsole)
183-
.failOnErrors(true)
184-
.skipCompatibilityCheck(skipCompatibilityCheck)
185-
.includeObjects(finalIncludeObjectsList)
186-
.excludeObjects(finalExcludeObjectsList);
187-
188-
logger.info("Running tests now.");
189-
logger.info("--------------------------------------");
190-
testRunner.run(conn);
191-
} catch (SomeTestsFailedException e) {
192-
returnCode[0] = this.failureExitCode;
193-
} catch (SQLException e) {
194-
System.out.println(e.getMessage());
195-
returnCode[0] = Cli.DEFAULT_ERROR_CODE;
196-
executorService.shutdownNow();
197-
}
198-
});
168+
Future<Boolean> future = executorService.submit(new RunTestRunnerTask(dataSource, newTestRunner(reporterList)));
199169

200170
// Gather each reporter results on a separate thread.
201-
getReporterManager().startReporterGatherers(executorService, dataSource, returnCode);
202-
203-
executorService.shutdown();
204-
executorService.awaitTermination(timeoutInMinutes, TimeUnit.MINUTES);
171+
getReporterManager().startReporterGatherers(executorService, dataSource);
172+
173+
try {
174+
future.get(timeoutInMinutes, TimeUnit.MINUTES);
175+
} catch (TimeoutException e) {
176+
executorService.shutdownNow();
177+
throw new ReporterTimeoutException(timeoutInMinutes);
178+
} catch (ExecutionException e) {
179+
if (e.getCause() instanceof SomeTestsFailedException) {
180+
returnCode = failureExitCode;
181+
} else {
182+
executorService.shutdownNow();
183+
throw e.getCause();
184+
}
185+
} catch (InterruptedException e) {
186+
executorService.shutdownNow();
187+
throw e;
188+
}
189+
finally {
190+
executorService.shutdown();
191+
if (!executorService.awaitTermination(timeoutInMinutes, TimeUnit.MINUTES)) {
192+
throw new ReporterTimeoutException(timeoutInMinutes);
193+
}
194+
}
205195

206196
logger.info("--------------------------------------");
207197
logger.info("All tests done.");
208-
209-
return returnCode[0];
210-
}
211-
catch ( DatabaseNotCompatibleException | UtPLSQLNotInstalledException | DatabaseConnectionFailed e ) {
198+
} catch ( OracleCreateStatmenetStuckException e ) {
199+
throw e;
200+
} catch ( DatabaseNotCompatibleException | UtPLSQLNotInstalledException | DatabaseConnectionFailed | ReporterTimeoutException e ) {
212201
System.out.println(e.getMessage());
213-
} catch (Exception e) {
202+
returnCode = Cli.DEFAULT_ERROR_CODE;
203+
} catch (Throwable e) {
214204
e.printStackTrace();
205+
returnCode = Cli.DEFAULT_ERROR_CODE;
206+
} finally {
207+
if ( dataSource != null )
208+
dataSource.close();
215209
}
216-
return 1;
210+
return returnCode;
211+
}
212+
213+
public int run() {
214+
for ( int i = 1; i<5; i++ ) {
215+
try {
216+
return doRun();
217+
} catch (OracleCreateStatmenetStuckException e) {
218+
logger.warn("WARNING: Caught Oracle stuck during creation of Runner-Statement. Retrying ({})", i);
219+
}
220+
}
221+
222+
return Cli.DEFAULT_ERROR_CODE;
223+
}
224+
225+
private TestRunner newTestRunner( List<Reporter> reporterList) {
226+
227+
final File baseDir = new File("").getAbsoluteFile();
228+
229+
return new TestRunner()
230+
.addPathList(testPaths)
231+
.addReporterList(reporterList)
232+
.sourceMappingOptions(getFileMapperOptionsByParamListItem(this.sourcePathParams, baseDir))
233+
.testMappingOptions(getFileMapperOptionsByParamListItem(this.testPathParams, baseDir))
234+
.colorConsole(this.colorConsole)
235+
.failOnErrors(true)
236+
.skipCompatibilityCheck(skipCompatibilityCheck)
237+
.includeObjects(getObjectList(includeObjects))
238+
.excludeObjects(getObjectList(excludeObjects));
217239
}
218240

219241
private ArrayList<String> getObjectList(String includeObjects) {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package org.utplsql.cli;
2+
3+
import org.slf4j.Logger;
4+
import org.slf4j.LoggerFactory;
5+
import org.utplsql.api.TestRunner;
6+
import org.utplsql.api.exception.OracleCreateStatmenetStuckException;
7+
import org.utplsql.api.exception.SomeTestsFailedException;
8+
9+
import javax.sql.DataSource;
10+
import java.sql.Connection;
11+
import java.sql.SQLException;
12+
import java.util.concurrent.Callable;
13+
import java.util.concurrent.Executors;
14+
15+
/** Runs the utPLSQL Test-Runner
16+
*
17+
* Takes care of its connection.
18+
* In case of an OracleCreateStatementStuckException it will abort the connection, otherwise close it.
19+
*
20+
* @author pesse
21+
*/
22+
public class RunTestRunnerTask implements Callable<Boolean> {
23+
24+
private static final Logger logger = LoggerFactory.getLogger(RunTestRunnerTask.class);
25+
private DataSource dataSource;
26+
private TestRunner testRunner;
27+
28+
RunTestRunnerTask(DataSource dataSource, TestRunner testRunner) {
29+
this.dataSource = dataSource;
30+
this.testRunner = testRunner;
31+
}
32+
33+
@Override
34+
public Boolean call() throws Exception {
35+
Connection conn = null;
36+
try {
37+
conn = dataSource.getConnection();
38+
logger.info("Running tests now.");
39+
logger.info("--------------------------------------");
40+
testRunner.run(conn);
41+
} catch (SomeTestsFailedException e) {
42+
throw e;
43+
} catch (OracleCreateStatmenetStuckException e ) {
44+
try {
45+
conn.abort(Executors.newSingleThreadExecutor());
46+
conn = null;
47+
} catch (SQLException e1) {
48+
logger.error(e1.getMessage(), e1);
49+
}
50+
throw e;
51+
} catch (SQLException e) {
52+
System.out.println(e.getMessage());
53+
throw e;
54+
} finally {
55+
if ( conn != null ) {
56+
try {
57+
conn.close();
58+
} catch (SQLException e) {
59+
logger.error(e.getMessage(), e);
60+
}
61+
}
62+
}
63+
return true;
64+
}
65+
}

0 commit comments

Comments
 (0)