/* * eXist SQL Module Extension * Copyright (C) 2006-10 Adam Retter <adam@exist-db.org> * www.adamretter.co.uk * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * 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. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id$ */ package org.exist.xquery.modules.sql; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.exist.xquery.AbstractInternalModule; import org.exist.xquery.FunctionDef; import org.exist.xquery.XQueryContext; import java.sql.Connection; import java.sql.SQLException; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.exist.xquery.modules.ModuleUtils; import org.exist.xquery.modules.ModuleUtils.ContextMapEntryModifier; /** * eXist SQL Module Extension. * * <p>An extension module for the eXist Native XML Database that allows queries against SQL Databases, returning an XML representation of the result * set.</p> * * @author Adam Retter <adam@exist-db.org> * @author ljo * @version 1.2 * @see org.exist.xquery.AbstractInternalModule#AbstractInternalModule(org.exist.xquery.FunctionDef[], java.util.Map) * @serial 2010-03-18 */ public class SQLModule extends AbstractInternalModule { protected final static Logger LOG = LogManager.getLogger(SQLModule.class); public final static String NAMESPACE_URI = "http://exist-db.org/xquery/sql"; public final static String PREFIX = "sql"; public final static String INCLUSION_DATE = "2006-09-25"; public final static String RELEASED_IN_VERSION = "eXist-1.2"; private final static FunctionDef[] functions = { new FunctionDef(GetConnectionFunction.signatures[0], GetConnectionFunction.class), new FunctionDef(GetConnectionFunction.signatures[1], GetConnectionFunction.class), new FunctionDef(GetConnectionFunction.signatures[2], GetConnectionFunction.class), new FunctionDef(GetJNDIConnectionFunction.signatures[0], GetJNDIConnectionFunction.class), new FunctionDef(GetJNDIConnectionFunction.signatures[1], GetJNDIConnectionFunction.class), new FunctionDef(ExecuteFunction.signatures[0], ExecuteFunction.class), new FunctionDef(ExecuteFunction.signatures[1], ExecuteFunction.class), new FunctionDef(PrepareFunction.signatures[0], PrepareFunction.class) }; private static final long currentUID = System.currentTimeMillis(); public final static String CONNECTIONS_CONTEXTVAR = "_eXist_sql_connections"; public final static String PREPARED_STATEMENTS_CONTEXTVAR = "_eXist_sql_prepared_statements"; public SQLModule(Map<String, List<? extends Object>> parameters) { super(functions, parameters); } @Override public String getNamespaceURI() { return (NAMESPACE_URI); } @Override public String getDefaultPrefix() { return (PREFIX); } @Override public String getDescription() { return ("A module for performing SQL queries against Databases, returning XML representations of the result sets."); } @Override public String getReleaseVersion() { return (RELEASED_IN_VERSION); } /** * Retrieves a previously stored Connection from the Context of an XQuery. * * @param context The Context of the XQuery containing the Connection * @param connectionUID The UID of the Connection to retrieve from the Context of the XQuery * * @return DOCUMENT ME! */ public static Connection retrieveConnection(XQueryContext context, long connectionUID) { return ModuleUtils.retrieveObjectFromContextMap(context, SQLModule.CONNECTIONS_CONTEXTVAR, connectionUID); } /** * Stores a Connection in the Context of an XQuery. * * @param context The Context of the XQuery to store the Connection in * @param con The connection to store * * @return A unique ID representing the connection */ public static synchronized long storeConnection(XQueryContext context, Connection con) { return ModuleUtils.storeObjectInContextMap(context, SQLModule.CONNECTIONS_CONTEXTVAR, con); } /** * Retrieves a previously stored PreparedStatement from the Context of an XQuery. * * @param context The Context of the XQuery containing the PreparedStatement * @param preparedStatementUID The UID of the PreparedStatement to retrieve from the Context of the XQuery * * @return DOCUMENT ME! */ public static PreparedStatementWithSQL retrievePreparedStatement(XQueryContext context, long preparedStatementUID) { return ModuleUtils.retrieveObjectFromContextMap(context, SQLModule.PREPARED_STATEMENTS_CONTEXTVAR, preparedStatementUID); } /** * Stores a PreparedStatement in the Context of an XQuery. * * @param context The Context of the XQuery to store the PreparedStatement in * @param stmt preparedStatement The PreparedStatement to store * * @return A unique ID representing the PreparedStatement */ public static synchronized long storePreparedStatement(XQueryContext context, PreparedStatementWithSQL stmt) { return ModuleUtils.storeObjectInContextMap(context, SQLModule.PREPARED_STATEMENTS_CONTEXTVAR, stmt); } /** * Resets the Module Context and closes any DB connections for the XQueryContext. * * @param xqueryContext The XQueryContext */ @Override public void reset(XQueryContext xqueryContext) { // reset the module context super.reset(xqueryContext); // close any open PreparedStatements closeAllPreparedStatements(xqueryContext); // close any open Connections closeAllConnections(xqueryContext); } /** * Closes all the open DB Connections for the specified XQueryContext. * * @param xqueryContext The context to close JDBC Connections for */ private static void closeAllConnections(XQueryContext xqueryContext) { ModuleUtils.modifyContextMap(xqueryContext, SQLModule.CONNECTIONS_CONTEXTVAR, new ContextMapEntryModifier<Connection>(){ @Override public void modify(Map<Long, Connection> map) { super.modify(map); //empty the map map.clear(); } @Override public void modify(Entry<Long, Connection> entry) { final Connection con = entry.getValue(); try { // close the Connection con.close(); } catch(SQLException se) { LOG.warn("Unable to close JDBC Connection: " + se.getMessage(), se); } } }); // update the context //ModuleUtils.storeContextMap(xqueryContext, SQLModule.CONNECTIONS_CONTEXTVAR, connections); } /** * Closes all the open DB PreparedStatements for the specified XQueryContext. * * @param xqueryContext The context to close JDBC PreparedStatements for */ private static void closeAllPreparedStatements(XQueryContext xqueryContext) { ModuleUtils.modifyContextMap(xqueryContext, SQLModule.PREPARED_STATEMENTS_CONTEXTVAR, new ContextMapEntryModifier<PreparedStatementWithSQL>(){ @Override public void modify(Map<Long, PreparedStatementWithSQL> map) { super.modify(map); //empty the map map.clear(); } @Override public void modify(Entry<Long, PreparedStatementWithSQL> entry) { final PreparedStatementWithSQL stmt = entry.getValue(); try { // close the PreparedStatement stmt.getStmt().close(); } catch(SQLException se) { LOG.warn("Unable to close JDBC PreparedStatement: " + se.getMessage(), se); } } }); // update the context //ModuleUtils.storeContextMap(xqueryContext, SQLModule.PREPARED_STATEMENTS_CONTEXTVAR, preparedStatements); } }