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