/**
*
*/
package org.identityconnectors.oracle;
import static org.identityconnectors.oracle.OracleMessages.MSG_CS_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_CS_HELP;
import static org.identityconnectors.oracle.OracleMessages.MSG_DATABASE_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_DATABASE_HELP;
import static org.identityconnectors.oracle.OracleMessages.MSG_DATASOURCE_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_DATASOURCE_HELP;
import static org.identityconnectors.oracle.OracleMessages.MSG_DRIVER_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_DRIVER_HELP;
import static org.identityconnectors.oracle.OracleMessages.MSG_DROP_CASCADE_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_DSJNDIENV_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_DSJNDIENV_HELP;
import static org.identityconnectors.oracle.OracleMessages.MSG_EXTRA_ATTRS_POLICY_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_EXTRA_ATTRS_POLICY_HELP;
import static org.identityconnectors.oracle.OracleMessages.MSG_HOST_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_HOST_HELP;
import static org.identityconnectors.oracle.OracleMessages.MSG_INVALID_SOURCE_TYPE;
import static org.identityconnectors.oracle.OracleMessages.MSG_NORMALIZER_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_NORMALIZER_HELP;
import static org.identityconnectors.oracle.OracleMessages.MSG_PASSWORD_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_PASSWORD_HELP;
import static org.identityconnectors.oracle.OracleMessages.MSG_PORT_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_PORT_HELP;
import static org.identityconnectors.oracle.OracleMessages.MSG_SOURCE_TYPE_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_SOURCE_TYPE_HELP;
import static org.identityconnectors.oracle.OracleMessages.MSG_URL_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_URL_HELP;
import static org.identityconnectors.oracle.OracleMessages.MSG_USER_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_USER_HELP;
import static org.identityconnectors.oracle.OracleMessages.MSG_USE_DRIVER_FOR_AUTHENTICATION_DISPLAY;
import static org.identityconnectors.oracle.OracleMessages.MSG_USE_DRIVER_FOR_AUTHENTICATION_HELP;
import java.sql.Connection;
import java.sql.SQLException;
import org.identityconnectors.common.StringUtil;
import org.identityconnectors.common.logging.Log;
import org.identityconnectors.common.security.GuardedString;
import org.identityconnectors.dbcommon.JNDIUtil;
import org.identityconnectors.dbcommon.LocalizedAssert;
import org.identityconnectors.dbcommon.SQLUtil;
import org.identityconnectors.framework.common.exceptions.ConnectorException;
import org.identityconnectors.framework.common.objects.ConnectorMessages;
import org.identityconnectors.framework.spi.AbstractConfiguration;
import org.identityconnectors.framework.spi.ConfigurationProperty;
import org.identityconnectors.oracle.OracleDriverConnectionInfo.Builder;
/**
* Set of configuration properties for connecting to Oracle database. We support
* 4 types how user can connect to oracle resource :
* <ul>
* <li>Using dataSource</li>
* <li>Using custom driver with full jdbc url</li>
* <li>Using thin driver</li>
* <li>Using oci driver</li>
* </ul>
*
* <h4><a name="dataSource"/>Getting connection from DataSource. Used when
* <code>dataSource</code> property is set</h4> We will support these properties
* when connecting to oracle using dataSource
* <ul>
* <li>dataSource : Name of jndi name of dataSource : required. It must be
* logical or absolute name of dataSource. No prefix will be added when trying
* to do lookup</li>
* <li>
* dsJNDIEnv : JNDI environment entries needed to lookup datasource. In most
* cases should be empty, needed only when lookuping datasource from different
* server as server where connectors are running.</li>
* <li>user : Administrative account : optional, default we will get connection
* from DS without user/password parameters</li>
* <li>password : Administrative password : optional, default we will get
* connection from DS without user/password parameters</li>
* </ul>
*
* <h4><a name="url"/>Getting connection from DriverManager using full jdbc url.
* Used when <code>url</code> property is set</h4> We will support/require these
* properties when connecting to oracle with url:
* <ul>
* <li>url : Full jdbc url for connecting to oracle</li>
* <li>driver : Classname of jdbc driver</li>
* <li>user : Administrative account when connecting to oracle</li>
* <li>password : Password for admin account.</li>
* </ul>
*
*
* <h4><a name="databaseName"/>Getting connection from DriverManager using Thin
* driver. Used when driver has value 'thin'</h4> We will support/require these
* properties when connecting to oracle :
* <ul>
* <li>host : Name or IP of oracle instance host. This is required property</li>
* <li>port : Port oracle listener is listening to. Default to 1521</li>
* <li>database : Name of local/remote database</li>
* <li>user : Administrative account when connecting to oracle</li>
* <li>adminPassword : Password for admin account.</li>
* </ul>
*
* <h4><a name="aliasName"/>Getting connection from DriverManager using Type 2
* oci driver. Used when driver has value 'oci'</h4> We will require these
* properties when connecting to oracle using oci driver
* <ul>
* <li>database : Name of local alias</li>
* <li>user : Administrative account when connecting to oracle</li>
* <li>password : Password for admin account.</li>
* </ul>
*
* Additionally to these 'connect properties', oracle connector supports
* following configuration properties :
* <ul>
* <li>CaseSensitivityString : String format that influences built ddl
* statements</li>
* <li>sourceType : We can explicitly force connector to use the connect method.
* Can have values : DataSource, Thin Driver, OCI Driver, Custom Driver</li>
* <li>extraAttributesPolicyString : String format that influences which policy
* is applied when extra not applicable attribute is passed into operation</li>
* <li>dropCascade : Flag used at delete user operation. When set to true, user
* is dropped with cascade flag</li>
* <li>normalizerString : String with the value of normalizer name. Can have
* values : FULL, INPUT, INPUT_AUTH</li>
* </ul>
*
* @author kitko
*
*/
public final class OracleConfiguration extends AbstractConfiguration implements Cloneable {
private static final Log LOG = Log.getLog(OracleConfiguration.class);
/** Host name or ip of oracle resource. */
private String host;
/** Port of oracle listener. */
private String port;
/** Driver (thin,oci) or full classname. */
private String driver;
/** Full classname of driver. */
private String driverClassName;
/** Name of oracle database when using thin or oci driver. */
private String database;
/** Name of admin user. */
private String user;
/** Admin password. */
private GuardedString password;
/** JNDI name of datasource. */
private String dataSource;
/** Full url when using custom driver. */
private String url;
/** Optional jndi entries to create initial context. */
private String[] dsJNDIEnv;
/**
* Type/manner how we connect to oracle resource. The field is set by
* validator.
*/
private ConnectionType connType;
/**
* CaseSensitivitySetup of builder. Field is set by validator using the
* caseSensitivityString.
*/
private OracleCaseSensitivitySetup cs;
/**
* String format in special map format using which we build
* OracleCaseSensitivitySetup.
*/
private String caseSensitivityString;
/** Source type is user defined ConnectionType. */
private String sourceType;
/**
* String is special map format using which we build
* ExtraAttributesPolicySetup.
*/
private String extraAttributesPolicyString;
/**
* Extra attributes policy. Field is set by validator from
* extraAttributesPolicyString.
*/
private ExtraAttributesPolicySetup extraAttributesPolicySetup;
/**
* Flag used at delete user operation. When set to true, user is dropped
* with cascade flag.
*/
private boolean dropCascade;
/** String with the value of name of OracleNormalizerName enum. */
private String normalizerString;
/**
* NormalizerName we create in validator using normalizerString. Normalizer
* created by
* {@link OracleNormalizerName#createNormalizer(OracleCaseSensitivitySetup)}
* is sent then to SPI operations.
*/
private OracleNormalizerName normalizerName;
/**
* Force connector to use driver in authentication even when datasource is
* set.
*/
private boolean useDriverForAuthentication;
/**
* Creates configuration.
*/
public OracleConfiguration() {
caseSensitivityString = "default";
cs = new OracleCaseSensitivityBuilder(getConnectorMessages()).build();
port = OracleSpecifics.LISTENER_DEFAULT_PORT;
dropCascade = true;
normalizerString = OracleNormalizerName.INPUT.name();
}
/** Type of connection we will use to connect to Oracle. */
static enum ConnectionType {
/** Connecting using datasource */
DATASOURCE("DataSource"),
/** Connecting using type 4 driver (host,port,databaseName) */
THIN("Thin Driver"),
/** Connecting using type 2 driver (using TNSNAMES.ora) */
OCI("OCI Driver"),
/** Custom driver with custom URL */
FULL_URL("Custom Driver");
private final String sourceType;
String getSourceType() {
return sourceType;
}
ConnectionType(String sourceType) {
this.sourceType = sourceType;
}
static ConnectionType resolveType(String name, ConnectorMessages msg) {
for (ConnectionType type : values()) {
if (type.sourceType.equals(name)) {
return type;
}
}
throw new IllegalArgumentException(msg.format(MSG_INVALID_SOURCE_TYPE, null));
}
}
/**
* Default clone implementation.
*
* @throws ConnectorException
* when super.clone fails
*/
protected OracleConfiguration clone() {
try {
return (OracleConfiguration) super.clone();
} catch (CloneNotSupportedException e) {
throw new ConnectorException("Clone of OracleConfiguration super class failed", e);
}
}
/**
* @return the dataSource
*/
@ConfigurationProperty(order = 0, displayMessageKey = MSG_DATASOURCE_DISPLAY,
helpMessageKey = MSG_DATASOURCE_HELP)
public String getDataSource() {
return dataSource;
}
/**
* @return the dsJNDIEnv
*/
@ConfigurationProperty(order = 1, displayMessageKey = MSG_DSJNDIENV_DISPLAY,
helpMessageKey = MSG_DSJNDIENV_HELP)
public String[] getDsJNDIEnv() {
if (dsJNDIEnv == null) {
return new String[0];
}
String[] res = new String[dsJNDIEnv.length];
System.arraycopy(dsJNDIEnv, 0, res, 0, dsJNDIEnv.length);
return res;
}
/**
* @return the url
*/
@ConfigurationProperty(order = 2, displayMessageKey = MSG_URL_DISPLAY,
helpMessageKey = MSG_URL_HELP)
public String getUrl() {
return url;
}
/**
* @return the driver
*/
@ConfigurationProperty(order = 3, displayMessageKey = MSG_DRIVER_DISPLAY,
helpMessageKey = MSG_DRIVER_HELP)
public String getDriver() {
return driver;
}
/**
* @return the host
*/
@ConfigurationProperty(order = 4, displayMessageKey = MSG_HOST_DISPLAY,
helpMessageKey = MSG_HOST_HELP)
public String getHost() {
return host;
}
/**
* @return the port
*/
@ConfigurationProperty(order = 5, displayMessageKey = MSG_PORT_DISPLAY,
helpMessageKey = MSG_PORT_HELP)
public String getPort() {
return port;
}
/**
* @return the database
*/
@ConfigurationProperty(order = 6, displayMessageKey = MSG_DATABASE_DISPLAY,
helpMessageKey = MSG_DATABASE_HELP)
public String getDatabase() {
return database;
}
/**
* @return the user
*/
@ConfigurationProperty(order = 7, displayMessageKey = MSG_USER_DISPLAY,
helpMessageKey = MSG_USER_HELP)
public String getUser() {
return user;
}
/**
* @return the password
*/
@ConfigurationProperty(order = 8, displayMessageKey = MSG_PASSWORD_DISPLAY,
helpMessageKey = MSG_PASSWORD_HELP, confidential = true)
public GuardedString getPassword() {
return password;
}
/**
* @return caseSensitivityString
*/
@ConfigurationProperty(order = 9, displayMessageKey = MSG_CS_DISPLAY,
helpMessageKey = MSG_CS_HELP, required = true)
public String getCaseSensitivityString() {
return caseSensitivityString;
}
/**
* @return the extraAttributesPolicy
*/
@ConfigurationProperty(order = 10, displayMessageKey = MSG_EXTRA_ATTRS_POLICY_DISPLAY,
helpMessageKey = MSG_EXTRA_ATTRS_POLICY_HELP)
public String getExtraAttributesPolicyString() {
return extraAttributesPolicyString;
}
@ConfigurationProperty(order = 11, displayMessageKey = MSG_SOURCE_TYPE_DISPLAY,
helpMessageKey = MSG_SOURCE_TYPE_HELP, required = false)
public String getSourceType() {
return sourceType;
}
@ConfigurationProperty(order = 12, displayMessageKey = MSG_DROP_CASCADE_DISPLAY,
helpMessageKey = MSG_DROP_CASCADE_DISPLAY, required = false)
public boolean isDropCascade() {
return dropCascade;
}
@ConfigurationProperty(order = 13, displayMessageKey = MSG_NORMALIZER_DISPLAY,
helpMessageKey = MSG_NORMALIZER_HELP, required = false)
public String getNormalizerString() {
return normalizerString;
}
@ConfigurationProperty(order = 14,
displayMessageKey = MSG_USE_DRIVER_FOR_AUTHENTICATION_DISPLAY,
helpMessageKey = MSG_USE_DRIVER_FOR_AUTHENTICATION_HELP, required = false)
public boolean isUseDriverForAuthentication() {
return useDriverForAuthentication;
}
public void setNormalizerString(String normalizerString) {
this.normalizerString = normalizerString;
this.connType = null;
}
/**
* @param extraAttributesPolicyString
* the extraAttributesPolicy to set
*/
public void setExtraAttributesPolicyString(String extraAttributesPolicyString) {
this.extraAttributesPolicyString = extraAttributesPolicyString;
this.connType = null;
}
public void setDropCascade(boolean dropCascade) {
this.dropCascade = dropCascade;
this.connType = null;
}
/**
* @param host
* the host to set
*/
public void setHost(String host) {
this.host = host;
this.connType = null;
}
/**
* @param port
* the port to set
*/
public void setPort(String port) {
this.port = port;
this.connType = null;
}
/**
* @param driver
* the driver to set
*/
public void setDriver(String driver) {
this.driver = driver;
this.connType = null;
}
/**
* @param database
* the database to set
*/
public void setDatabase(String database) {
this.database = database;
this.connType = null;
}
String getUserOwner() {
// if we were logged as system, owner will be SYSTEM
if ("".equals(cs.getAttributeFormatterAndNormalizer(OracleUserAttribute.SYSTEM_USER)
.getQuatesChar())) {
return user.toUpperCase();
}
return user;
}
/**
* @param user
* the user to set
*/
public void setUser(String user) {
this.user = user;
this.connType = null;
}
/**
* @param password
* the password to set
*/
public void setPassword(GuardedString password) {
// We cannot have empty password for oracle, so simplify test to set to
// null when empty
this.password = password;
if (this.password != null) {
this.password.access(new GuardedString.Accessor() {
public void access(char[] clearChars) {
if (clearChars.length == 0) {
OracleConfiguration.this.password = null;
}
}
});
}
this.connType = null;
}
/**
* @param dataSource
* the dataSource to set
*/
public void setDataSource(String dataSource) {
this.dataSource = dataSource;
this.connType = null;
}
/**
* @param dsJNDIEnv
* the dsJNDIEnv to set
*/
public void setDsJNDIEnv(String[] dsJNDIEnv) {
if (dsJNDIEnv == null) {
this.dsJNDIEnv = null;
} else {
this.dsJNDIEnv = new String[dsJNDIEnv.length];
System.arraycopy(dsJNDIEnv, 0, this.dsJNDIEnv, 0, dsJNDIEnv.length);
}
this.connType = null;
}
/**
* Sets useDriverForAuthentication
*
* @param useDriverForAuthentication
*/
public void setUseDriverForAuthentication(boolean useDriverForAuthentication) {
this.useDriverForAuthentication = useDriverForAuthentication;
this.connType = null;
}
/**
* @param url
* the url to set
*/
public void setUrl(String url) {
this.url = url;
this.connType = null;
}
/**
* Sets case sensitivity from string map.
*
* @param cs
*/
public void setCaseSensitivityString(String cs) {
this.caseSensitivityString = cs;
this.connType = null;
}
OracleCaseSensitivitySetup getCSSetup() {
return cs;
}
void setCSSetup(OracleCaseSensitivitySetup cs) {
this.cs = new LocalizedAssert(getConnectorMessages()).assertNotNull(cs, "cs");
}
void setExtraAttributesPolicySetup(ExtraAttributesPolicySetup setup) {
this.extraAttributesPolicySetup = setup;
}
ExtraAttributesPolicySetup getExtraAttributesPolicySetup() {
return extraAttributesPolicySetup;
}
OracleNormalizerName getNormalizerName() {
return normalizerName;
}
void setNormalizerName(OracleNormalizerName normalizer) {
this.normalizerName = normalizer;
}
public void setSourceType(String sourceType) {
this.sourceType = sourceType;
this.connType = null;
}
ConnectionType getConnType() {
return connType;
}
void setConnType(ConnectionType connType) {
this.connType = connType;
}
String getDriverClassName() {
return driverClassName;
}
void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
@Override
public void validate() {
if (connType != null) {
return;
}
try {
new OracleConfigurationValidator(this).validate();
} catch (RuntimeException e) {
// Just to be sure that failed validation does not set connType
setConnType(null);
throw e;
}
}
Connection createUserConnection(String user, GuardedString password) {
validate();
user = cs.formatToken(OracleUserAttribute.USER, user);
password = cs.formatToken(OracleUserAttribute.PASSWORD, password);
return createConnection(user, password);
}
Connection createAdminConnection() {
validate();
String user = cs.normalizeAndFormatToken(OracleUserAttribute.SYSTEM_USER, this.user);
GuardedString password =
cs.normalizeAndFormatToken(OracleUserAttribute.SYSTEM_PASSWORD, this.password);
Connection conn = createConnection(user, password);
return conn;
}
Connection createConnection(String user, GuardedString password) {
validate();
Connection connection = null;
if (ConnectionType.DATASOURCE.equals(connType)) {
if (StringUtil.isNotBlank(user) && password != null) {
// This could fail, but we cannot invoke method without
// user/password if user and password were specified
connection =
OracleSpecifics.createDataSourceConnection(dataSource, user, password,
JNDIUtil.arrayToHashtable(dsJNDIEnv, getConnectorMessages()),
getConnectorMessages());
} else {
connection =
OracleSpecifics.createDataSourceConnection(dataSource, JNDIUtil
.arrayToHashtable(dsJNDIEnv, getConnectorMessages()),
getConnectorMessages());
}
} else if (ConnectionType.THIN.equals(connType)) {
connection =
OracleSpecifics.createThinDriverConnection(new Builder().setDatabase(database)
.setDriver(driverClassName).setHost(host).setPassword(password)
.setPort(port).setUser(user).build(), getConnectorMessages());
} else if (ConnectionType.OCI.equals(connType)) {
connection =
OracleSpecifics.createOciDriverConnection(new Builder().setDatabase(database)
.setDriver(driverClassName).setHost(host).setPassword(password)
.setPort(port).setUser(user).build(), getConnectorMessages());
} else if (ConnectionType.FULL_URL.equals(connType)) {
connection =
OracleSpecifics.createCustomDriverConnection(
new Builder().setUrl(url).setDriver(driverClassName).setUser(user)
.setPassword(password).build(), getConnectorMessages());
} else {
throw new IllegalStateException(
"Invalid state of OracleConfiguration, connectionType = " + connType);
}
try {
checkAndAdjustConnection(connType, connection);
} catch (RuntimeException e) {
SQLUtil.closeQuietly(connection);
throw e;
}
return connection;
}
private void checkAndAdjustConnection(ConnectionType type, Connection connection) {
// Set autocommit to off
// When using datasource with sharable connection , it could throw
// exception or log warning
try {
if (connection.getAutoCommit()) {
LOG.info("connection.setAutoCommit(false)");
connection.setAutoCommit(false);
}
} catch (SQLException e) {
throw new ConnectorException("Cannot check or adjust connection autocommit flag", e);
}
// Set Transaction Isolation
// When using datasource with sharable connection , it could throw
// exception or log warning
try {
if (connection.getTransactionIsolation() == Connection.TRANSACTION_NONE
|| connection.getTransactionIsolation() == Connection.TRANSACTION_READ_UNCOMMITTED) {
LOG.info("connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED)");
connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
}
} catch (SQLException e) {
throw new ConnectorException("Cannot check or adjust transaction isolation settings", e);
}
}
void resetUseDriverForAuthentication() {
useDriverForAuthentication = false;
}
}