Java tutorial
/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package org.pentaho.platform.plugin.action.xml.xquery; import net.sf.saxon.trans.XPathException; import org.apache.commons.io.FilenameUtils; import org.apache.commons.logging.Log; import org.dom4j.Document; import org.dom4j.Node; import org.dom4j.io.SAXReader; import org.pentaho.actionsequence.dom.ActionInputConstant; import org.pentaho.actionsequence.dom.IActionDefinition; import org.pentaho.actionsequence.dom.IActionInput; import org.pentaho.actionsequence.dom.IActionOutput; import org.pentaho.actionsequence.dom.actions.XQueryAction; import org.pentaho.actionsequence.dom.actions.XQueryConnectionAction; import org.pentaho.commons.connection.IPentahoConnection; import org.pentaho.commons.connection.IPentahoResultSet; import org.pentaho.platform.api.data.IPreparedComponent; import org.pentaho.platform.api.engine.IActionSequenceResource; import org.pentaho.platform.api.repository2.unified.RepositoryFilePermission; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.services.connection.PentahoConnectionFactory; import org.pentaho.platform.engine.services.runtime.MapParameterResolver; import org.pentaho.platform.engine.services.runtime.TemplateUtil; import org.pentaho.platform.engine.services.solution.ComponentBase; import org.pentaho.platform.engine.services.solution.StandardSettings; import org.pentaho.platform.plugin.action.messages.Messages; import org.pentaho.platform.plugin.services.connections.xquery.XQConnection; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.Calendar; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.StringTokenizer; /** * XQueryBaseComponent provides a mechanism to run xqueries within the Pentaho BI Platform. * <p/> * <p/> * TODO: In regards to IPreparedComponent, implement a method for choosing the datasource on the fly */ public abstract class XQueryBaseComponent extends ComponentBase implements IPreparedComponent { private static final long serialVersionUID = -4390530975622541328L; private IPentahoResultSet rSet; /** * reference to connection object */ protected IPentahoConnection connection; /** * keeps track of ownership of connection */ protected boolean connectionOwner = true; private static final String FILENAME_PREFIX = "tmp"; //$NON-NLS-1$ private static final String EXTENSION = ".xml"; //$NON-NLS-1$ // private static final String TEMP_DIRECTORY = "system/tmp/"; //$NON-NLS-1$ private static final String XML_DOCUMENT_TAG = "XML_DOCUMENT"; //$NON-NLS-1$ @Override public abstract boolean validateSystemSettings(); @Override public abstract Log getLogger(); private int maxRows = -1; /** * string to hold prepared query until execution */ String preparedQuery = null; /** * string array to hold prepared column types until execution */ String[] preparedColumnTypes = null; public IPentahoResultSet getResultSet() { return rSet; } @Override protected boolean validateAction() { boolean result = false; IActionDefinition actionDefinition = getActionDefinition(); if (actionDefinition instanceof XQueryAction) { XQueryAction xQueryAction = (XQueryAction) actionDefinition; if ((xQueryAction.getSourceXml() == ActionInputConstant.NULL_INPUT) && (xQueryAction.getXmlDocument() == null)) { error(Messages.getInstance().getString("XQueryBaseComponent.ERROR_0008_SOURCE_NOT_DEFINED", //$NON-NLS-1$ getActionName())); } else if (xQueryAction.getQuery() == ActionInputConstant.NULL_INPUT) { error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0001_QUERY_NOT_SPECIFIED", //$NON-NLS-1$ getActionName())); } else if ((xQueryAction.getOutputPreparedStatement() == null) && (xQueryAction.getOutputResultSet() == null)) { error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0003_OUTPUT_NOT_SPECIFIED", //$NON-NLS-1$ getActionName())); } else { result = true; } } else if (actionDefinition instanceof XQueryConnectionAction) { XQueryConnectionAction xQueryConnectionAction = (XQueryConnectionAction) actionDefinition; if (xQueryConnectionAction.getOutputConnection() == null) { error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0003_OUTPUT_NOT_SPECIFIED", //$NON-NLS-1$ getActionName())); } else { result = true; } } else { error(Messages.getInstance().getErrorString("ComponentBase.ERROR_0001_UNKNOWN_ACTION_TYPE", //$NON-NLS-1$ actionDefinition.getElement().asXML())); } return result; } @Override public void done() { // TODO Auto-generated method stub } @Override protected boolean executeAction() { boolean result = false; IActionDefinition actionDefinition = getActionDefinition(); // int maxRows = -1; // int queryTimeout = -1; if (actionDefinition instanceof XQueryAction) { XQueryAction xQueryAction = (XQueryAction) actionDefinition; // Not implemented yet // IActionInput queryTimeoutInput = xQueryAction.getQueryTimeout(); IActionInput maxRowsInput = xQueryAction.getMaxRows(); if (maxRowsInput != ActionInputConstant.NULL_INPUT) { this.setMaxRows(maxRowsInput.getIntValue()); } IPreparedComponent sharedConnection = (IPreparedComponent) xQueryAction.getSharedConnection() .getValue(); if (sharedConnection != null) { connectionOwner = false; connection = sharedConnection.shareConnection(); } else { connection = getConnection(); } if (connection == null) { error(Messages.getInstance() .getErrorString("IPreparedComponent.ERROR_0002_CONNECTION_NOT_AVAILABLE", getActionName())); //$NON-NLS-1$ } else if (connection.getDatasourceType() != IPentahoConnection.XML_DATASOURCE) { error(Messages.getInstance().getErrorString("IPreparedComponent.ERROR_0001_INVALID_CONNECTION_TYPE", //$NON-NLS-1$ getActionName())); } else { result = runQuery(connection, xQueryAction.getQuery().getStringValue()); } } else if (actionDefinition instanceof XQueryConnectionAction) { XQueryConnectionAction xQueryConnectionAction = (XQueryConnectionAction) getActionDefinition(); connection = getConnection(); if (connection == null) { error(Messages.getInstance() .getErrorString("IPreparedComponent.ERROR_0002_CONNECTION_NOT_AVAILABLE", getActionName())); //$NON-NLS-1$ } else if (connection.getDatasourceType() != IPentahoConnection.XML_DATASOURCE) { error(Messages.getInstance().getErrorString("IPreparedComponent.ERROR_0001_INVALID_CONNECTION_TYPE", //$NON-NLS-1$ getActionName())); } else { xQueryConnectionAction.getOutputConnection().setValue(this); result = true; } } return result; } protected boolean runQuery(final IPentahoConnection localConnection, String rawQuery) { XQueryAction xQueryAction = (XQueryAction) getActionDefinition(); try { if (localConnection == null) { return false; } if (ComponentBase.debug) { debug(Messages.getInstance().getString("XQueryBaseComponent.DEBUG_RUNNING_QUERY", rawQuery)); //$NON-NLS-1$ } String documentPath = null; int resourceType = -1; String srcXml = xQueryAction.getSourceXml().getStringValue(); org.pentaho.actionsequence.dom.IActionResource xmlResource = xQueryAction.getXmlDocument(); InputStream inputStream = null; URL url = null; if (srcXml != null) { inputStream = new FileInputStream(new File(createTempXMLFile(srcXml))); } else if (xmlResource != null) { // we have a local document to use as the data source IActionSequenceResource resource = getResource(xmlResource.getName()); resourceType = resource.getSourceType(); if ((resourceType == IActionSequenceResource.SOLUTION_FILE_RESOURCE) || (resourceType == IActionSequenceResource.FILE_RESOURCE)) { inputStream = resource.getInputStream(RepositoryFilePermission.READ); } else if (resourceType == IActionSequenceResource.XML) { inputStream = new FileInputStream(new File(createTempXMLFile(resource.getAddress()))); } else { url = new URL(documentPath); } } // Retrieve the column types String[] columnTypes = null; if (retrieveColumnTypes()) { try { SAXReader reader = new SAXReader(); Document document; if (url != null) { document = reader.read(url); } else { document = reader.read(inputStream); } Node commentNode = document.selectSingleNode("/result-set/comment()"); //$NON-NLS-1$ if (commentNode != null) { String commentString = commentNode.getText(); StringTokenizer st = new StringTokenizer(commentString, ","); //$NON-NLS-1$ List columnTypesList = new LinkedList(); while (st.hasMoreTokens()) { String token = st.nextToken().trim(); columnTypesList.add(token); } columnTypes = (String[]) columnTypesList.toArray(new String[0]); } } catch (Exception e) { getLogger().warn(Messages.getInstance() .getString("XQueryBaseComponent.ERROR_0009_ERROR_BUILDING_COLUMN_TYPES"), e); //$NON-NLS-1$ } } if (rawQuery != null) { if (rawQuery.indexOf("{" + XQueryBaseComponent.XML_DOCUMENT_TAG + "}") >= 0) { //$NON-NLS-1$//$NON-NLS-2$ rawQuery = TemplateUtil.applyTemplate(rawQuery, XQueryBaseComponent.XML_DOCUMENT_TAG, documentPath); } else { Calendar now = Calendar.getInstance(); File temp = File.createTempFile("tempXQuery" + now.getTimeInMillis(), ".xml"); temp.deleteOnExit(); OutputStream out = new FileOutputStream(temp); IActionSequenceResource resource = getResource(xmlResource.getName()); inputStream = resource.getInputStream(RepositoryFilePermission.READ); byte[] buf = new byte[1024]; int len; while ((len = inputStream.read(buf)) > 0) { out.write(buf, 0, len); } out.close(); inputStream.close(); documentPath = temp.getAbsolutePath(); documentPath = FilenameUtils.separatorsToUnix(documentPath); rawQuery = "doc(\"" + documentPath + "\")" + rawQuery; //$NON-NLS-1$ //$NON-NLS-2$ } } if (xQueryAction.getOutputPreparedStatement() != null) { return prepareFinalQuery(rawQuery, columnTypes); } else { return runFinalQuery(localConnection, rawQuery, columnTypes); } } catch (Exception e) { getLogger().error( Messages.getInstance().getString("XQueryBaseComponent.ERROR_0010_ERROR_RUNNING_QUERY"), e); //$NON-NLS-1$ return false; } } protected boolean prepareFinalQuery(final String rawQuery, final String[] columnTypes) { if (rawQuery != null) { preparedQuery = applyInputsToFormat(rawQuery); } preparedColumnTypes = columnTypes; ((XQueryAction) getActionDefinition()).getOutputPreparedStatement().setValue(this); return true; } protected boolean runFinalQuery(final IPentahoConnection localConnection, final String rawQuery, final String[] columnTypes) { XQueryAction xQueryAction = (XQueryAction) getActionDefinition(); boolean success = false; String finalQuery = applyInputsToFormat(rawQuery); // execute the query, read the results and cache them try { IPentahoResultSet resultSet = ((XQConnection) localConnection).executeQuery(finalQuery, columnTypes); if (resultSet != null) { if (!xQueryAction.getLive().getBooleanValue(true)) { resultSet = resultSet.memoryCopy(); } try { IActionOutput resultSetOutput = xQueryAction.getOutputResultSet(); if (resultSetOutput != null) { resultSetOutput.setValue(resultSet); } success = true; } finally { resultSet.close(); } } } catch (XPathException e) { error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0006_EXECUTE_FAILED", getActionName()), e); //$NON-NLS-1$ } return success; } protected String createTempXMLFile(final String xmlString) { // Save it to a temporary file File file; String documentPath = null; try { file = PentahoSystem.getApplicationContext().createTempFile(getSession(), XQueryBaseComponent.FILENAME_PREFIX, XQueryBaseComponent.EXTENSION, true); documentPath = file.getCanonicalPath(); BufferedWriter out = new BufferedWriter(new FileWriter(file)); out.write(xmlString); out.close(); } catch (IOException e) { getLogger().error( Messages.getInstance().getString("XQueryBaseComponent.ERROR_0011_ERROR_CREATING_TEMP_FILE"), e); //$NON-NLS-1$ } documentPath = documentPath.replaceAll("\\\\", "/"); //$NON-NLS-1$ //$NON-NLS-2$ return documentPath; } protected IPentahoConnection getConnection() { IPentahoConnection conn = null; try { conn = PentahoConnectionFactory.getConnection(IPentahoConnection.XML_DATASOURCE, getSession(), this); if (conn == null) { error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0005_INVALID_CONNECTION")); //$NON-NLS-1$ return null; } if (this.getMaxRows() >= 0) { conn.setMaxRows(this.getMaxRows()); } return conn; } catch (Exception e) { error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0006_EXECUTE_FAILED", getActionName()), e); //$NON-NLS-1$ } return null; } @Override public boolean init() { return true; } /** * implements IPreparedComponents shareConnection, allowing other xquery components to access the connection * * @return shared connection */ public IPentahoConnection shareConnection() { return connection; } /** * implements the IPreparedComponent executePrepared, which allows other components to execute the prepared * statement. * * @param preparedParams lookup for prepared parameters * @return pentaho result set */ public IPentahoResultSet executePrepared(final Map preparedParams) { if (connection == null) { error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0012_NO_CONNECTION", getActionName())); //$NON-NLS-1$ return null; } if (!connection.initialized()) { error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0012_NO_CONNECTION", //$NON-NLS-1$ getActionName())); return null; } if (preparedQuery == null) { error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0001_QUERY_NOT_SPECIFIED", //$NON-NLS-1$ getActionName())); return null; } String finalQuery = TemplateUtil.applyTemplate(preparedQuery, getRuntimeContext(), new MapParameterResolver( preparedParams, IPreparedComponent.PREPARE_LATER_PREFIX, getRuntimeContext())); // execute the query, read the results and cache them try { IPentahoResultSet resultSet = ((XQConnection) connection).executeQuery(finalQuery, preparedColumnTypes); if (resultSet != null) { boolean live = getInputBooleanValue(StandardSettings.LIVE, true); if (!live) { resultSet = resultSet.memoryCopy(); } try { return resultSet; } finally { resultSet.close(); } } } catch (XPathException e) { error(Messages.getInstance().getErrorString("XQueryBaseComponent.ERROR_0006_EXECUTE_FAILED", getActionName()), e); //$NON-NLS-1$ } return null; } /** * Determines if the action should attempt to retrieve the columns types */ protected boolean retrieveColumnTypes() { return true; } public int getMaxRows() { return this.maxRows; } public void setMaxRows(final int value) { if (rSet == null) { this.maxRows = value; } else { throw new UnsupportedOperationException(Messages.getInstance() .getErrorString("XQueryBaseComponent.ERROR_0013_INVALID_ORDER_OF_OPERATION")); //$NON-NLS-1$ } } /** * disposes of the connection this is called by the runtime context if the object is used as an iprepared component */ public void dispose() { if (connectionOwner) { if (connection != null) { connection.close(); } } } }