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;
21  
22  import java.io.IOException;
23  import java.util.Map;
24  
25  import javax.ws.rs.Consumes;
26  import javax.ws.rs.DELETE;
27  import javax.ws.rs.GET;
28  import javax.ws.rs.POST;
29  import javax.ws.rs.PUT;
30  import javax.ws.rs.Produces;
31  import javax.ws.rs.core.CacheControl;
32  import javax.ws.rs.core.Context;
33  import javax.ws.rs.core.Response;
34  import javax.ws.rs.core.UriInfo;
35  import javax.ws.rs.core.Response.ResponseBuilder;
36  
37  import javax.xml.namespace.QName;
38  
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  
42  import org.apache.hadoop.classification.InterfaceAudience;
43  import org.apache.hadoop.hbase.HColumnDescriptor;
44  import org.apache.hadoop.hbase.HTableDescriptor;
45  import org.apache.hadoop.hbase.TableName;
46  import org.apache.hadoop.hbase.TableExistsException;
47  import org.apache.hadoop.hbase.TableNotFoundException;
48  import org.apache.hadoop.hbase.client.HBaseAdmin;
49  import org.apache.hadoop.hbase.client.HTableInterface;
50  import org.apache.hadoop.hbase.client.HTablePool;
51  import org.apache.hadoop.hbase.rest.model.ColumnSchemaModel;
52  import org.apache.hadoop.hbase.rest.model.TableSchemaModel;
53  import org.apache.hadoop.hbase.util.Bytes;
54  
55  @InterfaceAudience.Private
56  public class SchemaResource extends ResourceBase {
57    private static final Log LOG = LogFactory.getLog(SchemaResource.class);
58  
59    static CacheControl cacheControl;
60    static {
61      cacheControl = new CacheControl();
62      cacheControl.setNoCache(true);
63      cacheControl.setNoTransform(false);
64    }
65  
66    TableResource tableResource;
67  
68    /**
69     * Constructor
70     * @param tableResource
71     * @throws IOException
72     */
73    public SchemaResource(TableResource tableResource) throws IOException {
74      super();
75      this.tableResource = tableResource;
76    }
77  
78    private HTableDescriptor getTableSchema() throws IOException,
79        TableNotFoundException {
80      HTablePool pool = servlet.getTablePool();
81      HTableInterface table = pool.getTable(tableResource.getName());
82      try {
83        return table.getTableDescriptor();
84      } finally {
85        table.close();
86      }
87    }
88  
89    @GET
90    @Produces({MIMETYPE_TEXT, MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF,
91      MIMETYPE_PROTOBUF_IETF})
92    public Response get(final @Context UriInfo uriInfo) {
93      if (LOG.isDebugEnabled()) {
94        LOG.debug("GET " + uriInfo.getAbsolutePath());
95      }
96      servlet.getMetrics().incrementRequests(1);
97      try {
98        ResponseBuilder response =
99          Response.ok(new TableSchemaModel(getTableSchema()));
100       response.cacheControl(cacheControl);
101       servlet.getMetrics().incrementSucessfulGetRequests(1);
102       return response.build();
103     } catch (TableNotFoundException e) {
104       servlet.getMetrics().incrementFailedGetRequests(1);
105       return Response.status(Response.Status.NOT_FOUND)
106         .type(MIMETYPE_TEXT).entity("Not found" + CRLF)
107         .build();
108     } catch (IOException e) {
109       servlet.getMetrics().incrementFailedGetRequests(1);
110       return Response.status(Response.Status.SERVICE_UNAVAILABLE)
111         .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF)
112         .build();
113     }
114   }
115 
116   private Response replace(final byte[] name, final TableSchemaModel model,
117       final UriInfo uriInfo, final HBaseAdmin admin) {
118     if (servlet.isReadOnly()) {
119       return Response.status(Response.Status.FORBIDDEN)
120         .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF)
121         .build();
122     }
123     try {
124       HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name));
125       for (Map.Entry<QName,Object> e: model.getAny().entrySet()) {
126         htd.setValue(e.getKey().getLocalPart(), e.getValue().toString());
127       }
128       for (ColumnSchemaModel family: model.getColumns()) {
129         HColumnDescriptor hcd = new HColumnDescriptor(family.getName());
130         for (Map.Entry<QName,Object> e: family.getAny().entrySet()) {
131           hcd.setValue(e.getKey().getLocalPart(), e.getValue().toString());
132         }
133         htd.addFamily(hcd);
134       }
135       if (admin.tableExists(name)) {
136         admin.disableTable(name);
137         admin.modifyTable(name, htd);
138         admin.enableTable(name);
139         servlet.getMetrics().incrementSucessfulPutRequests(1);
140       } else try {
141         admin.createTable(htd);
142         servlet.getMetrics().incrementSucessfulPutRequests(1);
143       } catch (TableExistsException e) {
144         // race, someone else created a table with the same name
145         return Response.status(Response.Status.NOT_MODIFIED)
146           .type(MIMETYPE_TEXT).entity("Not modified" + CRLF)
147           .build();
148       }
149       return Response.created(uriInfo.getAbsolutePath()).build();
150     } catch (IOException e) {
151       return Response.status(Response.Status.SERVICE_UNAVAILABLE)
152         .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF)
153         .build();
154     }
155   }
156 
157   private Response update(final byte[] name, final TableSchemaModel model,
158       final UriInfo uriInfo, final HBaseAdmin admin) {
159     if (servlet.isReadOnly()) {
160       return Response.status(Response.Status.FORBIDDEN)
161         .type(MIMETYPE_TEXT).entity("Forbidden" + CRLF)
162         .build();
163     }
164     try {
165       HTableDescriptor htd = admin.getTableDescriptor(name);
166       admin.disableTable(name);
167       try {
168         for (ColumnSchemaModel family: model.getColumns()) {
169           HColumnDescriptor hcd = new HColumnDescriptor(family.getName());
170           for (Map.Entry<QName,Object> e: family.getAny().entrySet()) {
171             hcd.setValue(e.getKey().getLocalPart(), e.getValue().toString());
172           }
173           if (htd.hasFamily(hcd.getName())) {
174             admin.modifyColumn(name, hcd);
175           } else {
176             admin.addColumn(name, hcd);
177           }
178         }
179       } catch (IOException e) {
180         return Response.status(Response.Status.SERVICE_UNAVAILABLE)
181           .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF)
182           .build();
183       } finally {
184         admin.enableTable(tableResource.getName());
185       }
186       servlet.getMetrics().incrementSucessfulPutRequests(1);
187       return Response.ok().build();
188     } catch (IOException e) {
189       return Response.status(Response.Status.SERVICE_UNAVAILABLE)
190         .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF)
191         .build();
192     }
193   }
194 
195   private Response update(final TableSchemaModel model, final boolean replace,
196       final UriInfo uriInfo) {
197     try {
198       byte[] name = Bytes.toBytes(tableResource.getName());
199       HBaseAdmin admin = servlet.getAdmin();
200       if (replace || !admin.tableExists(name)) {
201         return replace(name, model, uriInfo, admin);
202       } else {
203         return update(name, model, uriInfo, admin);
204       }
205     } catch (IOException e) {
206       servlet.getMetrics().incrementFailedPutRequests(1);
207       return Response.status(Response.Status.SERVICE_UNAVAILABLE)
208         .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF)
209         .build();
210     }
211   }
212 
213   @PUT
214   @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF,
215     MIMETYPE_PROTOBUF_IETF})
216   public Response put(final TableSchemaModel model, 
217       final @Context UriInfo uriInfo) {
218     if (LOG.isDebugEnabled()) {
219       LOG.debug("PUT " + uriInfo.getAbsolutePath());
220     }
221     servlet.getMetrics().incrementRequests(1);
222     return update(model, true, uriInfo);
223   }
224 
225   @POST
226   @Consumes({MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF,
227     MIMETYPE_PROTOBUF_IETF})
228   public Response post(final TableSchemaModel model, 
229       final @Context UriInfo uriInfo) {
230     if (LOG.isDebugEnabled()) {
231       LOG.debug("PUT " + uriInfo.getAbsolutePath());
232     }
233     servlet.getMetrics().incrementRequests(1);
234     return update(model, false, uriInfo);
235   }
236 
237   @DELETE
238   public Response delete(final @Context UriInfo uriInfo) {
239     if (LOG.isDebugEnabled()) {
240       LOG.debug("DELETE " + uriInfo.getAbsolutePath());
241     }
242     servlet.getMetrics().incrementRequests(1);
243     try {
244       HBaseAdmin admin = servlet.getAdmin();
245       boolean success = false;
246       for (int i = 0; i < 10; i++) try {
247         admin.disableTable(tableResource.getName());
248         success = true;
249         break;
250       } catch (IOException e) {
251       }
252       if (!success) {
253         throw new IOException("could not disable table");
254       }
255       admin.deleteTable(tableResource.getName());
256       servlet.getMetrics().incrementSucessfulDeleteRequests(1);
257       return Response.ok().build();
258     } catch (TableNotFoundException e) {
259       servlet.getMetrics().incrementFailedDeleteRequests(1);
260       return Response.status(Response.Status.NOT_FOUND)
261         .type(MIMETYPE_TEXT).entity("Not found" + CRLF)
262         .build();
263     } catch (IOException e) {
264       servlet.getMetrics().incrementFailedDeleteRequests(1);
265       return Response.status(Response.Status.SERVICE_UNAVAILABLE)
266         .type(MIMETYPE_TEXT).entity("Unavailable" + CRLF)
267         .build();
268     }
269   }
270 }