src.main.java.com.aceql.client.jdbc.AceQLConnection Maven / Gradle / Ivy
Show all versions of aceql-http-client-sdk Show documentation
/*
* This file is part of AceQL Client SDK.
* AceQL Client SDK: Remote JDBC access over HTTP with AceQL HTTP.
* Copyright (C) 2020, KawanSoft SAS
* (http://www.kawansoft.com). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.aceql.client.jdbc;
import java.io.Closeable;
import java.net.HttpURLConnection;
import java.net.PasswordAuthentication;
import java.net.Proxy;
import java.net.URLConnection;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.kawanfw.driver.jdbc.abstracts.AbstractConnection;
import org.kawanfw.driver.util.Tag;
import com.aceql.client.jdbc.http.AceQLHttpApi;
import com.aceql.client.jdbc.util.AceQLConnectionUtil;
import com.aceql.client.jdbc.util.SimpleClassCaller;
import com.aceql.client.metadata.RemoteDatabaseMetaData;
import com.aceql.client.metadata.ResultSetMetaDataPolicy;
/**
* Provides a Connection
implementation that enable to use a
* virtual JDBC Connection that is mapped to a Server JDBC
* Connection
in order to access a remote SQL database through
* HTTP.
* This class acts as a wrapper of AceQL HTTP APIs.
*
* This Connection
implementation supports:
*
* - Main JDBC data formats.
* Blob/Clob
updates with clean streaming behavior when
* uploading.
* Blob/Clob
reads with clean streaming behavior when
* downloading.
* - Transaction through
commit
and rollback
orders.
*
*
*
* Supplementary specific methods that are not of instance of Connection are
* also added.
*
* After creating the AceQLConnection
, just use it like a regular
* Connection
to execute your PreparedStatement
and
* Statement
, and to navigate through your ResultSet
.
*
* All thrown exceptions are of type {@link AceQLException}. Use
* {@link SQLException#getCause()} to get the original wrapped Exception.
*
* The AceQL error_type value is available via the
* {@code AceQLException#getErrorCode()} and the remote_stack value as a string
* is available with {@link AceQLException#getRemoteStackTrace()}
*
* Example:
*
*
* // Define URL of the path to the AceQL Manager Servlet
* // We will use a secure SSL/TLS session. All uploads/downloads of SQL
* // commands & data will be encrypted.
* String url = "https://www.acme.org:9443/aceql";
*
* // The login info for strong authentication on server side.
* // These are *not* the username/password of the remote JDBC Driver,
* // but are the auth info checked by remote server
* // {@code DatabaseConfigurator.login(username, password)} method.
* String database = "mydatabase";
* String username = "MyUsername";
* String password = "MyPassword";
*
* // Attempts to establish a connection to the remote database:
* Connection connection = new AceQLConnection(serverUrl, database, username, password);
*
* // We can now use our remote JDBC Connection as a regular JDBC
* // Connection for our queries and updates:
* String sql = "SELECT CUSTOMER_ID, FNAME, LNAME FROM CUSTOMER " + "WHERE CUSTOMER_ID = ?";
* PreparedStatement prepStatement = connection.prepareStatement(sql);
* prepStatement.setInt(1, 1);
*
* ResultSet rs = prepStatement.executeQuery();
* while (rs.next()) {
* String customerId = rs.getString("customer_id");
* String fname = rs.getString("fname");
* String lname = rs.getString("lname");
*
* System.out.println("customer_id: " + customerId);
* System.out.println("fname : " + fname);
* System.out.println("lname : " + lname);
* // Etc.
* }
*
*
*
The following dedicated AceQLConnection
methods
* are specific to the software and may be accessed with a cast:
*
* - {@link #getCancelled()}
* - {@link #setCancelled(AtomicBoolean)}
* - {@link #getResultSetMetaDataPolicy()}
* - {@link #setResultSetMetaDataPolicy(ResultSetMetaDataPolicy)}
* - {@link #isGzipResult()}
* - {@link #setGzipResult(boolean)}
* - {@link #getProgress()}
* - {@link #setProgress(AtomicInteger)}
*
*
*
* All long Blobs update/reading that need to be run on a separated thread may
* be followed in Swing using a JProgressBar
,
* ProgressMonitor
or Android using a {@code ProgressDialog}
*
* This is done by sharing two atomic variables that will be declared as fields:
*
* - An {@code AtomicInteger} that represents the Blob/Clob transfer progress
* between 0 and 100.
* - An {@code AtomicBoolean} that says if the end user has cancelled the
* Blob/Clob transfer.
*
*
* The atomic variables values will be shared by AceQL download/upload processes
* and by the Progress Monitor used for the Progress Bar. The values are to be
* initialized and passed to {@code AceQLConnection} before the JDBC actions
* with the setters:
* {@link AceQLConnection#setProgress(AtomicInteger)}
* {@link AceQLConnection#setCancelled(AtomicBoolean)}
*
*
* Example:
*
*
* // Attempts to establish a connection to the remote database:
* Connection connection = new AceQLConnection(url, username, password, database);
*
* // Pass the mutable & sharable progress and canceled to the
* // underlying AceQLConnection.
* // - progress value will be updated by the AceQLConnection and
* // retrieved by progress monitors to increment the progress.
* // - cancelled value will be updated to true if user cancels the
* // task and AceQLConnection will interrupt the blob(s) transfer.
*
* ((AceQLConnection) connection).setProgress(progress);
* ((AceQLConnection) connection).setCancelled(cancelled);
*
* // Execute JDBC statement
*
*
*
See the source code of
* SqlProgressMonitorDemo.java that demonstrates the use of atomic
* variables when inserting a Blob.
*
* @author Nicolas de Pomereu
*
*/
public class AceQLConnection extends AbstractConnection implements Connection, Cloneable, Closeable {
/** The Http instance that does all Http stuff */
AceQLHttpApi aceQLHttpApi = null;
/** is Connection open or closed */
private boolean closed = false;
/**
* Sets the connect timeout.
*
* @param connectTimeout Sets a specified timeout value, in milliseconds, to be
* used when opening a communications link to the remote
* server. If the timeout expires before the connection
* can be established, a java.net.SocketTimeoutException
* is raised. A timeout of zero is interpreted as an
* infinite timeout. See
* {@link URLConnection#setConnectTimeout(int)}
*/
public static void setConnectTimeout(int connectTimeout) {
AceQLHttpApi.setConnectTimeout(connectTimeout);
}
/**
* Sets the read timeout.
*
* @param readTimeout Sets the read timeout to a specified timeout, in
* milliseconds. A non-zero value specifies the timeout when
* reading from Input stream when a connection is established
* to a resource. If the timeout expires before there is data
* available for read, a java.net.SocketTimeoutException is
* raised. A timeout of zero is interpreted as an infinite
* timeout. See {@link URLConnection#setReadTimeout(int)}
*/
public static void setReadTimeout(int readTimeout) {
AceQLHttpApi.setReadTimeout(readTimeout);
}
/**
* Login on the AceQL server and connect to a database.
*
* @param serverUrl the URL of the AceQL server. Example:
* http://localhost:9090/aceql
* @param database the server database to connect to.
* @param username the login
* @param password the password
* @throws SQLException if any I/O error occurs
*/
public AceQLConnection(String serverUrl, String database, String username, char[] password) throws SQLException {
this(serverUrl, database, username, password, null, null);
}
/**
* Login on the AceQL server and connect to a database.
*
* @param serverUrl the URL of the AceQL server. Example:
* http://localhost:9090/aceql
* @param database the server database to connect to.
* @param username the login
* @param password the password
* @param proxy the proxy to use. null if none.
* @param passwordAuthentication the username and password holder to use for
* authenticated proxy. Null if no proxy or if
* proxy does not require authentication.
* @throws SQLException if any I/O error occurs
*/
public AceQLConnection(String serverUrl, String database, String username, char[] password, Proxy proxy,
PasswordAuthentication passwordAuthentication) throws SQLException {
try {
if (serverUrl == null) {
Objects.requireNonNull(serverUrl, "serverUrl can not be null!");
}
if (database == null) {
Objects.requireNonNull(database, "database can not be null!");
}
if (username == null) {
Objects.requireNonNull(username, "username can not be null!");
}
if (password == null) {
Objects.requireNonNull(password, "password can not be null!");
}
aceQLHttpApi = new AceQLHttpApi(serverUrl, database, username, password, null, proxy,
passwordAuthentication);
} catch (AceQLException aceQlException) {
throw aceQlException;
} catch (Exception e) {
throw new AceQLException(e.getMessage(), 0, e, null, HttpURLConnection.HTTP_OK);
}
}
/**
* Returns the {@link ResultSetMetaDataPolicy} in use.
*
* @return the {@code ResultSetMetaDataPolicy} in use.
*/
public ResultSetMetaDataPolicy getResultSetMetaDataPolicy() {
return this.aceQLHttpApi.getResultSetMetaDataPolicy();
}
/**
* Sets the {@link ResultSetMetaDataPolicy} to use.
*
* @param resultSetMetaDataPolicy the {@code ResultSetMetaDataPolicy} to use
*/
public void setResultSetMetaDataPolicy(ResultSetMetaDataPolicy resultSetMetaDataPolicy) {
this.aceQLHttpApi.setResultSetMetaDataPolicy(resultSetMetaDataPolicy);
}
/**
* Connect to a database using an AceQL existing Session ID instead of a
* password.
*
* @param serverUrl the URL of the AceQL server. Example:
* http://localhost:9090/aceql
* @param database the server database to connect to
* @param username the login
* @param sessionId the existing AceQL Session ID
* @throws SQLException if any I/O error occurs
*/
public AceQLConnection(String serverUrl, String database, String username, String sessionId) throws SQLException {
this(serverUrl, database, username, sessionId, null, null);
}
/**
* Connect to a database using an AceQL existing Session ID instead of a
* password.
*
* @param serverUrl the URL of the AceQL server. Example:
* http://localhost:9090/aceql
* @param database the server database to connect to
* @param username the login
* @param sessionId the existing AceQL Session ID
* @param proxy the proxy to use. null if none.
* @param passwordAuthentication the username and password holder to use for
* authenticated proxy. Null if no proxy or if
* proxy does not require authentication.
* @throws SQLException if any I/O error occurs
*/
public AceQLConnection(String serverUrl, String database, String username, String sessionId, Proxy proxy,
PasswordAuthentication passwordAuthentication) throws SQLException {
try {
if (serverUrl == null) {
Objects.requireNonNull(serverUrl, "serverUrl can not be null!");
}
if (database == null) {
Objects.requireNonNull(database, "database can not be null!");
}
if (username == null) {
Objects.requireNonNull(username, "username can not be null!");
}
if (sessionId == null) {
Objects.requireNonNull(sessionId, "sessionId can not be null!");
}
aceQLHttpApi = new AceQLHttpApi(serverUrl, database, username, null, sessionId, proxy,
passwordAuthentication);
} catch (AceQLException aceQlException) {
throw aceQlException;
} catch (Exception e) {
throw new AceQLException(e.getMessage(), 0, e, null, HttpURLConnection.HTTP_OK);
}
}
/**
* Private constructor for Clone
*
* @param aceQLHttpApi the AceQL http Api Clone
*/
private AceQLConnection(AceQLHttpApi aceQLHttpApi) {
this.aceQLHttpApi = aceQLHttpApi;
}
/*
* (non-Javadoc)
*
* @see org.kawanfw.driver.jdbc.abstracts.AbstractConnection#getMetaData()
*/
@Override
public DatabaseMetaData getMetaData() throws SQLException {
List> params = new ArrayList<>();
List