001    /**
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    package org.apache.hadoop.hdfs.server.namenode.web.resources;
019    
020    import java.io.FileNotFoundException;
021    import java.io.IOException;
022    import java.io.OutputStream;
023    import java.io.OutputStreamWriter;
024    import java.io.PrintWriter;
025    import java.net.InetAddress;
026    import java.net.URI;
027    import java.net.URISyntaxException;
028    import java.security.PrivilegedExceptionAction;
029    import java.util.EnumSet;
030    import java.util.HashSet;
031    import java.util.List;
032    
033    import javax.servlet.ServletContext;
034    import javax.servlet.http.HttpServletRequest;
035    import javax.servlet.http.HttpServletResponse;
036    import javax.ws.rs.Consumes;
037    import javax.ws.rs.DELETE;
038    import javax.ws.rs.DefaultValue;
039    import javax.ws.rs.GET;
040    import javax.ws.rs.POST;
041    import javax.ws.rs.PUT;
042    import javax.ws.rs.Path;
043    import javax.ws.rs.PathParam;
044    import javax.ws.rs.Produces;
045    import javax.ws.rs.QueryParam;
046    import javax.ws.rs.core.Context;
047    import javax.ws.rs.core.MediaType;
048    import javax.ws.rs.core.Response;
049    import javax.ws.rs.core.StreamingOutput;
050    
051    import org.apache.commons.logging.Log;
052    import org.apache.commons.logging.LogFactory;
053    import org.apache.hadoop.conf.Configuration;
054    import org.apache.hadoop.fs.ContentSummary;
055    import org.apache.hadoop.fs.FileStatus;
056    import org.apache.hadoop.fs.Options;
057    import org.apache.hadoop.fs.XAttr;
058    import org.apache.hadoop.fs.permission.AclStatus;
059    import org.apache.hadoop.fs.permission.FsAction;
060    import org.apache.hadoop.hdfs.StorageType;
061    import org.apache.hadoop.hdfs.XAttrHelper;
062    import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
063    import org.apache.hadoop.hdfs.protocol.DirectoryListing;
064    import org.apache.hadoop.hdfs.protocol.HdfsFileStatus;
065    import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
066    import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenIdentifier;
067    import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenSecretManager;
068    import org.apache.hadoop.hdfs.server.blockmanagement.BlockManager;
069    import org.apache.hadoop.hdfs.server.blockmanagement.DatanodeDescriptor;
070    import org.apache.hadoop.hdfs.server.blockmanagement.DatanodeStorageInfo;
071    import org.apache.hadoop.hdfs.server.common.JspHelper;
072    import org.apache.hadoop.hdfs.server.namenode.NameNode;
073    import org.apache.hadoop.hdfs.server.protocol.NamenodeProtocols;
074    import org.apache.hadoop.hdfs.web.JsonUtil;
075    import org.apache.hadoop.hdfs.web.ParamFilter;
076    import org.apache.hadoop.hdfs.web.SWebHdfsFileSystem;
077    import org.apache.hadoop.hdfs.web.WebHdfsFileSystem;
078    import org.apache.hadoop.hdfs.web.resources.*;
079    import org.apache.hadoop.io.Text;
080    import org.apache.hadoop.ipc.RetriableException;
081    import org.apache.hadoop.ipc.Server;
082    import org.apache.hadoop.net.NetworkTopology.InvalidTopologyException;
083    import org.apache.hadoop.net.Node;
084    import org.apache.hadoop.net.NodeBase;
085    import org.apache.hadoop.security.Credentials;
086    import org.apache.hadoop.security.UserGroupInformation;
087    import org.apache.hadoop.security.token.Token;
088    import org.apache.hadoop.security.token.TokenIdentifier;
089    import org.apache.hadoop.util.StringUtils;
090    
091    import com.google.common.annotations.VisibleForTesting;
092    import com.google.common.base.Charsets;
093    import com.google.common.collect.Lists;
094    import com.sun.jersey.spi.container.ResourceFilters;
095    
096    /** Web-hdfs NameNode implementation. */
097    @Path("")
098    @ResourceFilters(ParamFilter.class)
099    public 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    }