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