| /******************************************************************************* |
| * Copyright (c) 1998, 2013 Oracle and/or its affiliates. All rights reserved. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 |
| * which accompanies this distribution. |
| * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html |
| * and the Eclipse Distribution License is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * Contributors: |
| * Oracle - initial API and implementation from Oracle TopLink |
| ******************************************************************************/ |
| package org.eclipse.persistence.tools.db.relational; |
| |
| import java.io.PrintWriter; |
| import java.sql.Connection; |
| import java.sql.Driver; |
| import java.sql.SQLException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.Properties; |
| import java.util.Vector; |
| import org.eclipse.persistence.exceptions.DatabaseException; |
| import org.eclipse.persistence.internal.security.JCEEncryptor; |
| import org.eclipse.persistence.sessions.Connector; |
| import org.eclipse.persistence.sessions.DatabaseLogin; |
| import org.eclipse.persistence.sessions.Session; |
| import org.eclipse.persistence.tools.db.relational.platformsmodel.DatabasePlatform; |
| import org.eclipse.persistence.tools.utility.ObjectTools; |
| import org.eclipse.persistence.tools.utility.StringTools; |
| import org.eclipse.persistence.tools.utility.collection.ListTools; |
| import org.eclipse.persistence.tools.utility.iterator.ArrayIterator; |
| import org.eclipse.persistence.tools.utility.iterator.CloneListIterator; |
| import org.eclipse.persistence.tools.utility.iterator.EmptyIterator; |
| import org.eclipse.persistence.tools.utility.iterator.TransformationIterator; |
| import org.eclipse.persistence.tools.utility.node.Node; |
| |
| /** |
| * @version 2.6 |
| */ |
| @SuppressWarnings("nls") |
| public final class ELLoginSpec extends ELModel { |
| |
| /** the name should never be null or empty */ |
| private volatile String name; |
| public static final String NAME_PROPERTY = "name"; |
| |
| private volatile String driverClassName; |
| public static final String DRIVER_CLASS_NAME_PROPERTY = "driverClassName"; |
| // virtual collection |
| public static final String CANDIDATE_URLS_COLLECTION = "candidateURLs"; |
| |
| private volatile String url; |
| public static final String URL_PROPERTY = "url"; |
| |
| private volatile String userName; |
| public static final String USER_NAME_PROPERTY = "userName"; |
| |
| private volatile String password; |
| public static final String PASSWORD_PROPERTY = "password"; |
| |
| private volatile boolean savePassword; |
| public static final String SAVE_PASSWORD_PROPERTY = "savePassword"; |
| |
| private List<String> driverClasspathEntries; |
| public static final String DRIVER_CLASSPATH_ENTRIES_LIST = "driverClasspathEntries"; |
| |
| /** the JDBC drivers we know about and their URLs */ |
| private static Collection<DriverSpec> driverSpecs; |
| |
| /** cache this - it's expensive to instantiate */ |
| private static JCEEncryptor encryptor; |
| |
| /** preferences */ |
| public static final String DB_DRIVER_CLASS_PREFERENCE = "database driver class"; |
| |
| public static final String DB_CONNECTION_URL_PREFERENCE = "database connection url"; |
| |
| // ********** static methods ********** |
| |
| /** |
| * Returns the driver class names we know about |
| */ |
| public static Iterator<String> commonDriverClassNames() { |
| return new TransformationIterator<DriverSpec, String>(driverSpecs()) { |
| @Override |
| protected String transform(DriverSpec next) { |
| return next.getDriverClassName(); |
| } |
| }; |
| } |
| |
| public static int commonDriverClassNamesSize() { |
| return getDriverSpecs().size(); |
| } |
| |
| /** |
| * Returns the URLs typically associated with the specified |
| * driver class name |
| */ |
| private static Iterator<String> urlsForDriverClassNamed(String driverClassName) { |
| DriverSpec ds = driverSpecFor(driverClassName); |
| return (ds == null) ? EmptyIterator.<String>instance() : ds.urls(); |
| } |
| |
| private static int urlsForDriverClassNamedSize(String driverClassName) { |
| DriverSpec ds = driverSpecFor(driverClassName); |
| return (ds == null) ? 0 : ds.urlsSize(); |
| } |
| |
| private static boolean urlsForDriverClassNamedContains(String driverClassName, String url) { |
| DriverSpec ds = driverSpecFor(driverClassName); |
| return (ds == null) ? false : ds.containsURL(url); |
| } |
| |
| /** |
| * Returns the URL most commonly used with the specified |
| * driver class name |
| */ |
| private static String defaultURLForDriverClassNamed(String driverClassName) { |
| DriverSpec ds = driverSpecFor(driverClassName); |
| return (ds == null) ? null : ds.defaultURL(); |
| } |
| |
| private static DriverSpec driverSpecFor(String driverClassName) { |
| for (Iterator<DriverSpec> stream = driverSpecs(); stream.hasNext(); ) { |
| DriverSpec ds = stream.next(); |
| if (ds.getDriverClassName().equals(driverClassName)) { |
| return ds; |
| } |
| } |
| return null; |
| } |
| |
| private static Iterator<DriverSpec> driverSpecs() { |
| return getDriverSpecs().iterator(); |
| } |
| |
| private synchronized static Collection<DriverSpec> getDriverSpecs() { |
| if (driverSpecs == null) { |
| driverSpecs = buildDriverSpecs(); |
| } |
| return driverSpecs; |
| } |
| |
| // TODO move to DatabasePlatform and store in its XML file? |
| private static Collection<DriverSpec> buildDriverSpecs() { |
| Collection<DriverSpec> specs = new ArrayList<DriverSpec>(30); |
| specs.add(new DriverSpec("com.neon.jdbc.Driver", "jdbc:neon:")); |
| specs.add(new DriverSpec("com.pointbase.jdbc.jdbcUniversalDriver", "jdbc:pointbase:")); |
| specs.add(new DriverSpec("com.sybase.jdbc3.jdbc.SybDriver", "jdbc:sybase:Tds:")); |
| specs.add(new DriverSpec("com.sybase.jdbc2.jdbc.SybDriver", "jdbc:sybase:Tds:")); |
| specs.add(new DriverSpec("com.sybase.jdbc.SybDriver", "jdbc:sybase:Tds:")); |
| specs.add(new DriverSpec("COM.ibm.db2.jdbc.app.DB2Driver", "jdbc:db2:")); |
| specs.add(new DriverSpec("COM.ibm.db2.jdbc.net.DB2Driver", "jdbc:db2:")); |
| specs.add(new DriverSpec("com.ibm.db2.jcc.DB2Driver", "jdbc:db2://")); |
| specs.add(new DriverSpec("com.mysql.jdbc.Driver", "jdbc:mysql://")); |
| specs.add(new DriverSpec("borland.jdbc.Bridge.LocalDriver", "jdbc:BorlandBridge:")); |
| specs.add(new DriverSpec("borland.jdbc.Broker.RemoteDriver", "jdbc:BorlandBridge:")); |
| specs.add(new DriverSpec("intersolv.jdbc.sequelink.SequeLinkDriver", "jdbc:sequelink:")); |
| String[] oracleURLs = |
| new String[] { |
| "jdbc:oracle:thin:@<HOST>:<PORT>:<SID>", |
| "jdbc:oracle:oci:@<HOST>:<PORT>:<SID>", |
| "jdbc:oracle:oci7:@<HOST>:<PORT>:<SID>", |
| "jdbc:oracle:oci8:@<HOST>:<PORT>:<SID>" |
| }; |
| specs.add(new DriverSpec("oracle.jdbc.OracleDriver", oracleURLs)); |
| specs.add(new DriverSpec("oracle.jdbc.driver.OracleDriver", oracleURLs)); |
| specs.add(new DriverSpec("com.oracle.ias.jdbc.db2.DB2Driver", "jdbc:oracle:db2://")); |
| specs.add(new DriverSpec("com.oracle.ias.jdbc.sqlserver.SQLServerDriver", "jdbc:oracle:sqlserver://")); |
| specs.add(new DriverSpec("com.oracle.ias.jdbc.sybase.SybaseDriver", "jdbc:oracle:sybase://")); |
| specs.add(new DriverSpec("org.hsqldb.jdbcDriver", "jdbc:hsqldb:")); |
| specs.add(new DriverSpec("sun.jdbc.odbc.JdbcOdbcDriver", "jdbc:odbc:")); |
| specs.add(new DriverSpec("weblogic.jdbc.oci.Driver", "jdbc:weblogic:oracle:")); |
| specs.add(new DriverSpec("weblogic.jdbc.dblib.Driver", new String[] { "jdbc:weblogic:mssqlserver:", "jdbc:weblogic:sybase" })); |
| specs.add(new DriverSpec("weblogic.jdbc.informix4.Driver", "jdbc:weblogic:informix4:")); |
| specs.add(new DriverSpec("weblogic.jdbc.jts.Driver", "jdbc:weblogic:jts:")); |
| specs.add(new DriverSpec("weblogic.jdbc.mssqlserver4.Driver", "jdbc:weblogic:mssqlserver4:")); |
| specs.add(new DriverSpec("weblogic.jdbc.pool.Driver", "jdbc:weblogic:pool:")); |
| specs.add(new DriverSpec("weblogic.jdbc.t3client.Driver", "jdbc:weblogic:t3Client:")); |
| specs.add(new DriverSpec("weblogic.jdbc.t3.Driver", "jdbc:weblogic:t3:")); |
| specs.add(new DriverSpec("com.timesten.jdbc.TimesTenDriver", "jdbc:timesten:direct:<SID>")); |
| return specs; |
| } |
| |
| private static JCEEncryptor getEncryptor() { |
| if (encryptor == null) { |
| try { |
| encryptor = new JCEEncryptor(); |
| } catch (Exception ex) { |
| throw new RuntimeException(ex); |
| } |
| } |
| return encryptor; |
| } |
| |
| |
| // ********** constructors ********** |
| |
| ELLoginSpec(ELDatabase database, String name) { |
| super(database); |
| this.name = name; |
| } |
| |
| |
| // ********** initialization ********** |
| |
| /** |
| * initialize persistent state |
| */ |
| @Override |
| protected void initialize(Node parent) { |
| super.initialize(parent); |
| this.savePassword = false; |
| this.driverClasspathEntries = new Vector<String>(); |
| } |
| |
| |
| // ********** accessors ********** |
| |
| @Override |
| public final ELDatabase getParent() { |
| return (ELDatabase)super.getParent(); |
| } |
| |
| public String getName() { |
| return this.name; |
| } |
| |
| public void setName(String name) { |
| Object old = this.name; |
| this.name = name; |
| this.firePropertyChanged(NAME_PROPERTY, old, name); |
| } |
| |
| public String getDriverClassName() { |
| return this.driverClassName; |
| } |
| |
| public void setDriverClassName(String driverClassName) { |
| String old = this.driverClassName; |
| this.driverClassName = driverClassName; |
| this.firePropertyChanged(DRIVER_CLASS_NAME_PROPERTY, old, driverClassName); |
| if (this.attributeValueHasChanged(old, driverClassName)) { |
| // only change the URL if it is easy to re-create |
| if ((this.url == null) || urlsForDriverClassNamedContains(old, this.url)) { |
| this.setURL(defaultURLForDriverClassNamed(driverClassName)); |
| } |
| } |
| } |
| |
| public String getURL() { |
| return this.url; |
| } |
| |
| public void setURL(String url) { |
| Object old = this.url; |
| this.url = url; |
| this.firePropertyChanged(URL_PROPERTY, old, url); |
| } |
| |
| public String getUserName() { |
| return this.userName; |
| } |
| |
| public void setUserName(String userName) { |
| Object old = this.userName; |
| this.userName = userName; |
| this.firePropertyChanged(USER_NAME_PROPERTY, old, userName); |
| } |
| |
| public String getPassword() { |
| return this.password; |
| } |
| |
| public void setPassword(String password) { |
| Object old = this.password; |
| this.password = password; |
| this.firePropertyChanged(PASSWORD_PROPERTY, old, password); |
| } |
| |
| public boolean isSavePassword() { |
| return this.savePassword; |
| } |
| |
| public void setSavePassword(boolean savePassword) { |
| boolean old = this.savePassword; |
| this.savePassword = savePassword; |
| this.firePropertyChanged(SAVE_PASSWORD_PROPERTY, old, savePassword); |
| } |
| |
| |
| /* NOTE: Driver classpath entries are Strings */ |
| |
| public ListIterator<String> driverClasspathEntries() { |
| return new CloneListIterator<String>(this.driverClasspathEntries); |
| } |
| |
| public int driverClasspathEntriesSize() { |
| return this.driverClasspathEntries.size(); |
| } |
| |
| public String getDriverClasspathEntry(int index) { |
| return this.driverClasspathEntries.get(index); |
| } |
| |
| public void addDriverClasspathEntry(int index, String entry) { |
| this.addItemToList(index, entry, this.driverClasspathEntries, DRIVER_CLASSPATH_ENTRIES_LIST); |
| } |
| |
| public void addDriverClasspathEntry(String entry) { |
| this.addDriverClasspathEntry(this.driverClasspathEntriesSize(), entry); |
| } |
| |
| public void addDriverClasspathEntries(int index, List<String> entries) { |
| this.addItemsToList(index, entries, this.driverClasspathEntries, DRIVER_CLASSPATH_ENTRIES_LIST); |
| } |
| |
| public void addDriverClasspathEntries(List<String> entries) { |
| this.addDriverClasspathEntries(this.driverClasspathEntriesSize(), entries); |
| } |
| |
| public void addDriverClasspathEntries(ListIterator<String> entries) { |
| this.addDriverClasspathEntries(ListTools.list(entries)); |
| } |
| |
| public String removeDriverClasspathEntry(int index) { |
| return this.removeItemFromList(index, this.driverClasspathEntries, DRIVER_CLASSPATH_ENTRIES_LIST); |
| } |
| |
| public List<String> removeDriverClasspathEntries(int index, int length) { |
| return this.removeItemsFromList(index, length, this.driverClasspathEntries, DRIVER_CLASSPATH_ENTRIES_LIST); |
| } |
| |
| public String replaceDriverClasspathEntry(int index, String newEntry) { |
| return this.setItemInList(index, newEntry, this.driverClasspathEntries, DRIVER_CLASSPATH_ENTRIES_LIST); |
| } |
| |
| |
| // ********** queries ********** |
| |
| public String defaultURL() { |
| return defaultURLForDriverClassNamed(this.driverClassName); |
| } |
| |
| public Iterator<String> candidateURLs() { |
| return urlsForDriverClassNamed(this.driverClassName); |
| } |
| |
| public int candidateURLsSize() { |
| return urlsForDriverClassNamedSize(this.driverClassName); |
| } |
| |
| // ********** behavior ********** |
| |
| Driver buildDriver() throws ClassNotFoundException, InstantiationException, IllegalAccessException { |
| if ((this.driverClassName == null) || (this.driverClassName.length() == 0)) { |
| throw new IllegalStateException("missing database driver class name"); |
| } |
| // we should be able to load the driver with only a minimum classpath |
| ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); |
| Class<?> driverClass = Class.forName(this.driverClassName, true, classLoader); |
| return (Driver) driverClass.newInstance(); |
| } |
| |
| /** |
| * Returns a TopLink runtime connector for the spec's driver. |
| */ |
| public Connector buildConnector() throws ClassNotFoundException, InstantiationException, IllegalAccessException { |
| return new MWConnector(this.buildDriver(), this.getURL()); |
| } |
| |
| |
| // ********** runtime conversion ********** |
| |
| private DatabasePlatform databasePlatform() { |
| return this.getParent().getDatabasePlatform(); |
| } |
| |
| /** |
| * this is used to log in to the database in the UI |
| */ |
| DatabaseLogin buildDevelopmentRuntimeDatabaseLogin() { |
| DatabaseLogin login = this.buildRuntimeDatabaseLogin(); |
| login.setConnector(this.getParent().buildRuntimeConnector()); |
| return login; |
| } |
| |
| /** |
| * this is used to generate deployment XML |
| */ |
| DatabaseLogin buildDeploymentRuntimeDatabaseLogin() { |
| DatabaseLogin login = this.buildRuntimeDatabaseLogin(); |
| if ( ! this.savePassword) { |
| login.setPassword(null); |
| } |
| return login; |
| } |
| |
| private DatabaseLogin buildRuntimeDatabaseLogin() { |
| DatabaseLogin login = new DatabaseLogin(); |
| |
| login.setDriverClassName(this.driverClassName); |
| login.setDriverURLHeader(StringTools.EMPTY_STRING); |
| login.setDatabaseURL(this.url); |
| login.setUserName(this.userName); |
| login.setPassword(this.password); |
| |
| login.setPlatformClassName(this.databasePlatform().getRuntimePlatformClassName()); |
| |
| return login; |
| } |
| |
| |
| // ********** displaying and printing ********** |
| |
| public void toString(StringBuffer sb) { |
| sb.append(this.name); |
| } |
| |
| @Override |
| public String displayString() { |
| return this.name; |
| } |
| |
| |
| // ********** TopLink methods ********** |
| @SuppressWarnings("unused") |
| private String getPasswordForTopLink() { |
| return (this.savePassword) ? this.encryptedPassword() : null; |
| } |
| |
| private String encryptedPassword() { |
| return (this.password == null) ? null : getEncryptor().encryptPassword(password); |
| } |
| |
| @SuppressWarnings("unused") |
| private void setPasswordForTopLink(String password) { |
| this.password = (password == null) ? null : getEncryptor().decryptPassword(password); |
| } |
| |
| /** |
| * passwords were not encrypted in 4.5 and earlier |
| */ |
| @SuppressWarnings("unused") |
| private void legacySetPasswordForTopLink(String password) { |
| this.password = password; |
| } |
| |
| /** |
| * Pair popular JDBC drivers with the typical URLs used |
| * with them. |
| */ |
| // TODO need a better class name |
| private static final class DriverSpec { |
| private String driverClassName; |
| private String[] urls; |
| |
| DriverSpec(String driverClassName, String[] urls) { |
| super(); |
| if ((driverClassName == null) || (driverClassName.length() == 0)) { |
| throw new IllegalArgumentException(); |
| } |
| this.driverClassName = driverClassName; |
| if ((urls == null) || (urls.length == 0)) { |
| throw new IllegalArgumentException(); |
| } |
| this.urls = urls; |
| } |
| |
| DriverSpec(String driverClassName, String url) { |
| this(driverClassName, new String[] {url}); |
| } |
| |
| public String getDriverClassName() { |
| return this.driverClassName; |
| } |
| |
| public Iterator<String> urls() { |
| return new ArrayIterator<String>(this.urls); |
| } |
| |
| public int urlsSize() { |
| return this.urls.length; |
| } |
| |
| public boolean containsURL(String url) { |
| for (int i = this.urls.length; i-- > 0; ) { |
| if (this.urls[i].equals(url)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public String defaultURL() { |
| return this.urls[0]; |
| } |
| |
| @Override |
| public String toString() { |
| return ObjectTools.toString(this, this.driverClassName); |
| } |
| } |
| |
| /** |
| * Implement a TopLink runtime Connector that ignores the JDBC |
| * DriverManager and uses a specified JDBC Driver. This allows us |
| * to load Drivers dynamically without worrying about which class |
| * loader loaded the driver and whether it is the same class loader |
| * that loaded the driver's client. |
| * |
| * @see java.sql.DriverManager |
| */ |
| private static class MWConnector implements Connector { |
| private Driver driver; |
| private String url; |
| |
| public MWConnector(Driver driver, String url) { |
| super(); |
| this.driver = driver; |
| this.url = url; |
| } |
| |
| @Override |
| public Connection connect(Properties properties, Session session) { |
| try { |
| return this.driver.connect(this.url, properties); |
| } catch (SQLException ex) { |
| throw DatabaseException.sqlException(ex); |
| } |
| } |
| |
| @Override |
| public Object clone() { |
| try { |
| return super.clone(); |
| } |
| catch (Exception exception) { |
| throw new InternalError("clone failed"); |
| } |
| } |
| |
| @Override |
| public String getConnectionDetails() { |
| return "MWConnector: " + this.url; |
| } |
| |
| @Override |
| public void toString(PrintWriter writer) { |
| writer.println(this.getConnectionDetails()); |
| } |
| } |
| } |