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.DFSUtil;
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      @QueryParam(NewLengthParam.NAME) @DefaultValue(NewLengthParam.DEFAULT)
619          final NewLengthParam newLength
620      ) throws IOException, InterruptedException {
621    return post(ugi, delegation, username, doAsUser, ROOT, op, concatSrcs,
622        bufferSize, excludeDatanodes, newLength);
623  }
624
625  /** Handle HTTP POST request. */
626  @POST
627  @Path("{" + UriFsPathParam.NAME + ":.*}")
628  @Consumes({"*/*"})
629  @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON})
630  public Response post(
631      @Context final UserGroupInformation ugi,
632      @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT)
633          final DelegationParam delegation,
634      @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT)
635          final UserParam username,
636      @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT)
637          final DoAsParam doAsUser,
638      @PathParam(UriFsPathParam.NAME) final UriFsPathParam path,
639      @QueryParam(PostOpParam.NAME) @DefaultValue(PostOpParam.DEFAULT)
640          final PostOpParam op,
641      @QueryParam(ConcatSourcesParam.NAME) @DefaultValue(ConcatSourcesParam.DEFAULT)
642          final ConcatSourcesParam concatSrcs,
643      @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT)
644          final BufferSizeParam bufferSize,
645      @QueryParam(ExcludeDatanodesParam.NAME) @DefaultValue(ExcludeDatanodesParam.DEFAULT)
646          final ExcludeDatanodesParam excludeDatanodes,
647      @QueryParam(NewLengthParam.NAME) @DefaultValue(NewLengthParam.DEFAULT)
648          final NewLengthParam newLength
649      ) throws IOException, InterruptedException {
650
651    init(ugi, delegation, username, doAsUser, path, op, concatSrcs, bufferSize,
652        excludeDatanodes, newLength);
653
654    return ugi.doAs(new PrivilegedExceptionAction<Response>() {
655      @Override
656      public Response run() throws IOException, URISyntaxException {
657        try {
658          return post(ugi, delegation, username, doAsUser,
659              path.getAbsolutePath(), op, concatSrcs, bufferSize,
660              excludeDatanodes, newLength);
661        } finally {
662          reset();
663        }
664      }
665    });
666  }
667
668  private Response post(
669      final UserGroupInformation ugi,
670      final DelegationParam delegation,
671      final UserParam username,
672      final DoAsParam doAsUser,
673      final String fullpath,
674      final PostOpParam op,
675      final ConcatSourcesParam concatSrcs,
676      final BufferSizeParam bufferSize,
677      final ExcludeDatanodesParam excludeDatanodes,
678      final NewLengthParam newLength
679      ) throws IOException, URISyntaxException {
680    final NameNode namenode = (NameNode)context.getAttribute("name.node");
681    final NamenodeProtocols np = getRPCServer(namenode);
682
683    switch(op.getValue()) {
684    case APPEND:
685    {
686      final URI uri = redirectURI(namenode, ugi, delegation, username,
687          doAsUser, fullpath, op.getValue(), -1L, -1L,
688          excludeDatanodes.getValue(), bufferSize);
689      return Response.temporaryRedirect(uri).type(MediaType.APPLICATION_OCTET_STREAM).build();
690    }
691    case CONCAT:
692    {
693      np.concat(fullpath, concatSrcs.getAbsolutePaths());
694      return Response.ok().build();
695    }
696    case TRUNCATE:
697    {
698      // We treat each rest request as a separate client.
699      final boolean b = np.truncate(fullpath, newLength.getValue(), 
700          "DFSClient_" + DFSUtil.getSecureRandom().nextLong());
701      final String js = JsonUtil.toJsonString("boolean", b);
702      return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
703    }
704    default:
705      throw new UnsupportedOperationException(op + " is not supported");
706    }
707  }
708
709  /** Handle HTTP GET request for the root. */
710  @GET
711  @Path("/")
712  @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON})
713  public Response getRoot(
714      @Context final UserGroupInformation ugi,
715      @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT)
716          final DelegationParam delegation,
717      @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT)
718          final UserParam username,
719      @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT)
720          final DoAsParam doAsUser,
721      @QueryParam(GetOpParam.NAME) @DefaultValue(GetOpParam.DEFAULT)
722          final GetOpParam op,
723      @QueryParam(OffsetParam.NAME) @DefaultValue(OffsetParam.DEFAULT)
724          final OffsetParam offset,
725      @QueryParam(LengthParam.NAME) @DefaultValue(LengthParam.DEFAULT)
726          final LengthParam length,
727      @QueryParam(RenewerParam.NAME) @DefaultValue(RenewerParam.DEFAULT)
728          final RenewerParam renewer,
729      @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT)
730          final BufferSizeParam bufferSize,
731      @QueryParam(XAttrNameParam.NAME) @DefaultValue(XAttrNameParam.DEFAULT) 
732          final List<XAttrNameParam> xattrNames,
733      @QueryParam(XAttrEncodingParam.NAME) @DefaultValue(XAttrEncodingParam.DEFAULT) 
734          final XAttrEncodingParam xattrEncoding,
735      @QueryParam(ExcludeDatanodesParam.NAME) @DefaultValue(ExcludeDatanodesParam.DEFAULT)
736          final ExcludeDatanodesParam excludeDatanodes,
737      @QueryParam(FsActionParam.NAME) @DefaultValue(FsActionParam.DEFAULT)
738          final FsActionParam fsAction,
739      @QueryParam(TokenKindParam.NAME) @DefaultValue(TokenKindParam.DEFAULT)
740          final TokenKindParam tokenKind,
741      @QueryParam(TokenServiceParam.NAME) @DefaultValue(TokenServiceParam.DEFAULT)
742          final TokenServiceParam tokenService
743      ) throws IOException, InterruptedException {
744    return get(ugi, delegation, username, doAsUser, ROOT, op, offset, length,
745        renewer, bufferSize, xattrNames, xattrEncoding, excludeDatanodes, fsAction,
746        tokenKind, tokenService);
747  }
748
749  /** Handle HTTP GET request. */
750  @GET
751  @Path("{" + UriFsPathParam.NAME + ":.*}")
752  @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON})
753  public Response get(
754      @Context final UserGroupInformation ugi,
755      @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT)
756          final DelegationParam delegation,
757      @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT)
758          final UserParam username,
759      @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT)
760          final DoAsParam doAsUser,
761      @PathParam(UriFsPathParam.NAME) final UriFsPathParam path,
762      @QueryParam(GetOpParam.NAME) @DefaultValue(GetOpParam.DEFAULT)
763          final GetOpParam op,
764      @QueryParam(OffsetParam.NAME) @DefaultValue(OffsetParam.DEFAULT)
765          final OffsetParam offset,
766      @QueryParam(LengthParam.NAME) @DefaultValue(LengthParam.DEFAULT)
767          final LengthParam length,
768      @QueryParam(RenewerParam.NAME) @DefaultValue(RenewerParam.DEFAULT)
769          final RenewerParam renewer,
770      @QueryParam(BufferSizeParam.NAME) @DefaultValue(BufferSizeParam.DEFAULT)
771          final BufferSizeParam bufferSize,
772      @QueryParam(XAttrNameParam.NAME) @DefaultValue(XAttrNameParam.DEFAULT) 
773          final List<XAttrNameParam> xattrNames,
774      @QueryParam(XAttrEncodingParam.NAME) @DefaultValue(XAttrEncodingParam.DEFAULT) 
775          final XAttrEncodingParam xattrEncoding,
776      @QueryParam(ExcludeDatanodesParam.NAME) @DefaultValue(ExcludeDatanodesParam.DEFAULT)
777          final ExcludeDatanodesParam excludeDatanodes,
778      @QueryParam(FsActionParam.NAME) @DefaultValue(FsActionParam.DEFAULT)
779          final FsActionParam fsAction,
780      @QueryParam(TokenKindParam.NAME) @DefaultValue(TokenKindParam.DEFAULT)
781          final TokenKindParam tokenKind,
782      @QueryParam(TokenServiceParam.NAME) @DefaultValue(TokenServiceParam.DEFAULT)
783          final TokenServiceParam tokenService
784      ) throws IOException, InterruptedException {
785
786    init(ugi, delegation, username, doAsUser, path, op, offset, length,
787        renewer, bufferSize, xattrEncoding, excludeDatanodes, fsAction,
788        tokenKind, tokenService);
789
790    return ugi.doAs(new PrivilegedExceptionAction<Response>() {
791      @Override
792      public Response run() throws IOException, URISyntaxException {
793        try {
794          return get(ugi, delegation, username, doAsUser,
795              path.getAbsolutePath(), op, offset, length, renewer, bufferSize,
796              xattrNames, xattrEncoding, excludeDatanodes, fsAction, tokenKind,
797              tokenService);
798        } finally {
799          reset();
800        }
801      }
802    });
803  }
804
805  private Response get(
806      final UserGroupInformation ugi,
807      final DelegationParam delegation,
808      final UserParam username,
809      final DoAsParam doAsUser,
810      final String fullpath,
811      final GetOpParam op,
812      final OffsetParam offset,
813      final LengthParam length,
814      final RenewerParam renewer,
815      final BufferSizeParam bufferSize,
816      final List<XAttrNameParam> xattrNames,
817      final XAttrEncodingParam xattrEncoding,
818      final ExcludeDatanodesParam excludeDatanodes,
819      final FsActionParam fsAction,
820      final TokenKindParam tokenKind,
821      final TokenServiceParam tokenService
822      ) throws IOException, URISyntaxException {
823    final NameNode namenode = (NameNode)context.getAttribute("name.node");
824    final NamenodeProtocols np = getRPCServer(namenode);
825
826    switch(op.getValue()) {
827    case OPEN:
828    {
829      final URI uri = redirectURI(namenode, ugi, delegation, username,
830          doAsUser, fullpath, op.getValue(), offset.getValue(), -1L,
831          excludeDatanodes.getValue(), offset, length, bufferSize);
832      return Response.temporaryRedirect(uri).type(MediaType.APPLICATION_OCTET_STREAM).build();
833    }
834    case GET_BLOCK_LOCATIONS:
835    {
836      final long offsetValue = offset.getValue();
837      final Long lengthValue = length.getValue();
838      final LocatedBlocks locatedblocks = np.getBlockLocations(fullpath,
839          offsetValue, lengthValue != null? lengthValue: Long.MAX_VALUE);
840      final String js = JsonUtil.toJsonString(locatedblocks);
841      return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
842    }
843    case GETFILESTATUS:
844    {
845      final HdfsFileStatus status = np.getFileInfo(fullpath);
846      if (status == null) {
847        throw new FileNotFoundException("File does not exist: " + fullpath);
848      }
849
850      final String js = JsonUtil.toJsonString(status, true);
851      return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
852    }
853    case LISTSTATUS:
854    {
855      final StreamingOutput streaming = getListingStream(np, fullpath);
856      return Response.ok(streaming).type(MediaType.APPLICATION_JSON).build();
857    }
858    case GETCONTENTSUMMARY:
859    {
860      final ContentSummary contentsummary = np.getContentSummary(fullpath);
861      final String js = JsonUtil.toJsonString(contentsummary);
862      return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
863    }
864    case GETFILECHECKSUM:
865    {
866      final URI uri = redirectURI(namenode, ugi, delegation, username, doAsUser,
867          fullpath, op.getValue(), -1L, -1L, null);
868      return Response.temporaryRedirect(uri).type(MediaType.APPLICATION_OCTET_STREAM).build();
869    }
870    case GETDELEGATIONTOKEN:
871    {
872      if (delegation.getValue() != null) {
873        throw new IllegalArgumentException(delegation.getName()
874            + " parameter is not null.");
875      }
876      final Token<? extends TokenIdentifier> token = generateDelegationToken(
877          namenode, ugi, renewer.getValue());
878
879      final String setServiceName = tokenService.getValue();
880      final String setKind = tokenKind.getValue();
881      if (setServiceName != null) {
882        token.setService(new Text(setServiceName));
883      }
884      if (setKind != null) {
885        token.setKind(new Text(setKind));
886      }
887      final String js = JsonUtil.toJsonString(token);
888      return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
889    }
890    case GETHOMEDIRECTORY:
891    {
892      final String js = JsonUtil.toJsonString(
893          org.apache.hadoop.fs.Path.class.getSimpleName(),
894          WebHdfsFileSystem.getHomeDirectoryString(ugi));
895      return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
896    }
897    case GETACLSTATUS: {
898      AclStatus status = np.getAclStatus(fullpath);
899      if (status == null) {
900        throw new FileNotFoundException("File does not exist: " + fullpath);
901      }
902
903      final String js = JsonUtil.toJsonString(status);
904      return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
905    }
906    case GETXATTRS: {
907      List<String> names = null;
908      if (xattrNames != null) {
909        names = Lists.newArrayListWithCapacity(xattrNames.size());
910        for (XAttrNameParam xattrName : xattrNames) {
911          if (xattrName.getXAttrName() != null) {
912            names.add(xattrName.getXAttrName());
913          }
914        }
915      }
916      List<XAttr> xAttrs = np.getXAttrs(fullpath, (names != null && 
917          !names.isEmpty()) ? XAttrHelper.buildXAttrs(names) : null);
918      final String js = JsonUtil.toJsonString(xAttrs,
919          xattrEncoding.getEncoding());
920      return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
921    }
922    case LISTXATTRS: {
923      final List<XAttr> xAttrs = np.listXAttrs(fullpath);
924      final String js = JsonUtil.toJsonString(xAttrs);
925      return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
926    }
927    case CHECKACCESS: {
928      np.checkAccess(fullpath, FsAction.getFsAction(fsAction.getValue()));
929      return Response.ok().build();
930    }
931    default:
932      throw new UnsupportedOperationException(op + " is not supported");
933    }
934  }
935
936  private static DirectoryListing getDirectoryListing(final NamenodeProtocols np,
937      final String p, byte[] startAfter) throws IOException {
938    final DirectoryListing listing = np.getListing(p, startAfter, false);
939    if (listing == null) { // the directory does not exist
940      throw new FileNotFoundException("File " + p + " does not exist.");
941    }
942    return listing;
943  }
944  
945  private static StreamingOutput getListingStream(final NamenodeProtocols np, 
946      final String p) throws IOException {
947    // allows exceptions like FNF or ACE to prevent http response of 200 for
948    // a failure since we can't (currently) return error responses in the
949    // middle of a streaming operation
950    final DirectoryListing firstDirList = getDirectoryListing(np, p,
951        HdfsFileStatus.EMPTY_NAME);
952
953    // must save ugi because the streaming object will be executed outside
954    // the remote user's ugi
955    final UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
956    return new StreamingOutput() {
957      @Override
958      public void write(final OutputStream outstream) throws IOException {
959        final PrintWriter out = new PrintWriter(new OutputStreamWriter(
960            outstream, Charsets.UTF_8));
961        out.println("{\"" + FileStatus.class.getSimpleName() + "es\":{\""
962            + FileStatus.class.getSimpleName() + "\":[");
963
964        try {
965          // restore remote user's ugi
966          ugi.doAs(new PrivilegedExceptionAction<Void>() {
967            @Override
968            public Void run() throws IOException {
969              long n = 0;
970              for (DirectoryListing dirList = firstDirList; ;
971                   dirList = getDirectoryListing(np, p, dirList.getLastName())
972              ) {
973                // send each segment of the directory listing
974                for (HdfsFileStatus s : dirList.getPartialListing()) {
975                  if (n++ > 0) {
976                    out.println(',');
977                  }
978                  out.print(JsonUtil.toJsonString(s, false));
979                }
980                // stop if last segment
981                if (!dirList.hasMore()) {
982                  break;
983                }
984              }
985              return null;
986            }
987          });
988        } catch (InterruptedException e) {
989          throw new IOException(e);
990        }
991        
992        out.println();
993        out.println("]}}");
994        out.flush();
995      }
996    };
997  }
998
999  /** Handle HTTP DELETE request for the root. */
1000  @DELETE
1001  @Path("/")
1002  @Produces(MediaType.APPLICATION_JSON)
1003  public Response deleteRoot(
1004      @Context final UserGroupInformation ugi,
1005      @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT)
1006          final DelegationParam delegation,
1007      @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT)
1008          final UserParam username,
1009      @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT)
1010          final DoAsParam doAsUser,
1011      @QueryParam(DeleteOpParam.NAME) @DefaultValue(DeleteOpParam.DEFAULT)
1012          final DeleteOpParam op,
1013      @QueryParam(RecursiveParam.NAME) @DefaultValue(RecursiveParam.DEFAULT)
1014          final RecursiveParam recursive,
1015      @QueryParam(SnapshotNameParam.NAME) @DefaultValue(SnapshotNameParam.DEFAULT)
1016          final SnapshotNameParam snapshotName
1017      ) throws IOException, InterruptedException {
1018    return delete(ugi, delegation, username, doAsUser, ROOT, op, recursive,
1019        snapshotName);
1020  }
1021
1022  /** Handle HTTP DELETE request. */
1023  @DELETE
1024  @Path("{" + UriFsPathParam.NAME + ":.*}")
1025  @Produces(MediaType.APPLICATION_JSON)
1026  public Response delete(
1027      @Context final UserGroupInformation ugi,
1028      @QueryParam(DelegationParam.NAME) @DefaultValue(DelegationParam.DEFAULT)
1029          final DelegationParam delegation,
1030      @QueryParam(UserParam.NAME) @DefaultValue(UserParam.DEFAULT)
1031          final UserParam username,
1032      @QueryParam(DoAsParam.NAME) @DefaultValue(DoAsParam.DEFAULT)
1033          final DoAsParam doAsUser,
1034      @PathParam(UriFsPathParam.NAME) final UriFsPathParam path,
1035      @QueryParam(DeleteOpParam.NAME) @DefaultValue(DeleteOpParam.DEFAULT)
1036          final DeleteOpParam op,
1037      @QueryParam(RecursiveParam.NAME) @DefaultValue(RecursiveParam.DEFAULT)
1038          final RecursiveParam recursive,
1039      @QueryParam(SnapshotNameParam.NAME) @DefaultValue(SnapshotNameParam.DEFAULT)
1040          final SnapshotNameParam snapshotName
1041      ) throws IOException, InterruptedException {
1042
1043    init(ugi, delegation, username, doAsUser, path, op, recursive, snapshotName);
1044
1045    return ugi.doAs(new PrivilegedExceptionAction<Response>() {
1046      @Override
1047      public Response run() throws IOException {
1048        try {
1049          return delete(ugi, delegation, username, doAsUser,
1050              path.getAbsolutePath(), op, recursive, snapshotName);
1051        } finally {
1052          reset();
1053        }
1054      }
1055    });
1056  }
1057
1058  private Response delete(
1059      final UserGroupInformation ugi,
1060      final DelegationParam delegation,
1061      final UserParam username,
1062      final DoAsParam doAsUser,
1063      final String fullpath,
1064      final DeleteOpParam op,
1065      final RecursiveParam recursive,
1066      final SnapshotNameParam snapshotName
1067      ) throws IOException {
1068    final NameNode namenode = (NameNode)context.getAttribute("name.node");
1069    final NamenodeProtocols np = getRPCServer(namenode);
1070
1071    switch(op.getValue()) {
1072    case DELETE: {
1073      final boolean b = np.delete(fullpath, recursive.getValue());
1074      final String js = JsonUtil.toJsonString("boolean", b);
1075      return Response.ok(js).type(MediaType.APPLICATION_JSON).build();
1076    }
1077    case DELETESNAPSHOT: {
1078      np.deleteSnapshot(fullpath, snapshotName.getValue());
1079      return Response.ok().type(MediaType.APPLICATION_OCTET_STREAM).build();
1080    }
1081    default:
1082      throw new UnsupportedOperationException(op + " is not supported");
1083    }
1084  }
1085}