View Javadoc

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