diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java index cf4b2c6..1aaf7e7 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java @@ -188,20 +188,7 @@ abstract class XmlaOlap4jConnection implements OlapConnection { this.databaseProperties = new HashMap(); for (String infoKey : map.keySet()) { - boolean addProperty = true; - for (XmlaOlap4jDriver.Property p - : XmlaOlap4jDriver.Property.values()) - { - if (infoKey.equals(p.name()) - || "USER".equals(infoKey) - || "PASSWORD".equals(infoKey)) - { - addProperty = false; - } - } - if (addProperty) { - databaseProperties.put(infoKey, map.get(infoKey)); - } + databaseProperties.put(infoKey, map.get(infoKey)); } this.databaseName = @@ -238,10 +225,12 @@ abstract class XmlaOlap4jConnection implements OlapConnection { new XmlaOlap4jServerInfos() { private String sessionId = null; public String getUsername() { - return map.get("user"); + return map.get( + XmlaOlap4jDriver.Property.USER.name()); } public String getPassword() { - return map.get("password"); + return map.get( + XmlaOlap4jDriver.Property.PASSWORD.name()); } public URL getUrl() { return serverUrlObject; @@ -286,9 +275,7 @@ private XmlaHelper getHelper() { */ private void initSoapCache(Map map) throws OlapException { // Test if a SOAP cache class was defined - if (map.containsKey(XmlaOlap4jDriver.Property.CACHE.name() - .toUpperCase())) - { + if (map.containsKey(XmlaOlap4jDriver.Property.CACHE.name())) { // Create a properties object to pass to the proxy // so it can configure it's cache Map props = new HashMap(); @@ -941,13 +928,18 @@ public String generateRequest( + " \n"); for (String prop : databaseProperties.keySet()) { - buf.append(" <"); - xmlEncode(buf,prop); - buf.append(">"); - xmlEncode(buf, databaseProperties.get(prop)); - buf.append(""); + try { + XmlaOlap4jDriver.Property.valueOf(prop); + continue; + } catch (IllegalArgumentException e) { + buf.append(" <"); + xmlEncode(buf,prop); + buf.append(">"); + xmlEncode(buf, databaseProperties.get(prop)); + buf.append(""); + } } // Add the datasource node only if this request requires it. diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jDriver.java b/src/org/olap4j/driver/xmla/XmlaOlap4jDriver.java index 4e6e4f3..421eede 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jDriver.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jDriver.java @@ -62,6 +62,13 @@ *

Unless otherwise stated, properties are optional. If a property occurs * multiple times in the connect string, the first occurrence is used. * + *

It is also possible to pass properties to the server end-point using + * JDBC connection properties as part of the XMLA driver connection properties. + * If the JDBC URL contains properties that are not enumerated in + * {@link Property}, they will be included as part of the SOAP PropertyList + * element. + * + * * * * @@ -75,35 +82,47 @@ * By default, the first one returned by the * XMLA server will be used. * - * * - * - * - * + * - * + * or your selected implementation for properties + * details. + * * - * + * - * + * + * *
Property Description
Database Name of the XMLA database. + *
Database Name of the XMLA database. * By default, the first one returned by the * XMLA server will be used.
Cache

Class name of the SOAP cache to use. - * Must implement interface + *

Cache

Class name of the SOAP cache to use. + * Must implement interface * {@link org.olap4j.driver.xmla.proxy.XmlaOlap4jCachedProxy}. - * A built-in memory cache is available with + * A built-in memory cache is available with * {@link org.olap4j.driver.xmla.cache.XmlaOlap4jNamedMemoryCache}. * - *

By default, no SOAP query cache will be - * used.

Cache.* Properties to transfer to the selected cache - * implementation. See + *

By default, no SOAP query cache will be + * used. + *

Cache.* Properties to transfer to the selected cache + * implementation. See * {@link org.olap4j.driver.xmla.cache.XmlaOlap4jCache} - * or your selected implementation for properties - * details.
TestProxyCookieString that uniquely identifies a proxy - * object in {@link #PROXY_MAP} via which to - * send XMLA requests for testing - * purposes.
Role Comma separated list of role names used for - * this connection (Optional).
- * Available role names can be retrieved via + * object in {@link #PROXY_MAP} via which to + * send XMLA requests for testing + * purposes. + *
Role Comma separated list of role names used for + * this connection (Optional).
+ * Available role names can be retrieved via * {@link org.olap4j.driver.xmla.XmlaOlap4jConnection#getAvailableRoleNames} *
User User name to use when establishing a + * connection to the server. The credentials are passed + * using the HTTP Basic authentication protocol, + * but are also sent as part of the SOAP Security + * headers. + *
Password Password to use when establishing a + * connection to the server. The credentials are passed + * using the HTTP Basic authentication protocol, + * but are also sent as part of the SOAP Security + * headers. + *
* * @author jhyde, Luc Boudreau @@ -366,7 +385,9 @@ public enum Property { CATALOG("Catalog name"), SCHEMA("Name of the schema"), CACHE("Class name of the SOAP cache implementation"), - ROLE("Comma separated list of roles this connection impersonates"); + ROLE("Comma separated list of roles this connection impersonates"), + USER("Username to use when creating connections to the server."), + PASSWORD("Password to use when creating connections to the server."); /** * Creates a property. diff --git a/testsrc/org/olap4j/XmlaConnectionTest.java b/testsrc/org/olap4j/XmlaConnectionTest.java index 4d21ead..38b07ba 100644 --- a/testsrc/org/olap4j/XmlaConnectionTest.java +++ b/testsrc/org/olap4j/XmlaConnectionTest.java @@ -335,6 +335,106 @@ public void testNoDoubleQuerySubmission() throws Exception { } } + /** + * This is a test to verify that server specific properties + * can be sent to the server in the PropertyList element + * of SOAP messages. A property that is not part of those enumerated + * in {@link org.olap4j.driver.xmla.XmlaOlap4jDriver.Property} must + * not be sent to the server. + */ + public void testPropertyList() throws Exception { + if (!testContext.getTester().getFlavor() + .equals(Tester.Flavor.XMLA) + && !testContext.getTester().getFlavor() + .equals(Tester.Flavor.REMOTE_XMLA)) + { + return; + } + final String oldValue = XmlaTester.getProxyClassName(); + try { + XmlaTester.setProxyClassName( + PropertyListTestProxy.class.getName()); + final TestContext.Tester tester = + testContext.getTester(); + + Connection connection = tester.createConnection(); + Statement statement = connection.createStatement(); + OlapStatement olapStatement = + tester.getWrapper().unwrap(statement, OlapStatement.class); + olapStatement.executeOlapQuery( + "SELECT\n" + + " {[Measures].[Unit Sales],\n" + + " [Measures].[Store Sales]} ON COLUMNS\n," + + " Crossjoin({[Gender].[M]}, [Product].Children) ON ROWS\n" + + "FROM [Sales]\n" + + "WHERE [Time].[1997].[Q2]"); + assertEquals(0, PropertyListTestProxy.count); + connection.close(); + + connection = tester.createConnectionWithUserPassword(); + statement = connection.createStatement(); + olapStatement = + tester.getWrapper().unwrap(statement, OlapStatement.class); + olapStatement.executeOlapQuery( + "SELECT\n" + + " {[Measures].[Unit Sales],\n" + + " [Measures].[Store Sales]} ON COLUMNS\n," + + " Crossjoin({[Gender].[M]}, [Product].Children) ON ROWS\n" + + "FROM [Sales]\n" + + "WHERE [Time].[1997].[Q2]"); + assertEquals(0, PropertyListTestProxy.count); + connection.close(); + + final Properties props = new Properties(); + props.put("FOOBAR", "Bacon"); + connection = + ((XmlaTester)tester).createConnectionWithUserPassword(props); + statement = connection.createStatement(); + olapStatement = + tester.getWrapper().unwrap(statement, OlapStatement.class); + try { + olapStatement.executeOlapQuery( + "SELECT\n" + + " {[Measures].[Unit Sales],\n" + + " [Measures].[Store Sales]} ON COLUMNS\n," + + " Crossjoin({[Gender].[M]}, [Product].Children) ON ROWS\n" + + "FROM [Sales]\n" + + "WHERE [Time].[1997].[Q2]"); + } catch (Throwable e) { + assertTrue(e.getCause().getMessage().contains("FOOBAR")); + } + connection.close(); + } finally { + XmlaTester.setProxyClassName(oldValue); + } + } + + /** + * This is a class for the test + * {@link XmlaConnectionTest#testPropertyList()}. + */ + public static class PropertyListTestProxy extends DelegatingTestProxy { + public PropertyListTestProxy(XmlaOlap4jProxy proxy) { + super(proxy); + } + private final static String[] lookup = + new String[] { + "" + }; + private static int count = 0; + public byte[] get( + XmlaOlap4jServerInfos serverInfos, String request) + throws XmlaOlap4jProxyException + { + for (String token : lookup) { + if (request.contains(token)) { + count++; + } + } + return super.get(serverInfos, request); + } + } + private static class Encoder { /** * Converts an array of bytes to a hex string. diff --git a/testsrc/org/olap4j/XmlaTester.java b/testsrc/org/olap4j/XmlaTester.java index cff2693..0fc9106 100644 --- a/testsrc/org/olap4j/XmlaTester.java +++ b/testsrc/org/olap4j/XmlaTester.java @@ -110,16 +110,28 @@ public Connection createConnection() throws SQLException { } public Connection createConnectionWithUserPassword() throws SQLException { + final Properties props = new Properties(); + return createConnectionWithUserPassword(props); + } + + public Connection createConnectionWithUserPassword( + Properties props) + throws SQLException + { try { Class.forName(DRIVER_CLASS_NAME); } catch (ClassNotFoundException e) { throw new RuntimeException("oops", e); } - Properties info = new Properties(); - info.setProperty( + props.setProperty( XmlaOlap4jDriver.Property.CATALOG.name(), "FoodMart"); - return DriverManager.getConnection( - getURL(), USER, PASSWORD); + if (USER != null) { + props.put("user", USER); + } + if (PASSWORD != null) { + props.put("password", PASSWORD); + } + return DriverManager.getConnection(getURL(), props); } public String getDriverUrlPrefix() {