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.TableExistsException; 024import org.apache.hadoop.hbase.TableName; 025import org.apache.hadoop.hbase.TableNotEnabledException; 026import org.apache.hadoop.hbase.TableNotFoundException; 027import org.apache.hadoop.hbase.client.Admin; 028import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 029import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 030import org.apache.hadoop.hbase.client.Table; 031import org.apache.hadoop.hbase.client.TableDescriptor; 032import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 033import org.apache.hadoop.hbase.rest.model.ColumnSchemaModel; 034import org.apache.hadoop.hbase.rest.model.TableSchemaModel; 035import org.apache.hadoop.hbase.util.Bytes; 036import org.apache.yetus.audience.InterfaceAudience; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039 040import org.apache.hbase.thirdparty.javax.ws.rs.Consumes; 041import org.apache.hbase.thirdparty.javax.ws.rs.DELETE; 042import org.apache.hbase.thirdparty.javax.ws.rs.GET; 043import org.apache.hbase.thirdparty.javax.ws.rs.POST; 044import org.apache.hbase.thirdparty.javax.ws.rs.PUT; 045import org.apache.hbase.thirdparty.javax.ws.rs.Produces; 046import org.apache.hbase.thirdparty.javax.ws.rs.WebApplicationException; 047import org.apache.hbase.thirdparty.javax.ws.rs.core.CacheControl; 048import org.apache.hbase.thirdparty.javax.ws.rs.core.Context; 049import org.apache.hbase.thirdparty.javax.ws.rs.core.Response; 050import org.apache.hbase.thirdparty.javax.ws.rs.core.Response.ResponseBuilder; 051import org.apache.hbase.thirdparty.javax.ws.rs.core.UriInfo; 052 053@InterfaceAudience.Private 054public class SchemaResource extends ResourceBase { 055 private static final Logger LOG = LoggerFactory.getLogger(SchemaResource.class); 056 057 static CacheControl cacheControl; 058 static { 059 cacheControl = new CacheControl(); 060 cacheControl.setNoCache(true); 061 cacheControl.setNoTransform(false); 062 } 063 064 TableResource tableResource; 065 066 /** 067 * Constructor 068 */ 069 public SchemaResource(TableResource tableResource) throws IOException { 070 super(); 071 this.tableResource = tableResource; 072 } 073 074 private TableDescriptor getTableSchema() throws IOException, TableNotFoundException { 075 try (Table table = servlet.getTable(tableResource.getName())) { 076 return table.getDescriptor(); 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 TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(name); 107 for (Map.Entry<QName, Object> e : model.getAny().entrySet()) { 108 tableDescriptorBuilder.setValue(e.getKey().getLocalPart(), e.getValue().toString()); 109 } 110 for (ColumnSchemaModel family : model.getColumns()) { 111 ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = 112 ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(family.getName())); 113 for (Map.Entry<QName, Object> e : family.getAny().entrySet()) { 114 columnFamilyDescriptorBuilder.setValue(e.getKey().getLocalPart(), 115 e.getValue().toString()); 116 } 117 tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptorBuilder.build()); 118 } 119 TableDescriptor tableDescriptor = tableDescriptorBuilder.build(); 120 if (admin.tableExists(name)) { 121 admin.disableTable(name); 122 admin.modifyTable(tableDescriptor); 123 admin.enableTable(name); 124 servlet.getMetrics().incrementSucessfulPutRequests(1); 125 } else { 126 try { 127 admin.createTable(tableDescriptor); 128 servlet.getMetrics().incrementSucessfulPutRequests(1); 129 } catch (TableExistsException e) { 130 // race, someone else created a table with the same name 131 return Response.status(Response.Status.NOT_MODIFIED).type(MIMETYPE_TEXT) 132 .entity("Not modified" + CRLF).build(); 133 } 134 } 135 return Response.created(uriInfo.getAbsolutePath()).build(); 136 } catch (Exception e) { 137 LOG.info("Caught exception", e); 138 servlet.getMetrics().incrementFailedPutRequests(1); 139 return processException(e); 140 } 141 } 142 143 private Response update(final TableName name, final TableSchemaModel model, final UriInfo uriInfo, 144 final Admin admin) { 145 if (servlet.isReadOnly()) { 146 return Response.status(Response.Status.FORBIDDEN).type(MIMETYPE_TEXT) 147 .entity("Forbidden" + CRLF).build(); 148 } 149 try { 150 TableDescriptorBuilder tableDescriptorBuilder = 151 TableDescriptorBuilder.newBuilder(admin.getDescriptor(name)); 152 admin.disableTable(name); 153 try { 154 for (ColumnSchemaModel family : model.getColumns()) { 155 ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = 156 ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(family.getName())); 157 for (Map.Entry<QName, Object> e : family.getAny().entrySet()) { 158 columnFamilyDescriptorBuilder.setValue(e.getKey().getLocalPart(), 159 e.getValue().toString()); 160 } 161 TableDescriptor tableDescriptor = tableDescriptorBuilder.build(); 162 ColumnFamilyDescriptor columnFamilyDescriptor = columnFamilyDescriptorBuilder.build(); 163 if (tableDescriptor.hasColumnFamily(columnFamilyDescriptor.getName())) { 164 admin.modifyColumnFamily(name, columnFamilyDescriptor); 165 } else { 166 admin.addColumnFamily(name, columnFamilyDescriptor); 167 } 168 } 169 } catch (IOException e) { 170 return Response.status(Response.Status.SERVICE_UNAVAILABLE).type(MIMETYPE_TEXT) 171 .entity("Unavailable" + CRLF).build(); 172 } finally { 173 admin.enableTable(TableName.valueOf(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 TableName name = TableName.valueOf(tableResource.getName()); 187 Admin 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 // Avoid re-unwrapping the exception 196 if (e instanceof WebApplicationException) { 197 throw (WebApplicationException) e; 198 } 199 return processException(e); 200 } 201 } 202 203 @PUT 204 @Consumes({ MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF, MIMETYPE_PROTOBUF_IETF }) 205 public Response put(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, true, uriInfo); 211 } 212 213 @POST 214 @Consumes({ MIMETYPE_XML, MIMETYPE_JSON, MIMETYPE_PROTOBUF, MIMETYPE_PROTOBUF_IETF }) 215 public Response post(final TableSchemaModel model, final @Context UriInfo uriInfo) { 216 if (LOG.isTraceEnabled()) { 217 LOG.trace("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.isTraceEnabled()) { 228 LOG.trace("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 Admin admin = servlet.getAdmin(); 237 try { 238 admin.disableTable(TableName.valueOf(tableResource.getName())); 239 } catch (TableNotEnabledException e) { 240 /* this is what we want anyway */ } 241 admin.deleteTable(TableName.valueOf(tableResource.getName())); 242 servlet.getMetrics().incrementSucessfulDeleteRequests(1); 243 return Response.ok().build(); 244 } catch (Exception e) { 245 servlet.getMetrics().incrementFailedDeleteRequests(1); 246 return processException(e); 247 } 248 } 249}