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