Skip to content

Commit 39343e9

Browse files
authored
Merge pull request #11 from djunglas/master
Minor code improvements (reading scalar and exception safety)
2 parents 970090f + 647ff82 commit 39343e9

File tree

3 files changed

+136
-45
lines changed

3 files changed

+136
-45
lines changed

lib/jdbc-custom-data-source.jar

1.28 KB
Binary file not shown.

src/main/java/com/ibm/opl/customdatasource/JdbcConfiguration.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ public void readXML(InputStream input) throws IOException {
215215

216216
// input parameters
217217
Node readNode = doc.getElementsByTagName(READ).item(0);
218-
if (readNode.getNodeType() == Node.ELEMENT_NODE) {
218+
if (readNode != null && readNode.getNodeType() == Node.ELEMENT_NODE) {
219219
NodeList queries = ((Element)readNode).getElementsByTagName(QUERY);
220220
for (int iquery = 0; iquery < queries.getLength(); iquery++) {
221221
Node qNode = queries.item(iquery);
@@ -230,7 +230,7 @@ public void readXML(InputStream input) throws IOException {
230230

231231
// write parameters
232232
Node writeNode = doc.getElementsByTagName(WRITE).item(0);
233-
if (writeNode.getNodeType() == Node.ELEMENT_NODE) {
233+
if (writeNode != null && writeNode.getNodeType() == Node.ELEMENT_NODE) {
234234
NodeList tables = ((Element)writeNode).getElementsByTagName(TABLE);
235235
for (int itable = 0; itable < tables.getLength(); itable++) {
236236
Node tNode = tables.item(itable);
@@ -246,4 +246,4 @@ public void readXML(InputStream input) throws IOException {
246246
throw new RuntimeException("Could not read XML configuration");
247247
}
248248
}
249-
}
249+
}

src/main/java/com/ibm/opl/customdatasource/JdbcCustomDataSource.java

Lines changed: 133 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -112,22 +112,33 @@ void fillNamesAndTypes(IloTupleSchema schema, String[] names, Type[] types) {
112112
* Overrides the IloCustomOplDataSource method to read data when the model
113113
* is generated.
114114
*/
115+
@Override
115116
public void customRead() {
116117
long startTime = System.currentTimeMillis();
117-
System.out.println("Reading elements from database");
118-
Properties prop = _configuration.getReadQueries();
119-
Enumeration<?> propertyNames = prop.propertyNames();
120-
while (propertyNames.hasMoreElements()) {
121-
String name = (String) propertyNames.nextElement();
122-
String query = prop.getProperty(name);
123-
System.out.println("Reading " + name + " using \"" + query + "\"");
124-
customRead(name, query);
118+
try {
119+
System.out.println("Reading elements from database");
120+
Properties prop = _configuration.getReadQueries();
121+
Enumeration<?> propertyNames = prop.propertyNames();
122+
while (propertyNames.hasMoreElements()) {
123+
String name = (String) propertyNames.nextElement();
124+
String query = prop.getProperty(name);
125+
System.out.println("Reading " + name + " using \"" + query + "\"");
126+
customRead(name, query);
127+
}
128+
long endTime = System.currentTimeMillis();
129+
System.out.println("Done (" + (endTime - startTime)/1000.0 + " s)");
130+
}
131+
catch (SQLException e) {
132+
// Since the superclass's method signature does not allow us to
133+
// throw an exception from here, we have to wrap the exception.
134+
long endTime = System.currentTimeMillis();
135+
System.err.println(e.getMessage() + " (after " + (endTime - startTime)/1000.0 + " s)");
136+
e.printStackTrace();
137+
throw new RuntimeException(e);
125138
}
126-
long endTime = System.currentTimeMillis();
127-
System.out.println("Done (" + (endTime - startTime)/1000.0 + " s)");
128139
}
129140

130-
public void customRead(String name, String query) {
141+
public void customRead(String name, String query) throws SQLException {
131142
IloOplElementDefinition def = _def.getElementDefinition(name);
132143
Type type = def.getElementDefinitionType();
133144
Type leaf = def.getLeaf().getElementDefinitionType();
@@ -139,17 +150,105 @@ public void customRead(String name, String query) {
139150
readSet(leaf, name, query);
140151
}
141152
}
153+
else if ( type == Type.INTEGER || type == Type.FLOAT || type == Type.STRING ) {
154+
readValue(name, query);
155+
}
156+
else
157+
throw new IllegalArgumentException("Cannot read element " + name + " of type " + type);
142158
}
143159

144-
public void readSet(Type leaf, String name, String query) {
160+
/** Helper class to execute queries in an exception safe way.
161+
* Use the class via the following template:
162+
* <pre>
163+
final RunQuery q = new RunQuery("SELECT * FROM table;");
164+
try {
165+
ResultSet rs = q.getResult();
166+
...
167+
}
168+
finally {
169+
q.close();
170+
}
171+
</pre>
172+
* This will correctly clean up and release all resources no matter whether
173+
* an exception is throw or not.
174+
*/
175+
private final class RunQuery {
176+
private Connection conn = null;
177+
private Statement stmt = null;
178+
private ResultSet rs = null;
179+
public RunQuery(String query) throws SQLException {
180+
Connection conn = DriverManager.getConnection(_configuration.getUrl(),
181+
_configuration.getUser(),
182+
_configuration.getPassword());
183+
Statement stmt = null;
184+
ResultSet rs = null;
185+
try {
186+
stmt = conn.createStatement();
187+
rs = stmt.executeQuery(query);
188+
// Everything worked without problem. Transfer ownership of
189+
// the objects to the newly constructed instance.
190+
this.conn = conn; conn = null;
191+
this.stmt = stmt; stmt = null;
192+
this.rs = rs; rs = null;
193+
}
194+
finally {
195+
if ( rs != null ) rs.close();
196+
if ( stmt != null ) stmt.close();
197+
if ( conn != null ) conn.close();
198+
}
199+
}
200+
public void close() throws SQLException {
201+
rs.close();
202+
stmt.close();
203+
conn.close();
204+
}
205+
ResultSet getResult() { return rs; }
206+
}
207+
208+
/** Read the scalar value for <code>name</code> from <code>query</code>.
209+
* <b>Note:</b> the function will just use the first value produced by
210+
* <code>query</code> and assign that to the element identified
211+
* by <code>name</code>. If the query produces more than one
212+
* value the surplus values are ignored.
213+
* @param name The name of the element to fill.
214+
* @param query The SQL query that produces the data for <code>name</code>.
215+
* @throws SQLException if querying the database fails or the query does
216+
* not produce at least one value.
217+
*/
218+
public void readValue(String name, String query) throws SQLException {
219+
IloOplElementDefinition def = _def.getElementDefinition(name);
145220
IloOplDataHandler handler = getDataHandler();
221+
final RunQuery q = new RunQuery(query);
146222
try {
147-
Connection conn = DriverManager.getConnection(_configuration.getUrl(),
148-
_configuration.getUser(),
149-
_configuration.getPassword());
150-
Statement stmt = conn.createStatement();
151-
ResultSet rs = stmt.executeQuery(query);
223+
ResultSet rs = q.getResult();
224+
rs.next();
225+
handler.startElement(name);
226+
Type type = def.getElementDefinitionType();
227+
if (type == Type.INTEGER) {
228+
handler.addIntItem(rs.getInt(1));
229+
}
230+
else if (type == Type.FLOAT) {
231+
handler.addNumItem(rs.getDouble(1));
232+
}
233+
else if (type == Type.STRING) {
234+
handler.addStringItem(rs.getString(1));
235+
}
236+
else
237+
throw new IllegalArgumentException("Cannot load element " + name + " of type " + type);
238+
handler.endElement();
239+
}
240+
finally {
241+
// We don't use try-with-resources so that we can compile
242+
// with pre-1.8 compilers as well.
243+
q.close();
244+
}
245+
}
152246

247+
public void readSet(Type leaf, String name, String query) throws SQLException {
248+
IloOplDataHandler handler = getDataHandler();
249+
final RunQuery q = new RunQuery(query);
250+
try {
251+
ResultSet rs = q.getResult();
153252
handler.startElement(name);
154253
handler.startSet();
155254

@@ -163,32 +262,27 @@ else if (leaf == Type.STRING)
163262
}
164263
handler.endSet();
165264
handler.endElement();
166-
rs.close();
167-
stmt.close();
168-
conn.close();
169-
} catch (SQLException e) {
170-
e.printStackTrace();
265+
}
266+
finally {
267+
q.close();
171268
}
172269
}
173270

174-
public void readTupleSet(String name, String query) {
271+
public void readTupleSet(String name, String query) throws SQLException {
175272
IloOplDataHandler handler = getDataHandler();
176-
try {
177-
IloOplElement elt = handler.getElement(name);
178-
ilog.opl_core.cppimpl.IloTupleSet tupleSet = (ilog.opl_core.cppimpl.IloTupleSet) elt.asTupleSet();
179-
IloTupleSchema schema = tupleSet.getSchema_cpp();
180-
int size = schema.getTotalColumnNumber();
273+
IloOplElement elt = handler.getElement(name);
274+
ilog.opl_core.cppimpl.IloTupleSet tupleSet = (ilog.opl_core.cppimpl.IloTupleSet) elt.asTupleSet();
275+
IloTupleSchema schema = tupleSet.getSchema_cpp();
276+
int size = schema.getTotalColumnNumber();
181277

182-
String[] oplFieldsName = new String[size];
183-
Type[] oplFieldsType = new Type[size];
278+
String[] oplFieldsName = new String[size];
279+
Type[] oplFieldsType = new Type[size];
184280

185-
fillNamesAndTypes(schema, oplFieldsName, oplFieldsType);
281+
fillNamesAndTypes(schema, oplFieldsName, oplFieldsType);
186282

187-
Connection conn = DriverManager.getConnection(_configuration.getUrl(),
188-
_configuration.getUser(),
189-
_configuration.getPassword());
190-
Statement stmt = conn.createStatement();
191-
ResultSet rs = stmt.executeQuery(query);
283+
final RunQuery q = new RunQuery(query);
284+
try {
285+
ResultSet rs = q.getResult();
192286

193287
handler.startElement(name);
194288
handler.startSet();
@@ -208,12 +302,9 @@ public void readTupleSet(String name, String query) {
208302
}
209303
handler.endSet();
210304
handler.endElement();
211-
rs.close();
212-
stmt.close();
213-
conn.close();
214-
} catch (SQLException e) {
215-
e.printStackTrace();
216305
}
217-
306+
finally {
307+
q.close();
308+
}
218309
}
219310
};

0 commit comments

Comments
 (0)