001/*
002 * $Id: JDBCLoginService.java 4147 2012-02-01 17:13:24Z kschaefe $
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 */
021package org.jdesktop.swingx.auth;
022import java.sql.Connection;
023import java.sql.DriverManager;
024import java.util.Properties;
025import java.util.logging.Level;
026import java.util.logging.Logger;
027
028import javax.naming.InitialContext;
029
030import org.jdesktop.beans.JavaBean;
031/**
032 * A login service for connecting to SQL based databases via JDBC
033 *
034 * @author rbair
035 */
036@JavaBean
037public class JDBCLoginService extends LoginService {
038    private static final Logger LOG = Logger.getLogger(JDBCLoginService.class
039            .getName());
040    
041    /**
042     * The connection to the database
043     */
044    private Connection conn;
045    /**
046     * If used, defines the JNDI context from which to get a connection to
047     * the data base
048     */
049    private String jndiContext;
050    /**
051     * When using the DriverManager to connect to the database, this specifies
052     * any additional properties to use when connecting.
053     */
054    private Properties properties;
055    
056    /**
057     * Create a new JDBCLoginService and initializes it to connect to a
058     * database using the given params.
059     * @param driver
060     * @param url
061     */
062    public JDBCLoginService(String driver, String url) {
063        super(url);
064        try {
065            Class.forName(driver);
066        } catch (Exception e) {
067            LOG.log(Level.WARNING, "The driver passed to the " +
068                    "JDBCLoginService constructor could not be loaded. " +
069                    "This may be due to the driver not being on the classpath", e);
070        }
071        this.setUrl(url);
072    }
073    
074    /**
075     * Create a new JDBCLoginService and initializes it to connect to a
076     * database using the given params.
077     * @param driver
078     * @param url
079     * @param props
080     */
081    public JDBCLoginService(String driver, String url, Properties props) {
082        super(url);
083        try {
084            Class.forName(driver);
085        } catch (Exception e) {
086            LOG.log(Level.WARNING, "The driver passed to the " +
087                    "JDBCLoginService constructor could not be loaded. " +
088                    "This may be due to the driver not being on the classpath", e);
089        }
090        this.setUrl(url);
091        this.setProperties(props);
092    }
093    
094    /**
095     * Create a new JDBCLoginService and initializes it to connect to a
096     * database using the given params.
097     * @param jndiContext
098     */
099    public JDBCLoginService(String jndiContext) {
100        super(jndiContext);
101        this.jndiContext = jndiContext;
102    }
103    
104    /**
105     * Default JavaBean constructor
106     */
107    public JDBCLoginService() {
108        super();
109    }
110    
111    /**
112     * @return the JDBC connection url
113     */
114    public String getUrl() {
115        return getServer();
116    }
117
118    /**
119     * @param url set the JDBC connection url
120     */
121    public void setUrl(String url) {
122        String old = getUrl();
123        setServer(url);
124        firePropertyChange("url", old, getUrl());
125    }
126
127    /**
128     * @return JDBC connection properties
129     */
130    public Properties getProperties() {
131        return properties;
132    }
133
134    /**
135     * @param properties miscellaneous JDBC properties to use when connecting
136     *        to the database via the JDBC driver
137     */
138    public void setProperties(Properties properties) {
139        Properties old = getProperties();
140        this.properties = properties;
141        firePropertyChange("properties", old, getProperties());
142    }
143    
144    public Connection getConnection() {
145        return conn;
146    }
147    
148    public void setConnection(Connection conn) {
149        Connection old = getConnection();
150        this.conn = conn;
151        firePropertyChange("connection", old, getConnection());
152    }
153    
154    /**
155     * Attempts to get a JDBC Connection from a JNDI javax.sql.DataSource, using
156     * that connection for interacting with the database.
157     * @throws Exception
158     */
159    private void connectByJNDI(String userName, char[] password) throws Exception {
160        InitialContext ctx = new InitialContext();
161        javax.sql.DataSource ds = (javax.sql.DataSource)ctx.lookup(jndiContext);
162        conn = ds.getConnection(userName, new String(password));
163        conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
164    }
165    
166    /**
167     * Attempts to get a JDBC Connection from a DriverManager. If properties
168     * is not null, it tries to connect with those properties. If that fails,
169     * it then attempts to connect with a user name and password. If that fails,
170     * it attempts to connect without any credentials at all.
171     * <p>
172     * If, on the other hand, properties is null, it first attempts to connect
173     * with a username and password. Failing that, it tries to connect without
174     * any credentials at all.
175     * @throws Exception
176     */
177    private void connectByDriverManager(String userName, char[] password) throws Exception {
178        if (getProperties() != null) {
179            try {
180                conn = DriverManager.getConnection(getUrl(), getProperties());
181                conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
182            } catch (Exception e) {
183                try {
184                    conn = DriverManager.getConnection(getUrl(), userName, new String(password));
185                    conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
186                } catch (Exception ex) {
187                    conn = DriverManager.getConnection(getUrl());
188                    conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
189                }
190            }
191        } else {
192            try {
193                conn = DriverManager.getConnection(getUrl(), userName, new String(password));
194            } catch (Exception e) {
195                LOG.log(Level.WARNING, "Connection with properties failed. " +
196                                "Tryint to connect without.", e);
197                //try to connect without using the userName and password
198                conn = DriverManager.getConnection(getUrl());
199
200            }
201        }
202    }
203
204    /**
205     * @param name user name
206     * @param password user password
207     * @param server Must be either a valid JDBC URL for the type of JDBC driver you are using,
208     * or must be a valid JNDIContext from which to get the database connection
209     */
210    @Override
211    public boolean authenticate(String name, char[] password, String server) throws Exception {
212        //try to form a connection. If it works, conn will not be null
213        //if the jndiContext is not null, then try to get the DataSource to use
214        //from jndi
215        if (jndiContext != null) {
216            try {
217                connectByJNDI(name, password);
218            } catch (Exception e) {
219                try {
220                    connectByDriverManager(name, password);
221                } catch (Exception ex) {
222                    LOG.log(Level.WARNING, "Login failed", ex);
223                    //login failed
224                    return false;
225                }
226            }
227        } else {
228            try {
229                connectByDriverManager(name, password);
230            } catch (Exception ex) {
231                LOG.log(Level.WARNING, "", ex);
232                return false;
233            }
234        }
235        return true;
236    }
237}