View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.rest.client;
21  
22  import java.io.ByteArrayInputStream;
23  import java.io.IOException;
24  import java.io.InterruptedIOException;
25  
26  import javax.xml.bind.JAXBContext;
27  import javax.xml.bind.JAXBException;
28  import javax.xml.bind.Unmarshaller;
29  
30  import org.apache.hadoop.classification.InterfaceAudience;
31  import org.apache.hadoop.classification.InterfaceStability;
32  import org.apache.hadoop.conf.Configuration;
33  
34  import org.apache.hadoop.hbase.HTableDescriptor;
35  import org.apache.hadoop.hbase.rest.Constants;
36  import org.apache.hadoop.hbase.rest.model.StorageClusterStatusModel;
37  import org.apache.hadoop.hbase.rest.model.StorageClusterVersionModel;
38  import org.apache.hadoop.hbase.rest.model.TableListModel;
39  import org.apache.hadoop.hbase.rest.model.TableSchemaModel;
40  import org.apache.hadoop.hbase.rest.model.VersionModel;
41  import org.apache.hadoop.hbase.util.Bytes;
42  
43  @InterfaceAudience.Public
44  @InterfaceStability.Stable
45  public class RemoteAdmin {
46  
47    final Client client;
48    final Configuration conf;
49    final String accessToken;
50    final int maxRetries;
51    final long sleepTime;
52  
53    // This unmarshaller is necessary for getting the /version/cluster resource.
54    // This resource does not support protobufs. Therefore this is necessary to
55    // request/interpret it as XML.
56    private static volatile Unmarshaller versionClusterUnmarshaller;
57  
58    /**
59     * Constructor
60     * 
61     * @param client
62     * @param conf
63     */
64    public RemoteAdmin(Client client, Configuration conf) {
65      this(client, conf, null);
66    }
67  
68    static Unmarshaller getUnmarsheller() throws JAXBException {
69  
70      if (versionClusterUnmarshaller == null) {
71  
72        RemoteAdmin.versionClusterUnmarshaller = JAXBContext.newInstance(
73            StorageClusterVersionModel.class).createUnmarshaller();
74      }
75      return RemoteAdmin.versionClusterUnmarshaller;
76    }
77  
78    /**
79     * Constructor
80     * @param client
81     * @param conf
82     * @param accessToken
83     */
84    public RemoteAdmin(Client client, Configuration conf, String accessToken) {
85      this.client = client;
86      this.conf = conf;
87      this.accessToken = accessToken;
88      this.maxRetries = conf.getInt("hbase.rest.client.max.retries", 10);
89      this.sleepTime = conf.getLong("hbase.rest.client.sleep", 1000);
90    }
91  
92    /**
93     * @param tableName name of table to check
94     * @return true if all regions of the table are available
95     * @throws IOException if a remote or network exception occurs
96     */
97    public boolean isTableAvailable(String tableName) throws IOException {
98      return isTableAvailable(Bytes.toBytes(tableName));
99    }
100 
101   /**
102    * @return string representing the rest api's version
103    * @throws IOEXception
104    *           if the endpoint does not exist, there is a timeout, or some other
105    *           general failure mode
106    */
107   public VersionModel getRestVersion() throws IOException {
108 
109     StringBuilder path = new StringBuilder();
110     path.append('/');
111     if (accessToken != null) {
112       path.append(accessToken);
113       path.append('/');
114     }
115 
116     path.append("version/rest");
117 
118     int code = 0;
119     for (int i = 0; i < maxRetries; i++) {
120       Response response = client.get(path.toString(),
121           Constants.MIMETYPE_PROTOBUF);
122       code = response.getCode();
123       switch (code) {
124       case 200:
125 
126         VersionModel v = new VersionModel();
127         return (VersionModel) v.getObjectFromMessage(response.getBody());
128       case 404:
129         throw new IOException("REST version not found");
130       case 509:
131         try {
132           Thread.sleep(sleepTime);
133         } catch (InterruptedException e) {
134           throw (InterruptedIOException)new InterruptedIOException().initCause(e);
135         }
136         break;
137       default:
138         throw new IOException("get request to " + path.toString()
139             + " returned " + code);
140       }
141     }
142     throw new IOException("get request to " + path.toString() + " timed out");
143   }
144 
145   /**
146    * @return string representing the cluster's version
147    * @throws IOEXception if the endpoint does not exist, there is a timeout, or some other general failure mode
148    */
149   public StorageClusterStatusModel getClusterStatus() throws IOException {
150 
151       StringBuilder path = new StringBuilder();
152       path.append('/');
153       if (accessToken !=null) {
154           path.append(accessToken);
155           path.append('/');
156       }
157 
158     path.append("status/cluster");
159 
160     int code = 0;
161     for (int i = 0; i < maxRetries; i++) {
162       Response response = client.get(path.toString(),
163           Constants.MIMETYPE_PROTOBUF);
164       code = response.getCode();
165       switch (code) {
166       case 200:
167         StorageClusterStatusModel s = new StorageClusterStatusModel();
168         return (StorageClusterStatusModel) s.getObjectFromMessage(response
169             .getBody());
170       case 404:
171         throw new IOException("Cluster version not found");
172       case 509:
173         try {
174           Thread.sleep(sleepTime);
175         } catch (InterruptedException e) {
176           throw (InterruptedIOException)new InterruptedIOException().initCause(e);
177         }
178         break;
179       default:
180         throw new IOException("get request to " + path + " returned " + code);
181       }
182     }
183     throw new IOException("get request to " + path + " timed out");
184   }
185 
186   /**
187    * @return string representing the cluster's version
188    * @throws IOEXception
189    *           if the endpoint does not exist, there is a timeout, or some other
190    *           general failure mode
191    */
192   public StorageClusterVersionModel getClusterVersion() throws IOException {
193 
194     StringBuilder path = new StringBuilder();
195     path.append('/');
196     if (accessToken != null) {
197       path.append(accessToken);
198       path.append('/');
199     }
200 
201     path.append("version/cluster");
202 
203     int code = 0;
204     for (int i = 0; i < maxRetries; i++) {
205       Response response = client.get(path.toString(), Constants.MIMETYPE_XML);
206       code = response.getCode();
207       switch (code) {
208       case 200:
209         try {
210 
211           return (StorageClusterVersionModel) getUnmarsheller().unmarshal(
212               new ByteArrayInputStream(response.getBody()));
213         } catch (JAXBException jaxbe) {
214 
215           throw new IOException(
216               "Issue parsing StorageClusterVersionModel object in XML form: "
217                   + jaxbe.getLocalizedMessage());
218         }
219       case 404:
220         throw new IOException("Cluster version not found");
221       case 509:
222         try {
223           Thread.sleep(sleepTime);
224         } catch (InterruptedException e) {
225           throw (InterruptedIOException)new InterruptedIOException().initCause(e);
226         }
227         break;
228       default:
229         throw new IOException(path.toString() + " request returned " + code);
230       }
231     }
232     throw new IOException("get request to " + path.toString()
233         + " request timed out");
234   }
235 
236   /**
237    * @param tableName name of table to check
238    * @return true if all regions of the table are available
239    * @throws IOException if a remote or network exception occurs
240    */
241   public boolean isTableAvailable(byte[] tableName) throws IOException {
242     StringBuilder path = new StringBuilder();
243     path.append('/');
244     if (accessToken != null) {
245       path.append(accessToken);
246       path.append('/');
247     }
248     path.append(Bytes.toStringBinary(tableName));
249     path.append('/');
250     path.append("exists");
251     int code = 0;
252     for (int i = 0; i < maxRetries; i++) {
253       Response response = client.get(path.toString(), Constants.MIMETYPE_PROTOBUF);
254       code = response.getCode();
255       switch (code) {
256       case 200:
257         return true;
258       case 404:
259         return false;
260       case 509:
261         try {
262           Thread.sleep(sleepTime);
263         } catch (InterruptedException e) {
264           throw (InterruptedIOException)new InterruptedIOException().initCause(e);
265         }
266         break;
267       default:
268         throw new IOException("get request to " + path.toString() + " returned " + code);
269       }
270     }
271     throw new IOException("get request to " + path.toString() + " timed out");
272   }
273 
274   /**
275    * Creates a new table.
276    * @param desc table descriptor for table
277    * @throws IOException if a remote or network exception occurs
278    */
279   public void createTable(HTableDescriptor desc)
280       throws IOException {
281     TableSchemaModel model = new TableSchemaModel(desc);
282     StringBuilder path = new StringBuilder();
283     path.append('/');
284     if (accessToken != null) {
285       path.append(accessToken);
286       path.append('/');
287     }
288     path.append(desc.getTableName());
289     path.append('/');
290     path.append("schema");
291     int code = 0;
292     for (int i = 0; i < maxRetries; i++) {
293       Response response = client.put(path.toString(), Constants.MIMETYPE_PROTOBUF,
294         model.createProtobufOutput());
295       code = response.getCode();
296       switch (code) {
297       case 201:
298         return;
299       case 509:
300         try {
301           Thread.sleep(sleepTime);
302         } catch (InterruptedException e) {
303           throw (InterruptedIOException)new InterruptedIOException().initCause(e);
304         }
305         break;
306       default:
307         throw new IOException("create request to " + path.toString() + " returned " + code);
308       }
309     }
310     throw new IOException("create request to " + path.toString() + " timed out");
311   }
312 
313   /**
314    * Deletes a table.
315    * @param tableName name of table to delete
316    * @throws IOException if a remote or network exception occurs
317    */
318   public void deleteTable(final String tableName) throws IOException {
319     deleteTable(Bytes.toBytes(tableName));
320   }
321 
322   /**
323    * Deletes a table.
324    * @param tableName name of table to delete
325    * @throws IOException if a remote or network exception occurs
326    */
327   public void deleteTable(final byte [] tableName) throws IOException {
328     StringBuilder path = new StringBuilder();
329     path.append('/');
330     if (accessToken != null) {
331       path.append(accessToken);
332       path.append('/');
333     }
334     path.append(Bytes.toStringBinary(tableName));
335     path.append('/');
336     path.append("schema");
337     int code = 0;
338     for (int i = 0; i < maxRetries; i++) {
339       Response response = client.delete(path.toString());
340       code = response.getCode();
341       switch (code) {
342       case 200:
343         return;
344       case 509:
345         try {
346           Thread.sleep(sleepTime);
347         } catch (InterruptedException e) {
348           throw (InterruptedIOException)new InterruptedIOException().initCause(e);
349         }
350         break;
351       default:
352         throw new IOException("delete request to " + path.toString() + " returned " + code);
353       }
354     }
355     throw new IOException("delete request to " + path.toString() + " timed out");
356   }
357 
358   /**
359    * @return string representing the cluster's version
360    * @throws IOEXception
361    *           if the endpoint does not exist, there is a timeout, or some other
362    *           general failure mode
363    */
364   public TableListModel getTableList() throws IOException {
365 
366     StringBuilder path = new StringBuilder();
367     path.append('/');
368     if (accessToken != null) {
369       path.append(accessToken);
370       path.append('/');
371     }
372 
373     int code = 0;
374     for (int i = 0; i < maxRetries; i++) {
375       // Response response = client.get(path.toString(),
376       // Constants.MIMETYPE_XML);
377       Response response = client.get(path.toString(),
378           Constants.MIMETYPE_PROTOBUF);
379       code = response.getCode();
380       switch (code) {
381       case 200:
382         TableListModel t = new TableListModel();
383         return (TableListModel) t.getObjectFromMessage(response.getBody());
384       case 404:
385         throw new IOException("Table list not found");
386       case 509:
387         try {
388           Thread.sleep(sleepTime);
389         } catch (InterruptedException e) {
390           throw (InterruptedIOException)new InterruptedIOException().initCause(e);
391         }
392         break;
393       default:
394         throw new IOException("get request to " + path.toString()
395             + " request returned " + code);
396       }
397     }
398     throw new IOException("get request to " + path.toString()
399         + " request timed out");
400   }
401 }