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