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.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.kawanfw.driver.jdbc.abstracts.AbstractConnection;
import com.aceql.client.jdbc.http.AceQLHttpApi;
import com.aceql.client.jdbc.util.AceQLConnectionUtil;
import com.aceql.client.metadata.RemoteDatabaseMetaData;
/**
* 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 #setCancelled(AtomicBoolean)}
* - {@link #setGzipResult(boolean)}
* - {@link #setPrettyPrinting(boolean)}
* - {@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 an int
that specifies the read timeout value,
* in milliseconds, to be used when an http connection is
* established to the remote server. 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) {
throw new NullPointerException("serverUrl is null!");
}
if (database == null) {
throw new NullPointerException("database is null!");
}
if (username == null) {
throw new NullPointerException("username is null!");
}
if (password == null) {
throw new NullPointerException("password is null!");
}
aceQLHttpApi = new AceQLHttpApi(serverUrl, database, username, password, proxy, passwordAuthentication);
} catch (Exception e) {
if (e instanceof AceQLException) {
throw (AceQLException) e;
} else {
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;
}
/**
* Returns a RemoteDatabaseMetaData instance in order to retrieve metadata info.
* @return a RemoteDatabaseMetaData instance in order to retrieve metadata info.
*/
public RemoteDatabaseMetaData getRemoteDatabaseMetaData() {
RemoteDatabaseMetaData remoteDatabaseMetaData = new RemoteDatabaseMetaData(this);
return remoteDatabaseMetaData;
}
/*
* (non-Javadoc)
*
* @see java.sql.Connection#close()
*/
@Override
public void close() {
this.closed = true;
try {
aceQLHttpApi.close();
} catch (AceQLException e) {
// Because close() can not throw an Exception, we wrap the
// AceQLException with a RuntimeException
throw new RuntimeException(e.getMessage(), e);
}
}
public void logout() {
try {
aceQLHttpApi.logout();
} catch (AceQLException e) {
// Because close() can not throw an Exception, we wrap the
// AceQLException with a RuntimeException
throw new RuntimeException(e.getMessage(), e);
}
}
/*
* (non-Javadoc)
*
* @see java.sql.Connection#commit()
*/
@Override
public void commit() throws SQLException {
aceQLHttpApi.commit();
}
/*
* (non-Javadoc)
*
* @see java.sql.Connection#rollback()
*/
@Override
public void rollback() throws SQLException {
aceQLHttpApi.rollback();
}
/*
* (non-Javadoc)
*
* @see java.sql.Connection#setHoldability(int)
*/
@Override
public void setTransactionIsolation(int level) throws SQLException {
String levelStr = AceQLConnectionUtil.getTransactionIsolationAsString(level);
aceQLHttpApi.setTransactionIsolation(levelStr);
}
/*
* (non-Javadoc)
*
* @see java.sql.Connection#setHoldability(int)
*/
@Override
public void setHoldability(int holdability) throws SQLException {
String holdabilityStr = AceQLConnectionUtil.getHoldabilityAsString(holdability);
aceQLHttpApi.setHoldability(holdabilityStr);
}
/*
* (non-Javadoc)
*
* @see java.sql.Connection#setAutoCommit(boolean)
*/
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
aceQLHttpApi.setAutoCommit(autoCommit);
}
/*
* (non-Javadoc)
*
* @see java.sql.Connection#isReadOnly()
*/
@Override
public boolean getAutoCommit() throws SQLException {
return aceQLHttpApi.getAutoCommit();
}
/*
* (non-Javadoc)
*
* @see
* org.kawanfw.driver.jdbc.abstracts.AbstractConnection#setReadOnly(boolean)
*/
@Override
public void setReadOnly(boolean readOnly) throws SQLException {
// TODO Auto-generated method stub
aceQLHttpApi.setReadOnly(readOnly);
}
/*
* (non-Javadoc)
*
* @see java.sql.Connection#isReadOnly()
*/
@Override
public boolean isReadOnly() throws SQLException {
return aceQLHttpApi.isReadOnly();
}
/*
* (non-Javadoc)
*
* @see java.sql.Connection#getHoldability()
*/
@Override
public int getHoldability() throws SQLException {
String result = aceQLHttpApi.getHoldability();
return AceQLConnectionUtil.getHoldability(result);
}
/*
* (non-Javadoc)
*
* @see java.sql.Connection#getTransactionIsolation()
*/
@Override
public int getTransactionIsolation() throws SQLException {
String result = aceQLHttpApi.getTransactionIsolation();
return AceQLConnectionUtil.getTransactionIsolation(result);
}
/*
* (non-Javadoc)
*
* @see java.sql.Connection##createStatement()
*/
@Override
public Statement createStatement() throws SQLException {
AceQLStatement aceQLStatement = new AceQLStatement(this);
return aceQLStatement;
}
/*
* (non-Javadoc)
*
* @see org.kawanfw.driver.jdbc.abstracts.AbstractConnection#prepareStatement
* (java.lang.String)
*/
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
AceQLPreparedStatement aceQLPreparedStatement = new AceQLPreparedStatement(this, sql);
return aceQLPreparedStatement;
}
/*
* (non-Javadoc)
*
* @see
* org.kawanfw.driver.jdbc.abstracts.AbstractConnection#prepareCall(java.lang.
* String)
*/
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
AceQLCallableStatement aceQLCallableStatement = new AceQLCallableStatement(this, sql);
return aceQLCallableStatement;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#clone()
*/
@Override
public Connection clone() {
AceQLHttpApi aceQLHttpApi = this.aceQLHttpApi.clone();
AceQLConnection aceQLConnection = new AceQLConnection(aceQLHttpApi);
return aceQLConnection;
}
// //////////////////////////////////////////////////////////////
// / AceQLConnection methods //
// /////////////////////////////////////////////////////////////
/**
* Returns the SDK current Version.
*
* @return the SDK current Version
*/
public String getClientVersion() {
return aceQLHttpApi.getClientVersion();
}
/**
* Returns the server product version
*
* @return the server product version
*
* @throws AceQLException if any Exception occurs
*/
public String getServerVersion() throws AceQLException {
return aceQLHttpApi.getServerVersion();
}
/**
* Says if trace is on
*
* @return true if trace is on
*/
public boolean isTraceOn() {
return aceQLHttpApi.isTraceOn();
}
/**
* Sets the trace on/off
*
* @param traceOn if true, trace will be on
*/
public void setTraceOn(boolean traceOn) {
aceQLHttpApi.setTraceOn(traceOn);
}
/**
* Says if JSON contents are to be pretty printed. Defaults to false.
*
* @param prettyPrinting if true, JSON contents are to be pretty printed
*/
public void setPrettyPrinting(boolean prettyPrinting) {
aceQLHttpApi.setPrettyPrinting(prettyPrinting);
}
/**
* Define if SQL result sets are returned compressed with the GZIP file format
* before download. Defaults to true.
*
* @param gzipResult if true, sets are compressed before download
*/
public void setGzipResult(boolean gzipResult) {
aceQLHttpApi.setGzipResult(gzipResult);
}
/**
* Returns the cancelled value set by the progress indicator
*
* @return the cancelled value set by the progress indicator
*/
public AtomicBoolean getCancelled() {
return aceQLHttpApi.getCancelled();
}
/**
* Sets the sharable canceled variable that will be used by the progress
* indicator to notify this instance that the user has cancelled the current
* Blob/Clob upload or download.
*
* @param cancelled the Sharable canceled variable that will be used by the
* progress indicator to notify this instance that the end user
* has cancelled the current Blob/Clob upload or download
*
*/
public void setCancelled(AtomicBoolean cancelled) {
aceQLHttpApi.setCancelled(cancelled);
}
/**
* Returns the sharable progress variable that will store Blob/Clob upload or
* download progress between 0 and 100
*
* @return the sharable progress variable that will store Blob/Clob upload or
* download progress between 0 and 100
*
*/
public AtomicInteger getProgress() {
return aceQLHttpApi.getProgress();
}
/**
* Sets the sharable progress variable that will store Blob/Clob upload or
* download progress between 0 and 100. Will be used by progress indicators to
* show the progress.
*
* @param progress the sharable progress variable
*/
public void setProgress(AtomicInteger progress) {
aceQLHttpApi.setProgress(progress);
}
/*
* (non-Javadoc)
*
* @see java.sql.Connection#close()
*/
@Override
public boolean isClosed() throws SQLException {
return closed;
}
}