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