Skip to content

Commit

Permalink
Add experimental API for client to ask server to notify when the cell…
Browse files Browse the repository at this point in the history
… set

changes. XMLA driver does not currently support the API.


git-svn-id: https://olap4j.svn.sourceforge.net/svnroot/olap4j/trunk@319 c6a108a4-781c-0410-a6c6-c2d559e19af0
  • Loading branch information
julianhyde committed Jun 16, 2010
1 parent 1a1d929 commit 141d730
Show file tree
Hide file tree
Showing 5 changed files with 260 additions and 2 deletions.
200 changes: 200 additions & 0 deletions src/org/olap4j/CellSetListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/*
// $Id: CellSet.java 310 2010-04-23 19:57:41Z jhyde $
// This software is subject to the terms of the Eclipse Public License v1.0
// Agreement, available at the following URL:
// http://www.eclipse.org/legal/epl-v10.html.
// Copyright (C) 2010-2010 Julian Hyde
// All Rights Reserved.
// You must accept the terms of that agreement to use this software.
*/
package org.olap4j;

import java.util.List;

/**
* Listener interface for receiving events when the contents of a
* {@link CellSet} have changed.
*
* <p>NOTE: This functionality is experimental and is subject to change or
* removal without notice.
*
* <p>The client can ask the server to provide the listener with a specific
* {@link Granularity granularity} of events, but the server can decline to
* provide that granularity.
*
* <p>Fine granularity deals with changes such as cell values changing (and
* reports the before and after value, before and after formatted value),
* positions being deleted, positions being changed.
*
* <p>When an atomic change happens on the server (say a cache flush, if the
* server is mondrian) then an event will arrive on the client containing all of
* those changes. Although {@link CellSetChange#getCellChanges} and
* {@link CellSetChange#getAxisChanges} return lists, the client should assume
* that all of the events in these lists simultaneously.
*
* <p>At any point, the server is free to throw up its hands and say 'there are
* too many changes' by sending null values for {@code getCellChanges} or
* {@code getAxisChanges}. This prevents situations where there are huge numbers
* of changes that might overwhelm the server, the network link, or the client,
* such as might happen if a large axis is re-sorted.
*
* <p>The client should always be ready for that to happen (even for providers
* that claim to provide fine granularity events), and should re-execute the
* query to get the cell set. In fact, we recommend that clients re-execute the
* query to get a new cellset whenever they get an event. Then the client can
* use the details in the event to highlight cells that have changed.
*
* <h3>Notes for implementors</h3>
*
* <p>The purpose of registering a listener before creating a cell set is to
* ensure that no events "leak out" between creating a cell set and registering
* a listener, or while a statement is being re-executed to produce a new cell
* set.
*
* <p>The {@link #cellSetOpened(CellSet)} and {@link #cellSetClosed(CellSet)}
* methods are provided so that the listener knows what is going on when a
* statement is re-executed. In particular, suppose a statement receives an
* change event decides to re-execute. The listener is attached to the
* statement, so receives notifications about both old and new cell sets. The
* driver implicitls closes the previous cell set and calls
* {@code cellSetClosed}, then calls {@code cellSetOpened} with the new cell
* set.
*
* <p>If changes are occurring regularly on the server, there will soon be a
* call to {@link #cellSetChanged}. It is important to note that this event
* contains only changes that have occurred since the new cell set was opened.
*
* <p>The granularity parameter is provided to {@link OlapStatement#addListener}
* for the server's benefit. If granularity is only {@link Granularity#COARSE},
* the server may be able to store less information in order to track the cell
* set.
*
* @version $Id: $
*/
public interface CellSetListener {

/**
* Invoked when a cell set is opened.
*
* @param cellSet Cell set
*/
void cellSetOpened(CellSet cellSet);

/**
* Invoked when a cell set is closed.
*
* @param cellSet Cell set
*/
void cellSetClosed(CellSet cellSet);

/**
* Invoked when a cell set has changed.
*
* @param cellSetChange Change descriptor
*/
void cellSetChanged(CellSetChange cellSetChange);

/**
* Granularity of notifications that should be sent to a cellset listener.
*/
enum Granularity {
FINE,
COARSE
}

/**
* Description of changes that have occurred to the cell set.
*/
interface CellSetChange {
/**
* Returns the cell set affected by this change.
*
* @return Cell set affected by this change.
*/
CellSet getCellSet();

/**
* Returns a list of cells that have changed, or null if the server
* cannot provide detailed changes.
*
* <p>The server is always at liberty to provide a {@code CellSetChange}
* without a detailed list of changes, even if
* {@link Granularity#COARSE} was specified when the listener was
* attached. Here are some typical reasons:<ul>
*
* <li>If there are very many changes. (Transmitting these changes over
* the network would be costly, and the user interface also might
* struggle to redisplay so many cells.)
*
* <li>If the axes have changed significantly. (If an axis position has
* changed, all of the cells at that position will necssarily have
* changed.)
*
* <li>If the client did not ask for detailed changes
*
* <li>If the the provider is not capable of giving detailed changes.
* </ul>
*/
List<CellChange> getCellChanges();

/**
* Returns a list of axis changes, or null if server cannot provide
* detailed changes.
*
* <p>The reasons why this method returns null are similar to the
* reasons why {@link #getCellChanges()} returns null.
*
* @return List of changes to positions on axes, or null if the server
* cannot provide detailed changes.
*/
List<AxisChange> getAxisChanges();
}

/**
* Description of a change to a particular {@link Cell}; part of a
* {@link CellSetChange}.
*/
interface CellChange {
/**
* Returns the cell before the change.
*/
Cell getBeforeCell();

/**
* Returns the cell after the change.
*/
Cell getAfterCell();
}

/**
* Description of a change to a particular {@link CellSetAxis}; part of a
* {@link CellSetChange}.
*/
interface AxisChange {
/**
* Returns the axis affected by this change.
*
* @return Axis affected by this change
*/
CellSetAxis getAxis();

/**
* Returns the position before the change. Null if the change created a
* new position.
*
* @return Position before the change, or null if the position is newly
* created
*/
Position getBeforePosition();

/**
* Returns the position after the change. Null if the change deleted
* this position.
*
* @return Position after the change, or null if the position is deleted
*/
Position getAfterPosition();
}
}

// End CellSetListener.java
15 changes: 15 additions & 0 deletions src/org/olap4j/OlapDatabaseMetaData.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,21 @@ public interface OlapDatabaseMetaData extends DatabaseMetaData, OlapWrapper {
*/
OlapConnection getConnection() throws SQLException;

/**
* Returns the granularity of changes to cell sets that the database is
* capable of providing.
*
* <p>It's optional whether an olap4j provider supports cellset listeners,
* and also optional which granularities it supports. If the provider does
* not support the cell set listener API, returns an empty set. Never
* returns null.
*
* @return set of the granularities that are supported when listening for
* changes to a cell set, never null
*/
Set<CellSetListener.Granularity> getSupportedCellSetListenerGranularities()
throws OlapException;

/**
* Retrieves a result set describing the Actions in this database.
*
Expand Down
28 changes: 28 additions & 0 deletions src/org/olap4j/OlapStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,34 @@ public interface OlapStatement extends Statement, OlapWrapper {
* or another thread cancels the statement (see {@link #cancel()})
*/
CellSet executeOlapQuery(SelectNode selectNode) throws OlapException;

/**
* Adds a listener to be notified of events to {@link CellSet}s created by
* this statement.
*
* <p>NOTE: You may wonder why this method belongs to the
* {@link OlapStatement} class and not {@code CellSet}. If the method
* belonged to {@code CellSet} there would be a window between creation and
* registering a listener during which events might be lost, whereas
* registering the listener with the statement ensures that the listener is
* attached immediately that the cell set is opened. It follows that
* registering a listener does not affect the cell set <em>currently
* open</em> (if any), and that no events will be received if the statement
* has no open cell sets.
*
* @param granularity Granularity of cell set events to listen for
*
* @param listener Listener to be notified of changes
*
* @throws OlapException if granularity is not one supported by this server,
* per the
* {@link OlapDatabaseMetaData#getSupportedCellSetListenerGranularities()}
* method
*/
void addListener(
CellSetListener.Granularity granularity,
CellSetListener listener)
throws OlapException;
}

// End OlapStatement.java
10 changes: 8 additions & 2 deletions src/org/olap4j/driver/xmla/XmlaOlap4jDatabaseMetaData.java
Original file line number Diff line number Diff line change
Expand Up @@ -957,7 +957,7 @@ public <T> T unwrap(Class<T> iface) throws SQLException {
*
* @return Error handler
*/
private final XmlaHelper getHelper() {
private XmlaHelper getHelper() {
return olap4jConnection.helper;
}

Expand All @@ -967,6 +967,13 @@ public boolean isWrapperFor(Class<?> iface) throws SQLException {

// implement OlapDatabaseMetaData

public Set<CellSetListener.Granularity>
getSupportedCellSetListenerGranularities()
throws OlapException
{
return Collections.emptySet();
}

public ResultSet getActions(
String catalog,
String schemaPattern,
Expand Down Expand Up @@ -1176,7 +1183,6 @@ public ResultSet getSets(
"SET_NAME", wildcard(setNamePattern));
}


/**
* Wrapper which indicates that a restriction is to be treated as a
* SQL-style wildcard match.
Expand Down
9 changes: 9 additions & 0 deletions src/org/olap4j/driver/xmla/XmlaOlap4jStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,15 @@ public CellSet executeOlapQuery(
return executeOlapQuery(mdx);
}

public void addListener(
CellSetListener.Granularity granularity,
CellSetListener listener)
throws OlapException
{
throw getHelper().createException(
"This driver does not support the cell listener API.");
}

/**
* Waits for an XMLA request to complete.
*
Expand Down

0 comments on commit 141d730

Please sign in to comment.