From bc26a4766607188e356abc0e08c129afddd6e086 Mon Sep 17 00:00:00 2001 From: Will Gorman Date: Fri, 25 Jul 2008 18:25:13 +0000 Subject: [PATCH] fixed deadlock bug in Xmla Driver, added unit test to verify fix. Also added generated java files and testclasses directory to svn:ignore. git-svn-id: https://olap4j.svn.sourceforge.net/svnroot/olap4j/trunk@102 c6a108a4-781c-0410-a6c6-c2d559e19af0 --- .../olap4j/driver/xmla/XmlaOlap4jCellSet.java | 5 +- .../driver/xmla/XmlaOlap4jConnection.java | 29 +++++---- .../xmla/XmlaOlap4jDatabaseMetaData.java | 4 ++ testsrc/org/olap4j/XmlaConnectionTest.java | 63 +++++++++++++++++++ 4 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 testsrc/org/olap4j/XmlaConnectionTest.java diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java index ef4c286..04cc76c 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java @@ -362,8 +362,9 @@ private XmlaOlap4jCellSetMetaData createMetaData(Element root) final Element cubeNameNode = findChild(cubeNode, MDDATASET_NS, "CubeName"); final String cubeName = gatherText(cubeNameNode); - final XmlaOlap4jCube cube = - this.olap4jStatement.olap4jConnection.olap4jSchema.cubes.get( + final XmlaOlap4jCube cube = + (XmlaOlap4jCube) + this.olap4jStatement.olap4jConnection.getSchema().getCubes().get( cubeName); if (cube == null) { throw olap4jStatement.olap4jConnection.helper.createException( diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java index 6f730af..1dcadfc 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java @@ -50,7 +50,7 @@ abstract class XmlaOlap4jConnection implements OlapConnection { /** *

Current schema. */ - final XmlaOlap4jSchema olap4jSchema; + private XmlaOlap4jSchema olap4jSchema; private final XmlaOlap4jDatabaseMetaData olap4jDatabaseMetaData; @@ -113,6 +113,10 @@ abstract class XmlaOlap4jConnection implements OlapConnection { * uses the traditional JDBC {@link java.sql.DriverManager}. * See {@link org.olap4j.driver.xmla.XmlaOlap4jDriver} for more details. * + *

Note that this constructor should make zero non-trivial calls, which + * could cause deadlocks due to java.sql.DriverManager synchronization + * issues. + * * @pre acceptsURL(url) * * @param factory Factory @@ -175,12 +179,6 @@ abstract class XmlaOlap4jConnection implements OlapConnection { this.olap4jDatabaseMetaData = factory.newDatabaseMetaData(this); - final XmlaOlap4jCatalog catalog = - (XmlaOlap4jCatalog) - this.olap4jDatabaseMetaData.getCatalogObjects().get( - catalogName); - this.olap4jSchema = (XmlaOlap4jSchema) catalog.getSchemas() - .get(0); } @@ -513,7 +511,16 @@ public MdxValidator createMdxValidator(OlapConnection connection) { }; } - public org.olap4j.metadata.Schema getSchema() throws OlapException { + public synchronized org.olap4j.metadata.Schema getSchema() throws OlapException { + // initializes the olap4jSchema if necessary + if (this.olap4jSchema == null) { + final XmlaOlap4jCatalog catalog = + (XmlaOlap4jCatalog) + this.olap4jDatabaseMetaData.getCatalogObjects().get( + catalogName); + this.olap4jSchema = (XmlaOlap4jSchema) catalog.getSchemas() + .get(0); + } return olap4jSchema; } @@ -1142,7 +1149,7 @@ public void handle(Element row, Context context, List list) } static class SchemaHandler extends HandlerImpl { - public void handle(Element row, Context context, List list) { + public void handle(Element row, Context context, List list) throws OlapException { /* LOCALDB @@ -1169,7 +1176,7 @@ public CatalogSchemaHandler(String catalogName) { this.catalogName = catalogName; } - public void handle(Element row, Context context, List list) + public void handle(Element row, Context context, List list) throws OlapException { /* @@ -1407,7 +1414,7 @@ public XmlaOlap4jLevel getLevel(Element row) { return getCube(row).levelsByUname.get(levelUniqueName); } - public XmlaOlap4jCatalog getCatalog(Element row) { + public XmlaOlap4jCatalog getCatalog(Element row) throws OlapException { if (olap4jCatalog != null) { return olap4jCatalog; } diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jDatabaseMetaData.java b/src/org/olap4j/driver/xmla/XmlaOlap4jDatabaseMetaData.java index 1133436..488b9c3 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jDatabaseMetaData.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jDatabaseMetaData.java @@ -39,6 +39,10 @@ abstract class XmlaOlap4jDatabaseMetaData implements OlapDatabaseMetaData { /** * Creates an XmlaOlap4jDatabaseMetaData. * + *

Note that this constructor should make zero non-trivial calls, which + * could cause deadlocks due to java.sql.DriverManager synchronization + * issues. + * * @param olap4jConnection Connection */ XmlaOlap4jDatabaseMetaData( diff --git a/testsrc/org/olap4j/XmlaConnectionTest.java b/testsrc/org/olap4j/XmlaConnectionTest.java new file mode 100644 index 0000000..7984ea9 --- /dev/null +++ b/testsrc/org/olap4j/XmlaConnectionTest.java @@ -0,0 +1,63 @@ +package org.olap4j; + +import java.io.IOException; +import java.net.URL; +import java.sql.DriverManager; +import java.util.Properties; +import java.util.concurrent.Future; + +import org.olap4j.driver.xmla.XmlaOlap4jDriver; +import org.olap4j.driver.xmla.proxy.XmlaOlap4jProxy; + +import junit.framework.TestCase; + +public class XmlaConnectionTest extends TestCase { + + public static final String DRIVER_CLASS_NAME = + "org.olap4j.driver.xmla.XmlaOlap4jDriver"; + + static class XmlaOlap4jProxyMock implements XmlaOlap4jProxy { + + public byte[] get(URL url, String request) throws IOException { + throw new RuntimeException("Non-Trivial Call!"); + } + + public String getEncodingCharsetName() { + return "UTF-8"; + } + + public Future submit(URL url, String request) { + throw new RuntimeException("Non-Trivial Call!"); + } + + } + + /** + * this test verifies that the construction of the necessary + * xmla objects during DriverManager.getConnection() do not make + * calls that could cause deadlocks. + */ + public void testNoNonTrivalCallsOnConnect() throws Exception { + String cookie = XmlaOlap4jDriver.nextCookie(); + try { + + XmlaOlap4jDriver.PROXY_MAP.put(cookie, new XmlaOlap4jProxyMock()); + + try { + Class.forName(DRIVER_CLASS_NAME); + } catch (ClassNotFoundException e) { + throw new RuntimeException("oops", e); + } + Properties info = new Properties(); + info.setProperty( + XmlaOlap4jDriver.Property.Catalog.name(), "FoodMart"); + DriverManager.getConnection( + "jdbc:xmla:Server=http://foo;Catalog=FoodMart;TestProxyCookie=" + cookie, + info); + } catch (Throwable t) { + t.printStackTrace(); + fail("Non-Trival Call executed during construction of XmlaOlap4j Connection"); + } + + } +}