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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 package org
.apache
.hadoop
.hbase
.rest
;
21 import java
.io
.IOException
;
22 import java
.util
.List
;
23 import javax
.servlet
.ServletContext
;
24 import org
.apache
.hadoop
.hbase
.NamespaceDescriptor
;
25 import org
.apache
.hadoop
.hbase
.client
.Admin
;
26 import org
.apache
.hadoop
.hbase
.client
.TableDescriptor
;
27 import org
.apache
.hadoop
.hbase
.rest
.model
.NamespacesInstanceModel
;
28 import org
.apache
.hadoop
.hbase
.rest
.model
.TableListModel
;
29 import org
.apache
.hadoop
.hbase
.rest
.model
.TableModel
;
30 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
31 import org
.apache
.yetus
.audience
.InterfaceAudience
;
32 import org
.slf4j
.Logger
;
33 import org
.slf4j
.LoggerFactory
;
35 import org
.apache
.hbase
.thirdparty
.javax
.ws
.rs
.Consumes
;
36 import org
.apache
.hbase
.thirdparty
.javax
.ws
.rs
.DELETE
;
37 import org
.apache
.hbase
.thirdparty
.javax
.ws
.rs
.GET
;
38 import org
.apache
.hbase
.thirdparty
.javax
.ws
.rs
.POST
;
39 import org
.apache
.hbase
.thirdparty
.javax
.ws
.rs
.PUT
;
40 import org
.apache
.hbase
.thirdparty
.javax
.ws
.rs
.Path
;
41 import org
.apache
.hbase
.thirdparty
.javax
.ws
.rs
.PathParam
;
42 import org
.apache
.hbase
.thirdparty
.javax
.ws
.rs
.Produces
;
43 import org
.apache
.hbase
.thirdparty
.javax
.ws
.rs
.core
.Context
;
44 import org
.apache
.hbase
.thirdparty
.javax
.ws
.rs
.core
.HttpHeaders
;
45 import org
.apache
.hbase
.thirdparty
.javax
.ws
.rs
.core
.Response
;
46 import org
.apache
.hbase
.thirdparty
.javax
.ws
.rs
.core
.UriInfo
;
49 * Implements the following REST end points:
51 * <tt>/namespaces/{namespace} GET: get namespace properties.</tt>
52 * <tt>/namespaces/{namespace} POST: create namespace.</tt>
53 * <tt>/namespaces/{namespace} PUT: alter namespace.</tt>
54 * <tt>/namespaces/{namespace} DELETE: drop namespace.</tt>
55 * <tt>/namespaces/{namespace}/tables GET: list namespace's tables.</tt>
58 @InterfaceAudience.Private
59 public class NamespacesInstanceResource
extends ResourceBase
{
61 private static final Logger LOG
= LoggerFactory
.getLogger(NamespacesInstanceResource
.class);
63 boolean queryTables
= false;
66 * Constructor for standard NamespaceInstanceResource.
69 public NamespacesInstanceResource(String namespace
) throws IOException
{
70 this(namespace
, false);
74 * Constructor for querying namespace table list via NamespaceInstanceResource.
77 public NamespacesInstanceResource(String namespace
, boolean queryTables
) throws IOException
{
79 this.namespace
= namespace
;
80 this.queryTables
= queryTables
;
84 * Build a response for GET namespace description or GET list of namespace tables.
85 * @param context servlet context
86 * @param uriInfo (JAX-RS context variable) request URL
87 * @return A response containing NamespacesInstanceModel for a namespace descriptions and
88 * TableListModel for a list of namespace tables.
91 @Produces({MIMETYPE_TEXT
, MIMETYPE_XML
, MIMETYPE_JSON
, MIMETYPE_PROTOBUF
,
92 MIMETYPE_PROTOBUF_IETF
})
93 public Response
get(final @Context ServletContext context
,
94 final @Context UriInfo uriInfo
) {
95 if (LOG
.isTraceEnabled()) {
96 LOG
.trace("GET " + uriInfo
.getAbsolutePath());
98 servlet
.getMetrics().incrementRequests(1);
100 // Respond to list of namespace tables requests.
102 TableListModel tableModel
= new TableListModel();
104 List
<TableDescriptor
> tables
=
105 servlet
.getAdmin().listTableDescriptorsByNamespace(Bytes
.toBytes(namespace
));
106 for (TableDescriptor table
: tables
) {
107 tableModel
.add(new TableModel(table
.getTableName().getQualifierAsString()));
110 servlet
.getMetrics().incrementSucessfulGetRequests(1);
111 return Response
.ok(tableModel
).build();
112 }catch(IOException e
) {
113 servlet
.getMetrics().incrementFailedGetRequests(1);
114 throw new RuntimeException("Cannot retrieve table list for '" + namespace
+ "'.");
118 // Respond to namespace description requests.
120 NamespacesInstanceModel rowModel
=
121 new NamespacesInstanceModel(servlet
.getAdmin(), namespace
);
122 servlet
.getMetrics().incrementSucessfulGetRequests(1);
123 return Response
.ok(rowModel
).build();
124 } catch (IOException e
) {
125 servlet
.getMetrics().incrementFailedGetRequests(1);
126 throw new RuntimeException("Cannot retrieve info for '" + namespace
+ "'.");
131 * Build a response for PUT alter namespace with properties specified.
132 * @param model properties used for alter.
133 * @param uriInfo (JAX-RS context variable) request URL
134 * @return response code.
137 @Consumes({MIMETYPE_XML
, MIMETYPE_JSON
, MIMETYPE_PROTOBUF
,
138 MIMETYPE_PROTOBUF_IETF
})
139 public Response
put(final NamespacesInstanceModel model
, final @Context UriInfo uriInfo
) {
140 return processUpdate(model
, true, uriInfo
);
144 * Build a response for POST create namespace with properties specified.
145 * @param model properties used for create.
146 * @param uriInfo (JAX-RS context variable) request URL
147 * @return response code.
150 @Consumes({MIMETYPE_XML
, MIMETYPE_JSON
, MIMETYPE_PROTOBUF
,
151 MIMETYPE_PROTOBUF_IETF
})
152 public Response
post(final NamespacesInstanceModel model
,
153 final @Context UriInfo uriInfo
) {
154 return processUpdate(model
, false, uriInfo
);
158 // Check that POST or PUT is valid and then update namespace.
159 private Response
processUpdate(NamespacesInstanceModel model
, final boolean updateExisting
,
160 final UriInfo uriInfo
) {
161 if (LOG
.isTraceEnabled()) {
162 LOG
.trace((updateExisting ?
"PUT " : "POST ") + uriInfo
.getAbsolutePath());
166 model
= new NamespacesInstanceModel(namespace
);
167 } catch(IOException ioe
) {
168 servlet
.getMetrics().incrementFailedPutRequests(1);
169 throw new RuntimeException("Cannot retrieve info for '" + namespace
+ "'.");
172 servlet
.getMetrics().incrementRequests(1);
174 if (servlet
.isReadOnly()) {
175 servlet
.getMetrics().incrementFailedPutRequests(1);
176 return Response
.status(Response
.Status
.FORBIDDEN
).type(MIMETYPE_TEXT
)
177 .entity("Forbidden" + CRLF
).build();
181 boolean namespaceExists
= false;
183 admin
= servlet
.getAdmin();
184 namespaceExists
= doesNamespaceExist(admin
, namespace
);
185 }catch (IOException e
) {
186 servlet
.getMetrics().incrementFailedPutRequests(1);
187 return processException(e
);
190 // Do not allow creation if namespace already exists.
191 if(!updateExisting
&& namespaceExists
){
192 servlet
.getMetrics().incrementFailedPutRequests(1);
193 return Response
.status(Response
.Status
.FORBIDDEN
).type(MIMETYPE_TEXT
).
194 entity("Namespace '" + namespace
+ "' already exists. Use REST PUT " +
195 "to alter the existing namespace.").build();
198 // Do not allow altering if namespace does not exist.
199 if (updateExisting
&& !namespaceExists
){
200 servlet
.getMetrics().incrementFailedPutRequests(1);
201 return Response
.status(Response
.Status
.FORBIDDEN
).type(MIMETYPE_TEXT
).
202 entity("Namespace '" + namespace
+ "' does not exist. Use " +
203 "REST POST to create the namespace.").build();
206 return createOrUpdate(model
, uriInfo
, admin
, updateExisting
);
209 // Do the actual namespace create or alter.
210 private Response
createOrUpdate(final NamespacesInstanceModel model
, final UriInfo uriInfo
,
211 final Admin admin
, final boolean updateExisting
) {
212 NamespaceDescriptor
.Builder builder
= NamespaceDescriptor
.create(namespace
);
213 builder
.addConfiguration(model
.getProperties());
214 if(model
.getProperties().size() > 0){
215 builder
.addConfiguration(model
.getProperties());
217 NamespaceDescriptor nsd
= builder
.build();
221 admin
.modifyNamespace(nsd
);
223 admin
.createNamespace(nsd
);
225 }catch (IOException e
) {
226 servlet
.getMetrics().incrementFailedPutRequests(1);
227 return processException(e
);
230 servlet
.getMetrics().incrementSucessfulPutRequests(1);
232 return updateExisting ? Response
.ok(uriInfo
.getAbsolutePath()).build() :
233 Response
.created(uriInfo
.getAbsolutePath()).build();
236 private boolean doesNamespaceExist(Admin admin
, String namespaceName
) throws IOException
{
237 NamespaceDescriptor
[] nd
= admin
.listNamespaceDescriptors();
238 for(int i
= 0; i
< nd
.length
; i
++){
239 if(nd
[i
].getName().equals(namespaceName
)){
247 * Build a response for DELETE delete namespace.
248 * @param message value not used.
249 * @param headers value not used.
250 * @return response code.
253 public Response
deleteNoBody(final byte[] message
,
254 final @Context UriInfo uriInfo
, final @Context HttpHeaders headers
) {
255 if (LOG
.isTraceEnabled()) {
256 LOG
.trace("DELETE " + uriInfo
.getAbsolutePath());
258 if (servlet
.isReadOnly()) {
259 servlet
.getMetrics().incrementFailedDeleteRequests(1);
260 return Response
.status(Response
.Status
.FORBIDDEN
).type(MIMETYPE_TEXT
)
261 .entity("Forbidden" + CRLF
).build();
265 Admin admin
= servlet
.getAdmin();
266 if (!doesNamespaceExist(admin
, namespace
)){
267 return Response
.status(Response
.Status
.NOT_FOUND
).type(MIMETYPE_TEXT
).
268 entity("Namespace '" + namespace
+ "' does not exists. Cannot " +
269 "drop namespace.").build();
272 admin
.deleteNamespace(namespace
);
273 servlet
.getMetrics().incrementSucessfulDeleteRequests(1);
274 return Response
.ok().build();
276 } catch (IOException e
) {
277 servlet
.getMetrics().incrementFailedDeleteRequests(1);
278 return processException(e
);
283 * Dispatch to NamespaceInstanceResource for getting list of tables.
286 public NamespacesInstanceResource
getNamespaceInstanceResource(
287 final @PathParam("tables") String namespace
) throws IOException
{
288 return new NamespacesInstanceResource(this.namespace
, true);