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

org.kawanfw.sql.api.client.RemoteDriver Maven / Gradle / Ivy

/*
 * This file is part of AceQL. 
 * AceQL: Remote JDBC access over HTTP.                                     
 * Copyright (C) 2015,  KawanSoft SAS
 * (http://www.kawansoft.com). All rights reserved.                                
 *                                                                               
 * AceQL 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 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.api.client;

import java.io.File;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.URL;
import java.net.Proxy.Type;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.commons.lang3.StringUtils;
import org.kawanfw.commons.api.client.SessionParameters;
import org.kawanfw.commons.json.SessionParametersGson;
import org.kawanfw.commons.util.ClientLogger;
import org.kawanfw.commons.util.FrameworkDebug;
import org.kawanfw.file.api.client.RemoteInputStream;
import org.kawanfw.file.api.client.RemoteOutputStream;
import org.kawanfw.file.api.client.RemoteSession;
import org.kawanfw.sql.util.JdbcUrlHeader;

/**
 * 
 * The Driver class in order to access remote
 * SQL databases through HTTP from Android or Java desktop programs.
*
* user & password are the only required properties.
*
* Properties: *
    *
  • user: username to connect to the remote database as.
  • *
  • password: password to use when authenticating.
  • *
  • proxyType: java.net.Proxy Type to use: HTTP or SOCKS. Defaults to HTTP.
  • *
  • proxyHostname: java.net.Proxy hostname to use.
  • *
  • proxyPort: java.net.Proxy Port to use.
  • *
  • proxyUsername: Proxy credential username.
  • *
  • proxyPassword: Proxy credential password.
  • *
  • maxLengthForString: int for the maximum authorized length for a * string for upload or download Should be <= 2097152 (2Mb). Defaults to * 2097152.
  • *
  • acceptAllSslCertificates: boolean to say if client sides allows * HTTPS call with all SSL Certificates, including "invalid" or self-signed * Certificates. Defaults to false.
  • *
  • encryptionPassword: the password to use to encrypt all request * parameter names and values. Defaults to null.
  • *
  • htmlEncodingOn: boolean to say if the upload/download of Clob * using character stream or ASCII stream must be html encoded. Defaults to * true.
  • *
  • compression: boolean to say if the Driver is configured to * contact the remote server using http compression. Defaults to true.
  • *
  • statelessMode: boolean to say if the Driver is configured to * contact the remote server in stateless mode. Defaults to false.
  • *
  • uploadChunkLength: long for upload chunk length to be used by * {@link RemoteOutputStream} and * {@link RemoteSession#upload(File, String)}. Defaults to 10Mb. 0 means * files are not chunked.
  • *
  • downloadChunkLength: long for download chunk length to be used by * {@link RemoteInputStream} and * {@link RemoteSession#download(String, File)}. Defaults to 10Mb. 0 * means files are not chunked.
  • *
  • joinResultSetMetaData: boolean to tell server to download ResultSet MetaData * along with ResultSet. Defaults to {@code false}.
  • *
*

* * @author Nicolas de Pomereu * @since 1.0 * */ public class RemoteDriver implements java.sql.Driver { /** The debug flag */ private static boolean DEBUG = FrameworkDebug.isSet(RemoteDriver.class); /** * Constructor. */ public RemoteDriver() { } static { try { DriverManager.registerDriver(new RemoteDriver()); } catch (SQLException e) { e.printStackTrace(); } } /** * Attempts to make a database connection to the given URL. * * The driver will return "null" if it realizes it is the wrong kind of * driver to connect to the given URL. {@link #acceptsURL} will return null. * *

* The driver will throwSQLException if it is the right driver * to connect to the given URL but has trouble connecting to the database. * *

* The java.util.Properties argument can be used to pass * arbitrary string tag/value pairs as connection arguments. At least "user" * and "password" properties should be included in the * Properties object. * * @param url * the URL of the database to which to connect * @param info * a list of arbitrary string tag/value pairs as connection * arguments. At least a "user" and "password" property should be * included. * @return a Connection object that represents a connection to * the URL * @exception SQLException * if a database access error occurs */ @Override public Connection connect(String url, Properties info) throws SQLException { if (url == null) { throw new SQLException("url not set. Please provide an url."); } if (!acceptsURL(url)) { return null; } Properties info2 = new Properties(); RemoteDriverUtil.copyProperties(info, info2); // Properties may be passed in url if (url.contains("?")) { String query = StringUtils.substringAfter(url, "?"); Map mapProps = RemoteDriverUtil.getQueryMap(query); Set set = mapProps.keySet(); for (String propName : set) { info2.setProperty(propName, mapProps.get(propName)); } url = StringUtils.substringBefore(url, "?"); } String username = info2.getProperty("user"); String password = info2.getProperty("password"); if (username == null) { throw new SQLException("user not set. Please provide a user."); } if (password == null) { throw new SQLException( "password not set. Please provide a password."); } // Add proxy lookup String proxyType = info2.getProperty("proxyType"); String proxyHostname = info2.getProperty("proxyHostname"); String proxyPort = info2.getProperty("proxyPort"); String proxyUsername = info2.getProperty("proxyUsername"); String proxyPassword = info2.getProperty("proxyPassword"); String statelessMode = info2.getProperty("statelessMode"); String joinResultSetMetaData = info2 .getProperty("joinResultSetMetaData"); int port = -1; Proxy proxy = null; if (proxyHostname != null) { try { port = Integer.parseInt(proxyPort); } catch (NumberFormatException e) { throw new SQLException( "Invalid proxy port. Port is not numeric: " + proxyPort); } if (proxyType == null) { proxyType = "HTTP"; } proxy = new Proxy(Type.valueOf(proxyType), new InetSocketAddress( proxyHostname, port)); } boolean statelessModeBoolean = Boolean.parseBoolean(statelessMode); SessionParameters sessionParameters = getSessionParameters(info2); debug(sessionParameters.toString()); // if (url.startsWith("jdbc:kawanfw://")) { // url = url.replace("jdbc:kawanfw", "http"); // } // If we have passed the "proxy" property, build back the // instance from the property value // 1) Treat the case the user did a property.put(proxy) instead of // property.setProperty(proxy.toString()) if (proxy == null) { Object objProxy = info2.get("proxy"); if (objProxy != null && objProxy instanceof Proxy) { proxy = (Proxy) proxy; } // 2) Treat the case the user as correctly used // property.setProperty(httpProxy.toString()) else { String proxyStr = info2.getProperty("proxy"); debug("proxyStr:" + proxyStr); if (proxyStr != null) { proxy = RemoteDriverUtil.buildProxy(proxyStr); } } } // If we have passed the "sessionParameters" property, build back // the // instance from the property value // 1) Treat the case the user did a property.put(sessionParameters) // instead of property.setProperty(sessionParameters.toString()) Object objSessionParameters = info2.get("sessionParameters"); if (objSessionParameters != null && objSessionParameters instanceof SessionParameters) { String jsonString = SessionParametersGson .toJson((SessionParameters) (objSessionParameters)); if (jsonString != null) { sessionParameters = SessionParametersGson .fromJson(jsonString); } } // 2) Treat the case the user as correctly used // property.setProperty(sessionParameters.toString()) else { String jsonString = info2.getProperty("sessionParameters"); if (jsonString != null) { sessionParameters = SessionParametersGson .fromJson(jsonString); } } debug("url : " + url); debug("Proxy : " + proxy); debug("sessionParameters: " + sessionParameters); boolean doJoinResultSetMetaData = false; if (joinResultSetMetaData != null) { doJoinResultSetMetaData = Boolean .parseBoolean(joinResultSetMetaData); debug("joinResultSetMetaData: " + doJoinResultSetMetaData); } PasswordAuthentication passwordAuthentication = null; if (proxy != null && proxyUsername != null) { passwordAuthentication = new PasswordAuthentication(proxyUsername, proxyPassword.toCharArray()); } Connection connection = new RemoteConnection(url, username, password.toCharArray(), proxy, passwordAuthentication, sessionParameters, statelessModeBoolean, doJoinResultSetMetaData); return connection; } /** * Returns the SessionParameters from the passed Properties * * @param info * the properties * @return SessionParameters from the passed Properties */ private SessionParameters getSessionParameters(Properties info) throws SQLException { SessionParameters sessionParameters = new SessionParameters(); String maxLengthForString = info.getProperty("maxLengthForString"); String encryptionPassword = info.getProperty("encryptionPassword"); String htmlEncoding = info.getProperty("htmlEncoding"); String acceptAllSslCertificates = info .getProperty("acceptAllSslCertificates"); String compression = info.getProperty("compression"); String uploadChunkLength = info.getProperty("uploadChunkLength"); String downloadChunkLength = info.getProperty("downloadChunkLength"); String joinResultSetMetaData = info .getProperty("joinResultSetMetaData"); // debug(""); // debug("maxLengthForString: " + maxLengthForString); // debug("uploadBufferSize : " + uploadBufferSize); // debug("downloadBufferSize: " + downloadBufferSize); // debug("encryptionPassword: " + encryptionPassword); debug("joinResultSetMetaData: " + joinResultSetMetaData); if (maxLengthForString != null) { int maxLengthForStringInteger; try { maxLengthForStringInteger = Integer .parseInt(maxLengthForString); sessionParameters .setMaxLengthForString(maxLengthForStringInteger); } catch (NumberFormatException e) { throw new SQLException( "Invalid maxLengthForString. Not numeric: " + maxLengthForString); } } if (encryptionPassword != null) { sessionParameters.setEncryptionPassword(encryptionPassword .toCharArray()); } if (htmlEncoding != null) { sessionParameters.setHtmlEncodingOn(Boolean .parseBoolean(htmlEncoding)); } if (compression != null) { sessionParameters.setCompressionOn(Boolean .parseBoolean(compression)); } if (acceptAllSslCertificates != null) { sessionParameters.setAcceptAllSslCertificates(Boolean .parseBoolean(acceptAllSslCertificates)); } if (uploadChunkLength != null) { long uploadChunkLengthLong; try { uploadChunkLengthLong = Long.parseLong(uploadChunkLength); sessionParameters .setUploadChunkLength(uploadChunkLengthLong); } catch (NumberFormatException e) { throw new SQLException( "Invalid uploadChunkLength. Not numeric: " + uploadChunkLength); } } if (downloadChunkLength != null) { long downloadChunkLengthLong; try { downloadChunkLengthLong = Long.parseLong(downloadChunkLength); sessionParameters .setDownloadChunkLength(downloadChunkLengthLong); } catch (NumberFormatException e) { throw new SQLException( "Invalid downloadChunkLength. Not numeric: " + downloadChunkLength); } } return sessionParameters; } /** * Retrieves whether the driver thinks that it can open a connection to the * given URL. Typically drivers will return true if they * understand the subprotocol specified in the URL and false if * they do not.
*
* The AceQL driver requires an URL which is an http url in the format:
* {@code jdbc:aceql:http(s):///} *
*
* Example:
* {@code jdbc:aceql:https://www.aceql.com:9443/ServerSqlManager}
*
* Note that the {@code "jdbc:aceql:"} prefix is optional and thus an URL * such as {@code https://www.aceql.com:9443/ServerSqlManager} is accepted * * @param url * the URL of the database * @return true if this driver understands the given URL; * false otherwise * @exception SQLException * if a database access error occurs */ @Override public boolean acceptsURL(String url) throws SQLException { if (url == null) { throw new IllegalArgumentException("url is null!"); } String urlHeader = JdbcUrlHeader.JDBC_URL_HEADER; if (url.startsWith(urlHeader)) { url = JdbcUrlHeader.getUrlHttpOnly(url); return isHttpProtocolUrl(url); } // We still accept for now old raw format that starts directly with // "http://" System.err.println("WARNING: url should be in: \"" + urlHeader + "http://hostname:port/ServerSqlManager\" format."); return isHttpProtocolUrl(url); } /** * Return true if the passed string is an URL with HTTP(S) protocol * * @param url * the URL to test * @return true if the URL is HTTP */ private boolean isHttpProtocolUrl(String url) { URL theUrl = null; try { theUrl = new URL(url); } catch (MalformedURLException e) { return false; } String protocol = theUrl.getProtocol(); if (protocol.equals("http") || protocol.equals("https")) { return true; } else { return false; } } /** * Build a new DriverPropertyInfo with the passed property * * @param property * the property to pass as name and value * @param info * the properties * @return a DriverPropertyInfo with the propery name and value */ private DriverPropertyInfo getNewDriverPropertyInfo(String property, Properties info) { return new DriverPropertyInfo(property, info.getProperty(property)); } /** * Gets information about the possible properties for this driver. *

* The getPropertyInfo method is intended to allow a generic * GUI tool to discover what properties it should prompt a human for in * order to get enough information to connect to a database. Note that * depending on the values the human has supplied so far, additional values * may become necessary, so it may be necessary to iterate though several * calls to the getPropertyInfo method. * * @param url * the URL of the database to which to connect * @param info * a proposed list of tag/value pairs that will be sent on * connect open * @return an array of DriverPropertyInfo objects describing * possible properties. * @exception SQLException * if a database access error occurs */ @Override public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { List driverPropertyInfoList = new ArrayList(); if (info != null) { info.remove("RemarksReporting"); } DriverPropertyInfo driverPropertyInfo = null; driverPropertyInfo = getNewDriverPropertyInfo("user", info); driverPropertyInfo.description = "Username to connect to the remote database as"; driverPropertyInfo.required = true; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = new DriverPropertyInfo("password", null); driverPropertyInfo.description = "Password to use when authenticating"; driverPropertyInfo.required = true; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo("proxyType", info); driverPropertyInfo.description = "Proxy Type to use: HTTP or SOCKS. Defaults to HTTP"; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo("proxyHostname", info); driverPropertyInfo.description = "Proxy hostname to use"; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo("proxyPort", info); driverPropertyInfo.description = "Proxy Port to use"; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo("proxyUsername", info); driverPropertyInfo.description = "Proxy credential username"; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo("proxyPassword", info); driverPropertyInfo.description = "Proxy credential password"; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo("maxLengthForString", info); driverPropertyInfo.description = "Int for the maximum authorized length for a string for upload or download Should be <= 2097152 (2Mb). Defaults to 2097152."; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo("encryptionPassword", info); driverPropertyInfo.description = "The password to use to encrypt all request parameter names and values. Defaults to null (no encryption)."; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo("htmlEncoding", info); driverPropertyInfo.description = "Boolean that says if the upload/download of Clob using character stream or ASCII stream must be html encoded. Defaults to true."; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo("compression", info); driverPropertyInfo.description = "Boolean to say if the Driver is configured to contact the remote server using http compression. Defaults to true. Defaults to true."; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo( "acceptAllSslCertificates", info); driverPropertyInfo.description = "Boolean thats says if client sides allows HTTPS call with all SSL Certificates, including \"invalid\" or self-signed Certificates. Defaults to false."; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo("statelessMode", info); driverPropertyInfo.description = "Boolean that says if the Driver is configured to contact the remote server in stateless mode. Defaults to false."; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo("uploadChunkLength", info); driverPropertyInfo.description = "Long for upload chunk length to be used by FileSession.upload(File, String). Defaults to 10Mb. 0 means files are not chunked."; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo("downloadChunkLength", info); driverPropertyInfo.description = "Long for download chunk length to be used by FileSession.download(String, File). Defaults to 10Mb. 0 means files are not chunked."; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); driverPropertyInfo = getNewDriverPropertyInfo("joinResultSetMetaData", info); driverPropertyInfo.description = "Boolean to say if ResultSet MetaData is to be downloaded along with ResultSet. Defaults to false."; driverPropertyInfo.required = false; driverPropertyInfoList.add(driverPropertyInfo); DriverPropertyInfo[] arrayOfDriverPropertyInfo = driverPropertyInfoList .toArray(new DriverPropertyInfo[driverPropertyInfoList.size()]); return arrayOfDriverPropertyInfo; } /** * Retrieves this driver's major version number. * * @return this driver's major version number */ @Override public int getMajorVersion() { return 3; } /** * Gets the driver's minor version number. * * @return this driver's minor version number */ @Override public int getMinorVersion() { return 1; } /** * Reports whether this driver is a genuine JDBC CompliantTM driver. A driver may only report * true here if it passes the JDBC compliance tests; otherwise * it is required to return false. *

* JDBC compliance requires full support for the JDBC API and full support * for SQL 92 Entry Level. *

* Because the driver is not a genuine JDBC CompliantTM driver, method returns false * * @return false */ @Override public boolean jdbcCompliant() { return false; } /** * debug tool */ private static void debug(String s) { if (DEBUG) { ClientLogger.getLogger().log(Level.WARNING, s); } } // ///////////////////////////////////////////////////////// // JAVA 7 METHOD EMULATION // // ///////////////////////////////////////////////////////// /** * Return the parent Logger of all the Loggers used by this driver. This * should be the Logger farthest from the root Logger that is still an * ancestor of all of the Loggers used by this driver. Configuring this * Logger will affect all of the log messages generated by the driver. In * the worst case, this may be the root Logger. * * @return the parent Logger for this driver * @throws SQLFeatureNotSupportedException * if the driver does not use java.util.logging. * @since 1.7 */ // @Override do not not override for Java 6 compatibility public Logger getParentLogger() throws SQLFeatureNotSupportedException { throw new SQLFeatureNotSupportedException(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy