001    /****************************************************************
002     * Licensed to the Apache Software Foundation (ASF) under one   *
003     * or more contributor license agreements.  See the NOTICE file *
004     * distributed with this work for additional information        *
005     * regarding copyright ownership.  The ASF licenses this file   *
006     * to you under the Apache License, Version 2.0 (the            *
007     * "License"); you may not use this file except in compliance   *
008     * with the License.  You may obtain a copy of the License at   *
009     *                                                              *
010     *   http://www.apache.org/licenses/LICENSE-2.0                 *
011     *                                                              *
012     * Unless required by applicable law or agreed to in writing,   *
013     * software distributed under the License is distributed on an  *
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015     * KIND, either express or implied.  See the License for the    *
016     * specific language governing permissions and limitations      *
017     * under the License.                                           *
018     ****************************************************************/
019    package org.apache.james.user.hbase;
020    
021    import java.io.IOException;
022    import java.util.ArrayList;
023    import java.util.Iterator;
024    import java.util.List;
025    
026    import org.apache.commons.configuration.ConfigurationException;
027    import org.apache.commons.configuration.HierarchicalConfiguration;
028    import org.apache.hadoop.hbase.KeyValue;
029    import org.apache.hadoop.hbase.client.Delete;
030    import org.apache.hadoop.hbase.client.Get;
031    import org.apache.hadoop.hbase.client.HTable;
032    import org.apache.hadoop.hbase.client.Put;
033    import org.apache.hadoop.hbase.client.Result;
034    import org.apache.hadoop.hbase.client.ResultScanner;
035    import org.apache.hadoop.hbase.client.Scan;
036    import org.apache.hadoop.hbase.util.Bytes;
037    import org.apache.james.system.hbase.TablePool;
038    import org.apache.james.user.api.UsersRepositoryException;
039    import org.apache.james.user.api.model.User;
040    import org.apache.james.user.hbase.def.HUsersRepository;
041    import org.apache.james.user.lib.AbstractUsersRepository;
042    import org.apache.james.user.lib.model.DefaultUser;
043    import org.slf4j.Logger;
044    import org.slf4j.LoggerFactory;
045    
046    /**
047     * Implementation of the UserRepository for a HBase persistence.
048     */
049    public class HBaseUsersRepository extends AbstractUsersRepository {
050        
051        /**
052         * The Logger.
053         */
054        private static Logger log = LoggerFactory.getLogger(HBaseUsersRepository.class.getName());
055    
056        /**
057         * Hashing algorithm for the password.
058         */
059        private String algo;
060    
061        /**
062         * @see org.apache.james.user.lib.AbstractUsersRepository#doConfigure(HierarchicalConfiguration)
063         */
064        public void doConfigure(HierarchicalConfiguration config) throws ConfigurationException {
065            algo = config.getString("algorithm", "MD5");
066            super.doConfigure(config);
067        }
068    
069        /**
070         * @see org.apache.james.user.api.UsersRepository#getUserByName(String)
071         */
072        @Override
073        public User getUserByName(String name) throws UsersRepositoryException {
074            KeyValue keyValue = getKeyValue(name);
075            User user = null;
076            if (keyValue != null) {
077                user = new DefaultUser(Bytes.toString(keyValue.getRow()), Bytes.toString(keyValue.getValue()), algo);
078            }
079            return user;
080        }
081    
082        /**
083         * @see org.apache.james.user.api.UsersRepository#updateUser(User)
084         */
085        @Override
086        public void updateUser(User user) throws UsersRepositoryException {
087            if (user == null) {
088                throw new UsersRepositoryException("Please provide a non null user");
089            }
090            if (! (user instanceof DefaultUser)) {
091                throw new UsersRepositoryException("Please provide a user instanceof DefaultUser");
092            }
093            User existingUser = getUserByName(user.getUserName());
094            if (existingUser == null) {
095                throw new UsersRepositoryException("Please provide an existing user to update");
096            }
097            putUser((DefaultUser) user, false);
098        }
099    
100        /**
101         * @see org.apache.james.user.api.UsersRepository#removeUser(String)
102         */
103        @Override
104        public void removeUser(String name) throws UsersRepositoryException {
105            HTable table = null;
106            try {
107                table = TablePool.getInstance().getUsersRepositoryTable();
108                Delete delete = new Delete(Bytes.toBytes(name));
109                table.delete(delete);
110                table.flushCommits();
111            } catch (IOException e) {
112                log.error("Error while deleting user from HBase", e);
113                throw new UsersRepositoryException("Error while deleting user from HBase", e);
114            } finally {
115                if (table != null) {
116                    try {
117                        TablePool.getInstance().putTable(table);
118                    } catch (IOException e) {
119                        // Do nothing, we can't get access to the HBaseSchema.
120                    }
121                }
122            }
123        }
124    
125        /**
126         * @see org.apache.james.user.api.UsersRepository#contains(String)
127         */
128        @Override
129        public boolean contains(String name) throws UsersRepositoryException {
130            KeyValue keyValue = getKeyValue(name.toLowerCase());
131            return (keyValue != null);
132        }
133    
134        /**
135         * @see org.apache.james.user.api.UsersRepository#test(String, String)
136         */
137        @Override
138        public boolean test(String name, String password) throws UsersRepositoryException {
139            KeyValue keyValue = getKeyValue(name);
140            if (keyValue != null) {
141                DefaultUser user = new DefaultUser(name, algo);
142                user.setPassword(password);
143                return Bytes.toString(keyValue.getValue()).equals(user.getHashedPassword());
144            }
145            return false;
146        }
147    
148        /**
149         * @see org.apache.james.user.api.UsersRepository#countUsers()
150         */
151        @Override
152        public int countUsers() throws UsersRepositoryException {
153            HTable table = null;
154            ResultScanner resultScanner = null;
155            try {
156                table = TablePool.getInstance().getUsersRepositoryTable();
157                Scan scan = new Scan();
158                scan.addFamily(HUsersRepository.COLUMN_FAMILY_NAME);
159                scan.setCaching(table.getScannerCaching() * 2);
160                resultScanner = table.getScanner(scan);
161                int resultCount = 0;
162                Result result = null;
163                while ((result = resultScanner.next()) != null) {
164                    resultCount++;
165                }
166                return resultCount;
167            } catch (IOException e) {
168                log.error("Error while counting users from HBase", e);
169                throw new UsersRepositoryException("Error while counting users from HBase", e);
170            } finally {
171                if (resultScanner != null) {
172                    resultScanner.close();
173                }
174                if (table != null) {
175                    try {
176                        TablePool.getInstance().putTable(table);
177                    } catch (IOException e) {
178                        // Do nothing, we can't get access to the HBaseSchema.
179                    }
180                }
181            }
182        }
183    
184        /**
185         * @see org.apache.james.user.api.UsersRepository#list()
186         */
187        @Override
188        public Iterator<String> list() throws UsersRepositoryException {
189            List<String> list = new ArrayList<String>();
190            HTable table = null;
191            ResultScanner resultScanner = null;
192            try {
193                table = TablePool.getInstance().getUsersRepositoryTable();
194                Scan scan = new Scan();
195                scan.addFamily(HUsersRepository.COLUMN_FAMILY_NAME);
196                scan.setCaching(table.getScannerCaching() * 2);
197                resultScanner = table.getScanner(scan);
198                Result result = null;
199                while ((result = resultScanner.next()) != null) {
200                    list.add(Bytes.toString(result.getRow()));
201                }
202            } catch (IOException e) {
203                log.error("Error while scanning users from HBase", e);
204                throw new UsersRepositoryException("Error while scanning users from HBase", e);
205            } finally {
206                if (resultScanner != null) {
207                    resultScanner.close();
208                }
209                if (table != null) {
210                    try {
211                        TablePool.getInstance().putTable(table);
212                    } catch (IOException e) {
213                        // Do nothing, we can't get access to the HBaseSchema.
214                    }
215                }
216            }
217            return list.iterator();
218        }
219    
220        /**
221         * @see org.apache.james.user.lib.AbstractUsersRepository#doAddUser(String, String)
222         */
223        @Override
224        protected void doAddUser(String username, String password) throws UsersRepositoryException {
225            DefaultUser user = new DefaultUser(username, algo);
226            user.setPassword(password);
227            putUser(user, true);
228        }
229        
230        /**
231         * Utility method to retrieve a HBase KeyValue for a given username.
232         * 
233         * @param username
234         * @return
235         * @throws UsersRepositoryException
236         */
237        private KeyValue getKeyValue(String username) throws UsersRepositoryException {
238            HTable table = null;
239            try {
240                table = TablePool.getInstance().getUsersRepositoryTable();
241                Get get = new Get(Bytes.toBytes(username));
242                Result result = table.get(get);
243                KeyValue keyValue = result.getColumnLatest(HUsersRepository.COLUMN_FAMILY_NAME, HUsersRepository.COLUMN.PWD);
244                return keyValue;
245            } catch (IOException e) {
246                log.error("Error while counting users from HBase", e);
247                throw new UsersRepositoryException("Error while counting users from HBase", e);
248            } finally {
249                if (table != null) {
250                    try {
251                        TablePool.getInstance().putTable(table);
252                    } catch (IOException e) {
253                        // Do nothing, we can't get access to the HBaseSchema.
254                    }
255                }
256            }
257        }
258        
259        /**
260         * Utility method to put a User in HBase.
261         * 
262         * @param user
263         * @throws UsersRepositoryException
264         */
265        private void putUser(DefaultUser user, boolean isAdd) throws UsersRepositoryException {
266            String username = user.getUserName();
267            if (isAdd) {
268                username = user.getUserName().toLowerCase();
269                if (contains(username)) {
270                    throw new UsersRepositoryException(username + " already exists.");
271                }
272            }
273            HTable table = null;
274            try {
275                table = TablePool.getInstance().getUsersRepositoryTable();
276                Put put = new Put(Bytes.toBytes(username));
277                put.add(HUsersRepository.COLUMN_FAMILY_NAME, HUsersRepository.COLUMN.PWD, Bytes.toBytes(user.getHashedPassword()));
278                table.put(put);
279                table.flushCommits();
280            } catch (IOException e) {
281                log.error("Error while adding user in HBase", e);
282                throw new UsersRepositoryException("Error while adding user in HBase", e);
283            } finally {
284                if (table != null) {
285                    try {
286                        TablePool.getInstance().putTable(table);
287                    } catch (IOException e) {
288                        // Do nothing, we can't get access to the HBaseSchema.
289                    }
290                }
291            }
292        }
293    
294    }