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