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

src-main.org.awakefw.sql.api.client.AwakeDriver Maven / Gradle / Ivy

Go to download

Awake SQL is an open source framework that allows remote and secure JDBC access through HTTP.

The newest version!
/*
 * This file is part of Awake SQL. 
 * Awake SQL: Remote JDBC access over HTTP.                                    
 * Copyright (C) 2013,  KawanSoft SAS
 * (http://www.kawansoft.com). All rights reserved.                    
 *                                                                         
 * Awake SQL is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.         
 *              
 * Awake SQL 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 General Public License
 * along with this program; if not, see .
 *
 * If you develop commercial activities using Awake SQL, you must: 
 * a) disclose and distribute all source code of your own product,
 * b) license your own product under the GNU General Public License.
 * 
 * You can be released from the requirements of the license by
 * purchasing a commercial license. Buying such a license will allow you 
 * to ship Awake SQL with your closed source products without disclosing 
 * the source code.
 *
 * For more information, please contact KawanSoft SAS at this
 * address: [email protected]
 * 
 * Any modifications to this file must keep this entire header
 * intact.
 */
package org.awakefw.sql.api.client;

import java.lang.reflect.Constructor;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverPropertyInfo;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Logger;

import org.apache.commons.lang3.StringUtils;
import org.awakefw.commons.api.client.HttpProtocolParameters;
import org.awakefw.commons.api.client.HttpProxy;
import org.awakefw.file.api.util.AwakeDebug;
import org.awakefw.file.util.AwakeClientLogger;

/**
 * 
 * The Awake SQL Driver class in order to access remote SQL databases through HTTP from
 * Android or Java desktop programs.
 * 
* Note that The preferred way to connect to a * remote database is {@link AwakeConnection}, as described in * the Tutorial.
*
* The Driver implementation is provided because it's necessary for usage with * third party programs as SQL client front end (SQuirreL, Netbeans IDE, etc.)
*
* user & password are the only required properties.
*
* Properties: *
    *
  • user: Username to connect to the remote database as.
  • *
  • password: Password to use when authenticating.
  • *
  • httpProxyAddress: Http proxy address to use.
  • *
  • httpProxyPort: Http proxy Port to use.
  • *
  • httpProxyUsername: Http proxy credential username.
  • *
  • httpProxyPassword: Http proxy credential password.
  • *
  • httpProxyWorkstation: For NTLM proxy: the workstation.
  • *
  • httpProxyDomain: For NTLM proxy: the domain.
  • *
  • maxLengthForString: Int for the maximum authorized length for a * string for upload or download Should be <= 2097152 (2Mb). Defaults to * 2097152.
  • *
  • uploadBufferSize: Int for the buffer size when uploading a * Blob/Clob. Defaults to 20480 (20 Kb).
  • *
  • downloadBufferSize: Int for the buffer size when downloading a * Blob/Clob. Defaults to 20480 (20 Kb).
  • *
  • acceptAllSslCertificates: Boolean to say if client sides allows HTTPS * call with all SSL Certificates, including "invalid" or self-signed Certificates.
  • *
  • 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. *
  • statelessMode: Boolean to say if the Driver is configured * to contact the remote server in stateless mode.
  • *
*

* * You may also pass any Jakarta HttpClient parameter as with * {@link HttpProtocolParameters}:
*
*

    *
  • The name of the HttpClient parameter must be in the format: * 'http-client-<name>'.
  • *
  • The value of HttpClient parameter: *
      *
    • The String value for String parameters
    • *
    • The String: * '[value.getClass().ClassName(), value.toString()]' for * parameters of other Java type.
    • *
    *
*

* * Examples:
*
*

    *
  • To pass a 'http.socket.timeout' of 10 seconds, pass the * following property: *
      *
    • property name: 'http-client-http.socket.timeout'.
    • *
    • property value: '[java.lang.Integer, 10000]'.
    • *
    *
  • *
  • To pass a 'http.useragent' of * 'www.acme.org User-Agent', pass the following property: *
      *
    • property name: 'http-client-http.useragent'.
    • *
    • property value: 'www.acme.org User-Agent'.
    • *
    * * @author Nicolas de Pomereu * @since 1.0 * */ public class AwakeDriver implements java.sql.Driver { /** The debug flag */ private static boolean DEBUG = AwakeDebug.isSet(AwakeDriver.class); /** * Constructor. */ public AwakeDriver() { } /** * Attempts to make a database connection to the given URL. * * The Awake 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 Awake 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; } String username = info.getProperty("user"); String password = info.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 httpProxyAddress = info.getProperty("httpProxyAddress"); String httpProxyPort = info.getProperty("httpProxyPort"); String httpProxyUsername = info.getProperty("httpProxyUsername"); String httpProxyPassword = info.getProperty("httpProxyPassword"); String httpProxyWorkstation = info.getProperty("httpProxyWorkstation"); String httpProxyDomain = info.getProperty("httpProxyDomain"); String statelessMode = info.getProperty("statelessMode"); int port = -1; HttpProxy httpProxy = null; if (httpProxyAddress != null) { try { port = Integer.parseInt(httpProxyPort); } catch (NumberFormatException e) { throw new SQLException( "Invalid HttpProxy port. Port is not numeric: " + httpProxyPort); } if (httpProxyWorkstation != null) { httpProxy = new HttpProxy(httpProxyAddress, port, httpProxyUsername, httpProxyPassword, httpProxyWorkstation, httpProxyDomain); } else { httpProxy = new HttpProxy(httpProxyAddress, port, httpProxyUsername, httpProxyPassword); } } boolean statelessModeBoolean = Boolean .parseBoolean(statelessMode); HttpProtocolParameters httpProtocolParameters = getHttpProtocolParameters(info); debug(httpProtocolParameters.toString()); Connection connection = new AwakeConnection(url, username, password.toCharArray(), httpProxy, httpProtocolParameters, statelessModeBoolean); return connection; } /** * Returns the HttpProtocolParameters from the passed Properties * * @param info * the properties * @return HttpProtocolParameters from the passed Properties */ private HttpProtocolParameters getHttpProtocolParameters(Properties info) throws SQLException { HttpProtocolParameters httpProtocolParameters = new HttpProtocolParameters(); String maxLengthForString = info.getProperty("maxLengthForString"); String uploadBufferSize = info.getProperty("uploadBufferSize"); String downloadBufferSize = info.getProperty("downloadBufferSize"); String encryptionPassword = info.getProperty("encryptionPassword"); String htmlEncoding = info.getProperty("htmlEncoding"); String acceptAllSslCertificates = info.getProperty("acceptAllSslCertificates"); // debug(""); // debug("maxLengthForString: " + maxLengthForString); // debug("uploadBufferSize : " + uploadBufferSize); // debug("downloadBufferSize: " + downloadBufferSize); // debug("encryptionPassword: " + encryptionPassword); if (maxLengthForString != null) { int maxLengthForStringInteger; try { maxLengthForStringInteger = Integer .parseInt(maxLengthForString); httpProtocolParameters .setMaxLengthForString(maxLengthForStringInteger); } catch (NumberFormatException e) { throw new SQLException( "Invalid maxLengthForString. Not numeric: " + maxLengthForString); } } if (uploadBufferSize != null) { int uploadBufferSizeInteger; try { uploadBufferSizeInteger = Integer.parseInt(uploadBufferSize); httpProtocolParameters .setUploadBufferSize(uploadBufferSizeInteger); } catch (NumberFormatException e) { throw new SQLException( "Invalid uploadBufferSize. Not numeric: " + uploadBufferSize); } } if (downloadBufferSize != null) { int downloadBufferSizeInteger; try { downloadBufferSizeInteger = Integer .parseInt(downloadBufferSize); httpProtocolParameters .setDownloadBufferSize(downloadBufferSizeInteger); } catch (NumberFormatException e) { throw new SQLException( "Invalid downloadBufferSize. Not numeric: " + downloadBufferSize); } } if (encryptionPassword != null) { httpProtocolParameters.setEncryptionPassword(encryptionPassword .toCharArray()); } if (htmlEncoding != null) { httpProtocolParameters.setHtmlEncodingOn(Boolean .parseBoolean(htmlEncoding)); } if (acceptAllSslCertificates != null) { httpProtocolParameters.setAcceptAllSslCertificates(Boolean .parseBoolean(acceptAllSslCertificates)); } // Set now the HttpClient parameters setHttpClientParameters(info, httpProtocolParameters); return httpProtocolParameters; } /** * Sets the HttpClient parameters on httpProtocolParameters * * @param info * the driver info * @param httpProtocolParameters * the HttpProtocolParameters to set * @throws SQLException * if any Exception occurs */ private void setHttpClientParameters(Properties info, HttpProtocolParameters httpProtocolParameters) throws SQLException { Map httpClientMap = new HashMap(); // First step: build the map httpClientParams for (Enumeration e = info.propertyNames(); e.hasMoreElements();) { String prop = (String) e.nextElement(); if (prop.startsWith("http-client-")) { String httpClientParam = StringUtils.substringAfter(prop, "http-client-"); String httpClientValue = info.getProperty(prop); httpClientMap.put(httpClientParam, httpClientValue); } } // Get the HttpClient epured properties Set keys = httpClientMap.keySet(); for (String key : keys) { String value = httpClientMap.get(key); if (value == null) { throw new SQLException( "HttpClient value is null for HttpClient parameter: http-client-" + key); } if (value.startsWith("[") && value.endsWith("]")) { // We must analyse the object type from the string // [value.getClass().getName(), value.toString()] setObjectHttpParameter(httpProtocolParameters, key, value); } else { httpProtocolParameters.setHttpClientParameter(key, value); } } } /** * Sets the HttpClient parameter value as an object value * * @param httpProtocolParameters * the HttpProtocolParameters to set * @param key * the HttpClient parameter name * @param value * the HttpClient parameter value * @throws SQLException * if any Exception occurs */ private void setObjectHttpParameter( HttpProtocolParameters httpProtocolParameters, String key, String value) throws SQLException { // We must analyse the object type from the string // [value.getClass().getName(), value.toString()] if (!value.contains(",")) { throw new IllegalArgumentException( "Invalid format for HttpClient parameter http-client-" + key + ". " + "Value does not contain a comma: " + value); } String originalValue = value; value = StringUtils.substringAfter(value, "["); value = StringUtils.substringBeforeLast(value, "]"); String className = StringUtils.substringBefore(value, ","); String stringValue = StringUtils.substringAfter(value, ","); className = className.trim(); stringValue = stringValue.trim(); Class c = null; try { c = Class.forName(className); } catch (ClassNotFoundException e) { throw new IllegalArgumentException( "Invalid format for HttpClient parameter http-client-" + key + ". " + "Value does not contain a valid Java class name: " + originalValue); } try { Constructor ctor = c.getDeclaredConstructor(String.class); ctor.setAccessible(true); Object objValue = ctor.newInstance(stringValue); httpProtocolParameters.setHttpClientParameter(key, objValue); } catch (Exception e) { throw new IllegalArgumentException( "Invalid format for HttpClient parameter http-client-" + key + ". " + "Java class name is not callable with the value: " + originalValue, e); } } /** * 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.
    *
    * Awake driver requires an URL which is an http url in the format:
    * {@code http(s):///}
    *
    * Example:
    * {@code http://www.acme.com/AwakeSqlManager} * * @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 { 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; } } /** * 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 { DriverPropertyInfo[] arrayOfDriverPropertyInfo = new DriverPropertyInfo[15]; if (info != null) { info.remove("RemarksReporting"); } int i = 0; DriverPropertyInfo driverPropertyInfo = null; driverPropertyInfo = new DriverPropertyInfo("user", info.getProperty("user")); driverPropertyInfo.description = "Username to connect to the remote database as"; driverPropertyInfo.required = true; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("password", null); driverPropertyInfo.description = "Password to use when authenticating"; driverPropertyInfo.required = true; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("httpProxyAddress", info.getProperty("httpProxyAddress")); driverPropertyInfo.description = "Http proxy address to use"; driverPropertyInfo.required = false; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("httpProxyPort", info.getProperty("httpProxyPort")); driverPropertyInfo.description = "Http proxy Port to use"; driverPropertyInfo.required = false; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("httpProxyUsername", info.getProperty("httpProxyUsername")); driverPropertyInfo.description = "Http proxy credential username"; driverPropertyInfo.required = false; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("httpProxyPassword", info.getProperty("httpProxyPassword")); driverPropertyInfo.description = "Http proxy credential password"; driverPropertyInfo.required = false; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("httpProxyWorkstation", info.getProperty("httpProxyWorkstation")); driverPropertyInfo.description = "For NTLM proxy: the workstation"; driverPropertyInfo.required = false; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("httpProxyDomain", info.getProperty("httpProxyDomain")); driverPropertyInfo.description = "For NTLM proxy: the domain"; driverPropertyInfo.required = false; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("maxLengthForString", info.getProperty("maxLengthForString")); 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; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("uploadBufferSize", info.getProperty("uploadBufferSize")); driverPropertyInfo.description = "Int for the buffer size when uploading a Blob/Clob. Defaults to 20480 (20 Kb)"; driverPropertyInfo.required = false; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("downloadBufferSize", info.getProperty("downloadBufferSize")); driverPropertyInfo.description = "Int for the buffer size when downloading a Blob/Clob. Defaults to 20480 (20 Kb)"; driverPropertyInfo.required = false; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("encryptionPassword", info.getProperty("encryptionPassword")); driverPropertyInfo.description = "The password to use to encrypt all request parameter names and values. Defaults to null (no encryption)."; driverPropertyInfo.required = false; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("htmlEncoding", info.getProperty("htmlEncoding")); 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; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("acceptAllSslCertificates", info.getProperty("acceptAllSslCertificates")); 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; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; driverPropertyInfo = new DriverPropertyInfo("statelessMode", info.getProperty("statelessMode")); driverPropertyInfo.description = "Boolean that says if the Driver is configured to contact the remote server in stateless mode. Defaults to false."; driverPropertyInfo.required = false; arrayOfDriverPropertyInfo[i++] = driverPropertyInfo; return arrayOfDriverPropertyInfo; } /** * Retrieves this driver's major version number. * * @return this driver's major version number */ @Override public int getMajorVersion() { return 2; } /** * Gets the driver's minor version number. * * @return this driver's minor version number */ @Override public int getMinorVersion() { return 0; } /** * 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 Awake driver is not a 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) { AwakeClientLogger.log(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