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.rrt.hbase;
020
021 import java.io.IOException;
022 import java.util.ArrayList;
023 import java.util.Collection;
024 import java.util.HashMap;
025 import java.util.List;
026 import java.util.Map;
027
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.rrt.api.RecipientRewriteTableException;
038 import org.apache.james.rrt.hbase.def.HRecipientRewriteTable;
039 import org.apache.james.rrt.lib.AbstractRecipientRewriteTable;
040 import org.apache.james.rrt.lib.RecipientRewriteTableUtil;
041 import org.apache.james.system.hbase.TablePool;
042 import org.slf4j.Logger;
043 import org.slf4j.LoggerFactory;
044
045 /**
046 * Implementation of the RecipientRewriteTable for a HBase persistence.
047 */
048 public class HBaseRecipientRewriteTable extends AbstractRecipientRewriteTable {
049
050 /**
051 * The Logger.
052 */
053 private static Logger log = LoggerFactory.getLogger(HBaseRecipientRewriteTable.class.getName());
054
055 private static final String ROW_SEPARATOR = "@";
056
057 /**
058 * @see org.apache.james.rrt.lib.AbstractRecipientRewriteTable#addMappingInternal(String, String, String)
059 */
060 @Override
061 protected void addMappingInternal(String user, String domain, String mapping) throws RecipientRewriteTableException {
062 String fixedUser = getFixedUser(user);
063 String fixedDomain = getFixedDomain(domain);
064 Collection<String> map = getUserDomainMappings(fixedUser, fixedDomain);
065 if (map != null && map.size() != 0) {
066 map.add(mapping);
067 doUpdateMapping(fixedUser, fixedDomain, RecipientRewriteTableUtil.CollectionToMapping(map));
068 } else {
069 doAddMapping(fixedUser, fixedDomain, mapping);
070 }
071 }
072
073 /**
074 * @see org.apache.james.rrt.lib.AbstractRecipientRewriteTable#getUserDomainMappingsInternal(String, String)
075 */
076 @Override
077 protected Collection<String> getUserDomainMappingsInternal(String user, String domain) throws RecipientRewriteTableException {
078 HTable table = null;
079 List<String> list = new ArrayList<String>();
080 try {
081 table = TablePool.getInstance().getRecipientRewriteTable();
082 // Optimize this to only make one call.
083 feedUserDomainMappingsList(table, user, domain, list);
084 } catch (IOException e) {
085 log.error("Error while getting user domain mapping in HBase", e);
086 throw new RecipientRewriteTableException("Error while getting user domain mapping in HBase", e);
087 } finally {
088 if (table != null) {
089 try {
090 TablePool.getInstance().putTable(table);
091 } catch (IOException e) {
092 // Do nothing, we can't get access to the HBaseSchema.
093 }
094 }
095 }
096 return list;
097 }
098
099 private void feedUserDomainMappingsList(HTable table, String user, String domain, Collection<String> list) throws IOException {
100 Get get = new Get(Bytes.toBytes(getRowKey(user, domain)));
101 Result result = table.get(get);
102 List<KeyValue> keyValues = result.getColumn(HRecipientRewriteTable.COLUMN_FAMILY_NAME, HRecipientRewriteTable.COLUMN.MAPPING);
103 if (keyValues.size() > 0) {
104 list.addAll(RecipientRewriteTableUtil.mappingToCollection(Bytes.toString(keyValues.get(0).getValue())));
105 }
106 }
107
108 /**
109 * @see org.apache.james.rrt.lib.AbstractRecipientRewriteTable#getAllMappingsInternal()
110 */
111 @Override
112 protected Map<String, Collection<String>> getAllMappingsInternal() throws RecipientRewriteTableException {
113 HTable table = null;
114 ResultScanner resultScanner = null;
115 Map<String, Collection<String>> map = null;
116 try {
117 table = TablePool.getInstance().getRecipientRewriteTable();
118 Scan scan = new Scan();
119 scan.addFamily(HRecipientRewriteTable.COLUMN_FAMILY_NAME);
120 scan.setCaching(table.getScannerCaching() * 2);
121 resultScanner = table.getScanner(scan);
122 Result result = null;
123 while ((result = resultScanner.next()) != null) {
124 List<KeyValue> keyValues = result.list();
125 if (keyValues != null) {
126 for (KeyValue keyValue: keyValues) {
127 String email = Bytes.toString(keyValue.getRow());
128 if (map == null) {
129 map = new HashMap<String, Collection<String>>();
130 }
131 Collection<String> list = map.get(email);
132 if (list == null) {
133 list = new ArrayList<String>();
134 }
135 list.add(Bytes.toString(keyValue.getRow()));
136 map.put(email, list);
137 }
138 }
139 }
140 } catch (IOException e) {
141 log.error("Error while getting all mapping from HBase", e);
142 throw new RecipientRewriteTableException("Error while getting all mappings from HBase", e);
143 } finally {
144 if (resultScanner != null) {
145 resultScanner.close();
146 }
147 if (table != null) {
148 try {
149 TablePool.getInstance().putTable(table);
150 } catch (IOException e) {
151 // Do nothing, we can't get access to the HBaseSchema.
152 }
153 }
154 }
155 return map;
156 }
157
158 /**
159 * @see org.apache.james.rrt.lib.AbstractRecipientRewriteTable#mapAddressInternal(String, String)
160 */
161 @Override
162 protected String mapAddressInternal(String user, String domain) throws RecipientRewriteTableException {
163 HTable table = null;
164 String mappings = null;
165 try {
166 table = TablePool.getInstance().getRecipientRewriteTable();
167 mappings = getMapping(table, user, domain);
168 if (mappings == null) {
169 mappings = getMapping(table, WILDCARD, domain);
170 }
171 if (mappings == null) {
172 mappings = getMapping(table, user, WILDCARD);
173 }
174 } catch (IOException e) {
175 log.error("Error while mapping address in HBase", e);
176 throw new RecipientRewriteTableException("Error while mapping address in HBase", e);
177 } finally {
178 if (table != null) {
179 try {
180 TablePool.getInstance().putTable(table);
181 } catch (IOException e) {
182 // Do nothing, we can't get access to the HBaseSchema.
183 }
184 }
185 }
186 return mappings;
187 }
188
189 private String getMapping(HTable table, String user, String domain) throws IOException {
190 Get get = new Get(Bytes.toBytes(getRowKey(user, domain)));
191 Result result = table.get(get);
192 List<KeyValue> keyValues = result.getColumn(HRecipientRewriteTable.COLUMN_FAMILY_NAME, HRecipientRewriteTable.COLUMN.MAPPING);
193 if (keyValues.size() > 0) {
194 return Bytes.toString(keyValues.get(0).getValue());
195 }
196 return null;
197 }
198
199 /**
200 * @see org.apache.james.rrt.lib.AbstractRecipientRewriteTable#removeMappingInternal(String, String, String)
201 */
202 @Override
203 protected void removeMappingInternal(String user, String domain, String mapping) throws RecipientRewriteTableException {
204 String fixedUser = getFixedUser(user);
205 String fixedDomain = getFixedDomain(domain);
206 Collection<String> map = getUserDomainMappings(fixedUser, fixedDomain);
207 if (map != null && map.size() > 1) {
208 map.remove(mapping);
209 doUpdateMapping(fixedUser, fixedDomain, RecipientRewriteTableUtil.CollectionToMapping(map));
210 } else {
211 doRemoveMapping(fixedUser, fixedDomain, mapping);
212 }
213 }
214
215 /**
216 * Update the mapping for the given user and domain.
217 * For HBase, this is simply achieved delegating
218 * the work to the doAddMapping method.
219 *
220 * @param user the user
221 * @param domain the domain
222 * @param mapping the mapping
223 * @throws RecipientRewriteTableException
224 */
225 private void doUpdateMapping(String user, String domain, String mapping) throws RecipientRewriteTableException {
226 doAddMapping(user, domain, mapping);
227 }
228
229 /**
230 * Remove a mapping for the given user and domain.
231 *
232 * @param user the user
233 * @param domain the domain
234 * @param mapping the mapping
235 * @throws RecipientRewriteTableException
236 */
237 private void doRemoveMapping(String user, String domain, String mapping) throws RecipientRewriteTableException {
238 HTable table = null;
239 try {
240 table = TablePool.getInstance().getRecipientRewriteTable();
241 Delete delete = new Delete(Bytes.toBytes(getRowKey(user, domain)));
242 table.delete(delete);
243 table.flushCommits();
244 } catch (IOException e) {
245 log.error("Error while removing mapping from HBase", e);
246 throw new RecipientRewriteTableException("Error while removing mapping from HBase", e);
247 } finally {
248 if (table != null) {
249 try {
250 TablePool.getInstance().putTable(table);
251 } catch (IOException e) {
252 // Do nothing, we can't get access to the HBaseSchema.
253 }
254 }
255 }
256 }
257
258 /**
259 * Add mapping for given user and domain
260 *
261 * @param user the user
262 * @param domain the domain
263 * @param mapping the mapping
264 * @throws RecipientRewriteTableException
265 */
266 private void doAddMapping(String user, String domain, String mapping) throws RecipientRewriteTableException {
267 HTable table = null;
268 try {
269 table = TablePool.getInstance().getRecipientRewriteTable();
270 Put put = new Put(Bytes.toBytes(getRowKey(user, domain)));
271 put.add(HRecipientRewriteTable.COLUMN_FAMILY_NAME, HRecipientRewriteTable.COLUMN.MAPPING, Bytes.toBytes(mapping));
272 table.put(put);
273 table.flushCommits();
274 } catch (IOException e) {
275 log.error("Error while adding mapping in HBase", e);
276 throw new RecipientRewriteTableException("Error while adding mapping in HBase", e);
277 } finally {
278 if (table != null) {
279 try {
280 TablePool.getInstance().putTable(table);
281 } catch (IOException e) {
282 // Do nothing, we can't get access to the HBaseSchema.
283 }
284 }
285 }
286 }
287
288 /**
289 * Constructs a Key based on the user and domain.
290 *
291 * @param user
292 * @param domain
293 * @return the key
294 */
295 private String getRowKey(String user, String domain) {
296 return user + ROW_SEPARATOR + domain;
297 }
298
299 }