1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.rest;
20
21 import java.io.IOException;
22 import java.util.Map;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.locks.Lock;
25
26 import org.apache.hadoop.classification.InterfaceAudience;
27 import org.apache.hadoop.conf.Configuration;
28 import org.apache.hadoop.hbase.Chore;
29 import org.apache.hadoop.hbase.Stoppable;
30 import org.apache.hadoop.hbase.client.HBaseAdmin;
31 import org.apache.hadoop.hbase.client.HConnection;
32 import org.apache.hadoop.hbase.client.HConnectionManager;
33 import org.apache.hadoop.hbase.client.HTableInterface;
34 import org.apache.hadoop.hbase.security.User;
35 import org.apache.hadoop.hbase.security.UserProvider;
36 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
37 import org.apache.hadoop.hbase.util.KeyLocker;
38 import org.apache.hadoop.hbase.util.Threads;
39 import org.apache.hadoop.security.UserGroupInformation;
40 import org.apache.log4j.Logger;
41
42
43
44
45 @InterfaceAudience.Private
46 public class RESTServlet implements Constants {
47 private static Logger LOG = Logger.getLogger(RESTServlet.class);
48 private static RESTServlet INSTANCE;
49 private final Configuration conf;
50 private final MetricsREST metrics = new MetricsREST();
51 private final Map<String, ConnectionInfo>
52 connections = new ConcurrentHashMap<String, ConnectionInfo>();
53 private final KeyLocker<String> locker = new KeyLocker<String>();
54 private final UserGroupInformation realUser;
55
56 static final String CLEANUP_INTERVAL = "hbase.rest.connection.cleanup-interval";
57 static final String MAX_IDLETIME = "hbase.rest.connection.max-idletime";
58
59 static final String NULL_USERNAME = "--NULL--";
60
61 private final ThreadLocal<String> effectiveUser = new ThreadLocal<String>() {
62 protected String initialValue() {
63 return NULL_USERNAME;
64 }
65 };
66
67
68 private final Chore connectionCleaner;
69 private final Stoppable stoppable;
70 private UserProvider userProvider;
71
72 class ConnectionInfo {
73 final HConnection connection;
74 final String userName;
75
76 volatile HBaseAdmin admin;
77 private long lastAccessTime;
78 private boolean closed;
79
80 ConnectionInfo(HConnection conn, String user) {
81 lastAccessTime = EnvironmentEdgeManager.currentTimeMillis();
82 connection = conn;
83 closed = false;
84 userName = user;
85 }
86
87 synchronized boolean updateAccessTime() {
88 if (closed) {
89 return false;
90 }
91 if (connection.isAborted() || connection.isClosed()) {
92 LOG.info("Unexpected: cached HConnection is aborted/closed, removed from cache");
93 connections.remove(userName);
94 return false;
95 }
96 lastAccessTime = EnvironmentEdgeManager.currentTimeMillis();
97 return true;
98 }
99
100 synchronized boolean timedOut(int maxIdleTime) {
101 long timeoutTime = lastAccessTime + maxIdleTime;
102 if (EnvironmentEdgeManager.currentTimeMillis() > timeoutTime) {
103 connections.remove(userName);
104 closed = true;
105 }
106 return false;
107 }
108 }
109
110 class ConnectionCleaner extends Chore {
111 private final int maxIdleTime;
112
113 public ConnectionCleaner(int cleanInterval, int maxIdleTime) {
114 super("REST-ConnectionCleaner", cleanInterval, stoppable);
115 this.maxIdleTime = maxIdleTime;
116 }
117
118 @Override
119 protected void chore() {
120 for (Map.Entry<String, ConnectionInfo> entry: connections.entrySet()) {
121 ConnectionInfo connInfo = entry.getValue();
122 if (connInfo.timedOut(maxIdleTime)) {
123 if (connInfo.admin != null) {
124 try {
125 connInfo.admin.close();
126 } catch (Throwable t) {
127 LOG.info("Got exception in closing idle admin", t);
128 }
129 }
130 try {
131 connInfo.connection.close();
132 } catch (Throwable t) {
133 LOG.info("Got exception in closing idle connection", t);
134 }
135 }
136 }
137 }
138 }
139
140
141
142
143 public synchronized static RESTServlet getInstance() {
144 assert(INSTANCE != null);
145 return INSTANCE;
146 }
147
148
149
150
151
152
153 public synchronized static RESTServlet getInstance(Configuration conf,
154 UserGroupInformation realUser) {
155 if (INSTANCE == null) {
156 INSTANCE = new RESTServlet(conf, realUser);
157 }
158 return INSTANCE;
159 }
160
161 public synchronized static void stop() {
162 if (INSTANCE != null) INSTANCE = null;
163 }
164
165
166
167
168
169
170 RESTServlet(final Configuration conf,
171 final UserGroupInformation realUser) {
172 this.userProvider = UserProvider.instantiate(conf);
173 stoppable = new Stoppable() {
174 private volatile boolean isStopped = false;
175 @Override public void stop(String why) { isStopped = true;}
176 @Override public boolean isStopped() {return isStopped;}
177 };
178
179 int cleanInterval = conf.getInt(CLEANUP_INTERVAL, 10 * 1000);
180 int maxIdleTime = conf.getInt(MAX_IDLETIME, 10 * 60 * 1000);
181 connectionCleaner = new ConnectionCleaner(cleanInterval, maxIdleTime);
182 Threads.setDaemonThreadRunning(connectionCleaner.getThread());
183
184 this.realUser = realUser;
185 this.conf = conf;
186 }
187
188
189
190
191
192 HBaseAdmin getAdmin() throws IOException {
193 ConnectionInfo connInfo = getCurrentConnection();
194 if (connInfo.admin == null) {
195 Lock lock = locker.acquireLock(effectiveUser.get());
196 try {
197 if (connInfo.admin == null) {
198 connInfo.admin = new HBaseAdmin(connInfo.connection);
199 }
200 } finally {
201 lock.unlock();
202 }
203 }
204 return connInfo.admin;
205 }
206
207
208
209
210 HTableInterface getTable(String tableName) throws IOException {
211 ConnectionInfo connInfo = getCurrentConnection();
212 return connInfo.connection.getTable(tableName);
213 }
214
215 Configuration getConfiguration() {
216 return conf;
217 }
218
219 MetricsREST getMetrics() {
220 return metrics;
221 }
222
223
224
225
226
227
228 boolean isReadOnly() {
229 return getConfiguration().getBoolean("hbase.rest.readonly", false);
230 }
231
232 void setEffectiveUser(String effectiveUser) {
233 this.effectiveUser.set(effectiveUser);
234 }
235
236 private ConnectionInfo getCurrentConnection() throws IOException {
237 String userName = effectiveUser.get();
238 ConnectionInfo connInfo = connections.get(userName);
239 if (connInfo == null || !connInfo.updateAccessTime()) {
240 Lock lock = locker.acquireLock(userName);
241 try {
242 connInfo = connections.get(userName);
243 if (connInfo == null) {
244 UserGroupInformation ugi = realUser;
245 if (!userName.equals(NULL_USERNAME)) {
246 ugi = UserGroupInformation.createProxyUser(userName, realUser);
247 }
248 User user = userProvider.create(ugi);
249 HConnection conn = HConnectionManager.createConnection(conf, user);
250 connInfo = new ConnectionInfo(conn, userName);
251 connections.put(userName, connInfo);
252 }
253 } finally {
254 lock.unlock();
255 }
256 }
257 return connInfo;
258 }
259 }