001    /*
002     * $Id: LoginService.java 3360 2009-06-14 20:29:45Z 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     */
021    package org.jdesktop.swingx.auth;
022    
023    import java.awt.EventQueue;
024    import java.util.logging.Logger;
025    
026    import javax.swing.SwingUtilities;
027    import javax.swing.SwingWorker;
028    import javax.swing.event.EventListenerList;
029    
030    import org.jdesktop.beans.AbstractBean;
031    
032    /**
033     * <b>LoginService</b> is the abstract base class for all classes implementing
034     * a login mechanism. It allows you to customize the threading behaviour used to
035     * perform the login. Subclasses need to override the <b>authenticate</b>
036     * method. Subclasses may implement the getUserRoles() method to return a
037     * meaningful value this method will be called once upon a successful login to
038     * determine the user roles. It is not defined as abstract to simplify the task
039     * of implementing a login service for those who do not require this
040     * functionality.
041     * 
042     * @author Bino George
043     * @author Shai Almog
044     * @author Karl Schaefer
045     */
046    public abstract class LoginService extends AbstractBean {
047        private Logger LOG = Logger.getLogger(LoginService.class.getName());
048    
049        private EventListenerList listenerList = new EventListenerList();
050    
051        private SwingWorker<Boolean, Void> loginWorker;
052    
053        /*
054         * Controls the authentication behaviour to be either synchronous or
055         * asynchronous
056         */
057        private boolean synchronous;
058    
059        private String server;
060    
061        public LoginService() {
062        }
063    
064        public LoginService(String server) {
065            setServer(server);
066        }
067    
068        /**
069         * This method is intended to be implemented by clients wishing to
070         * authenticate a user with a given password. Clients should implement the
071         * authentication in a manner that the authentication can be cancelled at
072         * any time.
073         * 
074         * @param name
075         *            username
076         * @param password
077         *            password
078         * @param server
079         *            server (optional)
080         * 
081         * @return <code>true</code> on authentication success
082         * @throws Exception
083         */
084        public abstract boolean authenticate(String name, char[] password,
085                String server) throws Exception;
086    
087        /**
088         * Called immediately after a successful authentication. This method should
089         * return an array of user roles or null if role based permissions are not
090         * used.
091         * 
092         * @return per default <code>null</code>
093         */
094        public String[] getUserRoles() {
095            return null;
096        }
097    
098        /**
099         * Notifies the LoginService that an already running authentication request
100         * should be cancelled. This method is intended to be used by clients who
101         * want to provide user with control over cancelling a long running
102         * authentication request.
103         */
104        public void cancelAuthentication() {
105            if (loginWorker != null) {
106                loginWorker.cancel(true);
107            }
108        }
109    
110        /**
111         * This method starts the authentication process and is either synchronous
112         * or asynchronous based on the synchronous property
113         * 
114         * @param user
115         *            user
116         * @param password
117         *            password
118         * @param server
119         *            server
120         * @throws Exception
121         */
122        public void startAuthentication(final String user, final char[] password,
123                final String server) throws Exception {
124            if (getSynchronous()) {
125                try {
126                    if (authenticate(user, password, server)) {
127                        fireLoginSucceeded(new LoginEvent(this));
128                    } else {
129                        fireLoginFailed(new LoginEvent(this));
130                    }
131                } catch (Throwable e) {
132                    fireLoginFailed(new LoginEvent(this, e));
133                }
134            } else {
135                loginWorker = new SwingWorker<Boolean, Void>() {
136                    protected Boolean doInBackground() throws Exception {
137                        try {
138                            final boolean result = authenticate(user, password,
139                                    server);
140                            if (isCancelled()) {
141                                EventQueue.invokeLater(new Runnable() {
142                                    public void run() {
143                                        fireLoginCanceled(new LoginEvent(this));
144                                    }
145                                });
146                                return false;
147                            }
148                            EventQueue.invokeLater(new Runnable() {
149                                public void run() {
150                                    if (result) {
151                                        fireLoginSucceeded(new LoginEvent(
152                                                LoginService.this));
153                                    } else {
154                                        fireLoginFailed(new LoginEvent(
155                                                LoginService.this));
156                                    }
157                                }
158                            });
159                            return result;
160                        } catch (final Throwable failed) {
161                            if (!isCancelled()) {
162                                SwingUtilities.invokeLater(new Runnable() {
163                                    public void run() {
164                                        fireLoginFailed(new LoginEvent(
165                                                LoginService.this, failed));
166                                    }
167                                });
168                            } else {
169                                EventQueue.invokeLater(new Runnable() {
170                                    public void run() {
171                                        fireLoginCanceled(new LoginEvent(this));
172                                    }
173                                });
174                            }
175                            return false;
176                        }
177                    }
178                };
179                loginWorker.execute();
180                fireLoginStarted(new LoginEvent(this));
181            }
182        }
183    
184        /**
185         * Get the synchronous property
186         * 
187         * @return the synchronous property
188         */
189        public boolean getSynchronous() {
190            return synchronous;
191        }
192    
193        /**
194         * Sets the synchronous property
195         * 
196         * @param synchronous
197         *            synchronous property
198         */
199        public void setSynchronous(boolean synchronous) {
200            boolean old = getSynchronous();
201            this.synchronous = synchronous;
202            firePropertyChange("synchronous", old, getSynchronous());
203        }
204    
205        /**
206         * Adds a <strong>LoginListener</strong> to the list of listeners
207         * 
208         * @param listener
209         *            listener
210         */
211    
212        public void addLoginListener(LoginListener listener) {
213            listenerList.add(LoginListener.class, listener);
214        }
215    
216        /**
217         * Removes a <strong>LoginListener</strong> from the list of listeners
218         * 
219         * @param listener
220         *            listener
221         */
222        public void removeLoginListener(LoginListener listener) {
223            listenerList.remove(LoginListener.class, listener);
224        }
225    
226        void fireLoginStarted(final LoginEvent source) {
227            // Guaranteed to return a non-null array
228            Object[] listeners = listenerList.getListenerList();
229            // Process the listeners last to first, notifying
230            // those that are interested in this event
231            for (int i = listeners.length-2; i>=0; i-=2) {
232                if (listeners[i] == LoginListener.class) {
233                    ((LoginListener) listeners[i+1]).loginStarted(source);
234                }
235            }
236        }
237    
238        void fireLoginSucceeded(final LoginEvent source) {
239            // Guaranteed to return a non-null array
240            Object[] listeners = listenerList.getListenerList();
241            // Process the listeners last to first, notifying
242            // those that are interested in this event
243            for (int i = listeners.length-2; i>=0; i-=2) {
244                if (listeners[i] == LoginListener.class) {
245                    ((LoginListener) listeners[i+1]).loginSucceeded(source);
246                }
247            }
248        }
249    
250        void fireLoginFailed(final LoginEvent source) {
251            // Guaranteed to return a non-null array
252            Object[] listeners = listenerList.getListenerList();
253            // Process the listeners last to first, notifying
254            // those that are interested in this event
255            for (int i = listeners.length-2; i>=0; i-=2) {
256                if (listeners[i] == LoginListener.class) {
257                    ((LoginListener) listeners[i+1]).loginFailed(source);
258                }
259            }
260        }
261    
262        void fireLoginCanceled(final LoginEvent source) {
263            // Guaranteed to return a non-null array
264            Object[] listeners = listenerList.getListenerList();
265            // Process the listeners last to first, notifying
266            // those that are interested in this event
267            for (int i = listeners.length-2; i>=0; i-=2) {
268                if (listeners[i] == LoginListener.class) {
269                    ((LoginListener) listeners[i+1]).loginCanceled(source);
270                }
271            }
272        }
273    
274        /**
275         * @return Returns the server.
276         */
277        public String getServer() {
278            return server;
279        }
280    
281        /**
282         * @param server
283         *            The server to set.
284         */
285        public void setServer(String server) {
286            String old = getServer();
287            this.server = server;
288            firePropertyChange("server", old, getServer());
289        }
290    }