001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.rest; 019 020import java.io.IOException; 021import java.util.Map; 022import javax.xml.namespace.QName; 023import org.apache.hadoop.hbase.HColumnDescriptor; 024import org.apache.hadoop.hbase.HTableDescriptor; 025import org.apache.hadoop.hbase.TableExistsException; 026import org.apache.hadoop.hbase.TableName; 027import org.apache.hadoop.hbase.TableNotEnabledException; 028import org.apache.hadoop.hbase.TableNotFoundException; 029import org.apache.hadoop.hbase.client.Admin; 030import org.apache.hadoop.hbase.client.Table; 031import org.apache.hadoop.hbase.rest.model.ColumnSchemaModel; 032import org.apache.hadoop.hbase.rest.model.TableSchemaModel; 033import org.apache.yetus.audience.InterfaceAudience; 034import org.slf4j.Logger; 035import org.slf4j.LoggerFactory; 036 037import org.apache.hbase.thirdparty.javax.ws.rs.Consumes; 038import org.apache.hbase.thirdparty.javax.ws.rs.DELETE; 039import org.apache.hbase.thirdparty.javax.ws.rs.GET; 040import org.apache.hbase.thirdparty.javax.ws.rs.POST; 041import org.apache.hbase.thirdparty.javax.ws.rs.PUT; 042import org.apache.hbase.thirdparty.javax.ws.rs.Produces; 043import org.apache.hbase.thirdparty.javax.ws.rs.WebApplicationException; 044import org.apache.hbase.thirdparty.javax.ws.rs.core.CacheControl; 045import org.apache.hbase.thirdparty.javax.ws.rs.core.Context; 046import org.apache.hbase.thirdparty.javax.ws.rs.core.Response; 047import org.apache.hbase.thirdparty.javax.ws.rs.core.Response.ResponseBuilder; 048import org.apache.hbase.thirdparty.javax.ws.rs.core.UriInfo; 049 050@InterfaceAudience.Private 051public class SchemaResource extends ResourceBase { 052 private static final Logger LOG = LoggerFactory.getLogger(SchemaResource.class); 053 054 static CacheControl cacheControl; 055 static { 056 cacheControl = new CacheControl(); 057 cacheControl.setNoCache(true); 058 cacheControl.setNoTransform(false); 059 } 060 061 TableResource tableResource; 062 063 /** 064 * Constructor 065 */ 066 public SchemaResource(TableResource tableResource) throws IOException { 067 super(); 068 this.tableResource = tableResource; 069 } 070 071 private HTableDescriptor getTableSchema() throws IOException, TableNotFoundException { 072 Table table = servlet.getTable(tableResource.getName()); 073 try { 074 return table.getTableDescriptor(); 075 } finally { 076 table.close(); 077 } 078 } 079 080 @GET 081 @Produces({ MIMETYPE_TEXT, MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF, 082 MIMETYPE_PROTOBUF_IETF }) 083 public Response get(final @Context UriInfo uriInfo) { 084 if (LOG.isTraceEnabled()) { 085 LOG.trace("GET " + uriInfo.getAbsolutePath()); 086 } 087 servlet.getMetrics().incrementRequests(1); 088 try { 089 ResponseBuilder response = Response.ok(new TableSchemaModel(getTableSchema())); 090 response.cacheControl(cacheControl); 091 servlet.getMetrics().incrementSucessfulGetRequests(1); 092 return response.build(); 093 } catch (Exception e) { 094 servlet.getMetrics().incrementFailedGetRequests(1); 095 return processException(e); 096 } 097 } 098 099 private Response replace(final TableName name, final TableSchemaModel model, 100 final UriInfo uriInfo, final Admin admin) { 101 if (servlet.isReadOnly()) { 102 return Response.status(Response.Status.FORBIDDEN).type(MIMETYPE_TEXT) 103 .entity("Forbidden" + CRLF).build(); 104 } 105 try { 106 HTableDescriptor htd = new HTableDescriptor(name); 107 for (Map.Entry<QName, Object> e : model.getAny().entrySet()) { 108 htd.setValue(e.getKey().getLocalPart(), e.getValue().toString()); 109 } 110 for (ColumnSchemaModel family : model.getColumns()) { 111 HColumnDescriptor hcd = new HColumnDescriptor(family.getName()); 112 for (Map.Entry<QName, Object> e : family.getAny().entrySet()) { 113 hcd.setValue(e.getKey().getLocalPart(), e.getValue().toString()); 114 } 115 htd.addFamily(hcd); 116 } 117 if (admin.tableExists(name)) { 118 admin.disableTable(name); 119 admin.modifyTable(name, htd); 120 admin.enableTable(name); 121 servlet.getMetrics().incrementSucessfulPutRequests(1); 122 } else try { 123 admin.createTable(htd); 124 servlet.getMetrics().incrementSucessfulPutRequests(1); 125 } catch (TableExistsException e) { 126 // race, someone else created a table with the same name 127 return Response.status(Response.Status.NOT_MODIFIED).type(MIMETYPE_TEXT) 128 .entity("Not modified" + CRLF).build(); 129 } 130 return Response.created(uriInfo.getAbsolutePath()).build(); 131 } catch (Exception e) { 132 LOG.info("Caught exception", e); 133 servlet.getMetrics().incrementFailedPutRequests(1); 134 return processException(e); 135 } 136 } 137 138 private Response update(final TableName name, final TableSchemaModel model, final UriInfo uriInfo, 139 final Admin admin) { 140 if (servlet.isReadOnly()) { 141 return Response.status(Response.Status.FORBIDDEN).type(MIMETYPE_TEXT) 142 .entity("Forbidden" + CRLF).build(); 143 } 144 try { 145 HTableDescriptor htd = admin.getTableDescriptor(name); 146 admin.disableTable(name); 147 try { 148 for (ColumnSchemaModel family : model.getColumns()) { 149 HColumnDescriptor hcd = new HColumnDescriptor(family.getName()); 150 for (Map.Entry<QName, Object> e : family.getAny().entrySet()) { 151 hcd.setValue(e.getKey().getLocalPart(), e.getValue().toString()); 152 } 153 if (htd.hasFamily(hcd.getName())) { 154 admin.modifyColumnFamily(name, hcd); 155 } else { 156 admin.addColumnFamily(name, hcd); 157 } 158 } 159 } catch (IOException e) { 160 return Response.status(Response.Status.SERVICE_UNAVAILABLE).type(MIMETYPE_TEXT) 161 .entity("Unavailable" + CRLF).build(); 162 } finally { 163 admin.enableTable(TableName.valueOf(tableResource.getName())); 164 } 165 servlet.getMetrics().incrementSucessfulPutRequests(1); 166 return Response.ok().build(); 167 } catch (Exception e) { 168 servlet.getMetrics().incrementFailedPutRequests(1); 169 return processException(e); 170 } 171 } 172 173 private Response update(final TableSchemaModel model, final boolean replace, 174 final UriInfo uriInfo) { 175 try { 176 TableName name = TableName.valueOf(tableResource.getName()); 177 Admin admin = servlet.getAdmin(); 178 if (replace || !admin.tableExists(name)) { 179 return replace(name, model, uriInfo, admin); 180 } else { 181 return update(name, model, uriInfo, admin); 182 } 183 } catch (Exception e) { 184 servlet.getMetrics().incrementFailedPutRequests(1); 185 // Avoid re-unwrapping the exception 186 if (e instanceof WebApplicationException) { 187 throw (WebApplicationException) e; 188 } 189 return processException(e); 190 } 191 } 192 193 @PUT 194 @Consumes({ MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF, MIMETYPE_PROTOBUF_IETF }) 195 public Response put(final TableSchemaModel model, final @Context UriInfo uriInfo) { 196 if (LOG.isTraceEnabled()) { 197 LOG.trace("PUT " + uriInfo.getAbsolutePath()); 198 } 199 servlet.getMetrics().incrementRequests(1); 200 return update(model, true, uriInfo); 201 } 202 203 @POST 204 @Consumes({ MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF, MIMETYPE_PROTOBUF_IETF }) 205 public Response post(final TableSchemaModel model, final @Context UriInfo uriInfo) { 206 if (LOG.isTraceEnabled()) { 207 LOG.trace("PUT " + uriInfo.getAbsolutePath()); 208 } 209 servlet.getMetrics().incrementRequests(1); 210 return update(model, false, uriInfo); 211 } 212 213 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "DE_MIGHT_IGNORE", 214 justification = "Expected") 215 @DELETE 216 public Response delete(final @Context UriInfo uriInfo) { 217 if (LOG.isTraceEnabled()) { 218 LOG.trace("DELETE " + uriInfo.getAbsolutePath()); 219 } 220 servlet.getMetrics().incrementRequests(1); 221 if (servlet.isReadOnly()) { 222 return Response.status(Response.Status.FORBIDDEN).type(MIMETYPE_TEXT) 223 .entity("Forbidden" + CRLF).build(); 224 } 225 try { 226 Admin admin = servlet.getAdmin(); 227 try { 228 admin.disableTable(TableName.valueOf(tableResource.getName())); 229 } catch (TableNotEnabledException e) { 230 /* this is what we want anyway */ } 231 admin.deleteTable(TableName.valueOf(tableResource.getName())); 232 servlet.getMetrics().incrementSucessfulDeleteRequests(1); 233 return Response.ok().build(); 234 } catch (Exception e) { 235 servlet.getMetrics().incrementFailedDeleteRequests(1); 236 return processException(e); 237 } 238 } 239}