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