001package org.apache.fulcrum.security.torque;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021import java.sql.Connection;
022import java.util.List;
023
024import org.apache.avalon.framework.configuration.Configuration;
025import org.apache.avalon.framework.configuration.ConfigurationException;
026import org.apache.fulcrum.security.entity.User;
027import org.apache.fulcrum.security.spi.AbstractUserManager;
028import org.apache.fulcrum.security.torque.security.TorqueAbstractSecurityEntity;
029import org.apache.fulcrum.security.util.DataBackendException;
030import org.apache.fulcrum.security.util.UnknownEntityException;
031import org.apache.fulcrum.security.util.UserSet;
032import org.apache.torque.NoRowsException;
033import org.apache.torque.TooManyRowsException;
034import org.apache.torque.TorqueException;
035import org.apache.torque.util.Transaction;
036
037/**
038 * This implementation persists to a database via Torque.
039 *
040 * @author <a href="mailto:tv@apache.org">Thomas Vandahl</a>
041 * @version $Id:$
042 */
043public abstract class TorqueAbstractUserManager extends AbstractUserManager {
044
045        /** Serial version */
046        private static final long serialVersionUID = 2050218990148719292L;
047
048        /**
049         * Avalon Service lifecycle method
050         */
051        @Override
052        public void configure(Configuration conf) throws ConfigurationException {
053                super.configure(conf);
054        }
055
056        /**
057         * Get all specialized Users
058         *
059         * @param con a database connection
060         *
061         * @return a List of User instances
062         *
063         * @throws TorqueException if any database error occurs
064         */
065        protected abstract <T extends User> List<T> doSelectAllUsers(Connection con) throws TorqueException;
066
067        /**
068         * Get a specialized User by name
069         *
070         * @param name the name of the group
071         * @param con  a database connection
072         *
073         * @return a User instance
074         *
075         * @throws NoRowsException      if no such group exists
076         * @throws TooManyRowsException if multiple groups with the given name exist
077         * @throws TorqueException      if any database error occurs if any other
078         *                              database error occurs
079         */
080        protected abstract <T extends User> T doSelectByName(String name, Connection con)
081                        throws NoRowsException, TooManyRowsException, TorqueException;
082
083        /**
084         * Get a specialized User by id
085         *
086         * @param id  the id of the group
087         * @param con a database connection
088         *
089         * @return a User instance
090         *
091         * @throws NoRowsException      if no such group exists
092         * @throws TooManyRowsException if multiple groups with the given id exist
093         * @throws TorqueException      if any database error occurs if any other
094         *                              database error occurs
095         */
096        protected abstract <T extends User> T doSelectById(Integer id, Connection con)
097                        throws NoRowsException, TooManyRowsException, TorqueException;
098
099        /**
100         * Removes an user account from the system.
101         *
102         * @param user the object describing the account to be removed.
103         * @throws DataBackendException   if there was an error accessing the data
104         *                                backend.
105         * @throws UnknownEntityException if the user account is not present.
106         */
107        @Override
108        public synchronized void removeUser(User user) throws DataBackendException, UnknownEntityException {
109                try {
110                        ((TorqueAbstractSecurityEntity) user).delete();
111                } catch (TorqueException e) {
112                        throw new DataBackendException("Removing User '" + user.getName() + "' failed", e);
113                }
114        }
115
116        /**
117         * Creates new user account with specified attributes.
118         *
119         * @param user the object describing account to be created.
120         *
121         * @throws DataBackendException if there was an error accessing the data
122         *                              backend.
123         */
124        @Override
125        protected synchronized <T extends User> T persistNewUser(T user) throws DataBackendException {
126                try {
127                        TorqueAbstractSecurityEntity u = (TorqueAbstractSecurityEntity) user;
128                        u.save();
129                } catch (Exception e) {
130                        throw new DataBackendException("Adding User '" + user.getName() + "' failed", e);
131                }
132
133                return user;
134        }
135
136        /**
137         * Stores User attributes. The User is required to exist in the system.
138         *
139         * @param user The User to be stored.
140         * @throws DataBackendException   if there was an error accessing the data
141         *                                backend.
142         * @throws UnknownEntityException if the role does not exist.
143         */
144        @Override
145        public synchronized void saveUser(User user) throws DataBackendException, UnknownEntityException {
146                if (checkExists(user)) {
147                        try {
148                                TorqueAbstractSecurityEntity u = (TorqueAbstractSecurityEntity) user;
149                                u.setNew(false);
150                                u.save();
151                        } catch (Exception e) {
152                                throw new DataBackendException("Saving User '" + user.getName() + "' failed", e);
153                        }
154                } else {
155                        throw new UnknownEntityException("Unknown user '" + user + "'");
156                }
157        }
158
159        /**
160         * Check whether a specified user's account exists.
161         *
162         * The login name is used for looking up the account.
163         *
164         * @param userName The name of the user to be checked.
165         * @return true if the specified account exists
166         * @throws DataBackendException if there was an error accessing the data
167         *                              backend.
168         */
169        @Override
170        public boolean checkExists(String userName) throws DataBackendException {
171                boolean exists = false;
172
173                Connection con = null;
174
175                try {
176                        con = Transaction.begin();
177
178                        doSelectByName(userName, con);
179
180                        Transaction.commit(con);
181                        con = null;
182
183                        exists = true;
184                } catch (NoRowsException e) {
185                        exists = false;
186                } catch (TooManyRowsException e) {
187                        throw new DataBackendException("Multiple Users with same username '" + userName + "'");
188                } catch (TorqueException e) {
189                        throw new DataBackendException("Error retrieving user information", e);
190                } finally {
191                        if (con != null) {
192                                Transaction.safeRollback(con);
193                        }
194                }
195
196                return exists;
197        }
198
199        /**
200         * Retrieve a user from persistent storage using username as the key.
201         * 
202         * Additionally retrieves all attached objects from {@link TorqueAbstractSecurityEntity#retrieveAttachedObjects(Connection, Boolean)}
203         *
204         * @param userName the name of the user.
205         * @return an User object.
206         * @exception UnknownEntityException if the user's account does not exist in the
207         *                                   database.
208         * @exception DataBackendException   if there is a problem accessing the
209         *                                   storage.
210         */
211        @Override
212        public <T extends User> T getUser(String userName) throws UnknownEntityException, DataBackendException {
213                T user = null;
214                Connection con = null;
215
216                try {
217                        con = Transaction.begin();
218
219                        user = doSelectByName(userName.toLowerCase(), con);
220
221                        // Add attached objects if they exist
222                        ((TorqueAbstractSecurityEntity) user).retrieveAttachedObjects(con, false);
223
224                        Transaction.commit(con);
225                        con = null;
226                } catch (NoRowsException e) {
227                        throw new UnknownEntityException("Unknown user '" + userName + "'");
228                } catch (TooManyRowsException e) {
229                        throw new DataBackendException("Multiple Users with same username '" + userName + "'");
230                } catch (TorqueException e) {
231                        throw new DataBackendException("Error retrieving user information", e);
232                } finally {
233                        if (con != null) {
234                                Transaction.safeRollback(con);
235                        }
236                }
237
238                return user;
239        }
240
241        /**
242         * Retrieves all users defined in the system.
243         *
244         * @return the names of all users defined in the system.
245         * @throws DataBackendException if there was an error accessing the data
246         *                              backend.
247         */
248        @Override
249        public <T extends User> UserSet<T> getAllUsers() throws DataBackendException {
250                UserSet<T> userSet = new UserSet<T>();
251                Connection con = null;
252
253                try {
254                        con = Transaction.begin();
255
256                        List<User> users = doSelectAllUsers(con);
257
258                        for (User user : users) {
259                                // Add attached objects if they exist
260                                ((TorqueAbstractSecurityEntity) user).retrieveAttachedObjects(con, false);
261
262                                userSet.add(user);
263                        }
264
265                        Transaction.commit(con);
266                        con = null;
267                } catch (TorqueException e) {
268                        throw new DataBackendException("Error retrieving all users", e);
269                } finally {
270                        if (con != null) {
271                                Transaction.safeRollback(con);
272                        }
273                }
274
275                return userSet;
276        }
277
278        /**
279         * Retrieve a User object with specified id.
280         *
281         * @param id the id of the User.
282         * @return an object representing the User with specified id.
283         * @throws DataBackendException   if there was an error accessing the data
284         *                                backend.
285         * @throws UnknownEntityException if the user does not exist.
286         */
287        @Override
288        public <T extends User> T getUserById(Object id) throws DataBackendException, UnknownEntityException {
289                T user;
290
291                if (id != null && id instanceof Integer) {
292                        Connection con = null;
293
294                        try {
295                                con = Transaction.begin();
296
297                                user = doSelectById((Integer) id, con);
298
299                                // Add attached objects if they exist
300                                ((TorqueAbstractSecurityEntity) user).retrieveAttachedObjects(con, false); //
301
302                                Transaction.commit(con);
303                                con = null;
304                        } catch (NoRowsException e) {
305                                throw new UnknownEntityException("User with id '" + id + "' does not exist.", e);
306                        } catch (TorqueException e) {
307                                throw new DataBackendException("Error retrieving user information", e);
308                        } finally {
309                                if (con != null) {
310                                        Transaction.safeRollback(con);
311                                }
312                        }
313                } else {
314                        throw new UnknownEntityException("Invalid user id '" + id + "'");
315                }
316
317                return user;
318        }
319
320}