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.PrintStream; 024import java.net.URI; 025import java.net.URISyntaxException; 026import java.security.PrivilegedExceptionAction; 027import java.util.EnumSet; 028 029import javax.servlet.ServletContext; 030import javax.servlet.http.HttpServletRequest; 031import javax.servlet.http.HttpServletResponse; 032import javax.ws.rs.Consumes; 033import javax.ws.rs.DELETE; 034import javax.ws.rs.DefaultValue; 035import javax.ws.rs.GET; 036import javax.ws.rs.POST; 037import javax.ws.rs.PUT; 038import javax.ws.rs.Path; 039import javax.ws.rs.PathParam; 040import javax.ws.rs.Produces; 041import javax.ws.rs.QueryParam; 042import javax.ws.rs.core.Context; 043import javax.ws.rs.core.MediaType; 044import javax.ws.rs.core.Response; 045import javax.ws.rs.core.StreamingOutput; 046 047import org.apache.commons.logging.Log; 048import org.apache.commons.logging.LogFactory; 049import org.apache.hadoop.conf.Configuration; 050import org.apache.hadoop.fs.ContentSummary; 051import org.apache.hadoop.fs.FileStatus; 052import org.apache.hadoop.fs.Options; 053import org.apache.hadoop.hdfs.protocol.DatanodeInfo; 054import org.apache.hadoop.hdfs.protocol.DirectoryListing; 055import org.apache.hadoop.hdfs.protocol.HdfsFileStatus; 056import org.apache.hadoop.hdfs.protocol.LocatedBlocks; 057import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier; 058import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenSecretManager; 059import org.apache.hadoop.hdfs.server.blockmanagement.DatanodeDescriptor; 060import org.apache.hadoop.hdfs.server.common.JspHelper; 061import org.apache.hadoop.hdfs.server.namenode.NameNode; 062import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocols; 063import org.apache.hadoop.hdfs.web.JsonUtil; 064import org.apache.hadoop.hdfs.web.ParamFilter; 065import org.apache.hadoop.hdfs.web.WebHdfsFileSystem; 066import org.apache.hadoop.hdfs.web.resources.AccessTimeParam; 067import org.apache.hadoop.hdfs.web.resources.BlockSizeParam; 068import org.apache.hadoop.hdfs.web.resources.BufferSizeParam; 069import org.apache.hadoop.hdfs.web.resources.CreateParentParam; 070import org.apache.hadoop.hdfs.web.resources.DelegationParam; 071import org.apache.hadoop.hdfs.web.resources.DeleteOpParam; 072import org.apache.hadoop.hdfs.web.resources.DestinationParam; 073import org.apache.hadoop.hdfs.web.resources.DoAsParam; 074import org.apache.hadoop.hdfs.web.resources.GetOpParam; 075import org.apache.hadoop.hdfs.web.resources.GroupParam; 076import org.apache.hadoop.hdfs.web.resources.HttpOpParam; 077import org.apache.hadoop.hdfs.web.resources.LengthParam; 078import org.apache.hadoop.hdfs.web.resources.ModificationTimeParam; 079import org.apache.hadoop.hdfs.web.resources.NamenodeRpcAddressParam; 080import org.apache.hadoop.hdfs.web.resources.OffsetParam; 081import org.apache.hadoop.hdfs.web.resources.OverwriteParam; 082import org.apache.hadoop.hdfs.web.resources.OwnerParam; 083import org.apache.hadoop.hdfs.web.resources.Param; 084import org.apache.hadoop.hdfs.web.resources.PermissionParam; 085import org.apache.hadoop.hdfs.web.resources.PostOpParam; 086import org.apache.hadoop.hdfs.web.resources.PutOpParam; 087import org.apache.hadoop.hdfs.web.resources.RecursiveParam; 088import org.apache.hadoop.hdfs.web.resources.RenameOptionSetParam; 089import org.apache.hadoop.hdfs.web.resources.RenewerParam; 090import org.apache.hadoop.hdfs.web.resources.ReplicationParam; 091import org.apache.hadoop.hdfs.web.resources.TokenArgumentParam; 092import org.apache.hadoop.hdfs.web.resources.UriFsPathParam; 093import org.apache.hadoop.hdfs.web.resources.UserParam; 094import org.apache.hadoop.net.NodeBase; 095import org.apache.hadoop.security.Credentials; 096import org.apache.hadoop.security.SecurityUtil; 097import org.apache.hadoop.security.UserGroupInformation; 098import org.apache.hadoop.security.token.Token; 099import org.apache.hadoop.security.token.TokenIdentifier; 100 101import com.sun.jersey.spi.container.ResourceFilters; 102 103/** Web-hdfs NameNode implementation. */ 104@Path("") 105@ResourceFilters(ParamFilter.class) 106public class NamenodeWebHdfsMethods { 107 public static final Log LOG = LogFactory.getLog(NamenodeWebHdfsMethods.class); 108 109 private static final UriFsPathParam ROOT = new UriFsPathParam(""); 110 111 private static final ThreadLocal<String> REMOTE_ADDRESS = new ThreadLocal<String>(); 112 113 /** @return the remote client address. */ 114 public static String getRemoteAddress() { 115 return REMOTE_ADDRESS.get(); 116 } 117 118 private @Context ServletContext context; 119 private @Context HttpServletRequest request; 120 private @Context HttpServletResponse response; 121 122 private void init(final UserGroupInformation ugi, 123 final DelegationParam delegation, 124 final UserParam username, final DoAsParam doAsUser, 125 final UriFsPathParam path, final HttpOpParam<?> op, 126 final Param<?, ?>... parameters) throws IOException { 127 if (LOG.isTraceEnabled()) { 128 LOG.trace("HTTP " + op.getValue().getType() + ": " + op + ", " + path 129 + ", ugi=" + ugi + ", " + username + ", " + doAsUser 130 + Param.toSortedString(", ", parameters)); 131 } 132 133 //clear content type 134 response.setContentType(null); 135 } 136 137 private static DatanodeInfo chooseDatanode(final NameNode namenode, 138 final String path, final HttpOpParam.Op op, final long openOffset, 139 Configuration conf) throws IOException { 140 if (op == GetOpParam.Op.OPEN 141 || op == GetOpParam.Op.GETFILECHECKSUM 142 || op == PostOpParam.Op.APPEND) { 143 final NamenodeProtocols np = namenode.getRpcServer(); 144 final HdfsFileStatus status = np.getFileInfo(path); 145 if (status == null) { 146 throw new FileNotFoundException("File " + path + " not found."); 147 } 148 final long len = status.getLen(); 149 if (op == GetOpParam.Op.OPEN) { 150 if (openOffset < 0L || (openOffset >= len && len > 0)) { 151 throw new IOException("Offset=" + openOffset 152 + " out of the range [0, " + len + "); " + op + ", path=" + path); 153 } 154 } 155 156 if (len > 0) { 157 final long offset = op == GetOpParam.Op.OPEN? openOffset: len - 1; 158 final LocatedBlocks locations = np.getBlockLocations(path, offset, 1); 159 final int count = locations.locatedBlockCount(); 160 if (count > 0) { 161 return JspHelper.bestNode(locations.get(0), conf); 162 } 163 } 164 } 165 166 return (DatanodeDescriptor)namenode.getNamesystem().getBlockManager( 167 ).getDatanodeManager().getNetworkTopology().chooseRandom( 168 NodeBase.ROOT); 169 } 170 171 private Token<? extends TokenIdentifier> generateDelegationToken( 172 final NameNode namenode, final UserGroupInformation ugi, 173 final String renewer) throws IOException { 174 final Credentials c = DelegationTokenSecretManager.createCredentials( 175 namenode, ugi, renewer != null? renewer: ugi.getShortUserName()); 176 final Token<? extends TokenIdentifier> t = c.getAllTokens().iterator().next(); 177 t.setKind(WebHdfsFileSystem.TOKEN_KIND); 178 SecurityUtil.setTokenService(t, namenode.getHttpAddress()); 179 return t; 180 } 181 182 private URI redirectURI(final NameNode namenode, 183 final UserGroupInformation ugi, final DelegationParam delegation, 184 final UserParam username, final DoAsParam doAsUser, 185 final String path, final HttpOpParam.Op op, final long openOffset, 186 final Param<?, ?>... parameters) throws URISyntaxException, IOException { 187 final Configuration conf = (Configuration)context.getAttribute(JspHelper.CURRENT_CONF); 188 final DatanodeInfo dn = chooseDatanode(namenode, path, op, openOffset, conf); 189 190 final String delegationQuery; 191 if (!UserGroupInformation.isSecurityEnabled()) { 192 //security disabled 193 delegationQuery = Param.toSortedString("&", doAsUser, username); 194 } else if (delegation.getValue() != null) { 195 //client has provided a token 196 delegationQuery = "&" + delegation; 197 } else { 198 //generate a token 199 final Token<? extends TokenIdentifier> t = generateDelegationToken( 200 namenode, ugi, request.getUserPrincipal().getName()); 201 delegationQuery = "&" + new DelegationParam(t.encodeToUrlString()); 202 } 203 final String query = op.toQueryString() + delegationQuery 204 + "&" + new NamenodeRpcAddressParam(namenode) 205 + Param.toSortedString("&", parameters); 206 final String uripath = WebHdfsFileSystem.PATH_PREFIX + path; 207 208 final URI uri = new URI("http", null, dn.getHostName(), dn.getInfoPort(), 209 uripath, query, null); 210 if (LOG.isTraceEnabled()) { 211 LOG.trace("redirectURI=" + uri); 212 } 213 return uri; 214 } 215 216 /** Handle HTTP PUT request for the root. */ 217 @PUT 218 @Path("/") 219 @Consumes({"*/*"}) 220 @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON}) 221 public Response putRoot( 222 @Context final UserGroupInformation ugi, 223 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 224 final DelegationParam delegation, 225 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 226 final UserParam username, 227 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 228 final DoAsParam doAsUser, 229 @QueryParam(PutOpParam.NAME) @DefaultValue(PutOpParam.DEFAULT) 230 final PutOpParam op, 231 @QueryParam(DestinationParam.NAME) @DefaultValue(DestinationParam.DEFAULT) 232 final DestinationParam destination, 233 @QueryParam(OwnerParam.NAME) @DefaultValue(OwnerParam.DEFAULT) 234 final OwnerParam owner, 235 @QueryParam(GroupParam.NAME) @DefaultValue(GroupParam.DEFAULT) 236 final GroupParam group, 237 @QueryParam(PermissionParam.NAME) @DefaultValue(PermissionParam.DEFAULT) 238 final PermissionParam permission, 239 @QueryParam(OverwriteParam.NAME) @DefaultValue(OverwriteParam.DEFAULT) 240 final OverwriteParam overwrite, 241 @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT) 242 final BufferSizeParam bufferSize, 243 @QueryParam(ReplicationParam.NAME) @DefaultValue(ReplicationParam.DEFAULT) 244 final ReplicationParam replication, 245 @QueryParam(BlockSizeParam.NAME) @DefaultValue(BlockSizeParam.DEFAULT) 246 final BlockSizeParam blockSize, 247 @QueryParam(ModificationTimeParam.NAME) @DefaultValue(ModificationTimeParam.DEFAULT) 248 final ModificationTimeParam modificationTime, 249 @QueryParam(AccessTimeParam.NAME) @DefaultValue(AccessTimeParam.DEFAULT) 250 final AccessTimeParam accessTime, 251 @QueryParam(RenameOptionSetParam.NAME) @DefaultValue(RenameOptionSetParam.DEFAULT) 252 final RenameOptionSetParam renameOptions, 253 @QueryParam(CreateParentParam.NAME) @DefaultValue(CreateParentParam.DEFAULT) 254 final CreateParentParam createParent, 255 @QueryParam(TokenArgumentParam.NAME) @DefaultValue(TokenArgumentParam.DEFAULT) 256 final TokenArgumentParam delegationTokenArgument 257 ) throws IOException, InterruptedException { 258 return put(ugi, delegation, username, doAsUser, ROOT, op, destination, 259 owner, group, permission, overwrite, bufferSize, replication, 260 blockSize, modificationTime, accessTime, renameOptions, createParent, 261 delegationTokenArgument); 262 } 263 264 /** Handle HTTP PUT request. */ 265 @PUT 266 @Path("{" + UriFsPathParam.NAME + ":.*}") 267 @Consumes({"*/*"}) 268 @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON}) 269 public Response put( 270 @Context final UserGroupInformation ugi, 271 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 272 final DelegationParam delegation, 273 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 274 final UserParam username, 275 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 276 final DoAsParam doAsUser, 277 @PathParam(UriFsPathParam.NAME) final UriFsPathParam path, 278 @QueryParam(PutOpParam.NAME) @DefaultValue(PutOpParam.DEFAULT) 279 final PutOpParam op, 280 @QueryParam(DestinationParam.NAME) @DefaultValue(DestinationParam.DEFAULT) 281 final DestinationParam destination, 282 @QueryParam(OwnerParam.NAME) @DefaultValue(OwnerParam.DEFAULT) 283 final OwnerParam owner, 284 @QueryParam(GroupParam.NAME) @DefaultValue(GroupParam.DEFAULT) 285 final GroupParam group, 286 @QueryParam(PermissionParam.NAME) @DefaultValue(PermissionParam.DEFAULT) 287 final PermissionParam permission, 288 @QueryParam(OverwriteParam.NAME) @DefaultValue(OverwriteParam.DEFAULT) 289 final OverwriteParam overwrite, 290 @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT) 291 final BufferSizeParam bufferSize, 292 @QueryParam(ReplicationParam.NAME) @DefaultValue(ReplicationParam.DEFAULT) 293 final ReplicationParam replication, 294 @QueryParam(BlockSizeParam.NAME) @DefaultValue(BlockSizeParam.DEFAULT) 295 final BlockSizeParam blockSize, 296 @QueryParam(ModificationTimeParam.NAME) @DefaultValue(ModificationTimeParam.DEFAULT) 297 final ModificationTimeParam modificationTime, 298 @QueryParam(AccessTimeParam.NAME) @DefaultValue(AccessTimeParam.DEFAULT) 299 final AccessTimeParam accessTime, 300 @QueryParam(RenameOptionSetParam.NAME) @DefaultValue(RenameOptionSetParam.DEFAULT) 301 final RenameOptionSetParam renameOptions, 302 @QueryParam(CreateParentParam.NAME) @DefaultValue(CreateParentParam.DEFAULT) 303 final CreateParentParam createParent, 304 @QueryParam(TokenArgumentParam.NAME) @DefaultValue(TokenArgumentParam.DEFAULT) 305 final TokenArgumentParam delegationTokenArgument 306 ) throws IOException, InterruptedException { 307 308 init(ugi, delegation, username, doAsUser, path, op, destination, owner, 309 group, permission, overwrite, bufferSize, replication, blockSize, 310 modificationTime, accessTime, renameOptions, delegationTokenArgument); 311 312 return ugi.doAs(new PrivilegedExceptionAction<Response>() { 313 @Override 314 public Response run() throws IOException, URISyntaxException { 315 REMOTE_ADDRESS.set(request.getRemoteAddr()); 316 try { 317 318 final String fullpath = path.getAbsolutePath(); 319 final Configuration conf = (Configuration)context.getAttribute(JspHelper.CURRENT_CONF); 320 final NameNode namenode = (NameNode)context.getAttribute("name.node"); 321 final NamenodeProtocols np = namenode.getRpcServer(); 322 323 switch(op.getValue()) { 324 case CREATE: 325 { 326 final URI uri = redirectURI(namenode, ugi, delegation, username, doAsUser, 327 fullpath, op.getValue(), -1L, 328 permission, overwrite, bufferSize, replication, blockSize); 329 return Response.temporaryRedirect(uri).type(MediaType.APPLICATION_OCTET_STREAM).build(); 330 } 331 case MKDIRS: 332 { 333 final boolean b = np.mkdirs(fullpath, permission.getFsPermission(), true); 334 final String js = JsonUtil.toJsonString("boolean", b); 335 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 336 } 337 case CREATESYMLINK: 338 { 339 np.createSymlink(destination.getValue(), fullpath, 340 PermissionParam.getDefaultFsPermission(), createParent.getValue()); 341 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 342 } 343 case RENAME: 344 { 345 final EnumSet<Options.Rename> s = renameOptions.getValue(); 346 if (s.isEmpty()) { 347 final boolean b = np.rename(fullpath, destination.getValue()); 348 final String js = JsonUtil.toJsonString("boolean", b); 349 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 350 } else { 351 np.rename2(fullpath, destination.getValue(), 352 s.toArray(new Options.Rename[s.size()])); 353 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 354 } 355 } 356 case SETREPLICATION: 357 { 358 final boolean b = np.setReplication(fullpath, replication.getValue(conf)); 359 final String js = JsonUtil.toJsonString("boolean", b); 360 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 361 } 362 case SETOWNER: 363 { 364 if (owner.getValue() == null && group.getValue() == null) { 365 throw new IllegalArgumentException("Both owner and group are empty."); 366 } 367 368 np.setOwner(fullpath, owner.getValue(), group.getValue()); 369 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 370 } 371 case SETPERMISSION: 372 { 373 np.setPermission(fullpath, permission.getFsPermission()); 374 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 375 } 376 case SETTIMES: 377 { 378 np.setTimes(fullpath, modificationTime.getValue(), accessTime.getValue()); 379 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 380 } 381 case RENEWDELEGATIONTOKEN: 382 { 383 final Token<DelegationTokenIdentifier> token = new Token<DelegationTokenIdentifier>(); 384 token.decodeFromUrlString(delegationTokenArgument.getValue()); 385 final long expiryTime = np.renewDelegationToken(token); 386 final String js = JsonUtil.toJsonString("long", expiryTime); 387 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 388 } 389 case CANCELDELEGATIONTOKEN: 390 { 391 final Token<DelegationTokenIdentifier> token = new Token<DelegationTokenIdentifier>(); 392 token.decodeFromUrlString(delegationTokenArgument.getValue()); 393 np.cancelDelegationToken(token); 394 return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build(); 395 } 396 default: 397 throw new UnsupportedOperationException(op + " is not supported"); 398 } 399 400 } finally { 401 REMOTE_ADDRESS.set(null); 402 } 403 } 404 }); 405 } 406 407 /** Handle HTTP POST request for the root. */ 408 @POST 409 @Path("/") 410 @Consumes({"*/*"}) 411 @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON}) 412 public Response postRoot( 413 @Context final UserGroupInformation ugi, 414 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 415 final DelegationParam delegation, 416 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 417 final UserParam username, 418 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 419 final DoAsParam doAsUser, 420 @QueryParam(PostOpParam.NAME) @DefaultValue(PostOpParam.DEFAULT) 421 final PostOpParam op, 422 @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT) 423 final BufferSizeParam bufferSize 424 ) throws IOException, InterruptedException { 425 return post(ugi, delegation, username, doAsUser, ROOT, op, bufferSize); 426 } 427 428 /** Handle HTTP POST request. */ 429 @POST 430 @Path("{" + UriFsPathParam.NAME + ":.*}") 431 @Consumes({"*/*"}) 432 @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON}) 433 public Response post( 434 @Context final UserGroupInformation ugi, 435 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 436 final DelegationParam delegation, 437 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 438 final UserParam username, 439 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 440 final DoAsParam doAsUser, 441 @PathParam(UriFsPathParam.NAME) final UriFsPathParam path, 442 @QueryParam(PostOpParam.NAME) @DefaultValue(PostOpParam.DEFAULT) 443 final PostOpParam op, 444 @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT) 445 final BufferSizeParam bufferSize 446 ) throws IOException, InterruptedException { 447 448 init(ugi, delegation, username, doAsUser, path, op, bufferSize); 449 450 return ugi.doAs(new PrivilegedExceptionAction<Response>() { 451 @Override 452 public Response run() throws IOException, URISyntaxException { 453 REMOTE_ADDRESS.set(request.getRemoteAddr()); 454 try { 455 456 final String fullpath = path.getAbsolutePath(); 457 final NameNode namenode = (NameNode)context.getAttribute("name.node"); 458 459 switch(op.getValue()) { 460 case APPEND: 461 { 462 final URI uri = redirectURI(namenode, ugi, delegation, username, doAsUser, 463 fullpath, op.getValue(), -1L, bufferSize); 464 return Response.temporaryRedirect(uri).type(MediaType.APPLICATION_OCTET_STREAM).build(); 465 } 466 default: 467 throw new UnsupportedOperationException(op + " is not supported"); 468 } 469 470 } finally { 471 REMOTE_ADDRESS.set(null); 472 } 473 } 474 }); 475 } 476 477 /** Handle HTTP GET request for the root. */ 478 @GET 479 @Path("/") 480 @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON}) 481 public Response getRoot( 482 @Context final UserGroupInformation ugi, 483 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 484 final DelegationParam delegation, 485 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 486 final UserParam username, 487 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 488 final DoAsParam doAsUser, 489 @QueryParam(GetOpParam.NAME) @DefaultValue(GetOpParam.DEFAULT) 490 final GetOpParam op, 491 @QueryParam(OffsetParam.NAME) @DefaultValue(OffsetParam.DEFAULT) 492 final OffsetParam offset, 493 @QueryParam(LengthParam.NAME) @DefaultValue(LengthParam.DEFAULT) 494 final LengthParam length, 495 @QueryParam(RenewerParam.NAME) @DefaultValue(RenewerParam.DEFAULT) 496 final RenewerParam renewer, 497 @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT) 498 final BufferSizeParam bufferSize 499 ) throws IOException, URISyntaxException, InterruptedException { 500 return get(ugi, delegation, username, doAsUser, ROOT, op, 501 offset, length, renewer, bufferSize); 502 } 503 504 /** Handle HTTP GET request. */ 505 @GET 506 @Path("{" + UriFsPathParam.NAME + ":.*}") 507 @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON}) 508 public Response get( 509 @Context final UserGroupInformation ugi, 510 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 511 final DelegationParam delegation, 512 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 513 final UserParam username, 514 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 515 final DoAsParam doAsUser, 516 @PathParam(UriFsPathParam.NAME) final UriFsPathParam path, 517 @QueryParam(GetOpParam.NAME) @DefaultValue(GetOpParam.DEFAULT) 518 final GetOpParam op, 519 @QueryParam(OffsetParam.NAME) @DefaultValue(OffsetParam.DEFAULT) 520 final OffsetParam offset, 521 @QueryParam(LengthParam.NAME) @DefaultValue(LengthParam.DEFAULT) 522 final LengthParam length, 523 @QueryParam(RenewerParam.NAME) @DefaultValue(RenewerParam.DEFAULT) 524 final RenewerParam renewer, 525 @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT) 526 final BufferSizeParam bufferSize 527 ) throws IOException, InterruptedException { 528 529 init(ugi, delegation, username, doAsUser, path, op, 530 offset, length, renewer, bufferSize); 531 532 return ugi.doAs(new PrivilegedExceptionAction<Response>() { 533 @Override 534 public Response run() throws IOException, URISyntaxException { 535 REMOTE_ADDRESS.set(request.getRemoteAddr()); 536 try { 537 538 final NameNode namenode = (NameNode)context.getAttribute("name.node"); 539 final String fullpath = path.getAbsolutePath(); 540 final NamenodeProtocols np = namenode.getRpcServer(); 541 542 switch(op.getValue()) { 543 case OPEN: 544 { 545 final URI uri = redirectURI(namenode, ugi, delegation, username, doAsUser, 546 fullpath, op.getValue(), offset.getValue(), offset, length, bufferSize); 547 return Response.temporaryRedirect(uri).type(MediaType.APPLICATION_OCTET_STREAM).build(); 548 } 549 case GET_BLOCK_LOCATIONS: 550 { 551 final long offsetValue = offset.getValue(); 552 final Long lengthValue = length.getValue(); 553 final LocatedBlocks locatedblocks = np.getBlockLocations(fullpath, 554 offsetValue, lengthValue != null? lengthValue: Long.MAX_VALUE); 555 final String js = JsonUtil.toJsonString(locatedblocks); 556 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 557 } 558 case GETFILESTATUS: 559 { 560 final HdfsFileStatus status = np.getFileInfo(fullpath); 561 if (status == null) { 562 throw new FileNotFoundException("File does not exist: " + fullpath); 563 } 564 565 final String js = JsonUtil.toJsonString(status, true); 566 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 567 } 568 case LISTSTATUS: 569 { 570 final StreamingOutput streaming = getListingStream(np, fullpath); 571 return Response.ok(streaming).type(MediaType.APPLICATION_JSON).build(); 572 } 573 case GETCONTENTSUMMARY: 574 { 575 final ContentSummary contentsummary = np.getContentSummary(fullpath); 576 final String js = JsonUtil.toJsonString(contentsummary); 577 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 578 } 579 case GETFILECHECKSUM: 580 { 581 final URI uri = redirectURI(namenode, ugi, delegation, username, doAsUser, 582 fullpath, op.getValue(), -1L); 583 return Response.temporaryRedirect(uri).type(MediaType.APPLICATION_OCTET_STREAM).build(); 584 } 585 case GETDELEGATIONTOKEN: 586 { 587 if (delegation.getValue() != null) { 588 throw new IllegalArgumentException(delegation.getName() 589 + " parameter is not null."); 590 } 591 final Token<? extends TokenIdentifier> token = generateDelegationToken( 592 namenode, ugi, renewer.getValue()); 593 final String js = JsonUtil.toJsonString(token); 594 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 595 } 596 case GETHOMEDIRECTORY: 597 { 598 final String js = JsonUtil.toJsonString( 599 org.apache.hadoop.fs.Path.class.getSimpleName(), 600 WebHdfsFileSystem.getHomeDirectoryString(ugi)); 601 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 602 } 603 default: 604 throw new UnsupportedOperationException(op + " is not supported"); 605 } 606 607 } finally { 608 REMOTE_ADDRESS.set(null); 609 } 610 } 611 }); 612 } 613 614 private static DirectoryListing getDirectoryListing(final NamenodeProtocols np, 615 final String p, byte[] startAfter) throws IOException { 616 final DirectoryListing listing = np.getListing(p, startAfter, false); 617 if (listing == null) { // the directory does not exist 618 throw new FileNotFoundException("File " + p + " does not exist."); 619 } 620 return listing; 621 } 622 623 private static StreamingOutput getListingStream(final NamenodeProtocols np, 624 final String p) throws IOException { 625 // allows exceptions like FNF or ACE to prevent http response of 200 for 626 // a failure since we can't (currently) return error responses in the 627 // middle of a streaming operation 628 final DirectoryListing firstDirList = getDirectoryListing(np, p, 629 HdfsFileStatus.EMPTY_NAME); 630 631 // must save ugi because the streaming object will be executed outside 632 // the remote user's ugi 633 final UserGroupInformation ugi = UserGroupInformation.getCurrentUser(); 634 return new StreamingOutput() { 635 @Override 636 public void write(final OutputStream outstream) throws IOException { 637 final PrintStream out = new PrintStream(outstream); 638 out.println("{\"" + FileStatus.class.getSimpleName() + "es\":{\"" 639 + FileStatus.class.getSimpleName() + "\":["); 640 641 try { 642 // restore remote user's ugi 643 ugi.doAs(new PrivilegedExceptionAction<Void>() { 644 @Override 645 public Void run() throws IOException { 646 long n = 0; 647 for (DirectoryListing dirList = firstDirList; ; 648 dirList = getDirectoryListing(np, p, dirList.getLastName()) 649 ) { 650 // send each segment of the directory listing 651 for (HdfsFileStatus s : dirList.getPartialListing()) { 652 if (n++ > 0) { 653 out.println(','); 654 } 655 out.print(JsonUtil.toJsonString(s, false)); 656 } 657 // stop if last segment 658 if (!dirList.hasMore()) { 659 break; 660 } 661 } 662 return null; 663 } 664 }); 665 } catch (InterruptedException e) { 666 throw new IOException(e); 667 } 668 669 out.println(); 670 out.println("]}}"); 671 } 672 }; 673 } 674 675 /** Handle HTTP DELETE request for the root. */ 676 @DELETE 677 @Path("/") 678 @Produces(MediaType.APPLICATION_JSON) 679 public Response deleteRoot( 680 @Context final UserGroupInformation ugi, 681 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 682 final DelegationParam delegation, 683 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 684 final UserParam username, 685 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 686 final DoAsParam doAsUser, 687 @QueryParam(DeleteOpParam.NAME) @DefaultValue(DeleteOpParam.DEFAULT) 688 final DeleteOpParam op, 689 @QueryParam(RecursiveParam.NAME) @DefaultValue(RecursiveParam.DEFAULT) 690 final RecursiveParam recursive 691 ) throws IOException, InterruptedException { 692 return delete(ugi, delegation, username, doAsUser, ROOT, op, recursive); 693 } 694 695 /** Handle HTTP DELETE request. */ 696 @DELETE 697 @Path("{" + UriFsPathParam.NAME + ":.*}") 698 @Produces(MediaType.APPLICATION_JSON) 699 public Response delete( 700 @Context final UserGroupInformation ugi, 701 @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT) 702 final DelegationParam delegation, 703 @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT) 704 final UserParam username, 705 @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT) 706 final DoAsParam doAsUser, 707 @PathParam(UriFsPathParam.NAME) final UriFsPathParam path, 708 @QueryParam(DeleteOpParam.NAME) @DefaultValue(DeleteOpParam.DEFAULT) 709 final DeleteOpParam op, 710 @QueryParam(RecursiveParam.NAME) @DefaultValue(RecursiveParam.DEFAULT) 711 final RecursiveParam recursive 712 ) throws IOException, InterruptedException { 713 714 init(ugi, delegation, username, doAsUser, path, op, recursive); 715 716 return ugi.doAs(new PrivilegedExceptionAction<Response>() { 717 @Override 718 public Response run() throws IOException { 719 REMOTE_ADDRESS.set(request.getRemoteAddr()); 720 try { 721 722 final NameNode namenode = (NameNode)context.getAttribute("name.node"); 723 final String fullpath = path.getAbsolutePath(); 724 725 switch(op.getValue()) { 726 case DELETE: 727 { 728 final boolean b = namenode.getRpcServer().delete(fullpath, recursive.getValue()); 729 final String js = JsonUtil.toJsonString("boolean", b); 730 return Response.ok(js).type(MediaType.APPLICATION_JSON).build(); 731 } 732 default: 733 throw new UnsupportedOperationException(op + " is not supported"); 734 } 735 736 } finally { 737 REMOTE_ADDRESS.set(null); 738 } 739 } 740 }); 741 } 742}