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.hdfs.server.namenode.web.resources; 019 020import java.io.FileNotFoundException; 021import java.io.IOException; 022import java.io.OutputStream; 023import java.io.OutputStreamWriter; 024import java.io.PrintWriter; 025import java.net.InetAddress; 026import java.net.URI; 027import java.net.URISyntaxException; 028import java.security.PrivilegedExceptionAction; 029import java.util.ArrayList; 030import java.util.EnumSet; 031import java.util.List; 032 033import javax.servlet.ServletContext; 034import javax.servlet.http.HttpServletRequest; 035import javax.servlet.http.HttpServletResponse; 036import javax.ws.rs.Consumes; 037import javax.ws.rs.DELETE; 038import javax.ws.rs.DefaultValue; 039import javax.ws.rs.GET; 040import javax.ws.rs.POST; 041import javax.ws.rs.PUT; 042import javax.ws.rs.Path; 043import javax.ws.rs.PathParam; 044import javax.ws.rs.Produces; 045import javax.ws.rs.QueryParam; 046import javax.ws.rs.core.Context; 047import javax.ws.rs.core.MediaType; 048import javax.ws.rs.core.Response; 049import javax.ws.rs.core.StreamingOutput; 050 051import org.apache.commons.logging.Log; 052import org.apache.commons.logging.LogFactory; 053import org.apache.hadoop.conf.Configuration; 054import org.apache.hadoop.fs.ContentSummary; 055import org.apache.hadoop.fs.FileStatus; 056import org.apache.hadoop.fs.Options; 057import org.apache.hadoop.fs.XAttr; 058import org.apache.hadoop.fs.permission.AclStatus; 059import org.apache.hadoop.hdfs.StorageType; 060import org.apache.hadoop.hdfs.XAttrHelper; 061import org.apache.hadoop.hdfs.protocol.DatanodeInfo; 062import org.apache.hadoop.hdfs.protocol.DirectoryListing; 063import org.apache.hadoop.hdfs.protocol.HdfsFileStatus; 064import org.apache.hadoop.hdfs.protocol.LocatedBlocks; 065import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; 066import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenSecretManager; 067import org.apache.hadoop.hdfs.server.blockmanagement.BlockManager; 068import org.apache.hadoop.hdfs.server.blockmanagement.DatanodeDescriptor; 069import org.apache.hadoop.hdfs.server.blockmanagement.DatanodeStorageInfo; 070import org.apache.hadoop.hdfs.server.common.JspHelper; 071import org.apache.hadoop.hdfs.server.namenode.NameNode; 072import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocols; 073import org.apache.hadoop.hdfs.web.JsonUtil; 074import org.apache.hadoop.hdfs.web.ParamFilter; 075import org.apache.hadoop.hdfs.web.SWebHdfsFileSystem; 076import org.apache.hadoop.hdfs.web.WebHdfsFileSystem; 077import org.apache.hadoop.hdfs.web.resources.AccessTimeParam; 078import org.apache.hadoop.hdfs.web.resources.AclPermissionParam; 079import org.apache.hadoop.hdfs.web.resources.BlockSizeParam; 080import org.apache.hadoop.hdfs.web.resources.BufferSizeParam; 081import org.apache.hadoop.hdfs.web.resources.ConcatSourcesParam; 082import org.apache.hadoop.hdfs.web.resources.CreateParentParam; 083import org.apache.hadoop.hdfs.web.resources.DelegationParam; 084import org.apache.hadoop.hdfs.web.resources.DeleteOpParam; 085import org.apache.hadoop.hdfs.web.resources.DestinationParam; 086import org.apache.hadoop.hdfs.web.resources.DoAsParam; 087import org.apache.hadoop.hdfs.web.resources.GetOpParam; 088import org.apache.hadoop.hdfs.web.resources.GroupParam; 089import org.apache.hadoop.hdfs.web.resources.HttpOpParam; 090import org.apache.hadoop.hdfs.web.resources.LengthParam; 091import org.apache.hadoop.hdfs.web.resources.ModificationTimeParam; 092import org.apache.hadoop.hdfs.web.resources.NamenodeAddressParam; 093import org.apache.hadoop.hdfs.web.resources.OffsetParam; 094import org.apache.hadoop.hdfs.web.resources.OldSnapshotNameParam; 095import org.apache.hadoop.hdfs.web.resources.OverwriteParam; 096import org.apache.hadoop.hdfs.web.resources.OwnerParam; 097import org.apache.hadoop.hdfs.web.resources.Param; 098import org.apache.hadoop.hdfs.web.resources.PermissionParam; 099import org.apache.hadoop.hdfs.web.resources.PostOpParam; 100import org.apache.hadoop.hdfs.web.resources.PutOpParam; 101import org.apache.hadoop.hdfs.web.resources.RecursiveParam; 102import org.apache.hadoop.hdfs.web.resources.RenameOptionSetParam; 103import org.apache.hadoop.hdfs.web.resources.RenewerParam; 104import org.apache.hadoop.hdfs.web.resources.ReplicationParam; 105import org.apache.hadoop.hdfs.web.resources.SnapshotNameParam; 106import org.apache.hadoop.hdfs.web.resources.TokenArgumentParam; 107import org.apache.hadoop.hdfs.web.resources.UriFsPathParam; 108import org.apache.hadoop.hdfs.web.resources.UserParam; 109import org.apache.hadoop.hdfs.web.resources.XAttrEncodingParam; 110import org.apache.hadoop.hdfs.web.resources.XAttrNameParam; 111import org.apache.hadoop.hdfs.web.resources.XAttrSetFlagParam; 112import org.apache.hadoop.hdfs.web.resources.XAttrValueParam; 113import org.apache.hadoop.io.Text; 114import org.apache.hadoop.ipc.Server; 115import org.apache.hadoop.net.NetworkTopology.InvalidTopologyException; 116import org.apache.hadoop.net.NodeBase; 117import org.apache.hadoop.security.Credentials; 118import org.apache.hadoop.security.UserGroupInformation; 119import org.apache.hadoop.security.token.Token; 120import org.apache.hadoop.security.token.TokenIdentifier; 121 122import com.google.common.annotations.VisibleForTesting; 123import com.google.common.base.Charsets; 124import com.google.common.collect.Lists; 125import com.sun.jersey.spi.container.ResourceFilters; 126 127/** Web-hdfs NameNode implementation. */ 128@Path("") 129@ResourceFilters(ParamFilter.class) 130public class NamenodeWebHdfsMethods { 131 public static final Log LOG = LogFactory.getLog(NamenodeWebHdfsMethods.class); 132 133 private static final UriFsPathParam ROOT = new UriFsPathParam(""); 134 135 private static final ThreadLocal<String> REMOTE_ADDRESS = new ThreadLocal<String>(); 136 137 /** @return the remote client address. */ 138 public static String getRemoteAddress() { 139 return REMOTE_ADDRESS.get(); 140 } 141 142 public static InetAddress getRemoteIp() { 143 try { 144 return InetAddress.getByName(getRemoteAddress()); 145 } catch (Exception e) { 146 return null; 147 } 148 } 149 150 /** 151 * Returns true if a WebHdfs request is in progress. Akin to 152 * {@link Server#isRpcInvocation()}. 153 */ 154 public static boolean isWebHdfsInvocation() { 155 return getRemoteAddress() != null; 156 } 157 158 private @Context ServletContext context; 159 private @Context HttpServletRequest request; 160 private @Context HttpServletResponse response; 161 162 private void init(final UserGroupInformation ugi, 163 final DelegationParam delegation, 164 final UserParam username, final DoAsParam doAsUser, 165 final UriFsPathParam path, final HttpOpParam<?> op, 166 final Param<?, ?>... parameters) { 167 if (LOG.isTraceEnabled()) { 168 LOG.trace("HTTP " + op.getValue().getType() + ": " + op + ", " + path 169 + ", ugi=" + ugi + ", " + username + ", " + doAsUser 170 + Param.toSortedString(", ", parameters)); 171 } 172 173 //clear content type 174 response.setContentType(null); 175 176 // set the remote address, if coming in via a trust proxy server then 177 // the address with be that of the proxied client 178 REMOTE_ADDRESS.set(JspHelper.getRemoteAddr(request)); 179 } 180 181 private void reset() { 182 REMOTE_ADDRESS.set(null); 183 } 184 185 private static NamenodeProtocols getRPCServer(NameNode namenode) 186 throws IOException { 187 final NamenodeProtocols np = namenode.getRpcServer(); 188 if (np == null) { 189 throw new IOException("Namenode is in startup mode"); 190 } 191 return np; 192 } 193 194 @VisibleForTesting 195 static DatanodeInfo chooseDatanode(final NameNode namenode, 196 final String path, final HttpOpParam.Op op, final long openOffset, 197 final long blocksize) throws IOException { 198 final BlockManager bm = namenode.getNamesystem().getBlockManager(); 199 200 if (op == PutOpParam.Op.CREATE) { 201 //choose a datanode near to client 202 final DatanodeDescriptor clientNode = bm.getDatanodeManager( 203 ).getDatanodeByHost(getRemoteAddress()); 204 if (clientNode != null) { 205 final DatanodeStorageInfo[] storages = bm.getBlockPlacementPolicy() 206 .chooseTarget(path, 1, clientNode, 207 new ArrayList<DatanodeStorageInfo>(), false, null, blocksize, 208 // TODO: get storage type from the file 209 StorageType.DEFAULT); 210 if (storages.length > 0) { 211 return storages[0].getDatanodeDescriptor(); 212 } 213 } 214 } else if (op == GetOpParam.Op.OPEN 215 || op == GetOpParam.Op.GETFILECHECKSUM 216 || op == PostOpParam.Op.APPEND) { 217 //choose a datanode containing a replica 218 final NamenodeProtocols np = getRPCServer(namenode); 219 final HdfsFileStatus status = np.getFileInfo(path); 220 if (status == null) { 221 throw new FileNotFoundException("File " + path + " not found."); 222 } 223 final long len = status.getLen(); 224 if (op == GetOpParam.Op.OPEN) { 225 if (openOffset < 0L || (openOffset >= len && len > 0)) { 226 throw new IOException("Offset=" + openOffset 227 + " out of the range [0, " + len + "); " + op + ", path=" + path); 228 } 229 } 230 231 if (len > 0) { 232 final long offset = op == GetOpParam.Op.OPEN? openOffset: len - 1; 233 final LocatedBlocks locations = np.getBlockLocations(path, offset, 1); 234 final int count = locations.locatedBlockCount(); 235 if (count > 0) { 236 return bestNode(locations.get(0).getLocations()); 237 } 238 } 239 } 240 241 return (DatanodeDescriptor)bm.getDatanodeManager().getNetworkTopology( 242 ).chooseRandom(NodeBase.ROOT); 243 } 244 245 /** 246 * Choose the datanode to redirect the request. Note that the nodes have been 247 * sorted based on availability and network distances, thus it is sufficient 248 * to return the first element of the node here. 249 */ 250 private static DatanodeInfo bestNode(DatanodeInfo[] nodes) throws IOException { 251 if (nodes.length == 0 || nodes[0].isDecommissioned()) { 252 throw new IOException("No active nodes contain this block"); 253 } 254 return nodes[0]; 255 } 256 257 private Token<? extends TokenIdentifier> generateDelegationToken( 258 final NameNode namenode, final UserGroupInformation ugi, 259 final String renewer) throws IOException { 260 final Credentials c = DelegationTokenSecretManager.createCredentials( 261 namenode, ugi, renewer != null? renewer: ugi.getShortUserName()); 262 final Token<? extends TokenIdentifier> t = c.getAllTokens().iterator().next(); 263 Text kind = request.getScheme().equals("http") ? WebHdfsFileSystem.TOKEN_KIND 264 : SWebHdfsFileSystem.TOKEN_KIND; 265 t.setKind(kind); 266 return t; 267 } 268 269 private URI redirectURI(final NameNode namenode, 270 final UserGroupInformation ugi, final DelegationParam delegation, 271 final UserParam username, final DoAsParam doAsUser, 272 final String path, final HttpOpParam.Op op, final long openOffset, 273 final long blocksize, 274 final Param<?, ?>... parameters) throws URISyntaxException, IOException { 275 final DatanodeInfo dn; 276 try { 277 dn = chooseDatanode(namenode, path, op, openOffset, blocksize); 278 } catch (InvalidTopologyException ite) { 279 throw new IOException("Failed to find datanode, suggest to check cluster health.", ite); 280 } 281 282 final String delegationQuery; 283 if (!UserGroupInformation.isSecurityEnabled()) { 284 //security disabled 285 delegationQuery = Param.toSortedString("&", doAsUser, username); 286 } else if (delegation.getValue() != null) { 287 //client has provided a token 288 delegationQuery = "&" + delegation; 289 } else { 290 //generate a token 291 final Token<? extends TokenIdentifier> t = generateDelegationToken( 292 namenode, ugi, request.getUserPrincipal().getName()); 293 delegationQuery = "&" + new DelegationParam(t.encodeToUrlString()); 294 } 295 final String query = op.toQueryString() + delegationQuery 296 + "&" + new NamenodeAddressParam(namenode) 297 + Param.toSortedString("&", parameters); 298 final String uripath = WebHdfsFileSystem.PATH_PREFIX + path; 299 300 final String scheme = request.getScheme(); 301 int port = "http".equals(scheme) ? dn.getInfoPort() : dn 302 .getInfoSecurePort(); 303 final URI uri = new URI(scheme, null, dn.getHostName(), port, uripath, 304 query, null); 305 306 if (LOG.isTraceEnabled()) { 307 LOG.trace("redirectURI=" + uri); 308 } 309 return uri; 310 } 311 312 /** Handle HTTP PUT request for the root. */ 313 @PUT 314 @Path("/") 315 @Consumes({"*/*"}) 316 @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON}) 317 public Response putRoot( 318 @Context final UserGroupInformation ugi, 319 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 320 final DelegationParam delegation, 321 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 322 final UserParam username, 323 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 324 final DoAsParam doAsUser, 325 @QueryParam(PutOpParam.NAME) @DefaultValue(PutOpParam.DEFAULT) 326 final PutOpParam op, 327 @QueryParam(DestinationParam.NAME) @DefaultValue(DestinationParam.DEFAULT) 328 final DestinationParam destination, 329 @QueryParam(OwnerParam.NAME) @DefaultValue(OwnerParam.DEFAULT) 330 final OwnerParam owner, 331 @QueryParam(GroupParam.NAME) @DefaultValue(GroupParam.DEFAULT) 332 final GroupParam group, 333 @QueryParam(PermissionParam.NAME) @DefaultValue(PermissionParam.DEFAULT) 334 final PermissionParam permission, 335 @QueryParam(OverwriteParam.NAME) @DefaultValue(OverwriteParam.DEFAULT) 336 final OverwriteParam overwrite, 337 @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT) 338 final BufferSizeParam bufferSize, 339 @QueryParam(ReplicationParam.NAME) @DefaultValue(ReplicationParam.DEFAULT) 340 final ReplicationParam replication, 341 @QueryParam(BlockSizeParam.NAME) @DefaultValue(BlockSizeParam.DEFAULT) 342 final BlockSizeParam blockSize, 343 @QueryParam(ModificationTimeParam.NAME) @DefaultValue(ModificationTimeParam.DEFAULT) 344 final ModificationTimeParam modificationTime, 345 @QueryParam(AccessTimeParam.NAME) @DefaultValue(AccessTimeParam.DEFAULT) 346 final AccessTimeParam accessTime, 347 @QueryParam(RenameOptionSetParam.NAME) @DefaultValue(RenameOptionSetParam.DEFAULT) 348 final RenameOptionSetParam renameOptions, 349 @QueryParam(CreateParentParam.NAME) @DefaultValue(CreateParentParam.DEFAULT) 350 final CreateParentParam createParent, 351 @QueryParam(TokenArgumentParam.NAME) @DefaultValue(TokenArgumentParam.DEFAULT) 352 final TokenArgumentParam delegationTokenArgument, 353 @QueryParam(AclPermissionParam.NAME) @DefaultValue(AclPermissionParam.DEFAULT) 354 final AclPermissionParam aclPermission, 355 @QueryParam(XAttrNameParam.NAME) @DefaultValue(XAttrNameParam.DEFAULT) 356 final XAttrNameParam xattrName, 357 @QueryParam(XAttrValueParam.NAME) @DefaultValue(XAttrValueParam.DEFAULT) 358 final XAttrValueParam xattrValue, 359 @QueryParam(XAttrSetFlagParam.NAME) @DefaultValue(XAttrSetFlagParam.DEFAULT) 360 final XAttrSetFlagParam xattrSetFlag, 361 @QueryParam(SnapshotNameParam.NAME) @DefaultValue(SnapshotNameParam.DEFAULT) 362 final SnapshotNameParam snapshotName, 363 @QueryParam(OldSnapshotNameParam.NAME) @DefaultValue(OldSnapshotNameParam.DEFAULT) 364 final OldSnapshotNameParam oldSnapshotName 365 )throws IOException, InterruptedException { 366 return put(ugi, delegation, username, doAsUser, ROOT, op, destination, 367 owner, group, permission, overwrite, bufferSize, replication, 368 blockSize, modificationTime, accessTime, renameOptions, createParent, 369 delegationTokenArgument, aclPermission, xattrName, xattrValue, 370 xattrSetFlag, snapshotName, oldSnapshotName); 371 } 372 373 /** Handle HTTP PUT request. */ 374 @PUT 375 @Path("{" + UriFsPathParam.NAME + ":.*}") 376 @Consumes({"*/*"}) 377 @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON}) 378 public Response put( 379 @Context final UserGroupInformation ugi, 380 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 381 final DelegationParam delegation, 382 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 383 final UserParam username, 384 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 385 final DoAsParam doAsUser, 386 @PathParam(UriFsPathParam.NAME) final UriFsPathParam path, 387 @QueryParam(PutOpParam.NAME) @DefaultValue(PutOpParam.DEFAULT) 388 final PutOpParam op, 389 @QueryParam(DestinationParam.NAME) @DefaultValue(DestinationParam.DEFAULT) 390 final DestinationParam destination, 391 @QueryParam(OwnerParam.NAME) @DefaultValue(OwnerParam.DEFAULT) 392 final OwnerParam owner, 393 @QueryParam(GroupParam.NAME) @DefaultValue(GroupParam.DEFAULT) 394 final GroupParam group, 395 @QueryParam(PermissionParam.NAME) @DefaultValue(PermissionParam.DEFAULT) 396 final PermissionParam permission, 397 @QueryParam(OverwriteParam.NAME) @DefaultValue(OverwriteParam.DEFAULT) 398 final OverwriteParam overwrite, 399 @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT) 400 final BufferSizeParam bufferSize, 401 @QueryParam(ReplicationParam.NAME) @DefaultValue(ReplicationParam.DEFAULT) 402 final ReplicationParam replication, 403 @QueryParam(BlockSizeParam.NAME) @DefaultValue(BlockSizeParam.DEFAULT) 404 final BlockSizeParam blockSize, 405 @QueryParam(ModificationTimeParam.NAME) @DefaultValue(ModificationTimeParam.DEFAULT) 406 final ModificationTimeParam modificationTime, 407 @QueryParam(AccessTimeParam.NAME) @DefaultValue(AccessTimeParam.DEFAULT) 408 final AccessTimeParam accessTime, 409 @QueryParam(RenameOptionSetParam.NAME) @DefaultValue(RenameOptionSetParam.DEFAULT) 410 final RenameOptionSetParam renameOptions, 411 @QueryParam(CreateParentParam.NAME) @DefaultValue(CreateParentParam.DEFAULT) 412 final CreateParentParam createParent, 413 @QueryParam(TokenArgumentParam.NAME) @DefaultValue(TokenArgumentParam.DEFAULT) 414 final TokenArgumentParam delegationTokenArgument, 415 @QueryParam(AclPermissionParam.NAME) @DefaultValue(AclPermissionParam.DEFAULT) 416 final AclPermissionParam aclPermission, 417 @QueryParam(XAttrNameParam.NAME) @DefaultValue(XAttrNameParam.DEFAULT) 418 final XAttrNameParam xattrName, 419 @QueryParam(XAttrValueParam.NAME) @DefaultValue(XAttrValueParam.DEFAULT) 420 final XAttrValueParam xattrValue, 421 @QueryParam(XAttrSetFlagParam.NAME) @DefaultValue(XAttrSetFlagParam.DEFAULT) 422 final XAttrSetFlagParam xattrSetFlag, 423 @QueryParam(SnapshotNameParam.NAME) @DefaultValue(SnapshotNameParam.DEFAULT) 424 final SnapshotNameParam snapshotName, 425 @QueryParam(OldSnapshotNameParam.NAME) @DefaultValue(OldSnapshotNameParam.DEFAULT) 426 final OldSnapshotNameParam oldSnapshotName 427 ) throws IOException, InterruptedException { 428 429 init(ugi, delegation, username, doAsUser, path, op, destination, owner, 430 group, permission, overwrite, bufferSize, replication, blockSize, 431 modificationTime, accessTime, renameOptions, delegationTokenArgument, 432 aclPermission, xattrName, xattrValue, xattrSetFlag, snapshotName, 433 oldSnapshotName); 434 435 return ugi.doAs(new PrivilegedExceptionAction<Response>() { 436 @Override 437 public Response run() throws IOException, URISyntaxException { 438 try { 439 return put(ugi, delegation, username, doAsUser, 440 path.getAbsolutePath(), op, destination, owner, group, 441 permission, overwrite, bufferSize, replication, blockSize, 442 modificationTime, accessTime, renameOptions, createParent, 443 delegationTokenArgument, aclPermission, xattrName, xattrValue, 444 xattrSetFlag, snapshotName, oldSnapshotName); 445 } finally { 446 reset(); 447 } 448 } 449 }); 450 } 451 452 private Response put( 453 final UserGroupInformation ugi, 454 final DelegationParam delegation, 455 final UserParam username, 456 final DoAsParam doAsUser, 457 final String fullpath, 458 final PutOpParam op, 459 final DestinationParam destination, 460 final OwnerParam owner, 461 final GroupParam group, 462 final PermissionParam permission, 463 final OverwriteParam overwrite, 464 final BufferSizeParam bufferSize, 465 final ReplicationParam replication, 466 final BlockSizeParam blockSize, 467 final ModificationTimeParam modificationTime, 468 final AccessTimeParam accessTime, 469 final RenameOptionSetParam renameOptions, 470 final CreateParentParam createParent, 471 final TokenArgumentParam delegationTokenArgument, 472 final AclPermissionParam aclPermission, 473 final XAttrNameParam xattrName, 474 final XAttrValueParam xattrValue, 475 final XAttrSetFlagParam xattrSetFlag, 476 final SnapshotNameParam snapshotName, 477 final OldSnapshotNameParam oldSnapshotName 478 ) throws IOException, URISyntaxException { 479 480 final Configuration conf = (Configuration)context.getAttribute(JspHelper.CURRENT_CONF); 481 final NameNode namenode = (NameNode)context.getAttribute("name.node"); 482 final NamenodeProtocols np = getRPCServer(namenode); 483 484 switch(op.getValue()) { 485 case CREATE: 486 { 487 final URI uri = redirectURI(namenode, ugi, delegation, username, doAsUser, 488 fullpath, op.getValue(), -1L, blockSize.getValue(conf), 489 permission, overwrite, bufferSize, replication, blockSize); 490 return Response.temporaryRedirect(uri).type(MediaType.APPLICATION_OCTET_STREAM).build(); 491 } 492 case MKDIRS: 493 { 494 final boolean b = np.mkdirs(fullpath, permission.getFsPermission(), true); 495 final String js = JsonUtil.toJsonString("boolean", b); 496 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 497 } 498 case CREATESYMLINK: 499 { 500 np.createSymlink(destination.getValue(), fullpath, 501 PermissionParam.getDefaultFsPermission(), createParent.getValue()); 502 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 503 } 504 case RENAME: 505 { 506 final EnumSet<Options.Rename> s = renameOptions.getValue(); 507 if (s.isEmpty()) { 508 final boolean b = np.rename(fullpath, destination.getValue()); 509 final String js = JsonUtil.toJsonString("boolean", b); 510 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 511 } else { 512 np.rename2(fullpath, destination.getValue(), 513 s.toArray(new Options.Rename[s.size()])); 514 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 515 } 516 } 517 case SETREPLICATION: 518 { 519 final boolean b = np.setReplication(fullpath, replication.getValue(conf)); 520 final String js = JsonUtil.toJsonString("boolean", b); 521 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 522 } 523 case SETOWNER: 524 { 525 if (owner.getValue() == null && group.getValue() == null) { 526 throw new IllegalArgumentException("Both owner and group are empty."); 527 } 528 529 np.setOwner(fullpath, owner.getValue(), group.getValue()); 530 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 531 } 532 case SETPERMISSION: 533 { 534 np.setPermission(fullpath, permission.getFsPermission()); 535 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 536 } 537 case SETTIMES: 538 { 539 np.setTimes(fullpath, modificationTime.getValue(), accessTime.getValue()); 540 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 541 } 542 case RENEWDELEGATIONTOKEN: 543 { 544 final Token<DelegationTokenIdentifier> token = new Token<DelegationTokenIdentifier>(); 545 token.decodeFromUrlString(delegationTokenArgument.getValue()); 546 final long expiryTime = np.renewDelegationToken(token); 547 final String js = JsonUtil.toJsonString("long", expiryTime); 548 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 549 } 550 case CANCELDELEGATIONTOKEN: 551 { 552 final Token<DelegationTokenIdentifier> token = new Token<DelegationTokenIdentifier>(); 553 token.decodeFromUrlString(delegationTokenArgument.getValue()); 554 np.cancelDelegationToken(token); 555 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 556 } 557 case MODIFYACLENTRIES: { 558 np.modifyAclEntries(fullpath, aclPermission.getAclPermission(true)); 559 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 560 } 561 case REMOVEACLENTRIES: { 562 np.removeAclEntries(fullpath, aclPermission.getAclPermission(false)); 563 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 564 } 565 case REMOVEDEFAULTACL: { 566 np.removeDefaultAcl(fullpath); 567 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 568 } 569 case REMOVEACL: { 570 np.removeAcl(fullpath); 571 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 572 } 573 case SETACL: { 574 np.setAcl(fullpath, aclPermission.getAclPermission(true)); 575 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 576 } 577 case SETXATTR: { 578 np.setXAttr( 579 fullpath, 580 XAttrHelper.buildXAttr(xattrName.getXAttrName(), 581 xattrValue.getXAttrValue()), xattrSetFlag.getFlag()); 582 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 583 } 584 case REMOVEXATTR: { 585 np.removeXAttr(fullpath, XAttrHelper.buildXAttr(xattrName.getXAttrName())); 586 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 587 } 588 case CREATESNAPSHOT: { 589 String snapshotPath = np.createSnapshot(fullpath, snapshotName.getValue()); 590 final String js = JsonUtil.toJsonString( 591 org.apache.hadoop.fs.Path.class.getSimpleName(), snapshotPath); 592 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 593 } 594 case RENAMESNAPSHOT: { 595 np.renameSnapshot(fullpath, oldSnapshotName.getValue(), 596 snapshotName.getValue()); 597 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 598 } 599 default: 600 throw new UnsupportedOperationException(op + " is not supported"); 601 } 602 } 603 604 /** Handle HTTP POST request for the root. */ 605 @POST 606 @Path("/") 607 @Consumes({"*/*"}) 608 @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON}) 609 public Response postRoot( 610 @Context final UserGroupInformation ugi, 611 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 612 final DelegationParam delegation, 613 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 614 final UserParam username, 615 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 616 final DoAsParam doAsUser, 617 @QueryParam(PostOpParam.NAME) @DefaultValue(PostOpParam.DEFAULT) 618 final PostOpParam op, 619 @QueryParam(ConcatSourcesParam.NAME) @DefaultValue(ConcatSourcesParam.DEFAULT) 620 final ConcatSourcesParam concatSrcs, 621 @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT) 622 final BufferSizeParam bufferSize 623 ) throws IOException, InterruptedException { 624 return post(ugi, delegation, username, doAsUser, ROOT, op, concatSrcs, bufferSize); 625 } 626 627 /** Handle HTTP POST request. */ 628 @POST 629 @Path("{" + UriFsPathParam.NAME + ":.*}") 630 @Consumes({"*/*"}) 631 @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON}) 632 public Response post( 633 @Context final UserGroupInformation ugi, 634 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 635 final DelegationParam delegation, 636 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 637 final UserParam username, 638 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 639 final DoAsParam doAsUser, 640 @PathParam(UriFsPathParam.NAME) final UriFsPathParam path, 641 @QueryParam(PostOpParam.NAME) @DefaultValue(PostOpParam.DEFAULT) 642 final PostOpParam op, 643 @QueryParam(ConcatSourcesParam.NAME) @DefaultValue(ConcatSourcesParam.DEFAULT) 644 final ConcatSourcesParam concatSrcs, 645 @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT) 646 final BufferSizeParam bufferSize 647 ) throws IOException, InterruptedException { 648 649 init(ugi, delegation, username, doAsUser, path, op, concatSrcs, bufferSize); 650 651 return ugi.doAs(new PrivilegedExceptionAction<Response>() { 652 @Override 653 public Response run() throws IOException, URISyntaxException { 654 try { 655 return post(ugi, delegation, username, doAsUser, 656 path.getAbsolutePath(), op, concatSrcs, bufferSize); 657 } finally { 658 reset(); 659 } 660 } 661 }); 662 } 663 664 private Response post( 665 final UserGroupInformation ugi, 666 final DelegationParam delegation, 667 final UserParam username, 668 final DoAsParam doAsUser, 669 final String fullpath, 670 final PostOpParam op, 671 final ConcatSourcesParam concatSrcs, 672 final BufferSizeParam bufferSize 673 ) throws IOException, URISyntaxException { 674 final NameNode namenode = (NameNode)context.getAttribute("name.node"); 675 676 switch(op.getValue()) { 677 case APPEND: 678 { 679 final URI uri = redirectURI(namenode, ugi, delegation, username, doAsUser, 680 fullpath, op.getValue(), -1L, -1L, bufferSize); 681 return Response.temporaryRedirect(uri).type(MediaType.APPLICATION_OCTET_STREAM).build(); 682 } 683 case CONCAT: 684 { 685 getRPCServer(namenode).concat(fullpath, concatSrcs.getAbsolutePaths()); 686 return Response.ok().build(); 687 } 688 default: 689 throw new UnsupportedOperationException(op + " is not supported"); 690 } 691 } 692 693 /** Handle HTTP GET request for the root. */ 694 @GET 695 @Path("/") 696 @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON}) 697 public Response getRoot( 698 @Context final UserGroupInformation ugi, 699 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 700 final DelegationParam delegation, 701 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 702 final UserParam username, 703 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 704 final DoAsParam doAsUser, 705 @QueryParam(GetOpParam.NAME) @DefaultValue(GetOpParam.DEFAULT) 706 final GetOpParam op, 707 @QueryParam(OffsetParam.NAME) @DefaultValue(OffsetParam.DEFAULT) 708 final OffsetParam offset, 709 @QueryParam(LengthParam.NAME) @DefaultValue(LengthParam.DEFAULT) 710 final LengthParam length, 711 @QueryParam(RenewerParam.NAME) @DefaultValue(RenewerParam.DEFAULT) 712 final RenewerParam renewer, 713 @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT) 714 final BufferSizeParam bufferSize, 715 @QueryParam(XAttrNameParam.NAME) @DefaultValue(XAttrNameParam.DEFAULT) 716 final List<XAttrNameParam> xattrNames, 717 @QueryParam(XAttrEncodingParam.NAME) @DefaultValue(XAttrEncodingParam.DEFAULT) 718 final XAttrEncodingParam xattrEncoding 719 ) throws IOException, InterruptedException { 720 return get(ugi, delegation, username, doAsUser, ROOT, op, offset, length, 721 renewer, bufferSize, xattrNames, xattrEncoding); 722 } 723 724 /** Handle HTTP GET request. */ 725 @GET 726 @Path("{" + UriFsPathParam.NAME + ":.*}") 727 @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON}) 728 public Response get( 729 @Context final UserGroupInformation ugi, 730 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 731 final DelegationParam delegation, 732 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 733 final UserParam username, 734 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 735 final DoAsParam doAsUser, 736 @PathParam(UriFsPathParam.NAME) final UriFsPathParam path, 737 @QueryParam(GetOpParam.NAME) @DefaultValue(GetOpParam.DEFAULT) 738 final GetOpParam op, 739 @QueryParam(OffsetParam.NAME) @DefaultValue(OffsetParam.DEFAULT) 740 final OffsetParam offset, 741 @QueryParam(LengthParam.NAME) @DefaultValue(LengthParam.DEFAULT) 742 final LengthParam length, 743 @QueryParam(RenewerParam.NAME) @DefaultValue(RenewerParam.DEFAULT) 744 final RenewerParam renewer, 745 @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT) 746 final BufferSizeParam bufferSize, 747 @QueryParam(XAttrNameParam.NAME) @DefaultValue(XAttrNameParam.DEFAULT) 748 final List<XAttrNameParam> xattrNames, 749 @QueryParam(XAttrEncodingParam.NAME) @DefaultValue(XAttrEncodingParam.DEFAULT) 750 final XAttrEncodingParam xattrEncoding 751 ) throws IOException, InterruptedException { 752 753 init(ugi, delegation, username, doAsUser, path, op, offset, length, 754 renewer, bufferSize, xattrEncoding); 755 756 return ugi.doAs(new PrivilegedExceptionAction<Response>() { 757 @Override 758 public Response run() throws IOException, URISyntaxException { 759 try { 760 return get(ugi, delegation, username, doAsUser, 761 path.getAbsolutePath(), op, offset, length, renewer, bufferSize, 762 xattrNames, xattrEncoding); 763 } finally { 764 reset(); 765 } 766 } 767 }); 768 } 769 770 private Response get( 771 final UserGroupInformation ugi, 772 final DelegationParam delegation, 773 final UserParam username, 774 final DoAsParam doAsUser, 775 final String fullpath, 776 final GetOpParam op, 777 final OffsetParam offset, 778 final LengthParam length, 779 final RenewerParam renewer, 780 final BufferSizeParam bufferSize, 781 final List<XAttrNameParam> xattrNames, 782 final XAttrEncodingParam xattrEncoding 783 ) throws IOException, URISyntaxException { 784 final NameNode namenode = (NameNode)context.getAttribute("name.node"); 785 final NamenodeProtocols np = getRPCServer(namenode); 786 787 switch(op.getValue()) { 788 case OPEN: 789 { 790 final URI uri = redirectURI(namenode, ugi, delegation, username, doAsUser, 791 fullpath, op.getValue(), offset.getValue(), -1L, offset, length, bufferSize); 792 return Response.temporaryRedirect(uri).type(MediaType.APPLICATION_OCTET_STREAM).build(); 793 } 794 case GET_BLOCK_LOCATIONS: 795 { 796 final long offsetValue = offset.getValue(); 797 final Long lengthValue = length.getValue(); 798 final LocatedBlocks locatedblocks = np.getBlockLocations(fullpath, 799 offsetValue, lengthValue != null? lengthValue: Long.MAX_VALUE); 800 final String js = JsonUtil.toJsonString(locatedblocks); 801 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 802 } 803 case GETFILESTATUS: 804 { 805 final HdfsFileStatus status = np.getFileInfo(fullpath); 806 if (status == null) { 807 throw new FileNotFoundException("File does not exist: " + fullpath); 808 } 809 810 final String js = JsonUtil.toJsonString(status, true); 811 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 812 } 813 case LISTSTATUS: 814 { 815 final StreamingOutput streaming = getListingStream(np, fullpath); 816 return Response.ok(streaming).type(MediaType.APPLICATION_JSON).build(); 817 } 818 case GETCONTENTSUMMARY: 819 { 820 final ContentSummary contentsummary = np.getContentSummary(fullpath); 821 final String js = JsonUtil.toJsonString(contentsummary); 822 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 823 } 824 case GETFILECHECKSUM: 825 { 826 final URI uri = redirectURI(namenode, ugi, delegation, username, doAsUser, 827 fullpath, op.getValue(), -1L, -1L); 828 return Response.temporaryRedirect(uri).type(MediaType.APPLICATION_OCTET_STREAM).build(); 829 } 830 case GETDELEGATIONTOKEN: 831 { 832 if (delegation.getValue() != null) { 833 throw new IllegalArgumentException(delegation.getName() 834 + " parameter is not null."); 835 } 836 final Token<? extends TokenIdentifier> token = generateDelegationToken( 837 namenode, ugi, renewer.getValue()); 838 final String js = JsonUtil.toJsonString(token); 839 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 840 } 841 case GETHOMEDIRECTORY: 842 { 843 final String js = JsonUtil.toJsonString( 844 org.apache.hadoop.fs.Path.class.getSimpleName(), 845 WebHdfsFileSystem.getHomeDirectoryString(ugi)); 846 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 847 } 848 case GETACLSTATUS: { 849 AclStatus status = np.getAclStatus(fullpath); 850 if (status == null) { 851 throw new FileNotFoundException("File does not exist: " + fullpath); 852 } 853 854 final String js = JsonUtil.toJsonString(status); 855 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 856 } 857 case GETXATTRS: { 858 List<String> names = null; 859 if (xattrNames != null) { 860 names = Lists.newArrayListWithCapacity(xattrNames.size()); 861 for (XAttrNameParam xattrName : xattrNames) { 862 if (xattrName.getXAttrName() != null) { 863 names.add(xattrName.getXAttrName()); 864 } 865 } 866 } 867 List<XAttr> xAttrs = np.getXAttrs(fullpath, (names != null && 868 !names.isEmpty()) ? XAttrHelper.buildXAttrs(names) : null); 869 final String js = JsonUtil.toJsonString(xAttrs, 870 xattrEncoding.getEncoding()); 871 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 872 } 873 case LISTXATTRS: { 874 final List<XAttr> xAttrs = np.listXAttrs(fullpath); 875 final String js = JsonUtil.toJsonString(xAttrs); 876 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 877 } 878 default: 879 throw new UnsupportedOperationException(op + " is not supported"); 880 } 881 } 882 883 private static DirectoryListing getDirectoryListing(final NamenodeProtocols np, 884 final String p, byte[] startAfter) throws IOException { 885 final DirectoryListing listing = np.getListing(p, startAfter, false); 886 if (listing == null) { // the directory does not exist 887 throw new FileNotFoundException("File " + p + " does not exist."); 888 } 889 return listing; 890 } 891 892 private static StreamingOutput getListingStream(final NamenodeProtocols np, 893 final String p) throws IOException { 894 // allows exceptions like FNF or ACE to prevent http response of 200 for 895 // a failure since we can't (currently) return error responses in the 896 // middle of a streaming operation 897 final DirectoryListing firstDirList = getDirectoryListing(np, p, 898 HdfsFileStatus.EMPTY_NAME); 899 900 // must save ugi because the streaming object will be executed outside 901 // the remote user's ugi 902 final UserGroupInformation ugi = UserGroupInformation.getCurrentUser(); 903 return new StreamingOutput() { 904 @Override 905 public void write(final OutputStream outstream) throws IOException { 906 final PrintWriter out = new PrintWriter(new OutputStreamWriter( 907 outstream, Charsets.UTF_8)); 908 out.println("{\"" + FileStatus.class.getSimpleName() + "es\":{\"" 909 + FileStatus.class.getSimpleName() + "\":["); 910 911 try { 912 // restore remote user's ugi 913 ugi.doAs(new PrivilegedExceptionAction<Void>() { 914 @Override 915 public Void run() throws IOException { 916 long n = 0; 917 for (DirectoryListing dirList = firstDirList; ; 918 dirList = getDirectoryListing(np, p, dirList.getLastName()) 919 ) { 920 // send each segment of the directory listing 921 for (HdfsFileStatus s : dirList.getPartialListing()) { 922 if (n++ > 0) { 923 out.println(','); 924 } 925 out.print(JsonUtil.toJsonString(s, false)); 926 } 927 // stop if last segment 928 if (!dirList.hasMore()) { 929 break; 930 } 931 } 932 return null; 933 } 934 }); 935 } catch (InterruptedException e) { 936 throw new IOException(e); 937 } 938 939 out.println(); 940 out.println("]}}"); 941 out.flush(); 942 } 943 }; 944 } 945 946 /** Handle HTTP DELETE request for the root. */ 947 @DELETE 948 @Path("/") 949 @Produces(MediaType.APPLICATION_JSON) 950 public Response deleteRoot( 951 @Context final UserGroupInformation ugi, 952 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 953 final DelegationParam delegation, 954 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 955 final UserParam username, 956 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 957 final DoAsParam doAsUser, 958 @QueryParam(DeleteOpParam.NAME) @DefaultValue(DeleteOpParam.DEFAULT) 959 final DeleteOpParam op, 960 @QueryParam(RecursiveParam.NAME) @DefaultValue(RecursiveParam.DEFAULT) 961 final RecursiveParam recursive, 962 @QueryParam(SnapshotNameParam.NAME) @DefaultValue(SnapshotNameParam.DEFAULT) 963 final SnapshotNameParam snapshotName 964 ) throws IOException, InterruptedException { 965 return delete(ugi, delegation, username, doAsUser, ROOT, op, recursive, 966 snapshotName); 967 } 968 969 /** Handle HTTP DELETE request. */ 970 @DELETE 971 @Path("{" + UriFsPathParam.NAME + ":.*}") 972 @Produces(MediaType.APPLICATION_JSON) 973 public Response delete( 974 @Context final UserGroupInformation ugi, 975 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 976 final DelegationParam delegation, 977 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 978 final UserParam username, 979 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 980 final DoAsParam doAsUser, 981 @PathParam(UriFsPathParam.NAME) final UriFsPathParam path, 982 @QueryParam(DeleteOpParam.NAME) @DefaultValue(DeleteOpParam.DEFAULT) 983 final DeleteOpParam op, 984 @QueryParam(RecursiveParam.NAME) @DefaultValue(RecursiveParam.DEFAULT) 985 final RecursiveParam recursive, 986 @QueryParam(SnapshotNameParam.NAME) @DefaultValue(SnapshotNameParam.DEFAULT) 987 final SnapshotNameParam snapshotName 988 ) throws IOException, InterruptedException { 989 990 init(ugi, delegation, username, doAsUser, path, op, recursive, snapshotName); 991 992 return ugi.doAs(new PrivilegedExceptionAction<Response>() { 993 @Override 994 public Response run() throws IOException { 995 try { 996 return delete(ugi, delegation, username, doAsUser, 997 path.getAbsolutePath(), op, recursive, snapshotName); 998 } finally { 999 reset(); 1000 } 1001 } 1002 }); 1003 } 1004 1005 private Response delete( 1006 final UserGroupInformation ugi, 1007 final DelegationParam delegation, 1008 final UserParam username, 1009 final DoAsParam doAsUser, 1010 final String fullpath, 1011 final DeleteOpParam op, 1012 final RecursiveParam recursive, 1013 final SnapshotNameParam snapshotName 1014 ) throws IOException { 1015 final NameNode namenode = (NameNode)context.getAttribute("name.node"); 1016 final NamenodeProtocols np = getRPCServer(namenode); 1017 1018 switch(op.getValue()) { 1019 case DELETE: { 1020 final boolean b = np.delete(fullpath, recursive.getValue()); 1021 final String js = JsonUtil.toJsonString("boolean", b); 1022 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 1023 } 1024 case DELETESNAPSHOT: { 1025 np.deleteSnapshot(fullpath, snapshotName.getValue()); 1026 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 1027 } 1028 default: 1029 throw new UnsupportedOperationException(op + " is not supported"); 1030 } 1031 } 1032}