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 javax
.ws
.rs
.Consumes
;
25 import javax
.ws
.rs
.DELETE
;
26 import javax
.ws
.rs
.GET
;
27 import javax
.ws
.rs
.POST
;
28 import javax
.ws
.rs
.PUT
;
29 import javax
.ws
.rs
.Path
;
30 import javax
.ws
.rs
.PathParam
;
31 import javax
.ws
.rs
.Produces
;
32 import javax
.ws
.rs
.core
.Context
;
33 import javax
.ws
.rs
.core
.HttpHeaders
;
34 import javax
.ws
.rs
.core
.Response
;
35 import javax
.ws
.rs
.core
.UriInfo
;
36 import org
.apache
.hadoop
.hbase
.NamespaceDescriptor
;
37 import org
.apache
.hadoop
.hbase
.client
.Admin
;
38 import org
.apache
.hadoop
.hbase
.client
.TableDescriptor
;
39 import org
.apache
.hadoop
.hbase
.rest
.model
.NamespacesInstanceModel
;
40 import org
.apache
.hadoop
.hbase
.rest
.model
.TableListModel
;
41 import org
.apache
.hadoop
.hbase
.rest
.model
.TableModel
;
42 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
43 import org
.apache
.yetus
.audience
.InterfaceAudience
;
44 import org
.slf4j
.Logger
;
45 import org
.slf4j
.LoggerFactory
;
48 * Implements the following REST end points:
50 * <tt>/namespaces/{namespace} GET: get namespace properties.</tt>
51 * <tt>/namespaces/{namespace} POST: create namespace.</tt>
52 * <tt>/namespaces/{namespace} PUT: alter namespace.</tt>
53 * <tt>/namespaces/{namespace} DELETE: drop namespace.</tt>
54 * <tt>/namespaces/{namespace}/tables GET: list namespace's tables.</tt>
57 @InterfaceAudience.Private
58 public class NamespacesInstanceResource
extends ResourceBase
{
60 private static final Logger LOG
= LoggerFactory
.getLogger(NamespacesInstanceResource
.class);
62 boolean queryTables
= false;
65 * Constructor for standard NamespaceInstanceResource.
68 public NamespacesInstanceResource(String namespace
) throws IOException
{
69 this(namespace
, false);
73 * Constructor for querying namespace table list via NamespaceInstanceResource.
76 public NamespacesInstanceResource(String namespace
, boolean queryTables
) throws IOException
{
78 this.namespace
= namespace
;
79 this.queryTables
= queryTables
;
83 * Build a response for GET namespace description or GET list of namespace tables.
84 * @param context servlet context
85 * @param uriInfo (JAX-RS context variable) request URL
86 * @return A response containing NamespacesInstanceModel for a namespace descriptions and
87 * TableListModel for a list of namespace tables.
90 @Produces({MIMETYPE_TEXT
, MIMETYPE_XML
, MIMETYPE_JSON
, MIMETYPE_PROTOBUF
,
91 MIMETYPE_PROTOBUF_IETF
})
92 public Response
get(final @Context ServletContext context
,
93 final @Context UriInfo uriInfo
) {
94 if (LOG
.isTraceEnabled()) {
95 LOG
.trace("GET " + uriInfo
.getAbsolutePath());
97 servlet
.getMetrics().incrementRequests(1);
99 // Respond to list of namespace tables requests.
101 TableListModel tableModel
= new TableListModel();
103 List
<TableDescriptor
> tables
=
104 servlet
.getAdmin().listTableDescriptorsByNamespace(Bytes
.toBytes(namespace
));
105 for (TableDescriptor table
: tables
) {
106 tableModel
.add(new TableModel(table
.getTableName().getQualifierAsString()));
109 servlet
.getMetrics().incrementSucessfulGetRequests(1);
110 return Response
.ok(tableModel
).build();
111 }catch(IOException e
) {
112 servlet
.getMetrics().incrementFailedGetRequests(1);
113 throw new RuntimeException("Cannot retrieve table list for '" + namespace
+ "'.");
117 // Respond to namespace description requests.
119 NamespacesInstanceModel rowModel
=
120 new NamespacesInstanceModel(servlet
.getAdmin(), namespace
);
121 servlet
.getMetrics().incrementSucessfulGetRequests(1);
122 return Response
.ok(rowModel
).build();
123 } catch (IOException e
) {
124 servlet
.getMetrics().incrementFailedGetRequests(1);
125 throw new RuntimeException("Cannot retrieve info for '" + namespace
+ "'.");
130 * Build a response for PUT alter namespace with properties specified.
131 * @param model properties used for alter.
132 * @param uriInfo (JAX-RS context variable) request URL
133 * @return response code.
136 @Consumes({MIMETYPE_XML
, MIMETYPE_JSON
, MIMETYPE_PROTOBUF
,
137 MIMETYPE_PROTOBUF_IETF
})
138 public Response
put(final NamespacesInstanceModel model
, final @Context UriInfo uriInfo
) {
139 return processUpdate(model
, true, uriInfo
);
143 * Build a response for POST create namespace with properties specified.
144 * @param model properties used for create.
145 * @param uriInfo (JAX-RS context variable) request URL
146 * @return response code.
149 @Consumes({MIMETYPE_XML
, MIMETYPE_JSON
, MIMETYPE_PROTOBUF
,
150 MIMETYPE_PROTOBUF_IETF
})
151 public Response
post(final NamespacesInstanceModel model
,
152 final @Context UriInfo uriInfo
) {
153 return processUpdate(model
, false, uriInfo
);
157 // Check that POST or PUT is valid and then update namespace.
158 private Response
processUpdate(NamespacesInstanceModel model
, final boolean updateExisting
,
159 final UriInfo uriInfo
) {
160 if (LOG
.isTraceEnabled()) {
161 LOG
.trace((updateExisting ?
"PUT " : "POST ") + uriInfo
.getAbsolutePath());
165 model
= new NamespacesInstanceModel(namespace
);
166 } catch(IOException ioe
) {
167 servlet
.getMetrics().incrementFailedPutRequests(1);
168 throw new RuntimeException("Cannot retrieve info for '" + namespace
+ "'.");
171 servlet
.getMetrics().incrementRequests(1);
173 if (servlet
.isReadOnly()) {
174 servlet
.getMetrics().incrementFailedPutRequests(1);
175 return Response
.status(Response
.Status
.FORBIDDEN
).type(MIMETYPE_TEXT
)
176 .entity("Forbidden" + CRLF
).build();
180 boolean namespaceExists
= false;
182 admin
= servlet
.getAdmin();
183 namespaceExists
= doesNamespaceExist(admin
, namespace
);
184 }catch (IOException e
) {
185 servlet
.getMetrics().incrementFailedPutRequests(1);
186 return processException(e
);
189 // Do not allow creation if namespace already exists.
190 if(!updateExisting
&& namespaceExists
){
191 servlet
.getMetrics().incrementFailedPutRequests(1);
192 return Response
.status(Response
.Status
.FORBIDDEN
).type(MIMETYPE_TEXT
).
193 entity("Namespace '" + namespace
+ "' already exists. Use REST PUT " +
194 "to alter the existing namespace.").build();
197 // Do not allow altering if namespace does not exist.
198 if (updateExisting
&& !namespaceExists
){
199 servlet
.getMetrics().incrementFailedPutRequests(1);
200 return Response
.status(Response
.Status
.FORBIDDEN
).type(MIMETYPE_TEXT
).
201 entity("Namespace '" + namespace
+ "' does not exist. Use " +
202 "REST POST to create the namespace.").build();
205 return createOrUpdate(model
, uriInfo
, admin
, updateExisting
);
208 // Do the actual namespace create or alter.
209 private Response
createOrUpdate(final NamespacesInstanceModel model
, final UriInfo uriInfo
,
210 final Admin admin
, final boolean updateExisting
) {
211 NamespaceDescriptor
.Builder builder
= NamespaceDescriptor
.create(namespace
);
212 builder
.addConfiguration(model
.getProperties());
213 if(model
.getProperties().size() > 0){
214 builder
.addConfiguration(model
.getProperties());
216 NamespaceDescriptor nsd
= builder
.build();
220 admin
.modifyNamespace(nsd
);
222 admin
.createNamespace(nsd
);
224 }catch (IOException e
) {
225 servlet
.getMetrics().incrementFailedPutRequests(1);
226 return processException(e
);
229 servlet
.getMetrics().incrementSucessfulPutRequests(1);
231 return updateExisting ? Response
.ok(uriInfo
.getAbsolutePath()).build() :
232 Response
.created(uriInfo
.getAbsolutePath()).build();
235 private boolean doesNamespaceExist(Admin admin
, String namespaceName
) throws IOException
{
236 NamespaceDescriptor
[] nd
= admin
.listNamespaceDescriptors();
237 for(int i
= 0; i
< nd
.length
; i
++){
238 if(nd
[i
].getName().equals(namespaceName
)){
246 * Build a response for DELETE delete namespace.
247 * @param message value not used.
248 * @param headers value not used.
249 * @return response code.
252 public Response
deleteNoBody(final byte[] message
,
253 final @Context UriInfo uriInfo
, final @Context HttpHeaders headers
) {
254 if (LOG
.isTraceEnabled()) {
255 LOG
.trace("DELETE " + uriInfo
.getAbsolutePath());
257 if (servlet
.isReadOnly()) {
258 servlet
.getMetrics().incrementFailedDeleteRequests(1);
259 return Response
.status(Response
.Status
.FORBIDDEN
).type(MIMETYPE_TEXT
)
260 .entity("Forbidden" + CRLF
).build();
264 Admin admin
= servlet
.getAdmin();
265 if (!doesNamespaceExist(admin
, namespace
)){
266 return Response
.status(Response
.Status
.NOT_FOUND
).type(MIMETYPE_TEXT
).
267 entity("Namespace '" + namespace
+ "' does not exists. Cannot " +
268 "drop namespace.").build();
271 admin
.deleteNamespace(namespace
);
272 servlet
.getMetrics().incrementSucessfulDeleteRequests(1);
273 return Response
.ok().build();
275 } catch (IOException e
) {
276 servlet
.getMetrics().incrementFailedDeleteRequests(1);
277 return processException(e
);
282 * Dispatch to NamespaceInstanceResource for getting list of tables.
285 public NamespacesInstanceResource
getNamespaceInstanceResource(
286 final @PathParam("tables") String namespace
) throws IOException
{
287 return new NamespacesInstanceResource(this.namespace
, true);