From b67641a05df566af333ac8bbe3b822581f4e32d8 Mon Sep 17 00:00:00 2001 From: Luc Boudreau Date: Tue, 19 Aug 2008 17:44:39 +0000 Subject: [PATCH] Created a messaging framework for Olap4j so that all messages and OlapExceptions can be easily localized, parametrized and build correctly by both Olap4j itself and implementing drivers. For now, Olap4j API doesn't use the messaging framework. Only the XML/A driver was converted. This will be a tedious work and I expect all developers to use it. Send all comments please. git-svn-id: https://olap4j.svn.sourceforge.net/svnroot/olap4j/trunk@111 c6a108a4-781c-0410-a6c6-c2d559e19af0 --- src/org/olap4j/OlapExceptionHelper.java | 51 ---- .../olap4j/driver/xmla/EmptyResultSet.java | 10 +- .../olap4j/driver/xmla/XmlaOlap4jCellSet.java | 110 +++---- .../driver/xmla/XmlaOlap4jConnection.java | 67 ++--- .../olap4j/driver/xmla/XmlaOlap4jCube.java | 1 + .../xmla/XmlaOlap4jDatabaseMetaData.java | 3 +- .../xmla/XmlaOlap4jPreparedStatement.java | 13 +- .../driver/xmla/XmlaOlap4jStatement.java | 30 +- .../XmlaOlap4jConcurrentMemoryCache.java | 10 +- .../cache/XmlaOlap4jNamedMemoryCache.java | 4 +- .../xmla/messages/XmlaOlap4jMessenger.java | 132 ++++++++ .../driver/xmla/messages/messages.properties | 39 +++ .../olap4j/driver/xmla/messages/package.html | 5 + .../proxy/XmlaOlap4jAbstractHttpProxy.java | 43 +-- .../messages/AbstractOlap4jMessenger.java | 282 ++++++++++++++++++ src/org/olap4j/messages/Olap4jMessenger.java | 50 ++++ src/org/olap4j/messages/messages.properties | 7 + src/org/olap4j/messages/package.html | 5 + testsrc/org/olap4j/ConnectionTest.java | 7 +- .../org/olap4j/messages/MessengerTest.java | 103 +++++++ .../messages/mock1/test_messages.properties | 2 + .../mock1/test_messages_de.properties | 2 + .../mock1/test_messages_fr.properties | 2 + .../messages/mock2/test_messages.properties | 2 + .../mock2/test_messages_de.properties | 2 + .../mock2/test_messages_fr.properties | 2 + 26 files changed, 796 insertions(+), 188 deletions(-) delete mode 100644 src/org/olap4j/OlapExceptionHelper.java create mode 100644 src/org/olap4j/driver/xmla/messages/XmlaOlap4jMessenger.java create mode 100644 src/org/olap4j/driver/xmla/messages/messages.properties create mode 100644 src/org/olap4j/driver/xmla/messages/package.html create mode 100644 src/org/olap4j/messages/AbstractOlap4jMessenger.java create mode 100644 src/org/olap4j/messages/Olap4jMessenger.java create mode 100644 src/org/olap4j/messages/messages.properties create mode 100644 src/org/olap4j/messages/package.html create mode 100644 testsrc/org/olap4j/messages/MessengerTest.java create mode 100644 testsrc/org/olap4j/messages/mock1/test_messages.properties create mode 100644 testsrc/org/olap4j/messages/mock1/test_messages_de.properties create mode 100644 testsrc/org/olap4j/messages/mock1/test_messages_fr.properties create mode 100644 testsrc/org/olap4j/messages/mock2/test_messages.properties create mode 100644 testsrc/org/olap4j/messages/mock2/test_messages_de.properties create mode 100644 testsrc/org/olap4j/messages/mock2/test_messages_fr.properties diff --git a/src/org/olap4j/OlapExceptionHelper.java b/src/org/olap4j/OlapExceptionHelper.java deleted file mode 100644 index 4bfe803..0000000 --- a/src/org/olap4j/OlapExceptionHelper.java +++ /dev/null @@ -1,51 +0,0 @@ -/* -// This software is subject to the terms of the Common Public License -// Agreement, available at the following URL: -// http://www.opensource.org/licenses/cpl.html. -// Copyright (C) 2007-2008 Julian Hyde -// All Rights Reserved. -// You must accept the terms of that agreement to use this software. -*/ -package org.olap4j; - -import java.sql.SQLException; - -/** - * Sugar class to help create OlapExceptions. - * @author Luc Boudreau - * @version $Id: $ - */ -public class OlapExceptionHelper { - - public static OlapException createException(String msg) { - return new OlapException(msg); - } - - public static OlapException createException(String msg, Throwable cause) { - return new OlapException(msg, cause); - } - - public static OlapException createException(Cell context, String msg) { - OlapException exception = new OlapException(msg); - exception.setContext(context); - return exception; - } - - public static OlapException createException( - Cell context, String msg, Throwable cause) - { - OlapException exception = new OlapException(msg, cause); - exception.setContext(context); - return exception; - } - - public static OlapException toOlapException(SQLException e) { - if (e instanceof OlapException) { - return (OlapException) e; - } else { - return new OlapException(null, e); - } - } -} - -//End ExceptionHelper.java \ No newline at end of file diff --git a/src/org/olap4j/driver/xmla/EmptyResultSet.java b/src/org/olap4j/driver/xmla/EmptyResultSet.java index 1f934f7..1a6b29d 100644 --- a/src/org/olap4j/driver/xmla/EmptyResultSet.java +++ b/src/org/olap4j/driver/xmla/EmptyResultSet.java @@ -8,11 +8,12 @@ */ package org.olap4j.driver.xmla; -import org.olap4j.OlapExceptionHelper; -import org.olap4j.OlapWrapper; +import org.olap4j.*; +import org.olap4j.driver.xmla.messages.*; + -import javax.sql.rowset.RowSetMetaDataImpl; import java.sql.*; +import javax.sql.rowset.*; import java.sql.Date; import java.math.BigDecimal; import java.io.InputStream; @@ -725,7 +726,8 @@ public T unwrap(Class iface) throws SQLException { if (iface.isInstance(this)) { return iface.cast(this); } - throw OlapExceptionHelper.createException("cannot cast"); + throw XmlaOlap4jMessenger.getInstance().createException( + "EmptyResultSet.unwrap_error"); //$NON-NLS-1$ } public boolean isWrapperFor(Class iface) throws SQLException { diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java index 6f60e1e..f84a0d5 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCellSet.java @@ -10,6 +10,7 @@ import org.olap4j.*; import org.olap4j.mdx.ParseTreeNode; +import org.olap4j.driver.xmla.messages.XmlaOlap4jMessenger; import org.olap4j.impl.Olap4jUtil; import static org.olap4j.driver.xmla.XmlaOlap4jUtil.*; import org.olap4j.metadata.*; @@ -77,11 +78,11 @@ void populate() throws OlapException { try { doc = parse(bytes); } catch (IOException e) { - throw OlapExceptionHelper.createException( - "error creating CellSet", e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jCellSet.parse_error", e); } catch (SAXException e) { - throw OlapExceptionHelper.createException( - "error creating CellSet", e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jCellSet.parse_error", e); } // // @@ -106,27 +107,8 @@ void populate() throws OlapException { Element fault = findChild(body, SOAP_NS, "Fault"); if (fault != null) { - /* - Example: - - - SOAP-ENV:Client.00HSBC01 - XMLA connection datasource not found - Mondrian - - - 00HSBC01 - The Mondrian XML: Mondrian Error:Internal - error: no catalog named 'LOCALDB' - - - - */ // TODO: log doc to logfile - final Element faultstring = findChild(fault, null, "faultstring"); - String message = faultstring.getTextContent(); - throw OlapExceptionHelper.createException( - "XMLA provider gave exception: " + message); + throw XmlaOlap4jMessenger.getInstance().createException(fault); } Element executeResponse = findChild(body, XMLA_NS, "ExecuteResponse"); @@ -294,7 +276,8 @@ void populate() throws OlapException { propertyValues)); } } - + + /** * Returns the value of a cell, cast to the appropriate Java object type * corresponding to the XML schema (XSD) type of the value. @@ -323,25 +306,33 @@ private Object getTypedValue(Element cell) throws OlapException { String type = elm.getAttribute("xsi:type"); try { if (type.equals("xsd:int")) { - return XmlaOlap4jUtil.intElement(cell, "Value"); - } else if (type.equals("xsd:integer")) { - return XmlaOlap4jUtil.integerElement(cell, "Value"); - } else if (type.equals("xsd:double")) { - return XmlaOlap4jUtil.doubleElement(cell, "Value"); - } else if (type.equals("xsd:float")) { - return XmlaOlap4jUtil.floatElement(cell, "Value"); - } else if (type.equals("xsd:long")) { - return XmlaOlap4jUtil.longElement(cell, "Value"); - } else if (type.equals("xsd:boolean")) { - return XmlaOlap4jUtil.booleanElement(cell, "Value"); + return XmlaOlap4jUtil + .intElement(cell, "Value"); //$NON-NLS-1$ + } else if (type.equals("xsd:integer")) { //$NON-NLS-1$ + return XmlaOlap4jUtil + .integerElement(cell, "Value"); //$NON-NLS-1$ + } else if (type.equals("xsd:double")) { //$NON-NLS-1$ + return XmlaOlap4jUtil + .doubleElement(cell, "Value"); //$NON-NLS-1$ + } else if (type.equals("xsd:float")) { //$NON-NLS-1$ + return XmlaOlap4jUtil + .floatElement(cell, "Value"); //$NON-NLS-1$ + } else if (type.equals("xsd:long")) { //$NON-NLS-1$ + return XmlaOlap4jUtil + .longElement(cell, "Value"); //$NON-NLS-1$ + } else if (type.equals("xsd:boolean")) { //$NON-NLS-1$ + return XmlaOlap4jUtil + .booleanElement(cell, "Value"); //$NON-NLS-1$ } else { - return XmlaOlap4jUtil.stringElement(cell, "Value"); + return XmlaOlap4jUtil + .stringElement(cell, "Value"); //$NON-NLS-1$ } } catch (Exception e) { - throw OlapExceptionHelper.createException( - "Error while casting a cell value to the correct java type for" - + " its XSD type " + type, - e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jCellSet.xsd_cast_error", + e, + type, + elm); } } @@ -369,7 +360,7 @@ private XmlaOlap4jCellSetMetaData createMetaData(Element root) this.olap4jStatement.olap4jConnection.getSchema().getCubes().get( cubeName); if (cube == null) { - throw OlapExceptionHelper.createException( + throw new RuntimeException( "Internal error: cube '" + cubeName + "' not found"); } final Element axesInfo = @@ -483,8 +474,7 @@ private Hierarchy lookupHierarchy(XmlaOlap4jCube cube, String hierarchyName) } } if (hierarchy == null) { - throw OlapExceptionHelper - .createException( + throw new RuntimeException( "Internal error: hierarchy '" + hierarchyName + "' not found in cube '" + cube.getName() + "'"); @@ -524,7 +514,12 @@ public Cell getCell(int ordinal) { public Cell getCell(Position... positions) { if (positions.length != getAxes().size()) { throw new IllegalArgumentException( - "cell coordinates should have dimension " + getAxes().size()); + XmlaOlap4jMessenger.getInstance() + .getMessage( + "XmlaOlap4jCellSet.wrong_nb_coordinates", + (Locale)null, + getAxes().size()) + ); } List coords = new ArrayList(positions.length); for (Position position : positions) { @@ -607,9 +602,11 @@ public List ordinalToCoordinates(int ordinal) { } if (ordinal < 0 || ordinal >= modulo) { throw new IndexOutOfBoundsException( - "Cell ordinal " + ordinal - + ") lies outside CellSet bounds (" - + getBoundsAsString() + ")"); + XmlaOlap4jMessenger.getInstance().getMessage( + "XmlaOlap4jCellSet.ordinal_not_within_range", + (Locale)null, + ordinal, + getBoundsAsString())); } return list; } @@ -618,8 +615,11 @@ public int coordinatesToOrdinal(List coordinates) { List axes = getAxes(); if (coordinates.size() != axes.size()) { throw new IllegalArgumentException( - "Coordinates have different dimension " + coordinates.size() - + " than axes " + axes.size()); + XmlaOlap4jMessenger.getInstance().getMessage( + "XmlaOlap4jCellSet.wrong_nb_coordinates", + (Locale)null, + coordinates.size(), + axes.size())); } int modulo = 1; int ordinal = 0; @@ -628,10 +628,12 @@ public int coordinatesToOrdinal(List coordinates) { final Integer coordinate = coordinates.get(k++); if (coordinate < 0 || coordinate >= axis.getPositionCount()) { throw new IndexOutOfBoundsException( - "Coordinate " + coordinate - + " of axis " + k - + " is out of range (" - + getBoundsAsString() + ")"); + XmlaOlap4jMessenger.getInstance().getMessage( + "XmlaOlap4jCellSet.coordinates_not_within_range", + null, + coordinate, + k, + getBoundsAsString())); } ordinal += coordinate * modulo; modulo *= axis.getPositionCount(); diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java index 59c9195..2e115f1 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jConnection.java @@ -12,6 +12,7 @@ import static org.olap4j.driver.xmla.XmlaOlap4jUtil.*; +import org.olap4j.driver.xmla.messages.XmlaOlap4jMessenger; import org.olap4j.driver.xmla.proxy.*; import org.olap4j.impl.*; import org.olap4j.mdx.ParseTreeWriter; @@ -50,7 +51,7 @@ abstract class XmlaOlap4jConnection implements OlapConnection { private final XmlaOlap4jDatabaseMetaData olap4jDatabaseMetaData; - private static final String CONNECT_STRING_PREFIX = "jdbc:xmla:"; + private static final String CONNECT_STRING_PREFIX = "jdbc:xmla:"; //$NON-NLS-1$ final Factory factory; @@ -145,9 +146,9 @@ abstract class XmlaOlap4jConnection implements OlapConnection { // Set URL of HTTP server. String serverUrl = map.get(XmlaOlap4jDriver.Property.Server.name()); if (serverUrl == null) { - throw OlapExceptionHelper.createException("Connection property '" - + XmlaOlap4jDriver.Property.Server.name() - + "' must be specified"); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jConnection.missing_connection_property", + XmlaOlap4jDriver.Property.Server.name()); } // Basic authentication. Make sure the credentials passed as standard @@ -169,8 +170,9 @@ abstract class XmlaOlap4jConnection implements OlapConnection { try { this.serverUrl = new URL(serverUrl); } catch (MalformedURLException e) { - throw OlapExceptionHelper.createException( - "Error while creating connection", e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jConnection.url_parsing_error", + e); } this.olap4jDatabaseMetaData = @@ -283,17 +285,21 @@ String getDataSourceInfo() throws OlapException { } // Throws exception to the client. - //Tells that there are no datasource corresponding to the search criterias. + //Tells that there are no datasource corresponding to the search criteria. if (this.nativeDatasourceName == null) { - throw OlapExceptionHelper.createException("No datasource could be found."); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jConnection.no_such_datasource", + this.providerName, + this.datasourceName); } - // If there is a provider return this.nativeDatasourceName; } catch (OlapException e) { throw e; } catch (SQLException e) { - throw OlapExceptionHelper.createException("Datasource name not found.", e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jConnection.datasource_lookup_error", + e); } finally { try { if (rSet != null) { @@ -480,7 +486,7 @@ public T unwrap(Class iface) throws SQLException { if (iface.isInstance(this)) { return iface.cast(this); } - throw OlapExceptionHelper.createException("does not implement '" + iface + "'"); + throw XmlaOlap4jMessenger.getInstance().createException("does not implement '" + iface + "'"); } public boolean isWrapperFor(Class iface) throws SQLException { @@ -539,7 +545,9 @@ String getURL() { public void setLocale(Locale locale) { if (locale == null) { - throw new IllegalArgumentException("locale must not be null"); + throw new IllegalArgumentException( + XmlaOlap4jMessenger.getInstance().getMessage( + "XmlaOlap4jConnection.null_locale")); } this.locale = locale; } @@ -582,17 +590,21 @@ Element xxx(String request) throws OlapException { try { bytes = proxy.get(serverUrl, request); } catch (IOException e) { - throw OlapExceptionHelper.createException(null, e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jConnection.communication_error", //$NON-NLS-1$ + e); } Document doc; try { doc = parse(bytes); } catch (IOException e) { - throw OlapExceptionHelper.createException( - "error discovering metadata", e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jConnection.parse_error", //$NON-NLS-1$ + e); } catch (SAXException e) { - throw OlapExceptionHelper.createException( - "error discovering metadata", e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jConnection.parse_error", //$NON-NLS-1$ + e); } // // @@ -617,26 +629,8 @@ Element xxx(String request) throws OlapException { Element fault = findChild(body, SOAP_NS, "Fault"); if (fault != null) { - /* - - SOAP-ENV:Client.00HSBC01 - XMLA connection datasource not found - Mondrian - - - 00HSBC01 - The Mondrian XML: Mondrian Error:Internal - error: no catalog named 'LOCALDB' - - - - */ // TODO: log doc to logfile - final Element faultstring = findChild(fault, null, "faultstring"); - String message = faultstring.getTextContent(); - throw OlapExceptionHelper.createException( - "XMLA provider gave exception: " + message - + "; request: " + request); + throw XmlaOlap4jMessenger.getInstance().createException(fault); } Element discoverResponse = findChild(body, XMLA_NS, "DiscoverResponse"); @@ -743,6 +737,7 @@ public String generateRequest( * @return an XML encode string or the value is not required. */ private static String xmlEncode(String value) { + // TODO Make this better and faster. value = Olap4jUtil.replace(value, "&", "&"); value = Olap4jUtil.replace(value, "<", "<"); value = Olap4jUtil.replace(value, ">", ">"); diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java b/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java index 43d1e08..497a578 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jCube.java @@ -433,6 +433,7 @@ public void lookupMembersByUniqueName( List memberUniqueNames, Map memberMap) throws OlapException { + // TODO This needs to be converted in a MetaData request. if (olap4jSchema.olap4jCatalog.olap4jDatabaseMetaData .olap4jConnection.getDataSourceInfo() .indexOf("Provider=Mondrian") != -1) //$NON-NLS-1$ diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jDatabaseMetaData.java b/src/org/olap4j/driver/xmla/XmlaOlap4jDatabaseMetaData.java index 808287d..750f88f 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jDatabaseMetaData.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jDatabaseMetaData.java @@ -9,6 +9,7 @@ package org.olap4j.driver.xmla; import org.olap4j.*; +import org.olap4j.driver.xmla.messages.XmlaOlap4jMessenger; import org.olap4j.impl.ArrayMap; import org.olap4j.impl.Olap4jUtil; import org.olap4j.metadata.*; @@ -905,7 +906,7 @@ public T unwrap(Class iface) throws SQLException { if (iface.isInstance(this)) { return iface.cast(this); } - throw OlapExceptionHelper.createException( + throw XmlaOlap4jMessenger.getInstance().createException( "does not implement '" + iface + "'"); } diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jPreparedStatement.java b/src/org/olap4j/driver/xmla/XmlaOlap4jPreparedStatement.java index 2670811..1760867 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jPreparedStatement.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jPreparedStatement.java @@ -9,6 +9,7 @@ package org.olap4j.driver.xmla; import org.olap4j.*; +import org.olap4j.driver.xmla.messages.XmlaOlap4jMessenger; import org.olap4j.impl.Olap4jUtil; import org.olap4j.metadata.*; import org.olap4j.type.*; @@ -56,9 +57,10 @@ abstract class XmlaOlap4jPreparedStatement statement.close(); } catch (SQLException e) { - throw OlapExceptionHelper.createException( - "Error while preparing statement '" + mdx + "'", - e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jPreparedStatement.parsing_error", + e, + mdx); } this.mdx = mdx; @@ -252,8 +254,9 @@ public String getParameterName(int param) throws OlapException { private Parameter getParameter(int param) throws OlapException { final List parameters = getParameters(); if (param < 1 || param > parameters.size()) { - throw OlapExceptionHelper.createException( - "parameter ordinal " + param + " out of range"); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jPreparedStatement.parameter_out_of_range", + param); } return parameters.get(param - 1); } diff --git a/src/org/olap4j/driver/xmla/XmlaOlap4jStatement.java b/src/org/olap4j/driver/xmla/XmlaOlap4jStatement.java index 5e7f8d0..12e2091 100644 --- a/src/org/olap4j/driver/xmla/XmlaOlap4jStatement.java +++ b/src/org/olap4j/driver/xmla/XmlaOlap4jStatement.java @@ -9,6 +9,7 @@ package org.olap4j.driver.xmla; import org.olap4j.*; +import org.olap4j.driver.xmla.messages.XmlaOlap4jMessenger; import org.olap4j.mdx.SelectNode; import org.olap4j.mdx.ParseTreeNode; import org.olap4j.mdx.ParseTreeWriter; @@ -53,9 +54,11 @@ public ResultSet executeQuery(String sql) throws SQLException { throw new UnsupportedOperationException(); } + // This method is not used anywhere thus marked as deprecated. + @Deprecated private void checkOpen() throws SQLException { if (closed) { - throw OlapExceptionHelper.createException("closed"); + throw XmlaOlap4jMessenger.getInstance().createException("closed"); } } @@ -100,8 +103,9 @@ public int getQueryTimeout() throws SQLException { public void setQueryTimeout(int seconds) throws SQLException { if (seconds < 0) { - throw OlapExceptionHelper.createException( - "illegal timeout value " + seconds); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jStatement.invalid_timeout_value", + seconds); } this.timeoutSeconds = seconds; } @@ -243,7 +247,7 @@ public T unwrap(Class iface) throws SQLException { if (iface.isInstance(this)) { return iface.cast(this); } - throw OlapExceptionHelper.createException( + throw XmlaOlap4jMessenger.getInstance().createException( "does not implement '" + iface + "'"); } @@ -299,7 +303,7 @@ public CellSet executeOlapQuery(String mdx) throws OlapException { try { cs.close(); } catch (SQLException e) { - throw OlapExceptionHelper.createException( + throw XmlaOlap4jMessenger.getInstance().createException( "Error while closing previous CellSet", e); } } @@ -347,14 +351,20 @@ byte[] getBytes() throws OlapException { return future.get(); } } catch (InterruptedException e) { - throw OlapExceptionHelper.createException(null, e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jStatement.interrupted", + e); } catch (ExecutionException e) { - throw OlapExceptionHelper.createException(null, e.getCause()); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jStatement.execution_exception", + e); } catch (TimeoutException e) { - throw OlapExceptionHelper.createException( - "Query timeout of " + timeoutSeconds + " seconds exceeded"); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jStatement.statement_timed_out", + timeoutSeconds); } catch (CancellationException e) { - throw OlapExceptionHelper.createException("Query canceled"); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jStatement.statement_canceled"); } finally { synchronized (this) { if (future == null) { diff --git a/src/org/olap4j/driver/xmla/cache/XmlaOlap4jConcurrentMemoryCache.java b/src/org/olap4j/driver/xmla/cache/XmlaOlap4jConcurrentMemoryCache.java index 3d7f720..db43550 100644 --- a/src/org/olap4j/driver/xmla/cache/XmlaOlap4jConcurrentMemoryCache.java +++ b/src/org/olap4j/driver/xmla/cache/XmlaOlap4jConcurrentMemoryCache.java @@ -16,6 +16,7 @@ import org.olap4j.driver.xmla.cache.XmlaOlap4jNamedMemoryCache.MODE; import org.olap4j.driver.xmla.cache.XmlaOlap4jNamedMemoryCache.Property; +import org.olap4j.driver.xmla.messages.XmlaOlap4jMessenger; /** *

Thread-safe cache object which supports concurrent access. @@ -109,7 +110,8 @@ private void setCacheSize(int size) { this.cacheSize = size; } else { throw new IllegalArgumentException( - "The XMLAOLap4jMemoryCache size cannot be less or equal to 0."); + XmlaOlap4jMessenger.getInstance().getMessage( + "XmlaOlap4jConcurrentMemoryCache.invalid_size")); } } @@ -124,7 +126,8 @@ private void setCacheMode(String mode) { this.evictionMode = MODE.valueOf(mode); } else { throw new IllegalArgumentException( - "The XMLAOLap4jMemoryCache mode has to be one of XmlaOlap4jMemoryCache.MODE"); + XmlaOlap4jMessenger.getInstance().getMessage( + "XmlaOlap4jConcurrentMemoryCache.invalid_mode")); } } @@ -139,7 +142,8 @@ private void setCacheTimeout(int seconds) { this.cacheTimeout = seconds; } else { throw new IllegalArgumentException( - "The XMLAOLap4jMemoryCache timeout cannot be less or equal to 0."); + XmlaOlap4jMessenger.getInstance().getMessage( + "XmlaOlap4jConcurrentMemoryCache.invalid_timeout")); } } diff --git a/src/org/olap4j/driver/xmla/cache/XmlaOlap4jNamedMemoryCache.java b/src/org/olap4j/driver/xmla/cache/XmlaOlap4jNamedMemoryCache.java index 4d1e8fd..aa791c2 100644 --- a/src/org/olap4j/driver/xmla/cache/XmlaOlap4jNamedMemoryCache.java +++ b/src/org/olap4j/driver/xmla/cache/XmlaOlap4jNamedMemoryCache.java @@ -186,7 +186,7 @@ public byte[] get( return caches.get(id).get(url, request); } else { throw new RuntimeException( - "There are no configured caches of this name yet configured."); + "There are no configured caches of this name yet."); } } } @@ -208,7 +208,7 @@ public void put( caches.get(id).put(url, request, response); } else { throw new RuntimeException( - "There are no configured caches of this name yet configured."); + "There are no configured caches of this name yet."); } } } diff --git a/src/org/olap4j/driver/xmla/messages/XmlaOlap4jMessenger.java b/src/org/olap4j/driver/xmla/messages/XmlaOlap4jMessenger.java new file mode 100644 index 0000000..1930466 --- /dev/null +++ b/src/org/olap4j/driver/xmla/messages/XmlaOlap4jMessenger.java @@ -0,0 +1,132 @@ +/* +// This software is subject to the terms of the Common Public License +// Agreement, available at the following URL: +// http://www.opensource.org/licenses/cpl.html. +// Copyright (C) 2007-2007 Julian Hyde +// All Rights Reserved. +// You must accept the terms of that agreement to use this software. +*/ +package org.olap4j.driver.xmla.messages; + +import org.olap4j.OlapException; +import org.olap4j.messages.AbstractOlap4jMessenger; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +/** + * Manages all messages for the XMLA driver through Olap4j messaging + * facilities. + * @author Luc Boudreau + * @version $Id: $ + */ +public class XmlaOlap4jMessenger extends AbstractOlap4jMessenger { + + /** + * Holds on at the static level to the singleton instance of this + * driver's resource bundle. + */ + private static XmlaOlap4jMessenger instance = + new XmlaOlap4jMessenger(); + + /** + * Returns an instance of the XMLA driver resource bundle. + * @return + */ + public static XmlaOlap4jMessenger getInstance() { + return instance; + } + + /* (non-Javadoc) + * @see org.olap4j.messages.Olap4jResourceBundle#getBundleName() + */ + public String getBundleName() { + return "org.olap4j.driver.xmla.messages.messages"; + } + + /* (non-Javadoc) + * @see org.olap4j.messages.Olap4jResourceBundle#getDriverName() + */ + public String getDriverName() { + return "org.olap4j.driver.xmla.XmlaOlap4jDriver"; + } + + /** + * Parses an XML fragment from a SOAP fault response and generates + * a user friendly message in an OlapException. + * @param soapFaultElement Pass it the SOAP-ENV:Fault xml element + * @return returns the String message. + */ + public OlapException createException(Element soapFaultElement) + { + /* Example: + * + SOAP-ENV:Client.00HSBC01 + XMLA connection datasource not found + Mondrian + + + 00HSBC01 + The Mondrian XML: Mondrian Error:Internal + error: no catalog named 'LOCALDB' + + + + */ + StringBuilder sb = new StringBuilder( + "-- The remote SOAP endpoint returned an error message --"); + appendStructuredErrorMessageElement( + sb, + soapFaultElement, + 0); + return new OlapException(sb.toString()); + } + + /** + * Recursive private method which parses an XML structure and + * creates a formatted message. + * @param sb A StringBuilder object into which the message will be appended. + * @param currentElement A Node object into which we'll descend. + * @param level The current level. Send 0 for the first iteration. + */ + private void appendStructuredErrorMessageElement(StringBuilder sb, Node currentElement, int level) + { + sb.append("\n"); + for(int cpt = 0; cpt < level; cpt++) { + sb.append("\t"); + } + sb.append("<"); + sb.append(currentElement.getNodeName()); + sb.append(">"); + + NodeList childrenList = currentElement.getChildNodes(); + for (int childrenPosition = 0; + childrenPosition < childrenList.getLength(); + childrenPosition++) + { + if (childrenList.item(childrenPosition).getNodeType() + != Node.TEXT_NODE) + { + appendStructuredErrorMessageElement( + sb, + childrenList.item(childrenPosition), + level+1); + } else { + sb.append("\n"); + for(int cpt = 0; cpt < level+1; cpt++) { + sb.append("\t"); + } + sb.append(currentElement.getTextContent()); + } + } + + sb.append("\n"); + for(int cpt = 0; cpt < level; cpt++) { + sb.append("\t"); + } + sb.append(""); + } +} +//End XmlaOlap4jMessenger.java \ No newline at end of file diff --git a/src/org/olap4j/driver/xmla/messages/messages.properties b/src/org/olap4j/driver/xmla/messages/messages.properties new file mode 100644 index 0000000..70a9dd3 --- /dev/null +++ b/src/org/olap4j/driver/xmla/messages/messages.properties @@ -0,0 +1,39 @@ +#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net) +# This software is subject to the terms of the Common Public License +# Agreement, available at the following URL: +# http://www.opensource.org/licenses/cpl.html. +# Copyright (C) 2007-2007 Julian Hyde +# All Rights Reserved. +# You must accept the terms of that agreement to use this software. + +EmptyResultSet.unwrap_error = cannot cast + +XmlaOlap4jAbstractHttpProxy.cannot_instanciate = The specified cache class name could not be instantiated +XmlaOlap4jAbstractHttpProxy.class_not_found = The specified cache class name could not be found + +XmlaOlap4jCellSet.coordinates_not_within_range = Coordinate {0} of axis {1} is out of range ({2}). +XmlaOlap4jCellSet.ordinal_not_within_range = The ordinal value you provided ({0}) does not fall in this CellSet limits. Current maximum is {1}. +XmlaOlap4jCellSet.parse_error = An error was encountered while parsing the server response and creating the CellSet. +XmlaOlap4jCellSet.wrong_nb_coordinates = The number of coordinates you provided is invalid since {0} coordinates were expected. +XmlaOlap4jCellSet.xsd_cast_error = There was an error while converting the cell of XSD type {0} to its java equivalent. The cell was {1} + +XmlaOlap4jConcurrentMemoryCache.invalid_mode = The XMLAOLap4jMemoryCache mode has to be one of XmlaOlap4jMemoryCache.MODE +XmlaOlap4jConcurrentMemoryCache.invalid_size = The XMLAOLap4jMemoryCache size cannot be less or equal to 0. +XmlaOlap4jConcurrentMemoryCache.invalid_timeout = The XMLAOLap4jMemoryCache timeout cannot be less or equal to 0. + +XmlaOlap4jConnection.communication_error = A communication error was encountered while executing the request. +XmlaOlap4jConnection.datasource_lookup_error = An exception was encountered while searching for a proper datasource and provider. +XmlaOlap4jConnection.missing_connection_property = Connection property {0} must be specified. +XmlaOlap4jConnection.no_such_datasource = Unable to find a proper provider and datasource. The search criterias provided were Provider={0} and Datasource={1} +XmlaOlap4jConnection.null_locale = The parameter 'locale' cannot be null. +XmlaOlap4jConnection.parse_error = An error was encountered while parsing the server response. +XmlaOlap4jConnection.url_parsing_error = An exception was encountered while parsing the given server URL. + +XmlaOlap4jPreparedStatement.parameter_out_of_range = Parameter ordinal ({0}) out of range. +XmlaOlap4jPreparedStatement.parsing_error = An exception was encountered while transforming the following MDX query in a PreparedStatement : {0} + +XmlaOlap4jStatement.execution_exception = An exception was encountered while executing the query. +XmlaOlap4jStatement.interrupted = The communication was interrupted. +XmlaOlap4jStatement.invalid_timeout_value = The timeout value you provided ({0}) is not valid. It needs to be a positive non-zero integer. +XmlaOlap4jStatement.statement_canceled = The statement execution was canceled. +XmlaOlap4jStatement.statement_timed_out = Query timeout of {0} seconds exceeded. diff --git a/src/org/olap4j/driver/xmla/messages/package.html b/src/org/olap4j/driver/xmla/messages/package.html new file mode 100644 index 0000000..f7afb5e --- /dev/null +++ b/src/org/olap4j/driver/xmla/messages/package.html @@ -0,0 +1,5 @@ + + +Messages files for Olap4j XML/A driver. + + diff --git a/src/org/olap4j/driver/xmla/proxy/XmlaOlap4jAbstractHttpProxy.java b/src/org/olap4j/driver/xmla/proxy/XmlaOlap4jAbstractHttpProxy.java index 37dcaad..4298b60 100644 --- a/src/org/olap4j/driver/xmla/proxy/XmlaOlap4jAbstractHttpProxy.java +++ b/src/org/olap4j/driver/xmla/proxy/XmlaOlap4jAbstractHttpProxy.java @@ -14,10 +14,10 @@ import java.util.*; import java.util.concurrent.*; -import org.olap4j.OlapException; -import org.olap4j.OlapExceptionHelper; -import org.olap4j.driver.xmla.XmlaOlap4jDriver; -import org.olap4j.driver.xmla.cache.XmlaOlap4jCache; +import org.olap4j.*; +import org.olap4j.driver.xmla.*; +import org.olap4j.driver.xmla.cache.*; +import org.olap4j.driver.xmla.messages.*; /** *

Abstract implementation of Proxy which adds a SOAP @@ -128,25 +128,30 @@ public void setCache(Map config, Map properties) // Configures it this.cacheId = this.cache.setParameters(config, properties); } catch (ClassNotFoundException e) { - throw OlapExceptionHelper.createException( - "The specified cache class name could not be found : " - + config.get(XmlaOlap4jDriver.Property.Cache.name()), e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jAbstractHttpProxy.class_not_found", + e, + config.get(XmlaOlap4jDriver.Property.Cache.name())); } catch (InstantiationException e) { - throw OlapExceptionHelper.createException( - "The specified cache class name could not be instanciated : " - + config.get(XmlaOlap4jDriver.Property.Cache.name()), e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jAbstractHttpProxy.cannot_instanciate", + e, + config.get(XmlaOlap4jDriver.Property.Cache.name())); } catch (IllegalAccessException e) { - throw OlapExceptionHelper.createException( - "An error was encountered while instanciating the cache : " - + config.get(XmlaOlap4jDriver.Property.Cache.name()), e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jAbstractHttpProxy.cannot_instanciate", + e, + config.get(XmlaOlap4jDriver.Property.Cache.name())); } catch (IllegalArgumentException e) { - throw OlapExceptionHelper.createException( - "An error was encountered while instanciating the cache : " - + config.get(XmlaOlap4jDriver.Property.Cache.name()), e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jAbstractHttpProxy.cannot_instanciate", + e, + config.get(XmlaOlap4jDriver.Property.Cache.name())); } catch (SecurityException e) { - throw OlapExceptionHelper.createException( - "An error was encountered while instanciating the cache : " - + config.get(XmlaOlap4jDriver.Property.Cache.name()), e); + throw XmlaOlap4jMessenger.getInstance().createException( + "XmlaOlap4jAbstractHttpProxy.cannot_instanciate", + e, + config.get(XmlaOlap4jDriver.Property.Cache.name())); } } diff --git a/src/org/olap4j/messages/AbstractOlap4jMessenger.java b/src/org/olap4j/messages/AbstractOlap4jMessenger.java new file mode 100644 index 0000000..8391084 --- /dev/null +++ b/src/org/olap4j/messages/AbstractOlap4jMessenger.java @@ -0,0 +1,282 @@ +/* +// This software is subject to the terms of the Common Public License +// Agreement, available at the following URL: +// http://www.opensource.org/licenses/cpl.html. +// Copyright (C) 2007-2007 Julian Hyde +// All Rights Reserved. +// You must accept the terms of that agreement to use this software. +*/ +package org.olap4j.messages; + +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.concurrent.ConcurrentHashMap; + +import org.olap4j.Cell; +import org.olap4j.OlapException; + +/** + *

Manages all messages to be sent to the end-users of this API and + * driver implementation. It is thread safe and supports localization + * of messages. + * + *

This is an abstract class and thus cannot be used as-is. It has to + * be subclassed by implementing drivers. Subclasses of this abstract + * messenger are required to override getBundleName() + * and getDriverName(). See their respective javadoc for a description. + * + *

For an implementation example, refer to + * {@link org.olap4j.driver.xmla.messages.XmlaOlap4jMessenger}. + * + *

It also serves as a OlapException localized helper by + * replacing exception messages by performing a lookup in messages bundles and + * replacing if a corresponding bundeled value is found. + * + *

Example : You call this class with «createException("test.message")». + * If your messages bundles include a 'test.message' property, the message will + * be substituted. If it doesn't include such a key, the key will be used as + * the exception message, thus allowing easy fallback. + * + * @author Luc Boudreau + * @version $Id: $ + */ +public abstract class AbstractOlap4jMessenger { + + + /** + * Internal hash that maintains all instanciated resource bundles for + * quick use. + */ + private static Map> resourceBundles = + new ConcurrentHashMap>(1); + + /** + * Initializes the ressourceBundles static hash and makes sure that + * everything is ready for use. + */ + private void init() { + assert (getBundleName() != null); + assert (getDriverName() != null); + synchronized (resourceBundles) { + if (!resourceBundles.containsKey(getDriverName())){ + resourceBundles.put( + getDriverName(), + new ConcurrentHashMap(1)); + } + } + } + + + /** + * Performs a lookup on a message key and returns the proper + * message string object. Uses the default JVM locale. + * @param key The message key to lookup. + * @return The message string. + */ + public String getMessage(String key) { + return getMessage( + key, + null); + } + + + /** + * Performs a lookup on a message key and returns the proper + * message string object. + * @param key The message key to lookup. + * @param locale The locale to use. + * @param arguments Strings to substitute in the correct order. + * @return The message string if found, the key otherwise. + */ + public String getMessage(String key, + Locale locale, Object... arguments) + { + // Initializes the facility + init(); + + // Tries to fetch the correct message + String message; + try { + message = getBundle(locale).getString(key); + } catch (MissingResourceException e) { + message = key; + } + + // + if ( message != null && + arguments != null && + arguments.length > 0 ) + { + for (int c = 0; c < arguments.length; c++) { + message = message.replaceAll( + "\\{" + c + "\\}", + (arguments[c]==null)?"null":arguments[c].toString()); + } + } + return (message==null)?key:message; + } + + /** + * Returns a ResourceBundle object. Will instanciate a new one + * if necessary or use a cached one if available. + * @param locale The locale to use. Null means use of the JVM default. + * @return The ReousourceBundle object with the correct locale. + */ + private ResourceBundle getBundle(Locale locale) { + if (locale == null) { + locale = Locale.getDefault(); + } + synchronized (resourceBundles) { + if (!resourceBundles.get(getDriverName()).containsKey(locale.getDisplayName())) { + resourceBundles.get(getDriverName()) + .put( + locale.getDisplayName(), + ResourceBundle.getBundle( + getBundleName(), //$NON-NLS-1$ + locale)); + } + } + return resourceBundles.get(getDriverName()).get(locale.getDisplayName()); + } + + + /** + * Creates an OlapException object and returns it. + * @param msg A string which describes the nature of the exception. + * @return The built OlapException object. + */ + public OlapException createException(String msg) { + return new OlapException(getMessage(msg)); + } + + /** + * Creates an OlapException object and returns it. + * @param msg A string which describes the nature of the exception. + * @param arguments Strings to substitute in the correct order. + * @return The built OlapException object. + */ + public OlapException createException(String msg, Object... arguments) { + return new OlapException(getMessage(msg, null, arguments)); + } + + + /** + * Creates an OlapException object and returns it. + * @param msg A string which describes the nature of the exception. + * @param cause A subclass of Throwable which triggered the exception. + * @return The built OlapException object. + */ + public OlapException createException(String msg, Throwable cause) { + return new OlapException(getMessage(msg), cause); + } + + + /** + * Creates an OlapException object and returns it. + * @param msg A string which describes the nature of the exception. + * @param cause A subclass of Throwable which triggered the exception. + * @param arguments Strings to substitute in the correct order. + * @return The built OlapException object. + */ + public OlapException createException( + String msg, + Throwable cause, + Object... arguments) + { + return new OlapException(getMessage(msg, null, arguments), cause); + } + + + /** + * Creates an OlapException object and returns it. + * @param msg A string which describes the nature of the exception. + * @param context The Olap4j Context from which originated the exception. + * @return The built OlapException object. + */ + public OlapException createException(Cell context, String msg) { + OlapException exception = new OlapException(getMessage(msg)); + exception.setContext(context); + return exception; + } + + + /** + * Creates an OlapException object and returns it. + * @param msg A string which describes the nature of the exception. + * @param context The Olap4j Context from which originated the exception. + * @param arguments Strings to substitute in the correct order. + * @return The built OlapException object. + */ + public OlapException createException( + Cell context, + String msg, + Object... arguments) + { + OlapException exception = new OlapException( + getMessage(msg,null,arguments)); + exception.setContext(context); + return exception; + } + + + /** + * Creates an OlapException object and returns it. + * @param msg A string which describes the nature of the exception. + * @param cause A subclass of Throwable which triggered the exception. + * @param context The Olap4j Context from which originated the exception. + * @return The built OlapException object. + */ + public OlapException createException( + Cell context, String msg, Throwable cause) + { + OlapException exception = new OlapException(getMessage(msg), cause); + exception.setContext(context); + return exception; + } + + + /** + * Creates an OlapException object and returns it. + * @param msg A string which describes the nature of the exception. + * @param cause A subclass of Throwable which triggered the exception. + * @param context The Olap4j Context from which originated the exception. + * @param arguments Strings to substitute in the correct order. + * @return The built OlapException object. + */ + public OlapException createException( + Cell context, String msg, Throwable cause, Object... arguments) + { + OlapException exception = new OlapException( + getMessage(msg,null,arguments), cause); + exception.setContext(context); + return exception; + } + + + /** + *

Must return a unique driver implementation identification string to help + * the messages facilities to find the correct messages bundles when a driver + * queries for a message. + *

as a rule of thumb, we return the driver class name along with full + * package path. + * @return A unique id for the messages bundle. + */ + public abstract String getDriverName(); + + + /** + *

This needs to be implemented by subclasses so that the messages facility + * can know where to find the driver's messages. + * + *

All messages bundles need to conform to the Java Property Ressource + * mechanism. + * + * @return The bundle location, including full package names. + * Ex : "org.olap4j.driver.xmla.messages.messages" would point to + * "org.olap4j.driver.xmla.messages.messages.properties" + */ + public abstract String getBundleName(); +} +//End AbstractOlap4jMessenger.java \ No newline at end of file diff --git a/src/org/olap4j/messages/Olap4jMessenger.java b/src/org/olap4j/messages/Olap4jMessenger.java new file mode 100644 index 0000000..6388038 --- /dev/null +++ b/src/org/olap4j/messages/Olap4jMessenger.java @@ -0,0 +1,50 @@ +/* +// This software is subject to the terms of the Common Public License +// Agreement, available at the following URL: +// http://www.opensource.org/licenses/cpl.html. +// Copyright (C) 2007-2007 Julian Hyde +// All Rights Reserved. +// You must accept the terms of that agreement to use this software. +*/ +package org.olap4j.messages; + + + +/** + *

This is the main messenger facility for the Olap4j API. All messages and + * exceptions are thrown through this facility and localized here. + * + * @author Luc boudreau + * @version $Id: $ + */ +public class Olap4jMessenger extends AbstractOlap4jMessenger { + + /** + * Holds on at the static level to the singleton instance of this + * API resource bundle. + */ + private static Olap4jMessenger instance = new Olap4jMessenger(); + + /** + * Returns an instance of the XMLA driver resource bundle. + * @return + */ + public static Olap4jMessenger getInstance() { + return instance; + } + + /* (non-Javadoc) + * @see org.olap4j.messages.AbstractOlap4jMessenger#getBundleName() + */ + public String getBundleName() { + return "org.olap4j.messages.messages"; //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.olap4j.messages.AbstractOlap4jMessenger#getDriverName() + */ + public String getDriverName() { + return "org.olap4j.API"; //$NON-NLS-1$ + } + +} diff --git a/src/org/olap4j/messages/messages.properties b/src/org/olap4j/messages/messages.properties new file mode 100644 index 0000000..3d22ba9 --- /dev/null +++ b/src/org/olap4j/messages/messages.properties @@ -0,0 +1,7 @@ +#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net) +# This software is subject to the terms of the Common Public License +# Agreement, available at the following URL: +# http://www.opensource.org/licenses/cpl.html. +# Copyright (C) 2007-2007 Julian Hyde +# All Rights Reserved. +# You must accept the terms of that agreement to use this software. \ No newline at end of file diff --git a/src/org/olap4j/messages/package.html b/src/org/olap4j/messages/package.html new file mode 100644 index 0000000..0cce86f --- /dev/null +++ b/src/org/olap4j/messages/package.html @@ -0,0 +1,5 @@ + + +Messages files for Olap4j API. + + diff --git a/testsrc/org/olap4j/ConnectionTest.java b/testsrc/org/olap4j/ConnectionTest.java index 9bae735..f97419a 100644 --- a/testsrc/org/olap4j/ConnectionTest.java +++ b/testsrc/org/olap4j/ConnectionTest.java @@ -395,7 +395,7 @@ public void testInvalidStatement() throws SQLException { switch (tester.getFlavor()) { case XMLA: assertTrue(e.getMessage().indexOf( - "XMLA provider gave exception: XMLA MDX parse failed") >= 0); + "XMLA MDX parse failed") >= 0); break; default: assertTrue( @@ -1786,7 +1786,7 @@ public void run() { fail("expected exception indicating stmt had been canceled," + " got cellSet " + cellSet); } catch (OlapException e) { - assertTrue(e.getMessage().indexOf("Query canceled") >= 0); + assertTrue(e.getMessage().indexOf("The statement execution was canceled") >= 0); } if (exceptions[0] != null) { throw exceptions[0]; @@ -1803,7 +1803,8 @@ public void testStatementTimeout() throws Throwable { olapStatement.setQueryTimeout(-1); fail("expected exception"); } catch (SQLException e) { - assertTrue(e.getMessage().indexOf("illegal timeout value ") >= 0); + assertTrue(e.getMessage().indexOf("The timeout value you provided") >= 0 + && e.getMessage().indexOf("is not valid. It needs to be a positive non-zero integer") >= 0); } olapStatement.setQueryTimeout(1); try { diff --git a/testsrc/org/olap4j/messages/MessengerTest.java b/testsrc/org/olap4j/messages/MessengerTest.java new file mode 100644 index 0000000..ec590a6 --- /dev/null +++ b/testsrc/org/olap4j/messages/MessengerTest.java @@ -0,0 +1,103 @@ +/* +// This software is subject to the terms of the Common Public License +// Agreement, available at the following URL: +// http://www.opensource.org/licenses/cpl.html. +// Copyright (C) 2007-2008 Julian Hyde +// All Rights Reserved. +// You must accept the terms of that agreement to use this software. +*/ +package org.olap4j.messages; + +import java.security.InvalidParameterException; +import java.util.Locale; + +import junit.framework.TestCase; + +import org.olap4j.messages.Olap4jMessenger; + +/** + *

This is a simple test that makes sure that the messages facilities + * work as expected. + * + * @author Luc Boudreau + */ +public class MessengerTest extends TestCase { + + public void testMessageFallback() throws Exception { + String message = "My test message should be the same."; + assertEquals(message, Olap4jMessenger.getInstance().createException(message).getMessage()); + assertEquals(message, Olap4jMessenger.getInstance().getMessage(message)); + } + + public void testLocales() throws Exception { + Locale deutch = new Locale("de"); + Locale french = new Locale("fr"); + assertEquals("FrenchTestValue", MockMessenger1.getInstance().getMessage("test.value.1", french)); + assertEquals("DeutchTestValue", MockMessenger1.getInstance().getMessage("test.value.1", deutch)); + } + + public void testSubstitutions() throws Exception { + String param1 = "param_1"; + String param2 = "param_2"; + String message_o = "Hello, this is my {0} nifty {1} message."; + String message_m = "Hello, this is my param_1 nifty param_2 message."; + assertEquals(message_o, MockMessenger1.getInstance().getMessage("nifty_message")); + assertEquals(message_m, MockMessenger1.getInstance().getMessage("nifty_message", null, param1, param2)); + assertEquals(message_o, MockMessenger1.getInstance().createException("nifty_message").getMessage()); + assertEquals(message_m, MockMessenger1.getInstance().createException("nifty_message", null, param1, param2).getMessage()); + } + + public void testCauses() throws Exception { + InvalidParameterException cause = new InvalidParameterException("Test value"); + assertEquals(cause, Olap4jMessenger.getInstance().createException("Test", cause).getCause()); + assertEquals("Test value", Olap4jMessenger.getInstance().createException("Test", cause).getCause().getMessage()); + } + + public void testMultipleMessengersConcurrency() throws Exception { + Locale deutch = new Locale("de"); + Locale french = new Locale("fr"); + assertEquals("FrenchTestValue", MockMessenger1.getInstance().getMessage("test.value.1", french)); + assertEquals("FrenchTestValue_2", MockMessenger2.getInstance().getMessage("test.value.1", french)); + assertEquals("DeutchTestValue", MockMessenger1.getInstance().getMessage("test.value.1", deutch)); + assertEquals("DeutchTestValue_2", MockMessenger2.getInstance().getMessage("test.value.1", deutch)); + } + + private static class MockMessenger1 extends AbstractOlap4jMessenger { + + private static MockMessenger1 instance = new MockMessenger1(); + + public static MockMessenger1 getInstance() { + return instance; + } + + public String getBundleName() { + return "org.olap4j.messages.mock1.test_messages"; + } + + public String getDriverName() { + return "MockMessenger1"; + } + + } + + private static class MockMessenger2 extends AbstractOlap4jMessenger { + + private static MockMessenger2 instance = new MockMessenger2(); + + public static MockMessenger2 getInstance() { + return instance; + } + + public String getBundleName() { + return "org.olap4j.messages.mock2.test_messages"; + } + + public String getDriverName() { + return "MockMessenger2"; + } + + } + +} + +// End MessengerTest.java diff --git a/testsrc/org/olap4j/messages/mock1/test_messages.properties b/testsrc/org/olap4j/messages/mock1/test_messages.properties new file mode 100644 index 0000000..61db8cc --- /dev/null +++ b/testsrc/org/olap4j/messages/mock1/test_messages.properties @@ -0,0 +1,2 @@ +test.value.1=EnglishTestValue +nifty_message=Hello, this is my {0} nifty {1} message. \ No newline at end of file diff --git a/testsrc/org/olap4j/messages/mock1/test_messages_de.properties b/testsrc/org/olap4j/messages/mock1/test_messages_de.properties new file mode 100644 index 0000000..453d1e9 --- /dev/null +++ b/testsrc/org/olap4j/messages/mock1/test_messages_de.properties @@ -0,0 +1,2 @@ +test.value.1=DeutchTestValue +nifty_message=Hello, this is my {0} nifty {1} message. \ No newline at end of file diff --git a/testsrc/org/olap4j/messages/mock1/test_messages_fr.properties b/testsrc/org/olap4j/messages/mock1/test_messages_fr.properties new file mode 100644 index 0000000..fab007c --- /dev/null +++ b/testsrc/org/olap4j/messages/mock1/test_messages_fr.properties @@ -0,0 +1,2 @@ +test.value.1=FrenchTestValue +nifty_message=Hello, this is my {0} nifty {1} message. \ No newline at end of file diff --git a/testsrc/org/olap4j/messages/mock2/test_messages.properties b/testsrc/org/olap4j/messages/mock2/test_messages.properties new file mode 100644 index 0000000..211ac81 --- /dev/null +++ b/testsrc/org/olap4j/messages/mock2/test_messages.properties @@ -0,0 +1,2 @@ +test.value.1=EnglishTestValue_2 +nifty_message=Hello, this is my {0} nifty {1} message. \ No newline at end of file diff --git a/testsrc/org/olap4j/messages/mock2/test_messages_de.properties b/testsrc/org/olap4j/messages/mock2/test_messages_de.properties new file mode 100644 index 0000000..ad72562 --- /dev/null +++ b/testsrc/org/olap4j/messages/mock2/test_messages_de.properties @@ -0,0 +1,2 @@ +test.value.1=DeutchTestValue_2 +nifty_message=Hello, this is my {0} nifty {1} message. \ No newline at end of file diff --git a/testsrc/org/olap4j/messages/mock2/test_messages_fr.properties b/testsrc/org/olap4j/messages/mock2/test_messages_fr.properties new file mode 100644 index 0000000..c480646 --- /dev/null +++ b/testsrc/org/olap4j/messages/mock2/test_messages_fr.properties @@ -0,0 +1,2 @@ +test.value.1=FrenchTestValue_2 +nifty_message=Hello, this is my {0} nifty {1} message. \ No newline at end of file