diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java index 3f28e16..f5cee2e 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java @@ -72,12 +72,12 @@ abstract class XmlaOlap4jConnection implements OlapConnection { /** *

Holds on to the provider name */ - private String providerName = null; + private String providerName; /** *

Holds on to the datasource name. */ - private String datasourceName = null; + private String datasourceName; /** *

Holds on to the datasource name as specified by the @@ -90,7 +90,9 @@ abstract class XmlaOlap4jConnection implements OlapConnection { * query value. * */ - private String nativeDatasourceName = null; + private String nativeDatasourceName; + private boolean autoCommit; + private boolean readOnly; /** *

Creates an Olap4j connection an XML/A provider. @@ -122,12 +124,7 @@ abstract class XmlaOlap4jConnection implements OlapConnection { throw new AssertionError( "does not start with '" + CONNECT_STRING_PREFIX + "'"); } - String x = url.substring(CONNECT_STRING_PREFIX.length()); - Map map = - ConnectStringParser.parseConnectString(x); - for (Map.Entry entry : toMap(info).entrySet()) { - map.put(entry.getKey(), entry.getValue()); - } + Map map = parseConnectString(url, info); this.providerName = map.get(XmlaOlap4jDriver.Property.Provider.name()); this.datasourceName = map.get(XmlaOlap4jDriver.Property.DataSource.name()); @@ -170,13 +167,20 @@ abstract class XmlaOlap4jConnection implements OlapConnection { this.olap4jSchema = new XmlaOlap4jSchema(catalog, catalogName); } + static Map parseConnectString(String url, Properties info) { + String x = url.substring(CONNECT_STRING_PREFIX.length()); + Map map = + ConnectStringParser.parseConnectString(x); + for (Map.Entry entry : toMap(info).entrySet()) { + map.put(entry.getKey(), entry.getValue()); + } + return map; + } + static boolean acceptsURL(String url) { return url.startsWith(CONNECT_STRING_PREFIX); } - - - // not part of public API String getDataSourceInfo() throws OlapException { @@ -285,11 +289,11 @@ public String nativeSQL(String sql) throws SQLException { } public void setAutoCommit(boolean autoCommit) throws SQLException { - throw new UnsupportedOperationException(); + this.autoCommit = autoCommit; } public boolean getAutoCommit() throws SQLException { - throw new UnsupportedOperationException(); + return autoCommit; } public void commit() throws SQLException { @@ -317,11 +321,11 @@ public NamedList getCatalogs() { } public void setReadOnly(boolean readOnly) throws SQLException { - throw new UnsupportedOperationException(); + this.readOnly = readOnly; } public boolean isReadOnly() throws SQLException { - throw new UnsupportedOperationException(); + return readOnly; } public void setCatalog(String catalog) throws SQLException { @@ -607,7 +611,7 @@ Element xxx(String request) throws OlapException { * values of the restriction) * *

This signature only relays the execution to - * {@link XmlaOlap4jConnection.generateRequest(Context,MetadataRequest,Object[])} + * {@link #generateRequest(Context,MetadataRequest,Object[])} * but passes it a true value to mark any request as datasource name * specific. * diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jDriver.java b/src/org/olap4j/driver/xmla/XmlaOlap4jDriver.java index 9e11569..6cdc2a2 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jDriver.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jDriver.java @@ -9,6 +9,7 @@ package org.olap4j.driver.xmla; import org.olap4j.impl.Base64; +import org.olap4j.impl.Olap4jUtil; import java.io.*; import java.net.URL; @@ -48,20 +49,27 @@ * *

Connection properties

* + *

Unless otherwise stated, properties are optional. If a property occurs + * multiple times in the connect string, the first occurrence is used. + * * * * - * + * * - * + * * - * + * * - * + * * - * + * * *
Property Description
Server URL of HTTP server.
Server URL of HTTP server. Required.
Catalog Catalog name to use
Catalog Catalog name to use. Required.
Provider Name of the XMLA provider. This option is facultative. By default, the first one available will be used.
Provider Name of the XMLA provider.
DataSource Name of the XMLA datasource. This option is facultative. By default, the first one available will be used. When using a Mondrian backed XMLA server, be sure to include the full datasource name between quotes.
DataSource Name of the XMLA datasource. When using a + * Mondrian backed XMLA server, be sure to + * include the full datasource name between + * quotes.
UseThreadProxy If true, use the proxy object in the - * {@link #THREAD_PROXY} field. For testing. - * Default is false.
TestProxyCookie String that uniquely identifies a proxy + * object in {@link #PROXY_MAP} via which to + * send XMLA requests for testing + * purposes.
* @@ -71,9 +79,9 @@ */ public class XmlaOlap4jDriver implements Driver { public static final String NAME = "olap4j driver for XML/A"; - public static final String VERSION = "0.9.4"; + public static final String VERSION = "0.9.5"; public static final int MAJOR_VERSION = 0; - public static final int MINOR_VERSION = 904; + public static final int MINOR_VERSION = 905; private final Factory factory; /** @@ -82,6 +90,8 @@ public class XmlaOlap4jDriver implements Driver { private static final ExecutorService executor = Executors.newCachedThreadPool(); + private static int nextCookie; + static { try { register(); @@ -93,6 +103,9 @@ public class XmlaOlap4jDriver implements Driver { } } + /** + * Creates an XmlaOlap4jDriver. + */ protected XmlaOlap4jDriver() { String factoryClassName; try { @@ -115,6 +128,11 @@ protected XmlaOlap4jDriver() { } } + /** + * Registers this driver. + * + * @throws SQLException on error + */ private static void register() throws SQLException { DriverManager.registerDriver(new XmlaOlap4jDriver()); } @@ -123,7 +141,9 @@ public Connection connect(String url, Properties info) throws SQLException { if (!XmlaOlap4jConnection.acceptsURL(url)) { return null; } - Proxy proxy = createProxy(info); + Map map = + XmlaOlap4jConnection.parseConnectString(url, info); + Proxy proxy = createProxy(map); return factory.newConnection(proxy, url, info); } @@ -166,16 +186,13 @@ public boolean jdbcCompliant() { * implementation, for testing, which talks to mondrian's XMLA service * in-process. * - * @param info Connection properties + * @param map Connection properties * @return A Proxy with which to submit XML requests */ - protected Proxy createProxy(Properties info) { - String useThreadProxy = - info.getProperty( - Property.UseThreadProxy.name()); - if (useThreadProxy != null && - Boolean.valueOf(useThreadProxy)) { - Proxy proxy = THREAD_PROXY.get(); + protected Proxy createProxy(Map map) { + String cookie = map.get(Property.TestProxyCookie.name()); + if (cookie != null) { + Proxy proxy = PROXY_MAP.get(cookie); if (proxy != null) { return proxy; } @@ -183,6 +200,16 @@ protected Proxy createProxy(Properties info) { return new HttpProxy(); } + /** + * Returns a future object representing an asynchronous submission of an + * XMLA request to a URL. + * + * @param proxy Proxy via which to send the request + * @param url URL of XMLA server + * @param request Request + * @return Future object from which the byte array containing the result + * of the XMLA call can be obtained + */ public Future getFuture( final Proxy proxy, final URL url, @@ -282,25 +309,42 @@ public String getEncodingCharsetName() { } /** - * For testing. + * For testing. Map from a cookie value (which is uniquely generated for + * each test) to a proxy object. Uses a weak hash map so that, if the code + * that created the proxy 'forgets' the cookie value, then the proxy can + * be garbage-collected. */ - public static final ThreadLocal THREAD_PROXY = - new ThreadLocal(); + public static final Map PROXY_MAP = + Collections.synchronizedMap(new WeakHashMap()); + + /** + * Generates and returns a unique string. + * + * @return unique string + */ + public static synchronized String nextCookie() { + return "cookie" + nextCookie++; + } /** * Properties supported by this driver. */ public enum Property { - UseThreadProxy( - "If true, use the proxy object in the THREAD_PROXY field. " - + "For testing. Default is false."), - + TestProxyCookie( + "String that uniquely identifies a proxy object via which to send " + + "XMLA requests for testing purposes."), Server("URL of HTTP server"), Catalog("Catalog name"), Provider("Name of the datasource provider"), DataSource("Name of the datasource"); + /** + * Creates a property. + * + * @param description Description of property + */ Property(String description) { + Olap4jUtil.discard(description); } } } diff --git a/testsrc/org/olap4j/XmlaTester.java b/testsrc/org/olap4j/XmlaTester.java index 4fad0d3..a2988ec 100644 --- a/testsrc/org/olap4j/XmlaTester.java +++ b/testsrc/org/olap4j/XmlaTester.java @@ -25,6 +25,7 @@ */ public class XmlaTester implements TestContext.Tester { final XmlaOlap4jDriver.Proxy proxy; + final String cookie; private Connection connection; /** @@ -57,6 +58,8 @@ public XmlaTester() this.proxy = (XmlaOlap4jDriver.Proxy) constructor.newInstance( catalogNameUrls, urlString); + this.cookie = XmlaOlap4jDriver.nextCookie(); + XmlaOlap4jDriver.PROXY_MAP.put(cookie, proxy); } public Connection createConnection() throws SQLException { @@ -68,21 +71,14 @@ public Connection createConnection() throws SQLException { } catch (ClassNotFoundException e) { throw new RuntimeException("oops", e); } - try { - XmlaOlap4jDriver.THREAD_PROXY.set(proxy); - Properties info = new Properties(); - info.setProperty( - XmlaOlap4jDriver.Property.UseThreadProxy.name(), "true"); - info.setProperty( - XmlaOlap4jDriver.Property.Catalog.name(), "FoodMart"); - connection = - DriverManager.getConnection( - getURL(), - info); - return connection; - } finally { - XmlaOlap4jDriver.THREAD_PROXY.set(null); - } + Properties info = new Properties(); + info.setProperty( + XmlaOlap4jDriver.Property.Catalog.name(), "FoodMart"); + connection = + DriverManager.getConnection( + getURL(), + info); + return connection; } public Connection createConnectionWithUserPassword() throws SQLException { @@ -91,15 +87,11 @@ public Connection createConnectionWithUserPassword() throws SQLException { } catch (ClassNotFoundException e) { throw new RuntimeException("oops", e); } - try { - XmlaOlap4jDriver.THREAD_PROXY.set(proxy); - Properties info = new Properties(); - info.setProperty("UseThreadProxy", "true"); - return DriverManager.getConnection( - getURL(), USER, PASSWORD); - } finally { - XmlaOlap4jDriver.THREAD_PROXY.set(null); - } + Properties info = new Properties(); + info.setProperty( + XmlaOlap4jDriver.Property.Catalog.name(), "FoodMart"); + return DriverManager.getConnection( + getURL(), USER, PASSWORD); } public String getDriverUrlPrefix() { @@ -111,7 +103,7 @@ public String getDriverClassName() { } public String getURL() { - return "jdbc:xmla:Server=http://foo;UseThreadProxy=true"; + return "jdbc:xmla:Server=http://foo;Catalog=FoodMart;TestProxyCookie=" + cookie; } public Flavor getFlavor() {