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
019package org.apache.hadoop.fs.http.server;
020
021import org.apache.hadoop.classification.InterfaceAudience;
022import org.apache.hadoop.conf.Configuration;
023import org.apache.hadoop.fs.FileSystem;
024import org.apache.hadoop.fs.XAttrCodec;
025import org.apache.hadoop.fs.XAttrSetFlag;
026import org.apache.hadoop.fs.http.client.HttpFSFileSystem;
027import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.AccessTimeParam;
028import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.AclPermissionParam;
029import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.BlockSizeParam;
030import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.DataParam;
031import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.DestinationParam;
032import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.FilterParam;
033import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.GroupParam;
034import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.LenParam;
035import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.ModifiedTimeParam;
036import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.NewLengthParam;
037import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.OffsetParam;
038import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.OperationParam;
039import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.OverwriteParam;
040import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.OwnerParam;
041import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.PermissionParam;
042import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.RecursiveParam;
043import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.ReplicationParam;
044import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.SourcesParam;
045import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.XAttrEncodingParam;
046import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.XAttrNameParam;
047import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.XAttrSetFlagParam;
048import org.apache.hadoop.fs.http.server.HttpFSParametersProvider.XAttrValueParam;
049import org.apache.hadoop.lib.service.FileSystemAccess;
050import org.apache.hadoop.lib.service.FileSystemAccessException;
051import org.apache.hadoop.lib.service.Groups;
052import org.apache.hadoop.lib.service.Instrumentation;
053import org.apache.hadoop.lib.servlet.FileSystemReleaseFilter;
054import org.apache.hadoop.lib.wsrs.InputStreamEntity;
055import org.apache.hadoop.lib.wsrs.Parameters;
056import org.apache.hadoop.security.UserGroupInformation;
057import org.apache.hadoop.security.token.delegation.web.HttpUserGroupInformation;
058import org.json.simple.JSONObject;
059import org.slf4j.Logger;
060import org.slf4j.LoggerFactory;
061import org.slf4j.MDC;
062
063import javax.servlet.http.HttpServletRequest;
064import javax.ws.rs.Consumes;
065import javax.ws.rs.DELETE;
066import javax.ws.rs.GET;
067import javax.ws.rs.POST;
068import javax.ws.rs.PUT;
069import javax.ws.rs.Path;
070import javax.ws.rs.PathParam;
071import javax.ws.rs.Produces;
072import javax.ws.rs.QueryParam;
073import javax.ws.rs.core.Context;
074import javax.ws.rs.core.MediaType;
075import javax.ws.rs.core.Response;
076import javax.ws.rs.core.UriBuilder;
077import javax.ws.rs.core.UriInfo;
078import java.io.IOException;
079import java.io.InputStream;
080import java.net.URI;
081import java.security.AccessControlException;
082import java.text.MessageFormat;
083import java.util.EnumSet;
084import java.util.List;
085import java.util.Map;
086
087/**
088 * Main class of HttpFSServer server.
089 * <p>
090 * The <code>HttpFSServer</code> class uses Jersey JAX-RS to binds HTTP requests to the
091 * different operations.
092 */
093@Path(HttpFSFileSystem.SERVICE_VERSION)
094@InterfaceAudience.Private
095public class HttpFSServer {
096  private static Logger AUDIT_LOG = LoggerFactory.getLogger("httpfsaudit");
097
098  /**
099   * Executes a {@link FileSystemAccess.FileSystemExecutor} using a filesystem for the effective
100   * user.
101   *
102   * @param ugi user making the request.
103   * @param executor FileSystemExecutor to execute.
104   *
105   * @return FileSystemExecutor response
106   *
107   * @throws IOException thrown if an IO error occurrs.
108   * @throws FileSystemAccessException thrown if a FileSystemAccess releated error occurred. Thrown
109   * exceptions are handled by {@link HttpFSExceptionProvider}.
110   */
111  private <T> T fsExecute(UserGroupInformation ugi, FileSystemAccess.FileSystemExecutor<T> executor)
112    throws IOException, FileSystemAccessException {
113    FileSystemAccess fsAccess = HttpFSServerWebApp.get().get(FileSystemAccess.class);
114    Configuration conf = HttpFSServerWebApp.get().get(FileSystemAccess.class).getFileSystemConfiguration();
115    return fsAccess.execute(ugi.getShortUserName(), conf, executor);
116  }
117
118  /**
119   * Returns a filesystem instance. The fileystem instance is wired for release at the completion of
120   * the current Servlet request via the {@link FileSystemReleaseFilter}.
121   * <p>
122   * If a do-as user is specified, the current user must be a valid proxyuser, otherwise an
123   * <code>AccessControlException</code> will be thrown.
124   *
125   * @param ugi principal for whom the filesystem instance is.
126   *
127   * @return a filesystem for the specified user or do-as user.
128   *
129   * @throws IOException thrown if an IO error occurred. Thrown exceptions are
130   * handled by {@link HttpFSExceptionProvider}.
131   * @throws FileSystemAccessException thrown if a FileSystemAccess releated error occurred. Thrown
132   * exceptions are handled by {@link HttpFSExceptionProvider}.
133   */
134  private FileSystem createFileSystem(UserGroupInformation ugi)
135      throws IOException, FileSystemAccessException {
136    String hadoopUser = ugi.getShortUserName();
137    FileSystemAccess fsAccess = HttpFSServerWebApp.get().get(FileSystemAccess.class);
138    Configuration conf = HttpFSServerWebApp.get().get(FileSystemAccess.class).getFileSystemConfiguration();
139    FileSystem fs = fsAccess.createFileSystem(hadoopUser, conf);
140    FileSystemReleaseFilter.setFileSystem(fs);
141    return fs;
142  }
143
144  private void enforceRootPath(HttpFSFileSystem.Operation op, String path) {
145    if (!path.equals("/")) {
146      throw new UnsupportedOperationException(
147        MessageFormat.format("Operation [{0}], invalid path [{1}], must be '/'",
148                             op, path));
149    }
150  }
151
152  /**
153   * Special binding for '/' as it is not handled by the wildcard binding.
154   *
155   * @param op the HttpFS operation of the request.
156   * @param params the HttpFS parameters of the request.
157   *
158   * @return the request response.
159   *
160   * @throws IOException thrown if an IO error occurred. Thrown exceptions are
161   * handled by {@link HttpFSExceptionProvider}.
162   * @throws FileSystemAccessException thrown if a FileSystemAccess releated
163   * error occurred. Thrown exceptions are handled by
164   * {@link HttpFSExceptionProvider}.
165   */
166  @GET
167  @Produces(MediaType.APPLICATION_JSON)
168  public Response getRoot(@QueryParam(OperationParam.NAME) OperationParam op,
169                          @Context Parameters params,
170                          @Context HttpServletRequest request)
171    throws IOException, FileSystemAccessException {
172    return get("", op, params, request);
173  }
174
175  private String makeAbsolute(String path) {
176    return "/" + ((path != null) ? path : "");
177  }
178
179  /**
180   * Binding to handle GET requests, supported operations are
181   *
182   * @param path the path for operation.
183   * @param op the HttpFS operation of the request.
184   * @param params the HttpFS parameters of the request.
185   *
186   * @return the request response.
187   *
188   * @throws IOException thrown if an IO error occurred. Thrown exceptions are
189   * handled by {@link HttpFSExceptionProvider}.
190   * @throws FileSystemAccessException thrown if a FileSystemAccess releated
191   * error occurred. Thrown exceptions are handled by
192   * {@link HttpFSExceptionProvider}.
193   */
194  @GET
195  @Path("{path:.*}")
196  @Produces({MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON})
197  public Response get(@PathParam("path") String path,
198                      @QueryParam(OperationParam.NAME) OperationParam op,
199                      @Context Parameters params,
200                      @Context HttpServletRequest request)
201    throws IOException, FileSystemAccessException {
202    UserGroupInformation user = HttpUserGroupInformation.get();
203    Response response;
204    path = makeAbsolute(path);
205    MDC.put(HttpFSFileSystem.OP_PARAM, op.value().name());
206    MDC.put("hostname", request.getRemoteAddr());
207    switch (op.value()) {
208      case OPEN: {
209        //Invoking the command directly using an unmanaged FileSystem that is
210        // released by the FileSystemReleaseFilter
211        FSOperations.FSOpen command = new FSOperations.FSOpen(path);
212        FileSystem fs = createFileSystem(user);
213        InputStream is = command.execute(fs);
214        Long offset = params.get(OffsetParam.NAME, OffsetParam.class);
215        Long len = params.get(LenParam.NAME, LenParam.class);
216        AUDIT_LOG.info("[{}] offset [{}] len [{}]",
217                       new Object[]{path, offset, len});
218        InputStreamEntity entity = new InputStreamEntity(is, offset, len);
219        response =
220          Response.ok(entity).type(MediaType.APPLICATION_OCTET_STREAM).build();
221        break;
222      }
223      case GETFILESTATUS: {
224        FSOperations.FSFileStatus command =
225          new FSOperations.FSFileStatus(path);
226        Map json = fsExecute(user, command);
227        AUDIT_LOG.info("[{}]", path);
228        response = Response.ok(json).type(MediaType.APPLICATION_JSON).build();
229        break;
230      }
231      case LISTSTATUS: {
232        String filter = params.get(FilterParam.NAME, FilterParam.class);
233        FSOperations.FSListStatus command = new FSOperations.FSListStatus(
234          path, filter);
235        Map json = fsExecute(user, command);
236        AUDIT_LOG.info("[{}] filter [{}]", path,
237                       (filter != null) ? filter : "-");
238        response = Response.ok(json).type(MediaType.APPLICATION_JSON).build();
239        break;
240      }
241      case GETHOMEDIRECTORY: {
242        enforceRootPath(op.value(), path);
243        FSOperations.FSHomeDir command = new FSOperations.FSHomeDir();
244        JSONObject json = fsExecute(user, command);
245        AUDIT_LOG.info("");
246        response = Response.ok(json).type(MediaType.APPLICATION_JSON).build();
247        break;
248      }
249      case INSTRUMENTATION: {
250        enforceRootPath(op.value(), path);
251        Groups groups = HttpFSServerWebApp.get().get(Groups.class);
252        List<String> userGroups = groups.getGroups(user.getShortUserName());
253        if (!userGroups.contains(HttpFSServerWebApp.get().getAdminGroup())) {
254          throw new AccessControlException(
255            "User not in HttpFSServer admin group");
256        }
257        Instrumentation instrumentation =
258          HttpFSServerWebApp.get().get(Instrumentation.class);
259        Map snapshot = instrumentation.getSnapshot();
260        response = Response.ok(snapshot).build();
261        break;
262      }
263      case GETCONTENTSUMMARY: {
264        FSOperations.FSContentSummary command =
265          new FSOperations.FSContentSummary(path);
266        Map json = fsExecute(user, command);
267        AUDIT_LOG.info("[{}]", path);
268        response = Response.ok(json).type(MediaType.APPLICATION_JSON).build();
269        break;
270      }
271      case GETFILECHECKSUM: {
272        FSOperations.FSFileChecksum command =
273          new FSOperations.FSFileChecksum(path);
274        Map json = fsExecute(user, command);
275        AUDIT_LOG.info("[{}]", path);
276        response = Response.ok(json).type(MediaType.APPLICATION_JSON).build();
277        break;
278      }
279      case GETFILEBLOCKLOCATIONS: {
280        response = Response.status(Response.Status.BAD_REQUEST).build();
281        break;
282      }
283      case GETACLSTATUS: {
284        FSOperations.FSAclStatus command =
285                new FSOperations.FSAclStatus(path);
286        Map json = fsExecute(user, command);
287        AUDIT_LOG.info("ACL status for [{}]", path);
288        response = Response.ok(json).type(MediaType.APPLICATION_JSON).build();
289        break;
290      }
291      case GETXATTRS: {
292        List<String> xattrNames = params.getValues(XAttrNameParam.NAME, 
293            XAttrNameParam.class);
294        XAttrCodec encoding = params.get(XAttrEncodingParam.NAME, 
295            XAttrEncodingParam.class);
296        FSOperations.FSGetXAttrs command = new FSOperations.FSGetXAttrs(path, 
297            xattrNames, encoding);
298        @SuppressWarnings("rawtypes")
299        Map json = fsExecute(user, command);
300        AUDIT_LOG.info("XAttrs for [{}]", path);
301        response = Response.ok(json).type(MediaType.APPLICATION_JSON).build();
302        break;
303      }
304      case LISTXATTRS: {
305        FSOperations.FSListXAttrs command = new FSOperations.FSListXAttrs(path);
306        @SuppressWarnings("rawtypes")
307        Map json = fsExecute(user, command);
308        AUDIT_LOG.info("XAttr names for [{}]", path);
309        response = Response.ok(json).type(MediaType.APPLICATION_JSON).build();
310        break;
311      }
312      default: {
313        throw new IOException(
314          MessageFormat.format("Invalid HTTP GET operation [{0}]",
315                               op.value()));
316      }
317    }
318    return response;
319  }
320
321
322  /**
323   * Binding to handle DELETE requests.
324   *
325   * @param path the path for operation.
326   * @param op the HttpFS operation of the request.
327   * @param params the HttpFS parameters of the request.
328   *
329   * @return the request response.
330   *
331   * @throws IOException thrown if an IO error occurred. Thrown exceptions are
332   * handled by {@link HttpFSExceptionProvider}.
333   * @throws FileSystemAccessException thrown if a FileSystemAccess releated
334   * error occurred. Thrown exceptions are handled by
335   * {@link HttpFSExceptionProvider}.
336   */
337  @DELETE
338  @Path("{path:.*}")
339  @Produces(MediaType.APPLICATION_JSON)
340  public Response delete(@PathParam("path") String path,
341                         @QueryParam(OperationParam.NAME) OperationParam op,
342                         @Context Parameters params,
343                         @Context HttpServletRequest request)
344    throws IOException, FileSystemAccessException {
345    UserGroupInformation user = HttpUserGroupInformation.get();
346    Response response;
347    path = makeAbsolute(path);
348    MDC.put(HttpFSFileSystem.OP_PARAM, op.value().name());
349    MDC.put("hostname", request.getRemoteAddr());
350    switch (op.value()) {
351      case DELETE: {
352        Boolean recursive =
353          params.get(RecursiveParam.NAME,  RecursiveParam.class);
354        AUDIT_LOG.info("[{}] recursive [{}]", path, recursive);
355        FSOperations.FSDelete command =
356          new FSOperations.FSDelete(path, recursive);
357        JSONObject json = fsExecute(user, command);
358        response = Response.ok(json).type(MediaType.APPLICATION_JSON).build();
359        break;
360      }
361      default: {
362        throw new IOException(
363          MessageFormat.format("Invalid HTTP DELETE operation [{0}]",
364                               op.value()));
365      }
366    }
367    return response;
368  }
369
370  /**
371   * Binding to handle POST requests.
372   *
373   * @param is the inputstream for the request payload.
374   * @param uriInfo the of the request.
375   * @param path the path for operation.
376   * @param op the HttpFS operation of the request.
377   * @param params the HttpFS parameters of the request.
378   *
379   * @return the request response.
380   *
381   * @throws IOException thrown if an IO error occurred. Thrown exceptions are
382   * handled by {@link HttpFSExceptionProvider}.
383   * @throws FileSystemAccessException thrown if a FileSystemAccess releated
384   * error occurred. Thrown exceptions are handled by
385   * {@link HttpFSExceptionProvider}.
386   */
387  @POST
388  @Path("{path:.*}")
389  @Consumes({"*/*"})
390  @Produces({MediaType.APPLICATION_JSON})
391  public Response post(InputStream is,
392                       @Context UriInfo uriInfo,
393                       @PathParam("path") String path,
394                       @QueryParam(OperationParam.NAME) OperationParam op,
395                       @Context Parameters params,
396                       @Context HttpServletRequest request)
397    throws IOException, FileSystemAccessException {
398    UserGroupInformation user = HttpUserGroupInformation.get();
399    Response response;
400    path = makeAbsolute(path);
401    MDC.put(HttpFSFileSystem.OP_PARAM, op.value().name());
402    MDC.put("hostname", request.getRemoteAddr());
403    switch (op.value()) {
404      case APPEND: {
405        Boolean hasData = params.get(DataParam.NAME, DataParam.class);
406        if (!hasData) {
407          response = Response.temporaryRedirect(
408            createUploadRedirectionURL(uriInfo,
409              HttpFSFileSystem.Operation.APPEND)).build();
410        } else {
411          FSOperations.FSAppend command =
412            new FSOperations.FSAppend(is, path);
413          fsExecute(user, command);
414          AUDIT_LOG.info("[{}]", path);
415          response = Response.ok().type(MediaType.APPLICATION_JSON).build();
416        }
417        break;
418      }
419      case CONCAT: {
420        System.out.println("HTTPFS SERVER CONCAT");
421        String sources = params.get(SourcesParam.NAME, SourcesParam.class);
422
423        FSOperations.FSConcat command =
424            new FSOperations.FSConcat(path, sources.split(","));
425        fsExecute(user, command);
426        AUDIT_LOG.info("[{}]", path);
427        System.out.println("SENT RESPONSE");
428        response = Response.ok().build();
429        break;
430      }
431      case TRUNCATE: {
432        Long newLength = params.get(NewLengthParam.NAME, NewLengthParam.class);
433        FSOperations.FSTruncate command = 
434            new FSOperations.FSTruncate(path, newLength);
435        JSONObject json = fsExecute(user, command);
436        AUDIT_LOG.info("Truncate [{}] to length [{}]", path, newLength);
437        response = Response.ok(json).type(MediaType.APPLICATION_JSON).build();
438        break;
439      }
440      default: {
441        throw new IOException(
442          MessageFormat.format("Invalid HTTP POST operation [{0}]",
443                               op.value()));
444      }
445    }
446    return response;
447  }
448
449  /**
450   * Creates the URL for an upload operation (create or append).
451   *
452   * @param uriInfo uri info of the request.
453   * @param uploadOperation operation for the upload URL.
454   *
455   * @return the URI for uploading data.
456   */
457  protected URI createUploadRedirectionURL(UriInfo uriInfo, Enum<?> uploadOperation) {
458    UriBuilder uriBuilder = uriInfo.getRequestUriBuilder();
459    uriBuilder = uriBuilder.replaceQueryParam(OperationParam.NAME, uploadOperation).
460      queryParam(DataParam.NAME, Boolean.TRUE);
461    return uriBuilder.build(null);
462  }
463
464
465  /**
466   * Binding to handle PUT requests.
467   *
468   * @param is the inputstream for the request payload.
469   * @param uriInfo the of the request.
470   * @param path the path for operation.
471   * @param op the HttpFS operation of the request.
472   * @param params the HttpFS parameters of the request.
473   *
474   * @return the request response.
475   *
476   * @throws IOException thrown if an IO error occurred. Thrown exceptions are
477   * handled by {@link HttpFSExceptionProvider}.
478   * @throws FileSystemAccessException thrown if a FileSystemAccess releated
479   * error occurred. Thrown exceptions are handled by
480   * {@link HttpFSExceptionProvider}.
481   */
482  @PUT
483  @Path("{path:.*}")
484  @Consumes({"*/*"})
485  @Produces({MediaType.APPLICATION_JSON})
486  public Response put(InputStream is,
487                       @Context UriInfo uriInfo,
488                       @PathParam("path") String path,
489                       @QueryParam(OperationParam.NAME) OperationParam op,
490                       @Context Parameters params,
491                       @Context HttpServletRequest request)
492    throws IOException, FileSystemAccessException {
493    UserGroupInformation user = HttpUserGroupInformation.get();
494    Response response;
495    path = makeAbsolute(path);
496    MDC.put(HttpFSFileSystem.OP_PARAM, op.value().name());
497    MDC.put("hostname", request.getRemoteAddr());
498    switch (op.value()) {
499      case CREATE: {
500        Boolean hasData = params.get(DataParam.NAME, DataParam.class);
501        if (!hasData) {
502          response = Response.temporaryRedirect(
503            createUploadRedirectionURL(uriInfo,
504              HttpFSFileSystem.Operation.CREATE)).build();
505        } else {
506          Short permission = params.get(PermissionParam.NAME,
507                                         PermissionParam.class);
508          Boolean override = params.get(OverwriteParam.NAME,
509                                        OverwriteParam.class);
510          Short replication = params.get(ReplicationParam.NAME,
511                                         ReplicationParam.class);
512          Long blockSize = params.get(BlockSizeParam.NAME,
513                                      BlockSizeParam.class);
514          FSOperations.FSCreate command =
515            new FSOperations.FSCreate(is, path, permission, override,
516                                      replication, blockSize);
517          fsExecute(user, command);
518          AUDIT_LOG.info(
519            "[{}] permission [{}] override [{}] replication [{}] blockSize [{}]",
520            new Object[]{path, permission, override, replication, blockSize});
521          response = Response.status(Response.Status.CREATED).build();
522        }
523        break;
524      }
525      case SETXATTR: {
526        String xattrName = params.get(XAttrNameParam.NAME, 
527            XAttrNameParam.class);
528        String xattrValue = params.get(XAttrValueParam.NAME, 
529            XAttrValueParam.class);
530        EnumSet<XAttrSetFlag> flag = params.get(XAttrSetFlagParam.NAME, 
531            XAttrSetFlagParam.class);
532
533        FSOperations.FSSetXAttr command = new FSOperations.FSSetXAttr(
534            path, xattrName, xattrValue, flag);
535        fsExecute(user, command);
536        AUDIT_LOG.info("[{}] to xAttr [{}]", path, xattrName);
537        response = Response.ok().build();
538        break;
539      }
540      case REMOVEXATTR: {
541        String xattrName = params.get(XAttrNameParam.NAME, XAttrNameParam.class);
542        FSOperations.FSRemoveXAttr command = new FSOperations.FSRemoveXAttr(
543            path, xattrName);
544        fsExecute(user, command);
545        AUDIT_LOG.info("[{}] removed xAttr [{}]", path, xattrName);
546        response = Response.ok().build();
547        break;
548      }
549      case MKDIRS: {
550        Short permission = params.get(PermissionParam.NAME,
551                                       PermissionParam.class);
552        FSOperations.FSMkdirs command =
553          new FSOperations.FSMkdirs(path, permission);
554        JSONObject json = fsExecute(user, command);
555        AUDIT_LOG.info("[{}] permission [{}]", path, permission);
556        response = Response.ok(json).type(MediaType.APPLICATION_JSON).build();
557        break;
558      }
559      case RENAME: {
560        String toPath = params.get(DestinationParam.NAME, DestinationParam.class);
561        FSOperations.FSRename command =
562          new FSOperations.FSRename(path, toPath);
563        JSONObject json = fsExecute(user, command);
564        AUDIT_LOG.info("[{}] to [{}]", path, toPath);
565        response = Response.ok(json).type(MediaType.APPLICATION_JSON).build();
566        break;
567      }
568      case SETOWNER: {
569        String owner = params.get(OwnerParam.NAME, OwnerParam.class);
570        String group = params.get(GroupParam.NAME, GroupParam.class);
571        FSOperations.FSSetOwner command =
572          new FSOperations.FSSetOwner(path, owner, group);
573        fsExecute(user, command);
574        AUDIT_LOG.info("[{}] to (O/G)[{}]", path, owner + ":" + group);
575        response = Response.ok().build();
576        break;
577      }
578      case SETPERMISSION: {
579        Short permission = params.get(PermissionParam.NAME,
580                                      PermissionParam.class);
581        FSOperations.FSSetPermission command =
582          new FSOperations.FSSetPermission(path, permission);
583        fsExecute(user, command);
584        AUDIT_LOG.info("[{}] to [{}]", path, permission);
585        response = Response.ok().build();
586        break;
587      }
588      case SETREPLICATION: {
589        Short replication = params.get(ReplicationParam.NAME,
590                                       ReplicationParam.class);
591        FSOperations.FSSetReplication command =
592          new FSOperations.FSSetReplication(path, replication);
593        JSONObject json = fsExecute(user, command);
594        AUDIT_LOG.info("[{}] to [{}]", path, replication);
595        response = Response.ok(json).build();
596        break;
597      }
598      case SETTIMES: {
599        Long modifiedTime = params.get(ModifiedTimeParam.NAME,
600                                       ModifiedTimeParam.class);
601        Long accessTime = params.get(AccessTimeParam.NAME,
602                                     AccessTimeParam.class);
603        FSOperations.FSSetTimes command =
604          new FSOperations.FSSetTimes(path, modifiedTime, accessTime);
605        fsExecute(user, command);
606        AUDIT_LOG.info("[{}] to (M/A)[{}]", path,
607                       modifiedTime + ":" + accessTime);
608        response = Response.ok().build();
609        break;
610      }
611      case SETACL: {
612        String aclSpec = params.get(AclPermissionParam.NAME,
613                AclPermissionParam.class);
614        FSOperations.FSSetAcl command =
615                new FSOperations.FSSetAcl(path, aclSpec);
616        fsExecute(user, command);
617        AUDIT_LOG.info("[{}] to acl [{}]", path, aclSpec);
618        response = Response.ok().build();
619        break;
620      }
621      case REMOVEACL: {
622        FSOperations.FSRemoveAcl command =
623                new FSOperations.FSRemoveAcl(path);
624        fsExecute(user, command);
625        AUDIT_LOG.info("[{}] removed acl", path);
626        response = Response.ok().build();
627        break;
628      }
629      case MODIFYACLENTRIES: {
630        String aclSpec = params.get(AclPermissionParam.NAME,
631                AclPermissionParam.class);
632        FSOperations.FSModifyAclEntries command =
633                new FSOperations.FSModifyAclEntries(path, aclSpec);
634        fsExecute(user, command);
635        AUDIT_LOG.info("[{}] modify acl entry with [{}]", path, aclSpec);
636        response = Response.ok().build();
637        break;
638      }
639      case REMOVEACLENTRIES: {
640        String aclSpec = params.get(AclPermissionParam.NAME,
641                AclPermissionParam.class);
642        FSOperations.FSRemoveAclEntries command =
643                new FSOperations.FSRemoveAclEntries(path, aclSpec);
644        fsExecute(user, command);
645        AUDIT_LOG.info("[{}] remove acl entry [{}]", path, aclSpec);
646        response = Response.ok().build();
647        break;
648      }
649      case REMOVEDEFAULTACL: {
650        FSOperations.FSRemoveDefaultAcl command =
651                new FSOperations.FSRemoveDefaultAcl(path);
652        fsExecute(user, command);
653        AUDIT_LOG.info("[{}] remove default acl", path);
654        response = Response.ok().build();
655        break;
656      }
657      default: {
658        throw new IOException(
659          MessageFormat.format("Invalid HTTP PUT operation [{0}]",
660                               op.value()));
661      }
662    }
663    return response;
664  }
665
666}