001    /*
002     * $Id: JDBCLoginService.java 3100 2008-10-14 22:33:10Z rah003 $
003     *
004     * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     * 
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     * 
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    package org.jdesktop.swingx.auth;
022    import java.sql.Connection;
023    import java.sql.DriverManager;
024    import java.util.Properties;
025    import java.util.logging.Level;
026    import java.util.logging.Logger;
027    
028    import javax.naming.InitialContext;
029    /**
030     * A login service for connecting to SQL based databases via JDBC
031     *
032     * @author rbair
033     */
034    public class JDBCLoginService extends LoginService {
035        private static final Logger LOG = Logger.getLogger(JDBCLoginService.class
036                .getName());
037        
038        /**
039         * The connection to the database
040         */
041        private Connection conn;
042        /**
043         * If used, defines the JNDI context from which to get a connection to
044         * the data base
045         */
046        private String jndiContext;
047        /**
048         * When using the DriverManager to connect to the database, this specifies
049         * any additional properties to use when connecting.
050         */
051        private Properties properties;
052        
053        /**
054         * Create a new JDBCLoginService and initializes it to connect to a
055         * database using the given params.
056         * @param driver
057         * @param url
058         */
059        public JDBCLoginService(String driver, String url) {
060            super(url);
061            try {
062                Class.forName(driver);
063            } catch (Exception e) {
064                LOG.log(Level.WARNING, "The driver passed to the " +
065                        "JDBCLoginService constructor could not be loaded. " +
066                        "This may be due to the driver not being on the classpath", e);
067            }
068            this.setUrl(url);
069        }
070        
071        /**
072         * Create a new JDBCLoginService and initializes it to connect to a
073         * database using the given params.
074         * @param driver
075         * @param url
076         * @param props
077         */
078        public JDBCLoginService(String driver, String url, Properties props) {
079            super(url);
080            try {
081                Class.forName(driver);
082            } catch (Exception e) {
083                LOG.log(Level.WARNING, "The driver passed to the " +
084                        "JDBCLoginService constructor could not be loaded. " +
085                        "This may be due to the driver not being on the classpath", e);
086            }
087            this.setUrl(url);
088            this.setProperties(props);
089        }
090        
091        /**
092         * Create a new JDBCLoginService and initializes it to connect to a
093         * database using the given params.
094         * @param jndiContext
095         */
096        public JDBCLoginService(String jndiContext) {
097            super(jndiContext);
098            this.jndiContext = jndiContext;
099        }
100        
101        /**
102         * Default JavaBean constructor
103         */
104        public JDBCLoginService() {
105            super();
106        }
107        
108        /**
109         * @return the JDBC connection url
110         */
111        public String getUrl() {
112            return getServer();
113        }
114    
115        /**
116         * @param url set the JDBC connection url
117         */
118        public void setUrl(String url) {
119            String old = getUrl();
120            setServer(url);
121            firePropertyChange("url", old, getUrl());
122        }
123    
124        /**
125         * @return JDBC connection properties
126         */
127        public Properties getProperties() {
128            return properties;
129        }
130    
131        /**
132         * @param properties miscellaneous JDBC properties to use when connecting
133         *        to the database via the JDBC driver
134         */
135        public void setProperties(Properties properties) {
136            Properties old = getProperties();
137            this.properties = properties;
138            firePropertyChange("properties", old, getProperties());
139        }
140        
141        public Connection getConnection() {
142            return conn;
143        }
144        
145        public void setConnection(Connection conn) {
146            Connection old = getConnection();
147            this.conn = conn;
148            firePropertyChange("connection", old, getConnection());
149        }
150        
151        /**
152         * Attempts to get a JDBC Connection from a JNDI javax.sql.DataSource, using
153         * that connection for interacting with the database.
154         * @throws Exception
155         */
156        private void connectByJNDI(String userName, char[] password) throws Exception {
157            InitialContext ctx = new InitialContext();
158            javax.sql.DataSource ds = (javax.sql.DataSource)ctx.lookup(jndiContext);
159            conn = ds.getConnection(userName, new String(password));
160            conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
161        }
162        
163        /**
164         * Attempts to get a JDBC Connection from a DriverManager. If properties
165         * is not null, it tries to connect with those properties. If that fails,
166         * it then attempts to connect with a user name and password. If that fails,
167         * it attempts to connect without any credentials at all.
168         * <p>
169         * If, on the other hand, properties is null, it first attempts to connect
170         * with a username and password. Failing that, it tries to connect without
171         * any credentials at all.
172         * @throws Exception
173         */
174        private void connectByDriverManager(String userName, char[] password) throws Exception {
175            if (getProperties() != null) {
176                try {
177                    conn = DriverManager.getConnection(getUrl(), getProperties());
178                    conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
179                } catch (Exception e) {
180                    try {
181                        conn = DriverManager.getConnection(getUrl(), userName, new String(password));
182                        conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
183                    } catch (Exception ex) {
184                        conn = DriverManager.getConnection(getUrl());
185                        conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
186                    }
187                }
188            } else {
189                try {
190                    conn = DriverManager.getConnection(getUrl(), userName, new String(password));
191                } catch (Exception e) {
192                    LOG.log(Level.WARNING, "Connection with properties failed. " +
193                                    "Tryint to connect without.", e);
194                    //try to connect without using the userName and password
195                    conn = DriverManager.getConnection(getUrl());
196    
197                }
198            }
199        }
200    
201        /**
202         * @param name user name
203         * @param password user password
204         * @param server Must be either a valid JDBC URL for the type of JDBC driver you are using,
205         * or must be a valid JNDIContext from which to get the database connection
206         */
207        public boolean authenticate(String name, char[] password, String server) throws Exception {
208            //try to form a connection. If it works, conn will not be null
209            //if the jndiContext is not null, then try to get the DataSource to use
210            //from jndi
211            if (jndiContext != null) {
212                try {
213                    connectByJNDI(name, password);
214                } catch (Exception e) {
215                    try {
216                        connectByDriverManager(name, password);
217                    } catch (Exception ex) {
218                        LOG.log(Level.WARNING, "Login failed", ex);
219                        //login failed
220                        return false;
221                    }
222                }
223            } else {
224                try {
225                    connectByDriverManager(name, password);
226                } catch (Exception ex) {
227                    LOG.log(Level.WARNING, "", ex);
228                    return false;
229                }
230            }
231            return true;
232        }
233    }