1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.rest;
22
23 import java.io.IOException;
24 import java.util.Map;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ConcurrentSkipListMap;
27 import java.util.regex.Matcher;
28 import java.util.regex.Pattern;
29
30 import javax.ws.rs.Encoded;
31 import javax.ws.rs.Path;
32 import javax.ws.rs.PathParam;
33 import javax.ws.rs.QueryParam;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.hadoop.hbase.HColumnDescriptor;
38 import org.apache.hadoop.hbase.HConstants;
39 import org.apache.hadoop.hbase.HTableDescriptor;
40 import org.apache.hadoop.hbase.TableNotFoundException;
41 import org.apache.hadoop.hbase.client.HBaseAdmin;
42 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
43 import org.apache.hadoop.hbase.rest.transform.NullTransform;
44 import org.apache.hadoop.hbase.rest.transform.Transform;
45 import org.apache.hadoop.hbase.util.Bytes;
46 import org.apache.hadoop.util.StringUtils;
47
48 public class TableResource extends ResourceBase {
49 private static final Log LOG = LogFactory.getLog(TableResource.class);
50
51
52
53
54
55 private static final String DIRECTIVE_KEY = "Transform$";
56
57
58
59
60
61
62
63
64 private static final Pattern DIRECTIVE_PATTERN =
65 Pattern.compile("([^\\:]+)\\:([^\\,]+)\\,?");
66 private static final Transform defaultTransform = new NullTransform();
67 private static final
68 Map<String,Map<byte[],Map<byte[],Transform>>> transformMap =
69 new ConcurrentHashMap<String,Map<byte[],Map<byte[],Transform>>>();
70 private static final Map<String,Long> lastCheckedMap =
71 new ConcurrentHashMap<String,Long>();
72
73
74
75
76
77
78
79
80 static Transform getTransform(String table, byte[] family, byte[] qualifier) {
81 if (qualifier == null) {
82 qualifier = HConstants.EMPTY_BYTE_ARRAY;
83 }
84 Map<byte[],Map<byte[],Transform>> familyMap = transformMap.get(table);
85 if (familyMap != null) {
86 Map<byte[],Transform> columnMap = familyMap.get(family);
87 if (columnMap != null) {
88 Transform t = columnMap.get(qualifier);
89
90 if (t == null) {
91 t = columnMap.get(HConstants.EMPTY_BYTE_ARRAY);
92 }
93
94
95 if (t != null) {
96 return t;
97 }
98 }
99 }
100 return defaultTransform;
101 }
102
103 synchronized static void setTransform(String table, byte[] family,
104 byte[] qualifier, Transform transform) {
105 Map<byte[],Map<byte[],Transform>> familyMap = transformMap.get(table);
106 if (familyMap == null) {
107 familyMap = new ConcurrentSkipListMap<byte[],Map<byte[],Transform>>(
108 Bytes.BYTES_COMPARATOR);
109 transformMap.put(table, familyMap);
110 }
111 Map<byte[],Transform> columnMap = familyMap.get(family);
112 if (columnMap == null) {
113 columnMap = new ConcurrentSkipListMap<byte[],Transform>(
114 Bytes.BYTES_COMPARATOR);
115 familyMap.put(family, columnMap);
116 }
117
118 if (transform != null) {
119 columnMap.put(qualifier, transform);
120 } else {
121 columnMap.remove(qualifier);
122 }
123 }
124
125 String table;
126
127
128
129
130
131
132
133
134 void scanTransformAttrs() throws IOException {
135 try {
136 HBaseAdmin admin = new HBaseAdmin(servlet.getConfiguration());
137 HTableDescriptor htd = admin.getTableDescriptor(Bytes.toBytes(table));
138 for (HColumnDescriptor hcd: htd.getFamilies()) {
139 for (Map.Entry<ImmutableBytesWritable, ImmutableBytesWritable> e:
140 hcd.getValues().entrySet()) {
141
142 String key = Bytes.toString(e.getKey().get());
143 if (!key.startsWith(DIRECTIVE_KEY)) {
144
145 continue;
146 }
147
148 byte[] value = e.getValue().get();
149 Matcher m = DIRECTIVE_PATTERN.matcher(Bytes.toString(value));
150 while (m.find()) {
151 byte[] qualifier = HConstants.EMPTY_BYTE_ARRAY;
152 String s = m.group(1);
153 if (s.length() > 0 && !s.equals("*")) {
154 qualifier = Bytes.toBytes(s);
155 }
156 boolean retry = false;
157 String className = m.group(2);
158 while (true) {
159 try {
160
161
162 setTransform(table, hcd.getName(), qualifier,
163 (Transform)Class.forName(className).newInstance());
164 break;
165 } catch (InstantiationException ex) {
166 LOG.error(StringUtils.stringifyException(ex));
167 if (retry) {
168 break;
169 }
170 retry = true;
171 } catch (IllegalAccessException ex) {
172 LOG.error(StringUtils.stringifyException(ex));
173 if (retry) {
174 break;
175 }
176 retry = true;
177 } catch (ClassNotFoundException ex) {
178 if (retry) {
179 LOG.error(StringUtils.stringifyException(ex));
180 break;
181 }
182 className = "org.apache.hadoop.hbase.rest.transform." + className;
183 retry = true;
184 }
185 }
186 }
187 }
188 }
189 } catch (TableNotFoundException e) {
190
191 }
192 }
193
194
195
196
197
198
199 public TableResource(String table) throws IOException {
200 super();
201 this.table = table;
202
203
204
205 long now = System.currentTimeMillis();
206 Long lastChecked = lastCheckedMap.get(table);
207 if (lastChecked != null) {
208 long interval = servlet.getConfiguration()
209 .getLong("hbase.rest.transform.check.interval", 60000);
210 if (interval > 0 && (now - lastChecked.longValue()) > interval) {
211 scanTransformAttrs();
212 lastCheckedMap.put(table, now);
213 }
214 } else {
215 scanTransformAttrs();
216 lastCheckedMap.put(table, now);
217 }
218 }
219
220
221 String getName() {
222 return table;
223 }
224
225
226
227
228
229 boolean exists() throws IOException {
230 HBaseAdmin admin = new HBaseAdmin(servlet.getConfiguration());
231 return admin.tableExists(table);
232 }
233
234
235
236
237
238
239
240
241
242
243 byte[] transform(byte[] family, byte[] qualifier, byte[] value,
244 Transform.Direction direction) throws IOException {
245 Transform t = getTransform(table, family, qualifier);
246 if (t != null) {
247 return t.transform(value, direction);
248 }
249 return value;
250 }
251
252 @Path("exists")
253 public ExistsResource getExistsResource() throws IOException {
254 return new ExistsResource(this);
255 }
256
257 @Path("regions")
258 public RegionsResource getRegionsResource() throws IOException {
259 return new RegionsResource(this);
260 }
261
262 @Path("scanner")
263 public ScannerResource getScannerResource() throws IOException {
264 return new ScannerResource(this);
265 }
266
267 @Path("schema")
268 public SchemaResource getSchemaResource() throws IOException {
269 return new SchemaResource(this);
270 }
271
272 @Path("{rowspec: .+}")
273 public RowResource getRowResource(
274
275
276 final @PathParam("rowspec") @Encoded String rowspec,
277 final @QueryParam("v") String versions) throws IOException {
278 return new RowResource(this, rowspec, versions);
279 }
280 }