All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kawanfw.sql.servlet.ServerSqlDispatch Maven / Gradle / Ivy

Go to download

AceQL HTTP is a framework of REST like http APIs that allow to access to remote SQL databases over http from any device that supports http. AceQL HTTP is provided with four client SDK: - The AceQL C# Client SDK allows to wrap the HTTP APIs using Microsoft SQL Server like calls in their code, just like they would for a local database. - The AceQL Java Client SDK allows to wrap the HTTP APIs using JDBC calls in their code, just like they would for a local database. - The AceQL Python Client SDK allows SQL calls to be encoded with standard unmodified DB-API 2.0 syntax

There is a newer version: 12.2
Show newest version
/*
 * Copyright (c)2022 KawanSoft S.A.S. All rights reserved.
 * 
 * Use of this software is governed by the Business Source License included
 * in the LICENSE.TXT file in the project's root directory.
 *
 * Change Date: 2026-11-01
 *
 * On the date above, in accordance with the Business Source License, use
 * of this software will be governed by version 2.0 of the Apache License.
 */
package org.kawanfw.sql.servlet;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.tomcat.util.http.fileupload.FileUploadException;
import org.apache.tomcat.util.http.fileupload.servlet.ServletFileUpload;
import org.kawanfw.sql.api.server.DatabaseConfigurator;
import org.kawanfw.sql.api.server.firewall.SqlFirewallManager;
import org.kawanfw.sql.metadata.dto.DatabaseInfoDto;
import org.kawanfw.sql.metadata.util.GsonWsUtil;
import org.kawanfw.sql.servlet.connection.ConnectionIdUtil;
import org.kawanfw.sql.servlet.connection.ConnectionStore;
import org.kawanfw.sql.servlet.connection.ConnectionStoreGetter;
import org.kawanfw.sql.servlet.connection.RollbackUtil;
import org.kawanfw.sql.servlet.connection.SavepointUtil;
import org.kawanfw.sql.servlet.connection.TransactionUtil;
import org.kawanfw.sql.servlet.injection.classes.InjectedClassesStore;
import org.kawanfw.sql.servlet.injection.properties.ConfPropertiesUtil;
import org.kawanfw.sql.servlet.jdbc.metadata.DefaultJdbcDatabaseMetadataActionManagerWrap;
import org.kawanfw.sql.servlet.sql.ServerStatement;
import org.kawanfw.sql.servlet.sql.ServerStatementRawExecute;
import org.kawanfw.sql.servlet.sql.batch.ServerPreparedStatementBatch;
import org.kawanfw.sql.servlet.sql.batch.ServerStatementBatch;
import org.kawanfw.sql.servlet.sql.callable.AdvancedServerCallableStatement;
import org.kawanfw.sql.servlet.sql.json_return.JsonErrorReturn;
import org.kawanfw.sql.servlet.sql.json_return.JsonOkReturn;
import org.kawanfw.sql.util.FrameworkDebug;
import org.kawanfw.sql.version.VersionWrapper;

/**
 * @author Nicolas de Pomereu
 *
 *         The method executeRequest() is to to be called from the SqlHttpServer
 *         Servlet and Class. 
* It will execute a client side request with a RemoteConnection * connection. * */ public class ServerSqlDispatch { private static final boolean DUMP_HEADERS = false; private static boolean DEBUG = FrameworkDebug.isSet(ServerSqlDispatch.class); /** * Execute the client sent sql request that is already wrapped in the calling * try/catch that handles Throwable * * @param request the http request * @param response the http response * @param out * @throws IOException if any IOException occurs * @throws SQLException * @throws FileUploadException */ public void executeRequestInTryCatch(HttpServletRequest request, HttpServletResponse response, OutputStream out) throws IOException, SQLException, FileUploadException { if (doBlobUpload(request, response, out)) { return; } // Prepare the response response.setContentType("text/html; charset=UTF-8"); String action = request.getParameter(HttpParameter.ACTION); String username = request.getParameter(HttpParameter.USERNAME); String database = request.getParameter(HttpParameter.DATABASE); String sessionId = request.getParameter(HttpParameter.SESSION_ID); String connectionId = request.getParameter(HttpParameter.CONNECTION_ID); debug(""); debug("action : " + action); debug("username : " + username); debug("database : " + database); debug("sessionId : " + sessionId); debug("connectionId: " + connectionId); BaseActionTreater baseActionTreater = new BaseActionTreater(request, response, out); if (!baseActionTreater.treatAndContinue()) { return; } DatabaseConfigurator databaseConfigurator = baseActionTreater.getDatabaseConfigurator(); if (isGetVersion(out, action)) { return; } Connection connection = null; try { if (ConfPropertiesUtil.isStatelessMode()) { // Create the Connection because passed client Id is stateless connection = databaseConfigurator.getConnection(database); } else { // Extracts the Connection from the store ConnectionStoreGetter connectionStoreGetter = new ConnectionStoreGetter(request, response); connection = connectionStoreGetter.getConnection(); if (connectionStoreGetter.getJsonErrorReturn() != null) { ServerSqlManager.writeLine(out, connectionStoreGetter.getJsonErrorReturn().build()); return; } } // Detect if user is banned if (ServerSqlDispatchUtil.isUsernameBanned(username, database, connection)) { JsonErrorReturn errorReturn = new JsonErrorReturn(response, HttpServletResponse.SC_FORBIDDEN, JsonErrorReturn.ERROR_ACEQL_UNAUTHORIZED, JsonErrorReturn.ACCESS_FORBIDDEN_FOR_USERNAME); ServerSqlManager.writeLine(out, errorReturn.build()); return; } // 9.1: isExecuteServerQuery if (ServerQueryExecutorUtil.isExecuteServerQuery(request, out, action, connection)) { return; } Set sqlFirewallManagers = InjectedClassesStore.get().getSqlFirewallManagerMap() .get(database); // get_database_info if (isGetDatabaseInfo(request, out, action, connection, sqlFirewallManagers)) { return; } // Do not treat if not in auto-commit mode if Server is Stateless if (!checkStatelessInAutoCommit(request, response, out, connection)) { return; } // Release connection in pool & remove all references if (action.equals(HttpParameter.CLOSE)) { treatCloseAction(response, out, username, sessionId, connectionId, databaseConfigurator, connection); return; } if (doTreatJdbcDatabaseMetaData(request, response, out, action, connection, sqlFirewallManagers)) { return; } if (doTreatMetadataQuery(request, response, out, action, connection, sqlFirewallManagers)) { return; } dumpHeaders(request); dispatch(request, response, out, action, connection, databaseConfigurator, sqlFirewallManagers); } catch (Exception e) { RollbackUtil.rollback(connection); throw e; } finally { // Immediate close of a Connection for stateless sessions if (ConfPropertiesUtil.isStatelessMode()) { databaseConfigurator.close(connection); } } } /** * Returns on out servlet stream all remote database & driver info. * * @param request * @param out * @param action * @param connection * @param sqlFirewallManagers * @return * @throws IOException * @throws SQLException */ private boolean isGetDatabaseInfo(HttpServletRequest request, OutputStream out, String action, Connection connection, Set sqlFirewallManagers) throws IOException, SQLException { if (action.equals(HttpParameter.GET_DATABASE_INFO)) { // Throws SecurityException if not authorized ServerSqlDispatchUtil.checkMetadataAuthorized(request, connection, sqlFirewallManagers); // Meta data DatabaseMetaData meta = connection.getMetaData(); DatabaseInfoDto databaseInfoDto = new DatabaseInfoDto(meta); String jsonString = GsonWsUtil.getJSonString(databaseInfoDto); ServerSqlManager.writeLine(out, jsonString); return true; } else { return false; } } /** * Checks that a stateless session is in auto commit, otherwise reply with error * message. * * @param request * @param response * @param out * @param connection * @return * @throws IOException * @throws SQLException */ private boolean checkStatelessInAutoCommit(HttpServletRequest request, HttpServletResponse response, OutputStream out, Connection connection) throws IOException, SQLException { // Don't care in stateful mode if (!ConfPropertiesUtil.isStatelessMode()) { return true; } // Stateless mode: 1) can not call setAutocommit(false): if (ServerSqlDispatchUtil.isActionsSetAutoCommitFalse(request)) { JsonErrorReturn errorReturn = new JsonErrorReturn(response, HttpServletResponse.SC_BAD_REQUEST, JsonErrorReturn.ERROR_JDBC_ERROR, "AceQL Server is in Stateless Mode: can not change auto-commit mode to false."); ServerSqlManager.writeLine(out, errorReturn.build()); return false; } // Stateless mode: 2) Connection must be in autocommit mode: if (!connection.getAutoCommit()) { JsonErrorReturn errorReturn = new JsonErrorReturn(response, HttpServletResponse.SC_BAD_REQUEST, JsonErrorReturn.ERROR_JDBC_ERROR, "AceQL Server is in Stateless Mode: can not process SQL request because Connection must be in auto-commit."); ServerSqlManager.writeLine(out, errorReturn.build()); return false; } else { return true; } } private void dumpHeaders(HttpServletRequest request) { if (!DUMP_HEADERS) { return; } Enumeration reqHeaderEnum = request.getHeaderNames(); while (reqHeaderEnum.hasMoreElements()) { String name = reqHeaderEnum.nextElement(); System.out.println("Header: " + name + " / " + request.getHeader(name)); } } /** * Treat if action is get_version * * @param out * @param action * @throws IOException */ private boolean isGetVersion(OutputStream out, String action) throws IOException { if (action.equals(HttpParameter.GET_VERSION)) { // String version = new // org.kawanfw.sql.version.DefaultVersion.PRODUCT().server(); String version = VersionWrapper.getServerVersion(); ServerSqlManager.writeLine(out, JsonOkReturn.build("result", version)); return true; } else { return false; } } /** * Dispatch the request. * * @param request * @param response * @param out * @param action * @param connection * @param databaseConfigurator * @param sqlFirewallManagers * @throws SQLException * @throws FileNotFoundException * @throws IOException * @throws IllegalArgumentException */ private void dispatch(HttpServletRequest request, HttpServletResponse response, OutputStream out, String action, Connection connection, DatabaseConfigurator databaseConfigurator, Set sqlFirewallManagers) throws SQLException, FileNotFoundException, IOException, IllegalArgumentException { // OperationType operationType = OperationTypeCreator.createInstance(); // String sql = request.getParameter(HttpParameter.SQL); // if (!operationType.isOperationAuthorized(sql)) { // throw new UnsupportedOperationException( // Tag.PRODUCT + " " + "DCL or DLL Operation " + Tag.REQUIRES_ACEQL_ENTERPRISE_EDITION); // } if (ServerSqlDispatchUtil.isExecute(action) && !ServerSqlDispatchUtil.isStoredProcedure(request)) { ServerStatementRawExecute serverStatement = new ServerStatementRawExecute(request, response, sqlFirewallManagers, connection); serverStatement.execute(out); } else if (ServerSqlDispatchUtil.isExecuteQueryOrExecuteUpdate(action) && !ServerSqlDispatchUtil.isStoredProcedure(request)) { ServerStatement serverStatement = new ServerStatement(request, response, sqlFirewallManagers, connection); serverStatement.executeQueryOrUpdate(out); } else if (ServerSqlDispatchUtil.isStatementExecuteBatch(action)) { ServerStatementBatch serverStatement = new ServerStatementBatch(request, response, sqlFirewallManagers, connection, databaseConfigurator); serverStatement.executeBatch(out); } else if (ServerSqlDispatchUtil.isPreparedStatementExecuteBatch(action)) { ServerPreparedStatementBatch serverPreparedStatementBatch = new ServerPreparedStatementBatch(request, response, sqlFirewallManagers, connection, databaseConfigurator); serverPreparedStatementBatch.executeBatch(out); } else if (ServerSqlDispatchUtil.isStoredProcedure(request)) { // try { // // ServerCallableStatementWrapper serverCallableStatementWrapper = ServerCallableStatementWrapperCreator // .createInstance(); // serverCallableStatementWrapper.executeOrExecuteQuery(request, response, sqlFirewallManagers, connection, // out); // // // } catch (SQLException exception) { // throw exception; // } catch (Exception exception) { // throw new SQLException(exception); // } AdvancedServerCallableStatement advancedServerCallableStatement = new AdvancedServerCallableStatement( request, response, sqlFirewallManagers, connection); advancedServerCallableStatement.executeOrExecuteQuery(out); } else if (ServerSqlDispatchUtil.isConnectionModifier(action)) { TransactionUtil.setConnectionModifierAction(request, response, out, action, connection); } else if (ServerSqlDispatchUtil.isSavepointModifier(action)) { SavepointUtil.setSavepointExecute(request, response, out, action, connection); } else if (ServerSqlDispatchUtil.isConnectionReader(action)) { TransactionUtil.getConnectionionInfosExecute(request, response, out, action, connection); } else { throw new IllegalArgumentException("Invalid Sql Action: " + action); } } /** * Treat * * @param request * @param response * @param out * @param action * @param connection * @param sqlFirewallManagers * @return */ private boolean doTreatJdbcDatabaseMetaData(HttpServletRequest request, HttpServletResponse response, OutputStream out, String action, Connection connection, Set sqlFirewallManagers) throws SQLException, IOException { // Redirect if it's a JDBC DatabaseMetaData call if (ActionUtil.isJdbcDatabaseMetaDataQuery(action)) { // try { // JdbcDatabaseMetadataActionManager jdbcDatabaseMetadataActionManager = JdbcDatabaseMetadataActionManagerCreator // .createInstance(); // jdbcDatabaseMetadataActionManager.execute(request, response, out, sqlFirewallManagers, connection); // } catch (ClassNotFoundException exception) { // throw new UnsupportedOperationException( // Tag.PRODUCT + " " + "MetaData call " + Tag.REQUIRES_ACEQL_ENTERPRISE_EDITION); // // } catch (Exception exception) { // throw new SQLException(exception); // } DefaultJdbcDatabaseMetadataActionManagerWrap.executeWrap(request, response, out, sqlFirewallManagers, connection); return true; } else { return false; } } /** * Tread metadata query. * * @param request * @param response * @param out * @param action * @param connection * @param sqlFirewallManagers * @throws SQLException * @throws IOException */ private boolean doTreatMetadataQuery(HttpServletRequest request, HttpServletResponse response, OutputStream out, String action, Connection connection, Set sqlFirewallManagers) throws SQLException, IOException { // Redirect if it's a metadaquery or a healthcheck info getter if (ActionUtil.isMetadataQueryAction(action) || ActionUtil.isHealthCheckInfo(action)) { MetadataQueryActionManager metadataQueryActionManager = new MetadataQueryActionManager(request, response, out, sqlFirewallManagers, connection); metadataQueryActionManager.execute(); return true; } else { return false; } } /** * @param response * @param out * @param username * @param sessionId * @param connectionId * @param databaseConfigurator * @param connection * @throws IOException */ private void treatCloseAction(HttpServletResponse response, OutputStream out, String username, String sessionId, final String connectionId, DatabaseConfigurator databaseConfigurator, Connection connection) throws IOException { try { // Nothing to do in stateless if (ConfPropertiesUtil.isStatelessMode()) { ServerSqlManager.writeLine(out, JsonOkReturn.build()); return; } databaseConfigurator.close(connection); String connectionIdNew = connectionId; if (connectionIdNew == null) { connectionIdNew = ConnectionIdUtil.getConnectionId(connection); } ConnectionStore connectionStore = new ConnectionStore(username, sessionId, connectionIdNew); connectionStore.remove(); ServerSqlManager.writeLine(out, JsonOkReturn.build()); } catch (SQLException e) { RollbackUtil.rollback(connection); JsonErrorReturn errorReturn = new JsonErrorReturn(response, HttpServletResponse.SC_BAD_REQUEST, JsonErrorReturn.ERROR_JDBC_ERROR, e.getMessage()); ServerSqlManager.writeLine(out, errorReturn.build()); } catch (Exception e) { RollbackUtil.rollback(connection); JsonErrorReturn errorReturn = new JsonErrorReturn(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, JsonErrorReturn.ERROR_ACEQL_FAILURE, e.getMessage(), ExceptionUtils.getStackTrace(e)); ServerSqlManager.writeLine(out, errorReturn.build()); } } /** * @param request * @param response * @param out * @throws IOException * @throws FileUploadException * @throws SQLException */ private boolean doBlobUpload(HttpServletRequest request, HttpServletResponse response, OutputStream out) throws IOException, FileUploadException, SQLException { // Immediate catch if we are asking a file upload, because // parameters are in unknown sequence. // We know it's a upload action if it's mime Multipart if (ServletFileUpload.isMultipartContent(request)) { BlobUploader blobUploader = new BlobUploader(request, response, out); blobUploader.blobUpload(); return true; } else { return false; } } public static void debug(String s) { if (DEBUG) { System.out.println(new Date() + " " + s); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy