Skip to content

Commit

Permalink
Fix code examples in functional spec, and improve a few javadoc comme…
Browse files Browse the repository at this point in the history
…nts. ResultSet returned from Cell.drillThrough() now closes its connection and statement on close, thereby fixing a connection leak.

git-svn-id: https://olap4j.svn.sourceforge.net/svnroot/olap4j/trunk@54 c6a108a4-781c-0410-a6c6-c2d559e19af0
  • Loading branch information
julianhyde committed Dec 18, 2007
1 parent 84e5841 commit 6d9c74d
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 43 deletions.
60 changes: 33 additions & 27 deletions doc/olap4j_fs.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@
<h1>olap4j Specification</h1>

<p>Authors: Julian Hyde, Barry Klawans<br>
Version: 0.9 (beta)<br>
Version: 0.9.4 (beta)<br>
Revision: $Id$
(<a href="http://olap4j.svn.sourceforge.net/viewvc/olap4j/trunk/doc/olap4j_fs.html?view=log">log</a>)<br>
Last modified: December 10<sup>th</sup>, 2007.</p>
Last modified: December 18<sup>th</sup>, 2007.</p>
<hr noshade="noshade">

<h2><a name="Contents">Contents</a></h2>
Expand Down Expand Up @@ -510,15 +510,15 @@ <h3>2.2. <a name="Connections">Connections</a></h3>
<br>
Class.forName(&quot;mondrian.olap4j.Driver&quot;);<br>
OlapConnection connection =<br>
&nbsp;&nbsp;&nbsp; DriverManager.createConnection(<br>
&nbsp;&nbsp;&nbsp; DriverManager.getConnection(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;jdbc:mondrian:local:Jdbc=jdbc:odbc:MondrianFoodMart;&quot;
+<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Catalog=/WEB-INF/queries/FoodMart.xml;&quot;
+<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Role='California manager'&quot;);<br>
OlapWrapper wrapper = (OlapWrapper) connection;<br>
OlapConnection olapConnection = wrapper.unwrap(OlapConnection.class);<br>
OlapStatement statement = olapConnection.createOlapStatement();<br>
OlapStatement statement = olapConnection.createStatement();<br>
<br>
OlapResult result =<br>
&nbsp;&nbsp;&nbsp; statement.execute(<br>
Expand All @@ -537,12 +537,12 @@ <h3>2.2. <a name="Connections">Connections</a></h3>
<br>
Class.forName(&quot;olap4j.impl.xmla.Driver&quot;);<br>
OlapConnection connection =<br>
&nbsp;&nbsp;&nbsp; DriverManager.createConnection(<br>
&nbsp;&nbsp;&nbsp; DriverManager.getConnection(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;jdbc:olap4jxmla:Server=http://localhost/xmla/msxisapi.dll;&quot;
+<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Catalog=FoodMart&quot;);<br>
OlapWrapper wrapper = (OlapWrapper) connection;<br>
OlapConnection olapConnection = wrapper.unwrap(OlapConnection.class);<br>
OlapStatement statement = connection.createOlapStatement();<br>
OlapStatement statement = connection.createStatement();<br>
<br>
CellSet result =<br>
&nbsp;&nbsp;&nbsp; statement.execute(<br>
Expand Down Expand Up @@ -574,20 +574,34 @@ <h4>2.2.1. <a name="Connection_pooling">Connection pooling</a></h4>
<a class="javadoc" href="api/org/olap4j/OlapWrapper.html">OlapWrapper</a>
interface with the method method <code>unwrap(Class)</code> to access the object underneath
the wrapped
connection. For instance,</p>
connection. For instance, if you were using DBCP, you could define and use a
pooling olap4j data source as follows:</p>

<div class="code">DataSource dataSource; // a data source using a connection
pool<br>
Connection connection =<br>
&nbsp;&nbsp;&nbsp; DriverManager.createConnection(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;jdbc:mondrian:local:Jdbc=jdbc:odbc:MondrianFoodMart;&quot;
+<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Catalog=/WEB-INF/queries/FoodMart.xml;&quot;
+<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Role='California manager'&quot;);<br>
<div class="code">import org.apache.commons.dbcp.*;<br>
import org.olap4j.*;<br>
<br>
GenericObjectPool connectionPool =<br>
&nbsp;&nbsp;&nbsp; new GenericObjectPool(null);<br>
ConnectionFactory connectionFactory =<br>
&nbsp;&nbsp;&nbsp; new DriverManagerConnectionFactory(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&quot;jdbc:mondrian:local:Jdbc=jdbc:odbc:MondrianFoodMart;&quot; +<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&quot;Catalog=/WEB-INF/queries/FoodMart.xml;&quot; +<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Role='California manager'&quot;)<br>
PoolableConnectionFactory poolableConnectionFactory =<br>
&nbsp;&nbsp;&nbsp; new PoolableConnectionFactory(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; connectionFactory, connectionPool,
null, null, false, true);<br>
DataSource dataSource =<br>
&nbsp;&nbsp;&nbsp; new PoolingDataSource(connectionPool);<br>
<br>
// and some time later...<br>
<br>
Connection connection = dataSource.getConnection();<br>
OlapWrapper wrapper = (OlapWrapper) connection;<br>
OlapConnection olapConnection = wrapper.unwrap(OlapConnection.class);<br>
OlapStatement statement = olapConnection.createOlapStatement();</div>
OlapStatement statement = olapConnection.createStatement();</div>

<p>The <a class="javadoc" href="api/org/olap4j/OlapStatement.html">OlapStatement</a>,
<a class="javadoc" href="api/org/olap4j/PreparedOlapStatement.html">PreparedOlapStatement</a>,
Expand All @@ -609,17 +623,9 @@ <h4>2.2.1. <a name="Connection_pooling">Connection pooling</a></h4>
<a class="javadoc" href="http://java.sun.com/javase/6/docs/api/java/sql/Wrapper.html">java.sql.Wrapper</a> interface introduced in JDBC 4.0,
so the code can be simplified:</p>

<div class="code">DataSource dataSource; // a data source using a connection
pool<br>
Connection connection =<br>
&nbsp;&nbsp;&nbsp; DriverManager.createConnection(<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;jdbc:mondrian:local:Jdbc=jdbc:odbc:MondrianFoodMart;&quot;
+<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Catalog=/WEB-INF/queries/FoodMart.xml;&quot;
+<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;Role='California manager'&quot;);<br>
<div class="code">Connection connection = DriverManager.getConnection();<br>
OlapConnection olapConnection = connection.unwrap(OlapConnection.class);<br>
OlapStatement statement = olapConnection.createOlapStatement();</div>
OlapStatement statement = olapConnection.createStatement();</div>

<p>Note that the <code>OlapWrapper</code> interface is not needed. This code
will work with any JDBC 4.0-compliant connection pool.</p>
Expand Down
39 changes: 34 additions & 5 deletions src/mondrian/olap4j/MondrianOlap4jCell.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
import java.util.List;
import java.util.ArrayList;
import java.sql.*;
import java.lang.reflect.Proxy;

import mondrian.util.DelegatingInvocationHandler;

/**
* Implementation of {@link Cell}
Expand Down Expand Up @@ -109,10 +112,6 @@ public String getFormattedValue() {
}

public ResultSet drillThrough() throws OlapException {
// REVIEW: This method returns a ResultSet without closing the
// Statement or the Connection. If we closed them, the ResultSet would
// be useless. But as it stands, we have a connection leak. Should we
// tell the client that they need to close the result set's connection?
if (!cell.canDrillThrough()) {
return null;
}
Expand All @@ -124,11 +123,41 @@ public ResultSet drillThrough() throws OlapException {
try {
final Connection connection = dataSource.getConnection();
final Statement statement = connection.createStatement();
return statement.executeQuery(sql);
final ResultSet resultSet = statement.executeQuery(sql);

// To prevent a connection leak, wrap the result set in a proxy
// which automatically closes the connection (and hence also the
// statement and result set) when the result set is closed.
// The caller still has to remember to call ResultSet.close(), of
// course.
return (ResultSet) Proxy.newProxyInstance(
null,
new Class<?>[] {ResultSet.class},
new MyDelegatingInvocationHandler(resultSet));
} catch (SQLException e) {
throw olap4jConnection.helper.toOlapException(e);
}
}

// must be public for reflection to work
public static class MyDelegatingInvocationHandler
extends DelegatingInvocationHandler
{
private final ResultSet resultSet;

MyDelegatingInvocationHandler(ResultSet resultSet) {
this.resultSet = resultSet;
}

protected Object getTarget() {
return resultSet;
}

// implement ResultSet.close()
public void close() throws SQLException {
resultSet.getStatement().getConnection().close();
}
}
}

// End MondrianOlap4jCell.java
3 changes: 3 additions & 0 deletions src/org/olap4j/OlapStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
* Object used for statically executing an MDX statement and returning a
* {@link CellSet}.
*
* <p>An <code>OlapStatement</code> is generally created using
* {@link OlapConnection#createStatement()}.</p>
*
* @see PreparedOlapStatement
*
* @author jhyde
Expand Down
10 changes: 7 additions & 3 deletions src/org/olap4j/PreparedOlapStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
* <code>PreparedOlapStatement</code> object. This object can then be used to
* efficiently execute this statement multiple times.</p>
*
* <p>A <code>PreparedOlapStatement</code> is generally created using
* {@link OlapConnection#prepareOlapStatement(String)}.</p>
*
* <p><B>Note:</B> The setter methods (<code>setShort</code>,
* <code>setString</code>, and so on) for setting IN parameter values
* must specify types that are compatible with the defined type of
Expand All @@ -40,15 +43,16 @@
* default value until they are set, and then retain their new values for each
* subsequent execution of this <code>PreparedOlapStatement</code>.
*
* @see java.sql.Connection#prepareStatement
* @see java.sql.ResultSet
* @see OlapConnection#prepareOlapStatement(String)
* @see CellSet
*
* @author jhyde
* @version $Id$
* @since Aug 22, 2006
*/
public interface PreparedOlapStatement
extends PreparedStatement, OlapStatement {
extends PreparedStatement, OlapStatement
{
/**
* Executes the MDX query in this <code>PreparedOlapStatement</code> object
* and returns the <code>CellSet</code> object generated by the query.
Expand Down
16 changes: 8 additions & 8 deletions src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,6 @@ abstract class XmlaOlap4jConnection implements OlapConnection {

this.catalogName = map.get(XmlaOlap4jDriver.Property.Catalog.name());

this.olap4jDatabaseMetaData =
factory.newDatabaseMetaData(this);
final XmlaOlap4jCatalog catalog =
(XmlaOlap4jCatalog)
this.olap4jDatabaseMetaData.getCatalogObjects().get(
catalogName);
this.olap4jSchema = new XmlaOlap4jSchema(catalog, catalogName);

// Set URL of HTTP server.
String serverUrl = map.get(XmlaOlap4jDriver.Property.Server.name());
if (serverUrl == null) {
Expand All @@ -128,6 +120,14 @@ abstract class XmlaOlap4jConnection implements OlapConnection {
throw helper.createException(
"Error while creating connection", e);
}

this.olap4jDatabaseMetaData =
factory.newDatabaseMetaData(this);
final XmlaOlap4jCatalog catalog =
(XmlaOlap4jCatalog)
this.olap4jDatabaseMetaData.getCatalogObjects().get(
catalogName);
this.olap4jSchema = new XmlaOlap4jSchema(catalog, catalogName);
}

static boolean acceptsURL(String url) {
Expand Down
1 change: 1 addition & 0 deletions testsrc/org/olap4j/ConnectionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,7 @@ public void testCell() throws Exception {
final ResultSet resultSet = cell.drillThrough();
assertEquals(5, resultSet.getMetaData().getColumnCount());
resultSet.close();
break;
}

// cell out of range using getCell(int)
Expand Down

0 comments on commit 6d9c74d

Please sign in to comment.