001: /*
002: * Copyright 2007 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt. The Original Code is the Pentaho
007: * BI Platform. The Initial Developer is Pentaho Corporation.
008: *
009: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
011: * the license for the specific language governing your rights and limitations.
012: */
013: package org.pentaho.plugin.mql;
014:
015: import java.sql.SQLException;
016: import java.util.Date;
017:
018: import org.apache.commons.logging.Log;
019: import org.apache.commons.logging.LogFactory;
020: import org.pentaho.actionsequence.dom.IActionInputValueProvider;
021: import org.pentaho.actionsequence.dom.actions.MQLAction;
022: import org.pentaho.commons.connection.IPentahoConnection;
023: import org.pentaho.commons.connection.IPentahoMetaData;
024: import org.pentaho.commons.connection.IPentahoResultSet;
025: import org.pentaho.core.audit.MessageTypes;
026: import org.pentaho.core.system.PentahoSystem;
027: import org.pentaho.data.PentahoConnectionFactory;
028: import org.pentaho.data.connection.sql.SQLConnection;
029: import org.pentaho.data.connection.sql.SQLResultSet;
030: import org.pentaho.di.core.database.DatabaseInterface;
031: import org.pentaho.di.core.database.DatabaseMeta;
032: import org.pentaho.messages.Messages;
033: import org.pentaho.messages.util.LocaleHelper;
034: import org.pentaho.plugin.sql.SQLLookupRule;
035: import org.pentaho.pms.core.exception.PentahoMetadataException;
036: import org.pentaho.pms.factory.CwmSchemaFactoryInterface;
037: import org.pentaho.pms.mql.MQLQuery;
038: import org.pentaho.pms.mql.MQLQueryFactory;
039: import org.pentaho.pms.mql.MappedQuery;
040:
041: public class MQLRelationalDataComponent extends SQLLookupRule {
042:
043: private static final long serialVersionUID = -6376955619869902045L;
044:
045: private MQLQuery mqlQuery;
046: private MappedQuery mappedQuery;
047:
048: // if true, skip the attempt to load the metadata source within the
049: // getConnection() method. This is used as part of the detection
050: // and overriding of the metadata.xmi database dialect.
051: private boolean skipMetadataDatasource = false;
052:
053: public MQLQuery getMqlQuery() {
054: return mqlQuery;
055: }
056:
057: public Log getLogger() {
058: return LogFactory.getLog(MQLRelationalDataComponent.class);
059: }
060:
061: private boolean initialize() {
062: return true;
063: }
064:
065: public boolean validateAction() {
066:
067: boolean result = true;
068: if (!(getActionDefinition() instanceof MQLAction)) {
069: error(Messages
070: .getErrorString(
071: "ComponentBase.ERROR_0001_UNKNOWN_ACTION_TYPE", getActionDefinition().getElement().asXML())); //$NON-NLS-1$
072: result = false;
073: } else if (!initialize()) {
074: result = false;
075: } else {
076: MQLAction mqlAction = (MQLAction) getActionDefinition();
077: IActionInputValueProvider query = mqlAction.getQuery();
078: result = (query != IActionInputValueProvider.NULL_INPUT);
079: }
080:
081: return result;
082: }
083:
084: /**
085: * makes the necessary calls to generate the SQL query based on the MQL XML provided.
086: *
087: * @return sql
088: */
089: public String getQuery() {
090: MQLAction mqlAction = (MQLAction) getActionDefinition();
091: String mql = mqlAction.getQuery().getStringValue();
092: String mqlQueryClassName = mqlAction.getMqlQueryClassName()
093: .getStringValue();
094: if (mql != null) {
095: if (debug) {
096: debug(Messages
097: .getString(
098: "MQLRelationalDataComponent.DEBUG_DISPLAY_MQL", mql)); //$NON-NLS-1$
099: }
100:
101: //GEM PMD-175 Display names no longer a legit param for this ocmponent
102: // boolean displayNames = this.getInputBooleanValue("display-names", true); //$NON-NLS-1$
103:
104: MetadataPublisher.loadMetadata(getSolutionName(),
105: getSession(), false);
106: CwmSchemaFactoryInterface cwmSchemaFactory = (CwmSchemaFactoryInterface) PentahoSystem
107: .getObject(getSession(), "ICwmSchemaFactory"); //$NON-NLS-1$
108: try {
109: if (mqlQueryClassName != null) {
110: mqlQuery = MQLQueryFactory.getMQLQuery(
111: mqlQueryClassName, mql, null, LocaleHelper
112: .getLocale().toString(),
113: cwmSchemaFactory);
114: } else {
115: mqlQuery = MQLQueryFactory.getMQLQuery(mql, null,
116: LocaleHelper.getLocale().toString(),
117: cwmSchemaFactory);
118: }
119:
120: // detect the actual db dialect and apply it to the MQLQuery if different from the XMI dialect
121: if (!mqlAction.getForceDbDialect().getBooleanValue(
122: false)) {
123: // retrieve a temporary connection to determine if a dialect change is necessary
124: // for generating the MQL Query.
125: SQLConnection tempConnection = (SQLConnection) getConnection();
126: try {
127:
128: // if the connection type is not of the current dialect, regenerate the query
129: DatabaseInterface di = getDatabaseInterface(tempConnection);
130:
131: if (di != null
132: && mqlQuery.getDatabaseMeta()
133: .getDatabaseType() != di
134: .getDatabaseType()) {
135: // we need to reinitialize our mqlQuery object and reset the query.
136: // note that using this di object wipes out connection info
137: DatabaseMeta meta = (DatabaseMeta) mqlQuery
138: .getDatabaseMeta().clone();
139:
140: DatabaseInterface di2 = (DatabaseInterface) di
141: .clone();
142:
143: di2.setAccessType(mqlQuery
144: .getDatabaseMeta().getAccessType());
145: di2.setDatabaseName(mqlQuery
146: .getDatabaseMeta()
147: .getDatabaseName());
148:
149: meta.setDatabaseInterface(di2);
150: if (mqlQueryClassName != null) {
151: mqlQuery = MQLQueryFactory.getMQLQuery(
152: mqlQueryClassName, mql, meta,
153: LocaleHelper.getLocale()
154: .toString(),
155: cwmSchemaFactory);
156: } else {
157: mqlQuery = MQLQueryFactory.getMQLQuery(
158: mql, meta,
159: LocaleHelper.getLocale()
160: .toString(),
161: cwmSchemaFactory);
162: }
163: // don't attempt to use the metadata's connection info when retrieving a connection in the
164: // future. because of the dialect change, the connection info is now invalid.
165: skipMetadataDatasource = true;
166: }
167: } finally {
168: tempConnection.close();
169: }
170: }
171:
172: if (mqlAction.getDisableDistinct() != IActionInputValueProvider.NULL_INPUT) {
173: mqlQuery.setDisableDistinct(mqlAction
174: .getDisableDistinct().getBooleanValue()
175: .booleanValue());
176: }
177:
178: mappedQuery = mqlQuery.getQuery();
179: String sqlQuery = mappedQuery.getQuery();
180: if (debug) {
181: debug(Messages
182: .getString(
183: "MQLRelationalDataComponent.DEBUG_DISPLAY_SQL", sqlQuery)); //$NON-NLS-1$
184: }
185: return sqlQuery;
186: } catch (PentahoMetadataException e) {
187: error(
188: Messages
189: .getErrorString(
190: "SQLBaseComponent.ERROR_0006_EXECUTE_FAILED", getActionName()), e); //$NON-NLS-1$
191: }
192: } else {
193: error(Messages
194: .getErrorString(
195: "MQLRelationalDataComponent.ERROR_0001_QUERY_XML_EMPTY", getActionName())); //$NON-NLS-1$
196: }
197: return null;
198: }
199:
200: public boolean executeAction() {
201:
202: long start = new Date().getTime();
203:
204: boolean result = super .executeAction();
205:
206: MQLAction actionDefinition = (MQLAction) getActionDefinition();
207:
208: long end = new Date().getTime();
209: // Fix for BISERVER-459 - MQL can be too large for the audit message column.
210: // audit(MessageTypes.INSTANCE_ATTRIBUTE, "metadata query", mql, (int) (end - start)); //$NON-NLS-1$
211: //
212: audit(
213: MessageTypes.INSTANCE_ATTRIBUTE,
214: "metadata query action sequence", this .getActionName(), (int) (end - start)); //$NON-NLS-1$
215: // Use debug logging instead
216: trace(actionDefinition.getQuery().getStringValue());
217: return result;
218:
219: }
220:
221: protected IPentahoConnection getConnection() {
222: // use the connection specified in the query.
223:
224: IPentahoConnection localConnection = null;
225: if (mqlQuery != null) {
226: try {
227: DatabaseMeta databaseMeta = mqlQuery.getDatabaseMeta();
228: if (databaseMeta.getAccessType() == DatabaseMeta.TYPE_ACCESS_JNDI) {
229: String jndiName = databaseMeta.getDatabaseName();
230: if (jndiName != null) {
231: localConnection = PentahoConnectionFactory
232: .getConnection(
233: IPentahoConnection.SQL_DATASOURCE,
234: jndiName, this );
235: }
236: }
237: if (localConnection == null && !skipMetadataDatasource) {
238: String driver = databaseMeta.getDriverClass();
239: String userId = databaseMeta.getUsername();
240: String password = databaseMeta.getPassword();
241: String connectionInfo = databaseMeta.getURL();
242: if (driver == null && connectionInfo == null) {
243: // TODO raise an error
244: }
245: localConnection = PentahoConnectionFactory
246: .getConnection(
247: IPentahoConnection.SQL_DATASOURCE,
248: driver, connectionInfo, userId,
249: password, this );
250: }
251: // try the parent to allow the connection to be overridden
252: localConnection = getConnection(localConnection);
253: return localConnection;
254: } catch (Exception e) {
255: error(
256: Messages
257: .getErrorString(
258: "SQLBaseComponent.ERROR_0006_EXECUTE_FAILED", getActionName()), e); //$NON-NLS-1$
259: }
260: }
261: return null;
262: }
263:
264: /**
265: * determines the PDI database interface of a given connection object
266: *
267: * @param conn
268: * @return
269: */
270: protected DatabaseInterface getDatabaseInterface(SQLConnection conn) {
271: String prod = null;
272: try {
273: prod = conn.getNativeConnection().getMetaData()
274: .getDatabaseProductName();
275:
276: if (prod == null) {
277: return null;
278: }
279:
280: prod = prod.toLowerCase();
281:
282: // special case to map hsql to hypersonic
283: if (prod.indexOf("hsql") >= 0) { //$NON-NLS-1$
284: prod = "hypersonic"; //$NON-NLS-1$
285: }
286:
287: // look through all available database dialects for a match
288: for (int i = 0; i < DatabaseMeta.getDatabaseInterfaces().length; i++) {
289: String typeDesc = DatabaseMeta.getDatabaseInterfaces()[i]
290: .getDatabaseTypeDesc().toLowerCase();
291: if (prod.indexOf(typeDesc) >= 0) {
292: return DatabaseMeta.getDatabaseInterfaces()[i];
293: }
294: }
295:
296: warn(Messages
297: .getString(
298: "MQLRelationalDataComponent.WARN_0001_NO_DIALECT_DETECTED", prod)); //$NON-NLS-1$
299:
300: } catch (SQLException e) {
301: warn(
302: Messages
303: .getString(
304: "MQLRelationalDataComponent.WARN_0002_DIALECT_EXCEPTION", prod), e); //$NON-NLS-1$
305: }
306: return null;
307: }
308:
309: protected IPentahoMetaData getMetadata(IPentahoResultSet resultSet,
310: boolean live) {
311: IPentahoMetaData metadata = mappedQuery
312: .generateMetadata(resultSet.getMetaData());
313: ((SQLResultSet) resultSet).setMetaData(metadata);
314: return metadata;
315:
316: }
317: }
|