001: /*
002: * Copyright 2006 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: * Created Sep 21, 2005
014: * @author wseyler
015: */
016:
017: package org.pentaho.plugin.mdx;
018:
019: import java.io.File;
020: import java.util.Map;
021: import java.util.Properties;
022:
023: import org.apache.commons.logging.Log;
024: import org.pentaho.actionsequence.dom.ActionOutput;
025: import org.pentaho.actionsequence.dom.IActionInputValueProvider;
026: import org.pentaho.actionsequence.dom.actions.MdxConnectionAction;
027: import org.pentaho.actionsequence.dom.actions.MdxQueryAction;
028: import org.pentaho.core.component.IDataComponent;
029: import org.pentaho.core.component.IPreparedComponent;
030: import org.pentaho.commons.connection.IPentahoConnection;
031: import org.pentaho.commons.connection.IPentahoResultSet;
032: import org.pentaho.core.solution.IActionResource;
033: import org.pentaho.core.system.PentahoSystem;
034: import org.pentaho.core.util.DatasourceHelper;
035: import org.pentaho.core.util.MapParameterResolver;
036: import org.pentaho.core.util.TemplateUtil;
037: import org.pentaho.data.PentahoConnectionFactory;
038: import org.pentaho.messages.Messages;
039: import org.pentaho.plugin.ComponentBase;
040:
041: public abstract class MDXBaseComponent extends ComponentBase implements
042: IDataComponent, IPreparedComponent {
043:
044: private IPentahoResultSet rSet;
045:
046: /** is set to false if using another IPreparedComponents connection vs own */
047: private boolean connectionOwner = true;
048:
049: /** keep a reference to the connection for prepared component functionality */
050: private IPentahoConnection connection;
051:
052: /** stores the prepared query for later use */
053: String preparedQuery = null;
054:
055: public abstract boolean validateSystemSettings();
056:
057: public abstract Log getLogger();
058:
059: public IPentahoResultSet getResultSet() {
060: return rSet;
061: }
062:
063: protected boolean validateAction() {
064: boolean actionValidated = true;
065: MdxQueryAction queryAction = null;
066: MdxConnectionAction connAction = null;
067:
068: try {
069: if (getActionDefinition() instanceof MdxQueryAction) {
070: queryAction = (MdxQueryAction) getActionDefinition();
071: actionValidated = isConnectionInfoSpecified(queryAction);
072:
073: if (actionValidated) {
074: if (queryAction.getQuery() == IActionInputValueProvider.NULL_INPUT) {
075: error(Messages
076: .getErrorString(
077: "MDXBaseComponent.ERROR_0001_QUERY_NOT_SPECIFIED", getActionName())); //$NON-NLS-1$
078: actionValidated = false;
079: }
080: }
081:
082: if (actionValidated) {
083: if (queryAction.getOutputResultSet() == null
084: && queryAction.getOutputPreparedStatement() == null) {
085: error(Messages
086: .getErrorString(
087: "MDXBaseComponent.ERROR_0003_OUTPUT_NOT_SPECIFIED", getActionName())); //$NON-NLS-1$
088: actionValidated = false;
089: }
090: }
091: } else if (getActionDefinition() instanceof MdxConnectionAction) {
092: connAction = (MdxConnectionAction) getActionDefinition();
093: actionValidated = isConnectionInfoSpecified(connAction);
094: if (connAction.getOutputConnection() == null) {
095: error(Messages
096: .getErrorString(
097: "MDXBaseComponent.ERROR_0003_OUTPUT_NOT_SPECIFIED", getActionName())); //$NON-NLS-1$
098: actionValidated = false;
099: }
100:
101: }
102: } catch (Exception e) {
103: actionValidated = false;
104: error(
105: Messages
106: .getErrorString(
107: "MDXBaseComponent.ERROR_0004_VALIDATION_FAILED", getActionName()), e); //$NON-NLS-1$
108: }
109:
110: return actionValidated;
111: }
112:
113: /*
114: *
115: */
116: private boolean isConnectionInfoSpecified(
117: MdxConnectionAction connAction) {
118: boolean value = true;
119:
120: if (connAction instanceof MdxQueryAction) {
121: if (connAction.getConnection() == IActionInputValueProvider.NULL_INPUT
122: && connAction.getMdxConnectionString() == null
123: && connAction.getJndi() == IActionInputValueProvider.NULL_INPUT
124: && connAction.getConnectionProps() == IActionInputValueProvider.NULL_INPUT
125: && ((MdxQueryAction) connAction).getMdxConnection() == IActionInputValueProvider.NULL_INPUT) {
126: error(Messages
127: .getErrorString(
128: "MDXBaseComponent.ERROR_0002_CONNECTION_NOT_SPECIFIED", getActionName())); //$NON-NLS-1$
129: value = false;
130: }
131: } else if (connAction instanceof MdxConnectionAction) {
132: if (connAction.getConnection() == IActionInputValueProvider.NULL_INPUT
133: && connAction.getMdxConnectionString() == null
134: && connAction.getJndi() == IActionInputValueProvider.NULL_INPUT
135: && connAction.getConnectionProps() == IActionInputValueProvider.NULL_INPUT) {
136: error(Messages
137: .getErrorString(
138: "MDXBaseComponent.ERROR_0002_CONNECTION_NOT_SPECIFIED", getActionName())); //$NON-NLS-1$
139: value = false;
140: }
141: }
142:
143: return value;
144: }
145:
146: public void done() {
147: }
148:
149: protected boolean executeAction() {
150: boolean value = false;
151: /*
152: * This is the query part. You would need a connection to execute the query.
153: * The connection will either come in as an INPUT (prepared_component) or
154: * will be specified right there.
155: *
156: * So check if a prepared component exists, if not create a new connection.
157: * If connection is not null, proceed to work on the query part.
158: *
159: * In the query section you can either execute the query right away or prepare it to
160: * be used later by a sub report.
161: */
162: try {
163: if (getActionDefinition() instanceof MdxQueryAction) {
164: MdxQueryAction queryAction = (MdxQueryAction) getActionDefinition();
165: // if there is a prepared component specified as an input, use its connection
166: // instead of creating our own.
167: if (queryAction.getMdxConnection() != IActionInputValueProvider.NULL_INPUT) {
168: connectionOwner = false;
169: IPreparedComponent component = (IPreparedComponent) getInputValue(PREPARED_COMPONENT_NAME);
170: IPentahoConnection conn = component
171: .shareConnection();
172: if (conn.getDatasourceType() == IPentahoConnection.MDX_DATASOURCE) {
173: connection = conn;
174: } else {
175: error(Messages
176: .getErrorString(
177: "IPreparedComponent.ERROR_0001_INVALID_CONNECTION_TYPE", getActionName())); //$NON-NLS-1$
178: }
179: } else {
180: dispose();
181: connection = getDatasourceConnection();
182: }
183:
184: if (connection != null) {
185: String query = queryAction.getQuery()
186: .getStringValue();
187: if (queryAction.getOutputPreparedStatement() != null) {
188: // prepare the query for execution, but don't execute quite yet.
189: prepareQuery(query);
190: // set the output as self, which will be used later by another component.
191: setOutputValue(PREPARED_COMPONENT_NAME, this );
192: value = true;
193: } else {
194: value = runQuery(connection, query);
195: }
196: } else {
197: error(Messages
198: .getErrorString(
199: "No connection info specified or could not establish a connection to the database.", getActionName())); //$NON-NLS-1$
200: }
201: } else if (getActionDefinition() instanceof MdxConnectionAction) {
202: dispose();
203: connection = getDatasourceConnection();
204:
205: if (connection != null) {
206: setOutputValue(PREPARED_COMPONENT_NAME, this );
207: value = true;
208: }
209: } else /**/{
210: error(Messages.getErrorString(
211: "Invalid MDX component.", getActionName())); //$NON-NLS-1$
212: }
213: } catch (Exception e) {
214: error(
215: Messages
216: .getErrorString(
217: "MDXBaseComponent.ERROR_0006_EXECUTE_FAILED", getActionName()), e); //$NON-NLS-1$
218: }
219:
220: return value;
221: }
222:
223: /**
224: * called when in prepared-component mode, this method populates the preparedQuery string and
225: * preparedParameters object.
226: *
227: * @param rawQuery
228: * @return
229: */
230: protected boolean prepareQuery(String rawQuery) {
231:
232: try {
233: if (connection == null) {
234: error(Messages
235: .getErrorString("MDXBaseComponent.ERROR_0007_NO_CONNECTION")); //$NON-NLS-1$
236: return false;
237: }
238: if (!connection.initialized()) {
239: error(Messages
240: .getErrorString("MDXBaseComponent.ERROR_0007_NO_CONNECTION")); //$NON-NLS-1$
241: return false;
242: }
243:
244: if (rawQuery != null) {
245: preparedQuery = applyInputsToFormat(rawQuery);
246: }
247:
248: return true;
249: } catch (Exception e) {
250: error(
251: Messages
252: .getErrorString(
253: "MDXBaseComponent.ERROR_0006_EXECUTE_FAILED", getActionName()), e); //$NON-NLS-1$
254: }
255:
256: return false;
257: }
258:
259: /**
260: * if the owner, dispose of the connection
261: */
262: public void dispose() {
263: if (connectionOwner) {
264: if (connection != null) {
265: connection.close();
266: }
267: }
268: connection = null;
269: }
270:
271: /**
272: * return this class's connection. This implements the IPreparedComponent
273: * interface, which may share its connection with others.
274: *
275: * @return connection object
276: */
277: public IPentahoConnection shareConnection() {
278: return connection;
279: }
280:
281: /**
282: * executes a prepared method that returns a result set
283: * executePrepared looks up any "PREPARELATER" params
284: * in the preparedParams map.
285: *
286: * @param preparedParams a map of possible parameters.
287: * @return result set
288: */
289: public IPentahoResultSet executePrepared(Map preparedParams) {
290: try {
291: if (connection == null) {
292: error(Messages
293: .getErrorString("MDXBaseComponent.ERROR_0007_NO_CONNECTION")); //$NON-NLS-1$
294: return null;
295: }
296: if (!connection.initialized()) {
297: error(Messages
298: .getErrorString("MDXBaseComponent.ERROR_0007_NO_CONNECTION")); //$NON-NLS-1$
299: return null;
300: }
301: if (preparedQuery == null) {
302: error(Messages
303: .getErrorString(
304: "MDXBaseComponent.ERROR_0001_QUERY_NOT_SPECIFIED", getActionName())); //$NON-NLS-1$
305: return null;
306: }
307:
308: // parse preparedQuery, replacing any {PREPARELATER:NAME} with appropriate values
309: String query = TemplateUtil.applyTemplate(preparedQuery,
310: getRuntimeContext(), new MapParameterResolver(
311: preparedParams, PREPARE_LATER_PREFIX,
312: getRuntimeContext()));
313:
314: if (debug)
315: debug(Messages.getString(
316: "MDXBaseComponent.DEBUG_RUNNING_QUERY", query)); //$NON-NLS-1$
317:
318: // evaluate
319: IPentahoResultSet resultSet = connection
320: .executeQuery(query);
321: rSet = resultSet;
322: return resultSet;
323: } catch (Exception e) {
324: error(
325: Messages
326: .getErrorString(
327: "MDXBaseComponent.ERROR_0006_EXECUTE_FAILED", getActionName()), e); //$NON-NLS-1$
328: }
329: return null;
330: }
331:
332: protected boolean runQuery(IPentahoConnection localConnection,
333: String rawQuery) {
334:
335: try {
336: if (localConnection == null) {
337: error(Messages
338: .getErrorString("MDXBaseComponent.ERROR_0008_NO_CONNECTION")); //$NON-NLS-1$
339: return false;
340: }
341: if (!localConnection.initialized()) {
342: error(Messages
343: .getErrorString("MDXBaseComponent.ERROR_0008_NO_CONNECTION")); //$NON-NLS-1$
344: return false;
345: }
346:
347: if (debug)
348: debug(Messages
349: .getString(
350: "MDXBaseComponent.DEBUG_RUNNING_QUERY", rawQuery)); //$NON-NLS-1$
351:
352: // execute the query, read the results and cache them
353: IPentahoResultSet resultSet = localConnection
354: .executeQuery(rawQuery);
355: rSet = resultSet;
356: if (resultSet != null) {
357: MdxQueryAction mdxQueryAction = (MdxQueryAction) getActionDefinition();
358: ActionOutput actionOutput = mdxQueryAction
359: .getOutputResultSet();
360: if (actionOutput != null) {
361: actionOutput.setValue(resultSet);
362: }
363: return true;
364: } else {
365: // close the connection
366: error(Messages
367: .getErrorString(
368: "MDXBaseComponent.ERROR_0006_EXECUTE_FAILED", getActionName())); //$NON-NLS-1$
369: localConnection.close();
370: return false;
371: }
372:
373: } catch (Exception e) {
374: error(
375: Messages
376: .getErrorString(
377: "MDXBaseComponent.ERROR_0006_EXECUTE_FAILED", getActionName()), e); //$NON-NLS-1$
378: }
379:
380: return false;
381: }
382:
383: /**
384: * attempt to aquire a connection. if connection isn't available, wait a certain period of time
385: * before trying again.
386: *
387: * @return connection
388: */
389: public IPentahoConnection getDatasourceConnection() {
390: IPentahoConnection con;
391: int timeouts[] = { 200, 500, 2000 };
392: for (int i = 0; i < timeouts.length; i++) {
393: try {
394: con = getConnection();
395: try {
396: con.clearWarnings();
397: } catch (Exception ex) {
398: }
399: return con;
400: } catch (Exception ex) {
401: }
402: waitFor(timeouts[i]);
403: }
404: con = getConnection();
405: try {
406: con.clearWarnings();
407: } catch (Exception ex) {
408: }
409: return con;
410: }
411:
412: protected void waitFor(int millis) {
413: try {
414: if (debug)
415: debug(Messages
416: .getString(
417: "MDXBaseComponent.DEBUG_WAITING_FOR_CONNECTION", Integer.toString(millis))); //$NON-NLS-1$
418: Thread.sleep(millis);
419: } catch (Exception ex) {
420: // ignore the interrupted exception, if it happens
421: }
422: }
423:
424: protected IPentahoConnection getConnection() {
425: IPentahoConnection localConnection = null;
426: MdxConnectionAction connAction = (MdxConnectionAction) getActionDefinition();
427: try {
428: String mdxConnectionStr = connAction
429: .getMdxConnectionString().getStringValue();
430: Properties mdxConnectionProps = (Properties) connAction
431: .getConnectionProps().getValue();
432: String jdbcStr = connAction.getConnection()
433: .getStringValue();
434: String jndiStr = connAction.getJndi().getStringValue();
435: String location = connAction.getLocation().getStringValue();
436: String role = connAction.getRole().getStringValue();
437: String catalog = connAction.getCatalog().getStringValue();
438:
439: if (catalog == null
440: && connAction.getCatalogResource() != null) {
441: IActionResource resource = getResource(connAction
442: .getCatalogResource().getName());
443: catalog = resource.getAddress();
444: if (resource.getSourceType() == IActionResource.SOLUTION_FILE_RESOURCE) {
445: catalog = PentahoSystem.getApplicationContext()
446: .getSolutionPath(catalog);
447: }
448: if (resource.getSourceType() == IActionResource.URL_RESOURCE
449: && catalog.indexOf("solution:") != 0) { //$NON-NLS-1$
450: // catalog = PentahoSystem.getApplicationContext().getBaseUrl() + "GetMondrianModel?model=" + catalog; //$NON-NLS-1$
451:
452: // Extra step to make sure that remote mondrian models
453: // fully qualified aren't munged
454: // MB
455: if (!catalog.startsWith("http:")) { //$NON-NLS-1$
456: catalog = "solution:" + catalog; //$NON-NLS-1$
457: }
458: } else if (resource.getSourceType() == IActionResource.SOLUTION_FILE_RESOURCE
459: || resource.getSourceType() == IActionResource.FILE_RESOURCE) {
460: File file = new File(catalog);
461: if (file.exists()) {
462: catalog = file.toURI().toString();
463: }
464: }
465: }
466: if (catalog == null) {
467: warn(Messages
468: .getString(
469: "MDXBaseComponent.ERROR_0007_CATALOG_NOT_DEFINED", getActionName())); //$NON-NLS-1$
470: } else {
471: if (mdxConnectionProps != null) {
472: mdxConnectionProps.put(
473: MdxConnectionAction.CATALOG_ELEMENT,
474: catalog);
475: }
476: }
477:
478: String userId = connAction.getUserId().getStringValue();
479: String password = connAction.getPassword().getStringValue();
480: if (mdxConnectionProps != null) {
481: localConnection = PentahoConnectionFactory
482: .getConnection(
483: IPentahoConnection.MDX_DATASOURCE,
484: mdxConnectionProps, null);
485: } else {
486: if (mdxConnectionStr != null) {
487: localConnection = PentahoConnectionFactory
488: .getConnection(
489: IPentahoConnection.MDX_DATASOURCE,
490: mdxConnectionStr, null);
491: } else {
492: String connectStr = null;
493: if (jdbcStr != null) {
494: connectStr = jdbcStr + "; Catalog=" + catalog; //$NON-NLS-1$
495: } else if (jndiStr != null) {
496: String dsName = DatasourceHelper
497: .getDSBoundName(jndiStr);
498: if (dsName != null) {
499: connectStr = "dataSource=" + dsName + "; Catalog=" + catalog; //$NON-NLS-1$ //$NON-NLS-2$
500: } else {
501: error(Messages
502: .getErrorString("MDXBaseComponent.ERROR_0005_INVALID_CONNECTION")); //$NON-NLS-1$
503: return null;
504: }
505: }
506: if (role != null) {
507: connectStr += "; Role=" + role; //$NON-NLS-1$
508: }
509: localConnection = PentahoConnectionFactory
510: .getConnection(
511: IPentahoConnection.MDX_DATASOURCE,
512: connectStr, location, userId,
513: password, null);
514: }
515: if (localConnection == null) {
516: error(Messages
517: .getErrorString("MDXBaseComponent.ERROR_0005_INVALID_CONNECTION")); //$NON-NLS-1$
518: return null;
519: }
520: }
521: return localConnection;
522: } catch (Exception e) {
523: error(
524: Messages
525: .getErrorString(
526: "MDXBaseComponent.ERROR_0006_EXECUTE_FAILED", getActionName()), e); //$NON-NLS-1$
527: }
528: return null;
529: }
530:
531: public boolean init() {
532: return true;
533: }
534: }
|