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