/* * CDDL HEADER START * * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions Copyright [year] [name of copyright owner]". * * CDDL HEADER END * * Copyright 2013 ForgeRock AS. * Portions Copyright 2013 IS4U. */ package org.forgerock.opendj.virtual; import java.io.IOException; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import org.forgerock.opendj.ldap.Connection; import org.forgerock.opendj.ldap.DN; import org.forgerock.opendj.ldap.ErrorResultException; import org.forgerock.opendj.ldap.ErrorResultIOException; import org.forgerock.opendj.ldap.SearchResultReferenceIOException; import org.forgerock.opendj.ldap.SearchScope; import org.forgerock.opendj.ldap.responses.SearchResultEntry; import org.forgerock.opendj.ldap.schema.AttributeType; import org.forgerock.opendj.ldap.schema.Schema; import org.forgerock.opendj.ldif.ConnectionEntryReader; import org.forgerock.opendj.ldif.EntryReader; /** * The mapping for the JDBCConnection which holds information about the database * and directory structures. */ public final class JDBCMapper { final private java.sql.Connection jdbcConnection; final private Connection ldapConnection; private String dbName; final private ArrayList tablesList = new ArrayList(); final private ArrayList baseDNList = new ArrayList(); final private Map> tableColumnsMap = new HashMap>(); final private Map tableColumnNullableMap = new HashMap(); final private Map tableColumnDataTypeMap = new HashMap(); final private Map> organizationalUnitsMap = new HashMap>(); final private Map> organizationalUnitAttributesMap = new HashMap>(); private Map SQLToLDAPMap = new HashMap(); /** * Creates a new JDBC mapping. * * @param jdbcconnection * The JDBCConnection for the Database Server. * @param ldapconnection * The LDAPConnection for the Directory Server. */ JDBCMapper(final Connection jdbcconnection, final Connection ldapconnection) { this.jdbcConnection = ((JDBCConnection) jdbcconnection).getSqlConnection(); this.ldapConnection = ldapconnection; } /** * Sets the SQL database name for this mapping. * * @param DBName * The database name. */ public void setDatabaseName(String DBName) { this.dbName = DBName; } /** * Releases this Connection object's database/directory and JDBC/LDAP resources immediately instead of * waiting for them to be automatically released. * * Calling the method close on a Connection object that is already closed is a no-op. * * @throws SQLException * If a database access error occurs. */ public void closeConnections() throws SQLException { jdbcConnection.close(); ldapConnection.close(); } /** * Fills this mapping with the structure of the currently connected database. * * @throws ErrorResultException * If the result code indicates that the request failed for some * reason. * @throws SQLException * If a database access error occurs. * @throws IOException * If an I/O exception error occurs. */ public void fillMaps() throws ErrorResultException, SQLException, IOException { fillBaseDNList(); fillTablesList(); fillTableColumnsMap(); fillOrganizationalUnitsAndAttributesMap(); } /** * Fills this mapping with the base distinguished names of the currently connected directory. * * @throws IOException * If an I/O exception error occurs. */ private void fillBaseDNList() throws IOException { final EntryReader reader = ldapConnection.search(" ", SearchScope.SINGLE_LEVEL, "objectClass=*"); while(reader.hasNext()){ baseDNList.add(reader.readEntry().getName().toString()); } } /** * Fills this mapping with the tables of the currently connected database. * * @throws SQLException * If a database access error occurs. */ private void fillTablesList() throws SQLException { /*For connection to h2 database, use "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE " + "TABLE_TYPE = 'TABLE' AND TABLE_SCHEMA = 'PUBLIC'";*/ final String sql = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE " + "TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = '" + this.dbName + "'"; final Statement st = jdbcConnection.createStatement(); final ResultSet rs = st.executeQuery(sql); while(rs.next()){ tablesList.add(rs.getString(1)); } } /** * Fills this mapping with the columns for each table of the currently connected database. Also checks * the column's data type and not null definition. * * @throws SQLException * If the result code indicates that the request failed for some * reason. * @throws ErrorResultIOException * If an I/O exception error occurs in the result code. * @throws SearchResultReferenceIOException * If an iteration over a set of search results using a ConnectionEntryReader encounters * a SearchResultReference. */ private void fillTableColumnsMap() throws SQLException, ErrorResultIOException, SearchResultReferenceIOException { for(int i =0; i < tablesList.size(); i++){ final String tableName = tablesList.get(i); final String sql = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '" + tableName + "'"; final Statement st = jdbcConnection.createStatement(); final ResultSet rs = st.executeQuery(sql); final ArrayList columnsList = new ArrayList(); String columnName = "", columnNullable = "", columnDataType = ""; while(rs.next()){ columnName = rs.getString(4); columnNullable = rs.getString(7); columnDataType = rs.getString(8); columnsList.add(columnName); tableColumnNullableMap.put(tableName + ":" + columnName, columnNullable); tableColumnDataTypeMap.put(tableName + ":" + columnName, columnDataType); } tableColumnsMap.put(tableName, columnsList); } } /** * Fills this mapping with the organizational units and attributes for for each organizational unit * of the currently connected directory. * * @throws ErrorResultException * If a database access error occurs. * @throws ErrorResultIOException * If an I/O exception error occurs in the result code. * @throws SearchResultReferenceIOException * If an iteration over a set of search results using a ConnectionEntryReader encounters * a SearchResultReference. */ private void fillOrganizationalUnitsAndAttributesMap() throws ErrorResultException, ErrorResultIOException, SearchResultReferenceIOException { for(int i = 0; i < baseDNList.size(); i++){ final String baseDN = baseDNList.get(i); final ConnectionEntryReader baseDNReader = ldapConnection.search(baseDN, SearchScope.SINGLE_LEVEL, "objectClass=*"); ArrayList organizationalUnitsList = new ArrayList(); while (baseDNReader.hasNext()) { final SearchResultEntry entry = baseDNReader.readEntry(); final String organizationalUnitDNName = entry.getName().toString(); final String organizationalUnitName = DN.valueOf(organizationalUnitDNName).rdn().getFirstAVA().getAttributeValue().toString(); organizationalUnitsList.add(organizationalUnitName); Schema.readSchemaForEntry(ldapConnection, DN.valueOf(organizationalUnitDNName)); final Collection attributeTypes = Schema.getCoreSchema().getAttributeTypes(); final ArrayList attributesList; if(attributeTypes.iterator().hasNext()){ final Iterator it = attributeTypes.iterator(); attributesList = new ArrayList(); while(it.hasNext()){ final AttributeType at = it.next(); attributesList.add(at.getNameOrOID()); } } else{ attributesList = new ArrayList(); attributesList.add(""); } organizationalUnitAttributesMap.put(baseDN + ":" + organizationalUnitName, attributesList); } organizationalUnitsMap.put(baseDN, organizationalUnitsList); } } /** * Returns a list of table names of the currently connected database. * * @return A list of table names. * @throws SQLException * If a database access error occurs. */ public ArrayList getTables() throws SQLException { return tablesList; } /** * Returns a list of base distinguished names of the currently connected directory. * * @return A list of base distinguished names. * @throws SQLException * If a database access error occurs. */ public ArrayList getBaseDNs() { return baseDNList; } /** * Returns a list of column names in the provided table name of the currently connected database. * * @param tableName * The table name of the columns to retrieve. * @return A list of column names. * @throws SQLException * If a database access error occurs. */ public ArrayList getTableColumns(final String tableName) throws SQLException { final ArrayList tableColumnsList = tableColumnsMap.get(tableName); return tableColumnsList; } /** * Returns a boolean value which indicates whether the specified column name has NOT NULL defined * in the provided table name of the currently connected database. * * @param tableName * The table name in which to search. * @param columnName * The column name in which to search. * @return A boolean value to indicate the value of NOT NULL in the column. */ public boolean getTableColumnNullable(final String tableName, final String columnName) { final String mappingKey = tableName + ":" + columnName; final String nullable = tableColumnNullableMap.get(mappingKey); final String nullableString = "NO"; if(nullable.equals(nullableString)) return false; else return true; } /** * Returns an object which indicates the data type of the specified column name in * the provided table name of the currently connected database. * * @param tableName * The table name in which to search. * @param columnName * The column name in which to search. * @return A boolean value to indicate the value of NOT NULL in the column. */ public Object getTableColumnDataType(final String tableName, final String columnName) { final String mappingKey = tableName + ":" + columnName; final String mappingValue = tableColumnDataTypeMap.get(mappingKey); final String objectTypeString = "int"; if(mappingValue.equals(objectTypeString)) return Integer.class; return String.class; } /** * Returns a list of organizational unit names in the provided base distinguished name * of the currently connected directory. * * @param baseDN * The base distinguished name of the organizational units to retrieve. * @return A list of organizational unit names. */ public ArrayList getOrganizationalUnits(final String baseDN) { return organizationalUnitsMap.get(baseDN); } /** * Returns a list of attributes within the specified organizational unit name in * the provided base distinguished name of the currently connected directory. * * @param baseDN * The base distinguished name in which to search. * @param organizationalUnitName * The organizational unit name in which to search. * @return A a list of attributes. */ public ArrayList getOrganizationalUnitAttributes(final String baseDN, final String organizationalUnitName) { return organizationalUnitAttributesMap.get(baseDN + ":" + organizationalUnitName); } /** * Create a map with the provided parameters and save it to the mapping. * * @param tableName * The database table name to save. * @param columnNames * The database column names to save. * @param baseDN * The directory base distinguished name to save. * @param OUName * The directory organizational unit name to save. * @param attributeNames * The directory attribute name to save. */ public void addCurrentMapToMapping(final String tableName, final String[] columnNames, final String baseDN, final String OUName, final String[] attributeNames) { String mappingKey, mappingValue; for(int i = 0; i < columnNames.length; i++){ mappingKey = tableName + ":" + columnNames[i]; mappingValue = baseDN + ":" + OUName + ":" + attributeNames[i]; SQLToLDAPMap.put(mappingKey, mappingValue); } } /** * Returns a the full mapping of the directory structure to the database structure. * * @return A full mapping of directory to database structure. */ public Map getMapping() { return SQLToLDAPMap; } /** * Returns a map which holds the provided parameters. * * @param tableName * The database table name in which to search. * @param baseDN * The directory base distinguished name in which to search. * @param organizationalUnit * The directory organizational unit to search for. * @return A a list of attributes. */ public Map loadCurrentMapFromMapping(final String tableName, String baseDN, String organizationalUnit) { baseDN = baseDN.replace(" ", ""); String mappingKey, mappingValue; final ArrayList tableColumnsList = tableColumnsMap.get(tableName); final Map currentMap = new HashMap(); for(int i = 0; i < tableColumnsList.size(); i++){ mappingKey = tableName + ":" + tableColumnsList.get(i); if(!SQLToLDAPMap.containsKey(mappingKey)) continue; mappingValue = SQLToLDAPMap.get(mappingKey); if(mappingValue.contains(baseDN + ":" + organizationalUnit)) currentMap.put(mappingKey, mappingValue); } return currentMap; } /** * Sets the full mapping to the mapping provided. * * @param mapping * The mapping to which to set the full mapping to. */ public void loadMappingConfig(Map mapping) { this.SQLToLDAPMap = mapping; } /** * Returns the table name from the mapping that corresponds to the parameters provided. * * @param baseDN * The directory base distinguished name in which to search. * @param organizationalUnit * The directory organizational unit to search for. * @return The table name that is mapped to the provided distinguished name. */ public String getTableNameFromMapping(String baseDN, final String organizationalUnit) { baseDN = baseDN.replace(" ", ""); for (Entry entry : SQLToLDAPMap.entrySet()) { final String mappingValue = entry.getValue(); if (mappingValue.contains(baseDN + ":" + organizationalUnit)) { final String mappingKey = entry.getKey(); final String stringSplitter[] = mappingKey.split(":"); final String tableName = stringSplitter[0]; return tableName; } } return null; } /** * Returns the column name from the mapping that corresponds to the parameters provided. * * @param tableName * The database table name in which to search. * @param baseDN * The directory base distinguished name in which to search. * @param organizationalUnitName * The directory organizational unit name in which to search. * @param attributeName * The directory attribute name to search for. * @return The column name that is mapped to the provided attribute name. */ public String getColumnNameFromMapping(final String tableName, String baseDN, final String organizationalUnitName, final String attributeName) { baseDN = baseDN.replace(" ", ""); for (Entry entry : SQLToLDAPMap.entrySet()) { final String mappingValue = entry.getValue(); if (mappingValue.equals(baseDN + ":" + organizationalUnitName + ":" + attributeName)) { final String mappingKey = entry.getKey(); if(mappingKey.contains(tableName)){ final String stringSplitter[] = mappingKey.split(":"); final String columnName = stringSplitter[1]; return columnName; } } } return null; } }