📄 mondrianmodel.java
字号:
/*
* ====================================================================
* 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) 2003-2004 TONBELLER AG.
* All Rights Reserved.
* You must accept the terms of that agreement to use this software.
* ====================================================================
*
*
*/
package com.tonbeller.jpivot.mondrian;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.sql.DataSource;
import mondrian.olap.Category;
import mondrian.olap.Cube;
import mondrian.olap.Exp;
import mondrian.olap.Formula;
import mondrian.olap.FunCall;
import mondrian.olap.Literal;
import mondrian.olap.MondrianException;
import mondrian.olap.Query;
import mondrian.olap.QueryAxis;
import mondrian.olap.Role;
import mondrian.olap.SchemaReader;
import mondrian.olap.Syntax;
import mondrian.olap.Util;
import mondrian.olap.fun.FunTableImpl.MemberListScalarExp;
import mondrian.olap.fun.FunTableImpl.MemberScalarExp;
import mondrian.olap.fun.FunTableImpl.TupleScalarExp;
import mondrian.rolap.RolapConnection;
import mondrian.rolap.RolapConnectionProperties;
import org.apache.log4j.Logger;
import com.tonbeller.jpivot.core.Extension;
import com.tonbeller.jpivot.core.ModelChangeEvent;
import com.tonbeller.jpivot.core.ModelChangeListener;
import com.tonbeller.jpivot.olap.model.Dimension;
import com.tonbeller.jpivot.olap.model.Member;
import com.tonbeller.jpivot.olap.model.OlapException;
import com.tonbeller.jpivot.olap.model.OlapModel;
import com.tonbeller.jpivot.olap.model.Result;
import com.tonbeller.jpivot.olap.navi.SortRank;
import com.tonbeller.jpivot.olap.query.ExpBean;
import com.tonbeller.jpivot.olap.query.MdxOlapModel;
import com.tonbeller.jpivot.olap.query.Memento;
import com.tonbeller.jpivot.olap.query.PositionNodeBean;
import com.tonbeller.jpivot.olap.query.QueryAdapter;
import com.tonbeller.wcf.bookmarks.Bookmarkable;
/**
* The Model represents all (meta-)data for an MDX query.
*/
public class MondrianModel extends MdxOlapModel implements OlapModel,
QueryAdapter.QueryAdapterHolder {
static Logger logger = Logger.getLogger(MondrianModel.class);
/*
* sample value
* provider=Mondrian;Jdbc=jdbc:odbc:MondrianFoodMart;Catalog=file:///c:/dev/mondrian/demo/FoodMart.xml
*/
private String connectString = null;
/*
* sample values sun.jdbc.odbc.JdbcOdbcDriver com.mysql.jdbc.Driver
*/
private String jdbcDriver = null;
private mondrian.olap.Connection monConnection = null;
/**
* the initial MDX query. This is never changed except when the user enters a new MDX query.
*/
private String mdxQuery;
private String currentMdx;
private MondrianResult result = null;
private HashMap hDimensions = new HashMap();
private HashMap hHierarchies = new HashMap();
private HashMap hLevels = new HashMap();
private HashMap hMembers = new HashMap();
private ArrayList aMeasures = new ArrayList();
private MondrianQueryAdapter queryAdapter = null;
private boolean isInitialized = false;
private String ID = null;
private Locale loc = null;
private String sessionId = null;
private String dynresolver = null;
// selected locale to be used by dynResolver (if given)
private String dynLocale = null;;
private boolean connectionPooling = true; // Mondrian connection Pooling
private DataSource externalDataSource = null;
private ServletContext servletContext = null;
private Object bookMark = null;
public String getID() {
return ID;
}
public void setID(String ID) {
this.ID = ID;
}
/**
* constructor must be "default"
*/
public MondrianModel() {
this.mdxQuery = null;
this.currentMdx = null;
addModelChangeListener(new ModelChangeListener() {
public void modelChanged(ModelChangeEvent e) {
result = null; // will force re-execution of query
}
public void structureChanged(ModelChangeEvent e) {
result = null; // will force re-execution of query
}
});
}
/**
* Returns the queryAdapter.
*
* @return MondrianQueryAdapter
*/
public QueryAdapter getQueryAdapter() {
return queryAdapter;
}
/**
* Let Mondrian parse and execute the query
*
* @see com.tonbeller.jpivot.olap.model.OlapModel#getResult()
* @return Result of Query Execution
*/
public synchronized Result getResult() throws OlapException {
if (result != null)
return result;
if (!isInitialized) {
//logger.fatal(constructError);
throw new OlapException("Model not initialized");
}
queryAdapter.onExecute();
mondrian.olap.Result monResult = null;
boolean tryagain = false;
try {
long t1 = System.currentTimeMillis();
monResult = monConnection.execute(queryAdapter.getMonQuery());
long t2 = System.currentTimeMillis();
if (logger.isInfoEnabled())
logger.info("query execution time " + (t2 - t1) + " ms");
} catch (MondrianException ex) {
Throwable rootCause = getRootCause(ex);
if (rootCause instanceof mondrian.olap.ResultLimitExceeded) {
// the result limit was exceeded - roll back
logger.warn("Mondrian result limit exceeded: " + rootCause.getMessage());
if (bookMark != null) {
setBookmarkState(bookMark);
tryagain = true;
}
} else if (rootCause instanceof mondrian.olap.InvalidHierarchy) {
// there was no member for an hierarchy
logger.warn("Mondrian Hierarchy with no members: " + rootCause.getMessage());
throw new EmptyCubeException(rootCause);
} else {
// the cause of the exception is different from "Result Limit Exceeded"
throw new OlapException(ex);
}
if (!tryagain)
throw new ResultTooLargeException(ex);
}
if (tryagain) {
// roll back to bookmark occurred
// a Result Limit Overflow will not occur here
try {
long t1 = System.currentTimeMillis();
monResult = monConnection.execute(queryAdapter.getMonQuery());
long t2 = System.currentTimeMillis();
if (logger.isInfoEnabled())
logger.info("rollback query execution time " + (t2 - t1) + " ms");
} catch (MondrianException ex) {
// should not occur
// throw ResultTooLargeException because this was the original problem
throw new ResultTooLargeException(
"Error running previous query (prior to Result Overflow)", ex);
}
}
result = new MondrianResult(monResult, this);
if (tryagain)
result.setOverflowOccured(true);
queryAdapter.afterExecute(result);
// set a bookmark, so that we can roll back to that state
if (!tryagain)
bookMark = getBookmarkState(EXTENSIONAL);
return result;
}
/**
* get the result variable without any action
*
* @return current Mondrian result, or null
*/
MondrianResult currentResult() {
return result;
}
/**
* @see com.tonbeller.jpivot.olap.model.OlapModel#getDimensions()
*/
public Dimension[] getDimensions() {
return (Dimension[]) hDimensions.values().toArray(new Dimension[0]);
}
/**
* @see com.tonbeller.jpivot.olap.model.OlapModel#getMeasures()
*/
public Member[] getMeasures() {
return (Member[]) aMeasures.toArray(new Member[0]);
}
/**
* set the Mondrian Connect String
*
* @param connectString
* Connect String - default:
* provider=Mondrian;Jdbc=jdbc:odbc:MondrianFoodMart;
* Catalog=file:///c:/j/mondrian/demo/FoodMart.xml
*/
public void setConnectString(String connectString) {
this.connectString = connectString;
result = null;
queryAdapter = null;
monConnection = null;
if (logger.isInfoEnabled())
logger.info("connectString=" + connectString);
}
/**
* set the JDBC Driver
*
* @param jdbcDriver
* JDBC Driver - default: sun.jdbc.odbc.JdbcOdbcDriver
*/
public void setJdbcDriver(String jdbcDriver) {
this.jdbcDriver = jdbcDriver;
result = null;
queryAdapter = null;
monConnection = null;
if (logger.isInfoEnabled())
logger.info("jdbcDriver=" + jdbcDriver);
}
/**
* Sets the mdxQuery.
*
* @param mdxQuery
* The mdxQuery to set
*/
public void setMdxQuery(String mdxQuery) {
if (logger.isInfoEnabled())
logger.info("setMdxQuery:" + mdxQuery);
this.mdxQuery = mdxQuery;
this.currentMdx = mdxQuery.replaceAll("\r", "");
result = null;
queryAdapter = null;
}
/**
* complete the initilization.
*/
public void initialize() throws OlapException {
logger.info(this);
boolean logInfo = logger.isInfoEnabled();
// load the jdbc Driver
if (jdbcDriver != null) {
try {
Class.forName(jdbcDriver);
} catch (Exception ex) {
String err = "Could not load Jdbc Driver " + jdbcDriver;
logger.error(err);
throw new OlapException(err);
}
}
Util.PropertyList properties = Util.parseConnectString(connectString);
// get the Catalog from connect string
String catString = properties.get("Catalog");
URI uri = null;
try {
uri = new URI(catString);
} catch (URISyntaxException e) {
//throw new IllegalArgumentException("Illegal Schema Url " + catString );
// ignore;
}
if (uri != null && uri.getScheme().equalsIgnoreCase("http") && sessionId != null) {
// an http schema url will be dynamically resolved
// in that case, a session id has to be appended
if (uri.getQuery() != null) {
catString = catString + "&sessionId=" + sessionId;
} else {
catString = catString + "?sessionId=" + sessionId;
}
properties.put(RolapConnectionProperties.Catalog, catString);
}
if (dynresolver != null && dynresolver.length() > 0)
properties.put(RolapConnectionProperties.DynamicSchemaProcessor, dynresolver);
if (dynLocale!=null)
properties.put(RolapConnectionProperties.Locale, dynLocale);
// if we do *not* want connection pooling, we must explicitly tell Mondrian
if (!connectionPooling) {
properties.put(RolapConnectionProperties.PoolNeeded, "false");
}
// use external DataSource if present
monConnection = mondrian.olap.DriverManager.getConnection(properties, servletContext,
externalDataSource, false);
//monConnection = mondrian.olap.DriverManager.getConnection(connectString,
// null, false);
if (monConnection == null) {
String err = "Could not create Mondrian connection:" + connectString;
logger.error(err);
throw new OlapException(err);
}
if (logInfo)
logger.info("MondrianModel: opening connection " + connectString);
// do we have a special locale setting?
// if yes, promote it to the connection
loc = getLocale(); // Locale.GERMANY
if (loc != null) {
if (logInfo) {
String msg = "Locale language=" + loc.getLanguage() + " Country=" + loc.getCountry();
logger.info(msg);
}
((RolapConnection) monConnection).setLocale(loc);
}
mondrian.olap.Query monQuery = null;
try {
monQuery = parseMDX();
} catch (OlapException e) {
String err = e.getMessage();
logger.error(err);
throw new OlapException(err);
}
resetMetaData(monQuery); // reset the model data
queryAdapter = new MondrianQueryAdapter(this, monQuery);
MondrianSortRank sortExt = (MondrianSortRank) getExtension(SortRank.ID);
if (sortExt != null)
sortExt.reset();
isInitialized = true;
// as initialization is complete, notify extensions
Map extMap = getExtensions();
Collection extensions = extMap.values();
for (Iterator iter = extensions.iterator(); iter.hasNext();) {
Extension extension = (Extension) iter.next();
extension.modelInitialized();
}
}
/**
* parse
*
* @return @throws
* OlapException
*/
private mondrian.olap.Query parseMDX() throws OlapException {
mondrian.olap.Query monQuery = null;
try {
monQuery = getConnection().parseQuery(mdxQuery);
} catch (MondrianException ex) {
logger.error("Parse Failure", ex);
// try to get a meaningfullerror message on parse failure
Throwable rootCause = getRootCause(ex);
throw new OlapException(rootCause.getMessage());
} catch (Exception ex) {
// not expected
logger.fatal("unexpected parse failure " + ex.getMessage());
throw new OlapException(ex);
}
if (monQuery == null) {
logger.fatal("unexpected parse failure");
throw new OlapException("unexpected parse failure");
}
return monQuery;
}
/**
* find root cause for exception
*/
private Throwable getRootCause(MondrianException ex) {
Throwable rootCause = ex;
Throwable cause = ex.getCause();
while (cause != null && cause != rootCause) {
rootCause = cause;
cause = cause.getCause();
}
return rootCause;
}
/**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -