diff --git a/test.properties b/test.properties index a074e10..d352545 100644 --- a/test.properties +++ b/test.properties @@ -58,15 +58,16 @@ org.olap4j.test.connectUrl=jdbc:mondrian:Datasource=jdbc/SampleData;Catalog=./fo # known responses. So far, it is only a proof of concept. We do not recommend # to rely on this for testing. -# This example uses a cached Mondrian 3.3-SNAPSHOT server requests/responses data file -# to run the TCK. +# This example uses a cached Mondrian 3.3-SNAPSHOT server requests/responses +# hsqldb database to run the TCK. # To make this example work, one must first unzip the data file manually. -#org.olap4j.test.helperClassName=org.olap4j.RemoteXmlaTester -#org.olap4j.RemoteXmlaTester.JdbcUrl=jdbc:xmla:Server=http://example.com;Cache=org.olap4j.driver.xmla.cache.XmlaTextFileCache;Cache.Play=true;Cache.Input=./xmla-cache/mondrian-xmla-3.3-SNAPSHOT.dat -# This other example conencts to an olap server and records the requests to a file. #org.olap4j.test.helperClassName=org.olap4j.RemoteXmlaTester -#org.olap4j.RemoteXmlaTester.JdbcUrl=jdbc:xmla:Server=http://localhost:81/mondrian/xmla;Cache=org.olap4j.driver.xmla.cache.XmlaTextFileCache;Cache.Record=true;Cache.Output=./output-xmla.dat +#org.olap4j.RemoteXmlaTester.JdbcUrl=jdbc:xmla:Server=http://localhost:81/mondrian/xmla;Cache=org.olap4j.driver.xmla.cache.XmlaDatabaseCache;Cache.Play=true +# This other example connects to an olap server and records the requests to +# an hsqldb database that it creates. +#org.olap4j.test.helperClassName=org.olap4j.RemoteXmlaTester +#org.olap4j.RemoteXmlaTester.JdbcUrl=jdbc:xmla:Server=http://localhost:81/mondrian/xmla;Cache=org.olap4j.driver.xmla.cache.XmlaDatabaseCache;Cache.Init=true;Cache.Record=true # End test.properties diff --git a/testsrc/org/olap4j/driver/xmla/cache/XmlaDatabaseCache.java b/testsrc/org/olap4j/driver/xmla/cache/XmlaDatabaseCache.java new file mode 100644 index 0000000..adfb415 --- /dev/null +++ b/testsrc/org/olap4j/driver/xmla/cache/XmlaDatabaseCache.java @@ -0,0 +1,200 @@ +package org.olap4j.driver.xmla.cache; + +import java.net.URL; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Map; + +import org.olap4j.impl.Base64; + +/** + * This mock server cache is only used to save and load + * runs of the XMLA driver as a database table. + * @see Properties + * @author LBoudreau + */ +public class XmlaDatabaseCache implements XmlaOlap4jCache { + + private static Connection connection = null; + private final static String CACHE_IDENT = "Panda steak!"; + private Map props; + + public static enum Properties { + + /** + * Jdbc driver class to use. Defaults to + * org.hsqldb.jdbcDriver + */ + + JDBC_DRIVER("org.hsqldb.jdbcDriver"), + /** + * Jdbc url to use. Defaults to + * jdbc:hsqldb:file:xmla-cache/xmla-cache-hsqldb + */ + JDBC_URL("jdbc:hsqldb:file:xmla-cache/xmla-cache-hsqldb"), + + /** + * Jdbc username to use. Defaults to + * sa + */ + JDBC_USER("sa"), + + /** + * Jdbc password to use. Defaults to + * an empty string + */ + JDBC_PASS(""), + + /** + * Query to execute to insert elements. Defaults to + * insert into "cache" ("request", "response") values(?,?); + */ + QUERY_INSERT( + "insert into \"cache\"(\"request\", \"response\") values(?,?);"), + + /** + * Query to execute to select elements. Defaults to + * select "request", "response" from "cache" + * where "request" = ?; + */ + QUERY_SELECT( + "select \"request\", \"response\" from \"cache\" where" + + "\"request\" = ?;"), + + /** + * Query to initialize the cache. Can be a batch of SQL. Defaults to + * create table "cache" ("request" + * varchar, "response" varchar); + */ + QUERY_INIT_CACHE( + "drop table \"cache\" if exists; create table \"cache\" (\"request\" varchar, \"response\" varchar);"), + + /** + * Should the cache insert requests/responses. + * defaults to false. + */ + RECORD("false"), + + /** + * Should the cache return cached responses. + * defaults to false. + */ + PLAY("false"), + + /** + * Should the cache execute Properties.QUERY_INIT_CACHE. + * defaults to false. + */ + INIT("false"); + + private final String defaultValue; + + private Properties(String defaultValue) { + this.defaultValue = defaultValue; + } + String getValueOrDefault(Map props) { + if (props.containsKey(this.name())) { + return props.get(name()); + } else { + return this.defaultValue; + } + } + } + + public void flushCache() { + // no op + } + + public byte[] get(String id, URL url, byte[] request) + throws XmlaOlap4jInvalidStateException + { + if (!Boolean.valueOf(Properties.PLAY.getValueOrDefault(props))) { + return null; + } + try { + final PreparedStatement stm = + connection.prepareStatement( + Properties.QUERY_SELECT.getValueOrDefault(props)); + try { + stm.setString( + 1, + XmlaOlap4jShaEncoder.encodeSha1(new String(request))); + stm.execute(); + ResultSet rs = stm.getResultSet(); + if (rs.next()) { + return Base64.decode(rs.getString(2)); + } else { + return null; + } + } finally { + stm.close(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public void put(String id, URL url, byte[] request, byte[] response) + throws XmlaOlap4jInvalidStateException + { + if (!Boolean.valueOf(Properties.RECORD.getValueOrDefault(props))) { + return; + } + try { + final PreparedStatement stm = + connection.prepareStatement( + Properties.QUERY_INSERT.getValueOrDefault(props)); + try { + stm.setString( + 1, + XmlaOlap4jShaEncoder.encodeSha1(new String(request))); + stm.setString(2, Base64.encodeBytes(response)); + stm.execute(); + } finally { + stm.close(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public String setParameters( + Map config, + Map props) + { + this.props = props; + if (connection == null) { + try { + Class.forName(Properties.JDBC_DRIVER.getValueOrDefault(props)); + connection = + DriverManager.getConnection( + Properties.JDBC_URL.getValueOrDefault(props), + Properties.JDBC_USER.getValueOrDefault(props), + Properties.JDBC_PASS.getValueOrDefault(props)); + if (Boolean.valueOf( + Properties.INIT.getValueOrDefault(props))) + { + final Statement stm = connection.createStatement(); + try { + stm.addBatch( + Properties.QUERY_INIT_CACHE + .getValueOrDefault(props)); + stm.executeBatch(); + } catch (SQLException e) { + // no op + } finally { + stm.close(); + } + flushCache(); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return CACHE_IDENT; + } +} diff --git a/testsrc/org/olap4j/driver/xmla/cache/XmlaTextFileCache.java b/testsrc/org/olap4j/driver/xmla/cache/XmlaTextFileCache.java deleted file mode 100644 index 027609f..0000000 --- a/testsrc/org/olap4j/driver/xmla/cache/XmlaTextFileCache.java +++ /dev/null @@ -1,156 +0,0 @@ -package org.olap4j.driver.xmla.cache; - -import java.io.BufferedReader; -import java.io.DataInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.Map; -import java.util.StringTokenizer; -import java.util.concurrent.ConcurrentHashMap; - -import org.olap4j.impl.Base64; - -/** - * This mock server cache is only used to save and load - * runs of the XMLA driver as an external file. - * @author LBoudreau - */ -public class XmlaTextFileCache implements XmlaOlap4jCache { - - private static boolean initDone = false; - private final static String CACHE_IDENT= "Chunky Bacon!"; - private final static ConcurrentHashMap cache = - new ConcurrentHashMap(); - - public static enum Properties { - /** - * File to save the requests to, if RECORD is true. - * java.util.File(path) will be used. - */ - OUTPUT, - /** - * File to read the requests from, if PLAY is true. - * java.util.File(path) will be used. - */ - INPUT, - /** - * Whether to record the requests/responses to a file. - */ - RECORD, - /** - * Whether to playback responses from a file. - */ - PLAY, - } - - private static File output = null; - private static File input = null; - private static boolean record = false; - private static boolean play = false;; - - public void flushCache() { - // no op - } - - public byte[] get(String id, URL url, byte[] request) - throws XmlaOlap4jInvalidStateException - { - synchronized (cache) { - if (play && input != null) { - return cache.get(new String(request)); - } else { - return null; - } - } - } - - public void put(String id, URL url, byte[] request, byte[] response) - throws XmlaOlap4jInvalidStateException - { - synchronized (cache) { - if (record && output != null) { - String base64Request = Base64.encodeBytes(request); - String base64Response = Base64.encodeBytes(response); - try { - FileWriter writer = new FileWriter(output, true); - try - { - writer.write(base64Request); - writer.write("\r\n||\r\n"); - writer.write(base64Response); - writer.write("\r\n||\r\n"); - writer.flush(); - } finally { - writer.close(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - } - } - - public String setParameters(Map config, - Map props) - { - if (!initDone) { - synchronized (cache) { - cache.clear(); - try { - if (props.containsKey(Properties.RECORD.name()) - && props.containsKey(Properties.OUTPUT.name())) - { - record = true; - output = new File(props.get(Properties.OUTPUT.name())); - if (!output.exists() - && !output.createNewFile()) { - throw new RuntimeException( - "Failed to create output file."); - } - } - if (props.containsKey(Properties.PLAY.name()) - && props.containsKey(Properties.INPUT.name())) - { - play = true; - input = new File(props.get(Properties.INPUT.name())); - if (input.exists()) { - FileInputStream fstream = - new FileInputStream(input); - DataInputStream in = - new DataInputStream(fstream); - BufferedReader br = - new BufferedReader(new InputStreamReader(in)); - String strLine; - StringBuilder sb = new StringBuilder(); - while ((strLine = br.readLine()) != null) { - sb.append(strLine); - } - StringTokenizer st = - new StringTokenizer( - sb.toString(), - "\r\n|||\r\n"); - while (st.hasMoreTokens()) { - byte[] request = - Base64.decode(st.nextToken()); - byte[] response = - Base64.decode(st.nextToken()); - cache.put(new String(request), response); - } - in.close(); - } - } - } catch (Exception e) { - throw new RuntimeException( - "Failed to configure the mock server cache.", - e); - } - } - } - return CACHE_IDENT; - } - -} diff --git a/xmla-cache/mondrian-xmla-3.3-SNAPSHOT.zip b/xmla-cache/mondrian-xmla-3.3-SNAPSHOT.zip deleted file mode 100644 index 53ac385..0000000 Binary files a/xmla-cache/mondrian-xmla-3.3-SNAPSHOT.zip and /dev/null differ diff --git a/xmla-cache/xmla-cache-hsqldb-mondrian-3.3-SNAPSHOT.zip b/xmla-cache/xmla-cache-hsqldb-mondrian-3.3-SNAPSHOT.zip new file mode 100644 index 0000000..ad9946e Binary files /dev/null and b/xmla-cache/xmla-cache-hsqldb-mondrian-3.3-SNAPSHOT.zip differ