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