src.main.java.org.kawanfw.sql.servlet.ServerSqlDispatch Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aceql-http Show documentation
Show all versions of aceql-http Show documentation
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 three 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 JDBC Driver 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
/*
* This file is part of AceQL HTTP.
* AceQL HTTP: SQL Over HTTP
* Copyright (C) 2020, KawanSoft SAS
* (http://www.kawansoft.com). All rights reserved.
*
* AceQL HTTP 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.1 of the License, or (at your option) any later version.
*
* AceQL HTTP 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Any modifications to this file must keep this entire header
* intact.
*/
package org.kawanfw.sql.servlet;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
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.servlet.connection.ConnectionStore;
import org.kawanfw.sql.servlet.connection.ConnectionStoreCleaner;
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.jdbc.metadata.JdbcDatabaseMetadataActionManager;
import org.kawanfw.sql.servlet.sql.ServerStatement;
import org.kawanfw.sql.servlet.sql.ServerStatementRawExecute;
import org.kawanfw.sql.servlet.sql.callable.ServerCallableStatement;
import org.kawanfw.sql.servlet.sql.json_return.JsonErrorReturn;
import org.kawanfw.sql.servlet.sql.json_return.JsonOkReturn;
import org.kawanfw.sql.util.FrameworkDebug;
/**
* @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 (isBlobUpload(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;
}
// Start clean Connections thread
connectionStoreClean();
ConnectionGetter connectionGetter = new ConnectionGetter(request, response, out);
if (!connectionGetter.treatAndContinue()) {
return;
}
Connection connection = connectionGetter.getConnection();
//HACK NDP
//System.out.println("connectionGetter.getConnection(): connection.getAutoCommit(): " + connection.getAutoCommit());
// Release connection in pool & remove all references
if (action.equals(HttpParameter.CLOSE)) {
treatCloseAction(response, out, username, sessionId, connectionId, databaseConfigurator, connection);
return;
}
List sqlFirewallManagers = ServerSqlManager.getSqlFirewallMap().get(database);
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, sqlFirewallManagers);
}
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.Version.PRODUCT().server();
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 sqlFirewallManagers
* @throws SQLException
* @throws FileNotFoundException
* @throws IOException
* @throws IllegalArgumentException
*/
private void dispatch(HttpServletRequest request, HttpServletResponse response, OutputStream out, String action,
Connection connection, List sqlFirewallManagers)
throws SQLException, FileNotFoundException, IOException, IllegalArgumentException {
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.isStoredProcedure(request)) {
ServerCallableStatement serverCallableStatement = new ServerCallableStatement(request, response,
sqlFirewallManagers, connection);
serverCallableStatement.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, List sqlFirewallManagers)
throws SQLException, IOException {
// Redirect if it's a JDBC DatabaseMetaData call
if (ActionUtil.isJdbcDatabaseMetaDataQuery(action)) {
JdbcDatabaseMetadataActionManager jdbcDatabaseMetadataActionManager = new JdbcDatabaseMetadataActionManager(request, response,
out, sqlFirewallManagers, connection);
jdbcDatabaseMetadataActionManager.execute();
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, List sqlFirewallManagers)
throws SQLException, IOException {
// Redirect if it's a metadaquery
if (ActionUtil.isMetadataQueryAction(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 {
// ConnectionCloser.freeConnection(connection,
// databaseConfigurator);
databaseConfigurator.close(connection);
String connectionIdNew = connectionId;
if (connectionIdNew == null) {
connectionIdNew = ServerLoginActionSql.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());
}
}
/**
* @param request
* @param response
* @param out TODO
* @throws IOException
* @throws FileUploadException
* @throws SQLException
*/
private boolean isBlobUpload(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;
}
}
/**
* Clean connection store.
*/
private void connectionStoreClean() {
if (ConnectionStoreCleaner.timeToCleanConnectionStore()) {
ConnectionStoreCleaner cleaner = new ConnectionStoreCleaner();
cleaner.start();
}
}
/**
* Method called by children Servlet for debug purpose Println is done only if
* class name name is in kawansoft-debug.ini
*/
public static void debug(String s) {
if (DEBUG) {
System.out.println(new Date() + " " + s);
}
}
}