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.fs.http.server;
019
020import org.apache.hadoop.classification.InterfaceAudience;
021import org.apache.hadoop.fs.ContentSummary;
022import org.apache.hadoop.fs.FileChecksum;
023import org.apache.hadoop.fs.FileStatus;
024import org.apache.hadoop.fs.FileSystem;
025import org.apache.hadoop.fs.FilterFileSystem;
026import org.apache.hadoop.fs.GlobFilter;
027import org.apache.hadoop.fs.Path;
028import org.apache.hadoop.fs.PathFilter;
029import org.apache.hadoop.fs.XAttrCodec;
030import org.apache.hadoop.fs.XAttrSetFlag;
031import org.apache.hadoop.fs.http.client.HttpFSFileSystem;
032import org.apache.hadoop.fs.permission.AclEntry;
033import org.apache.hadoop.fs.permission.AclStatus;
034import org.apache.hadoop.fs.permission.FsPermission;
035import org.apache.hadoop.io.IOUtils;
036import org.apache.hadoop.lib.service.FileSystemAccess;
037import org.json.simple.JSONArray;
038import org.json.simple.JSONObject;
039
040import java.io.FileNotFoundException;
041import java.io.IOException;
042import java.io.InputStream;
043import java.io.OutputStream;
044import java.util.EnumSet;
045import java.util.LinkedHashMap;
046import java.util.List;
047import java.util.Map;
048import java.util.Map.Entry;
049
050/**
051 * FileSystem operation executors used by {@link HttpFSServer}.
052 */
053@InterfaceAudience.Private
054public class FSOperations {
055
056  /**
057   * @param fileStatus a FileStatus object
058   * @return JSON map suitable for wire transport
059   */
060  private static Map<String, Object> toJson(FileStatus fileStatus) {
061    Map<String, Object> json = new LinkedHashMap<>();
062    json.put(HttpFSFileSystem.FILE_STATUS_JSON, toJsonInner(fileStatus, true));
063    return json;
064  }
065
066  /**
067   * @param fileStatuses list of FileStatus objects
068   * @param isFile is the fileStatuses from a file path
069   * @return JSON map suitable for wire transport
070   */
071  @SuppressWarnings({"unchecked"})
072  private static Map<String, Object> toJson(FileStatus[] fileStatuses,
073      boolean isFile) {
074    Map<String, Object> json = new LinkedHashMap<>();
075    Map<String, Object> inner = new LinkedHashMap<>();
076    JSONArray statuses = new JSONArray();
077    for (FileStatus f : fileStatuses) {
078      statuses.add(toJsonInner(f, isFile));
079    }
080    inner.put(HttpFSFileSystem.FILE_STATUS_JSON, statuses);
081    json.put(HttpFSFileSystem.FILE_STATUSES_JSON, inner);
082    return json;
083  }
084
085  /**
086   * Not meant to be called directly except by the other toJson functions.
087   */
088  private static Map<String, Object> toJsonInner(FileStatus fileStatus,
089      boolean emptyPathSuffix) {
090    Map<String, Object> json = new LinkedHashMap<String, Object>();
091    json.put(HttpFSFileSystem.PATH_SUFFIX_JSON,
092        (emptyPathSuffix) ? "" : fileStatus.getPath().getName());
093    json.put(HttpFSFileSystem.TYPE_JSON,
094        HttpFSFileSystem.FILE_TYPE.getType(fileStatus).toString());
095    json.put(HttpFSFileSystem.LENGTH_JSON, fileStatus.getLen());
096    json.put(HttpFSFileSystem.OWNER_JSON, fileStatus.getOwner());
097    json.put(HttpFSFileSystem.GROUP_JSON, fileStatus.getGroup());
098    json.put(HttpFSFileSystem.PERMISSION_JSON,
099        HttpFSFileSystem.permissionToString(fileStatus.getPermission()));
100    json.put(HttpFSFileSystem.ACCESS_TIME_JSON, fileStatus.getAccessTime());
101    json.put(HttpFSFileSystem.MODIFICATION_TIME_JSON,
102        fileStatus.getModificationTime());
103    json.put(HttpFSFileSystem.BLOCK_SIZE_JSON, fileStatus.getBlockSize());
104    json.put(HttpFSFileSystem.REPLICATION_JSON, fileStatus.getReplication());
105    if (fileStatus.getPermission().getAclBit()) {
106      json.put(HttpFSFileSystem.ACL_BIT_JSON, true);
107    }
108    if (fileStatus.getPermission().getEncryptedBit()) {
109      json.put(HttpFSFileSystem.ENC_BIT_JSON, true);
110    }
111    return json;
112  }
113
114  /**
115   * Serializes a DirectoryEntries object into the JSON for a
116   * WebHDFS {@link org.apache.hadoop.hdfs.protocol.DirectoryListing}.
117   * <p>
118   * These two classes are slightly different, due to the impedance
119   * mismatches between the WebHDFS and FileSystem APIs.
120   * @param entries
121   * @param isFile is the entries from a file path
122   * @return json
123   */
124  private static Map<String, Object> toJson(FileSystem.DirectoryEntries
125      entries, boolean isFile) {
126    Map<String, Object> json = new LinkedHashMap<>();
127    Map<String, Object> inner = new LinkedHashMap<>();
128    Map<String, Object> fileStatuses = toJson(entries.getEntries(), isFile);
129    inner.put(HttpFSFileSystem.PARTIAL_LISTING_JSON, fileStatuses);
130    inner.put(HttpFSFileSystem.REMAINING_ENTRIES_JSON, entries.hasMore() ? 1
131        : 0);
132    json.put(HttpFSFileSystem.DIRECTORY_LISTING_JSON, inner);
133    return json;
134  }
135
136  /** Converts an <code>AclStatus</code> object into a JSON object.
137   *
138   * @param aclStatus AclStatus object
139   *
140   * @return The JSON representation of the ACLs for the file
141   */
142  @SuppressWarnings({"unchecked"})
143  private static Map<String,Object> aclStatusToJSON(AclStatus aclStatus) {
144    Map<String,Object> json = new LinkedHashMap<String,Object>();
145    Map<String,Object> inner = new LinkedHashMap<String,Object>();
146    JSONArray entriesArray = new JSONArray();
147    inner.put(HttpFSFileSystem.OWNER_JSON, aclStatus.getOwner());
148    inner.put(HttpFSFileSystem.GROUP_JSON, aclStatus.getGroup());
149    inner.put(HttpFSFileSystem.ACL_STICKY_BIT_JSON, aclStatus.isStickyBit());
150    for ( AclEntry e : aclStatus.getEntries() ) {
151      entriesArray.add(e.toString());
152    }
153    inner.put(HttpFSFileSystem.ACL_ENTRIES_JSON, entriesArray);
154    json.put(HttpFSFileSystem.ACL_STATUS_JSON, inner);
155    return json;
156  }
157
158  /**
159   * Converts a <code>FileChecksum</code> object into a JSON array
160   * object.
161   *
162   * @param checksum file checksum.
163   *
164   * @return The JSON representation of the file checksum.
165   */
166  @SuppressWarnings({"unchecked"})
167  private static Map fileChecksumToJSON(FileChecksum checksum) {
168    Map json = new LinkedHashMap();
169    json.put(HttpFSFileSystem.CHECKSUM_ALGORITHM_JSON, checksum.getAlgorithmName());
170    json.put(HttpFSFileSystem.CHECKSUM_BYTES_JSON,
171             org.apache.hadoop.util.StringUtils.byteToHexString(checksum.getBytes()));
172    json.put(HttpFSFileSystem.CHECKSUM_LENGTH_JSON, checksum.getLength());
173    Map response = new LinkedHashMap();
174    response.put(HttpFSFileSystem.FILE_CHECKSUM_JSON, json);
175    return response;
176  }
177
178  /**
179   * Converts xAttrs to a JSON object.
180   *
181   * @param xAttrs file xAttrs.
182   * @param encoding format of xattr values.
183   *
184   * @return The JSON representation of the xAttrs.
185   * @throws IOException 
186   */
187  @SuppressWarnings({"unchecked", "rawtypes"})
188  private static Map xAttrsToJSON(Map<String, byte[]> xAttrs, 
189      XAttrCodec encoding) throws IOException {
190    Map jsonMap = new LinkedHashMap();
191    JSONArray jsonArray = new JSONArray();
192    if (xAttrs != null) {
193      for (Entry<String, byte[]> e : xAttrs.entrySet()) {
194        Map json = new LinkedHashMap();
195        json.put(HttpFSFileSystem.XATTR_NAME_JSON, e.getKey());
196        if (e.getValue() != null) {
197          json.put(HttpFSFileSystem.XATTR_VALUE_JSON, 
198              XAttrCodec.encodeValue(e.getValue(), encoding));
199        }
200        jsonArray.add(json);
201      }
202    }
203    jsonMap.put(HttpFSFileSystem.XATTRS_JSON, jsonArray);
204    return jsonMap;
205  }
206
207  /**
208   * Converts xAttr names to a JSON object.
209   *
210   * @param names file xAttr names.
211   *
212   * @return The JSON representation of the xAttr names.
213   * @throws IOException 
214   */
215  @SuppressWarnings({"unchecked", "rawtypes"})
216  private static Map xAttrNamesToJSON(List<String> names) throws IOException {
217    Map jsonMap = new LinkedHashMap();
218    jsonMap.put(HttpFSFileSystem.XATTRNAMES_JSON, JSONArray.toJSONString(names));
219    return jsonMap;
220  }
221
222  /**
223   * Converts a <code>ContentSummary</code> object into a JSON array
224   * object.
225   *
226   * @param contentSummary the content summary
227   *
228   * @return The JSON representation of the content summary.
229   */
230  @SuppressWarnings({"unchecked"})
231  private static Map contentSummaryToJSON(ContentSummary contentSummary) {
232    Map json = new LinkedHashMap();
233    json.put(HttpFSFileSystem.CONTENT_SUMMARY_DIRECTORY_COUNT_JSON, contentSummary.getDirectoryCount());
234    json.put(HttpFSFileSystem.CONTENT_SUMMARY_FILE_COUNT_JSON, contentSummary.getFileCount());
235    json.put(HttpFSFileSystem.CONTENT_SUMMARY_LENGTH_JSON, contentSummary.getLength());
236    json.put(HttpFSFileSystem.CONTENT_SUMMARY_QUOTA_JSON, contentSummary.getQuota());
237    json.put(HttpFSFileSystem.CONTENT_SUMMARY_SPACE_CONSUMED_JSON, contentSummary.getSpaceConsumed());
238    json.put(HttpFSFileSystem.CONTENT_SUMMARY_SPACE_QUOTA_JSON, contentSummary.getSpaceQuota());
239    Map response = new LinkedHashMap();
240    response.put(HttpFSFileSystem.CONTENT_SUMMARY_JSON, json);
241    return response;
242  }
243
244  /**
245   * Converts an object into a Json Map with with one key-value entry.
246   * <p/>
247   * It assumes the given value is either a JSON primitive type or a
248   * <code>JsonAware</code> instance.
249   *
250   * @param name name for the key of the entry.
251   * @param value for the value of the entry.
252   *
253   * @return the JSON representation of the key-value pair.
254   */
255  @SuppressWarnings("unchecked")
256  private static JSONObject toJSON(String name, Object value) {
257    JSONObject json = new JSONObject();
258    json.put(name, value);
259    return json;
260  }
261
262  /**
263   * Executor that performs an append FileSystemAccess files system operation.
264   */
265  @InterfaceAudience.Private
266  public static class FSAppend implements FileSystemAccess.FileSystemExecutor<Void> {
267    private InputStream is;
268    private Path path;
269
270    /**
271     * Creates an Append executor.
272     *
273     * @param is input stream to append.
274     * @param path path of the file to append.
275     */
276    public FSAppend(InputStream is, String path) {
277      this.is = is;
278      this.path = new Path(path);
279    }
280
281    /**
282     * Executes the filesystem operation.
283     *
284     * @param fs filesystem instance to use.
285     *
286     * @return void.
287     *
288     * @throws IOException thrown if an IO error occured.
289     */
290    @Override
291    public Void execute(FileSystem fs) throws IOException {
292      int bufferSize = fs.getConf().getInt("httpfs.buffer.size", 4096);
293      OutputStream os = fs.append(path, bufferSize);
294      IOUtils.copyBytes(is, os, bufferSize, true);
295      os.close();
296      return null;
297    }
298
299  }
300
301  /**
302   * Executor that performs an append FileSystemAccess files system operation.
303   */
304  @InterfaceAudience.Private
305  public static class FSConcat implements FileSystemAccess.FileSystemExecutor<Void> {
306    private Path path;
307    private Path[] sources;
308
309    /**
310     * Creates a Concat executor.
311     *
312     * @param path target path to concat to.
313     * @param sources comma seperated absolute paths to use as sources.
314     */
315    public FSConcat(String path, String[] sources) {
316      this.sources = new Path[sources.length];
317
318      for(int i = 0; i < sources.length; i++) {
319        this.sources[i] = new Path(sources[i]);
320      }
321
322      this.path = new Path(path);
323    }
324
325    /**
326     * Executes the filesystem operation.
327     *
328     * @param fs filesystem instance to use.
329     *
330     * @return void.
331     *
332     * @throws IOException thrown if an IO error occured.
333     */
334    @Override
335    public Void execute(FileSystem fs) throws IOException {
336      fs.concat(path, sources);
337      return null;
338    }
339
340  }
341
342  /**
343   * Executor that performs a content-summary FileSystemAccess files system operation.
344   */
345  @InterfaceAudience.Private
346  public static class FSContentSummary implements FileSystemAccess.FileSystemExecutor<Map> {
347    private Path path;
348
349    /**
350     * Creates a content-summary executor.
351     *
352     * @param path the path to retrieve the content-summary.
353     */
354    public FSContentSummary(String path) {
355      this.path = new Path(path);
356    }
357
358    /**
359     * Executes the filesystem operation.
360     *
361     * @param fs filesystem instance to use.
362     *
363     * @return a Map object (JSON friendly) with the content-summary.
364     *
365     * @throws IOException thrown if an IO error occured.
366     */
367    @Override
368    public Map execute(FileSystem fs) throws IOException {
369      ContentSummary contentSummary = fs.getContentSummary(path);
370      return contentSummaryToJSON(contentSummary);
371    }
372
373  }
374
375  /**
376   * Executor that performs a create FileSystemAccess files system operation.
377   */
378  @InterfaceAudience.Private
379  public static class FSCreate implements FileSystemAccess.FileSystemExecutor<Void> {
380    private InputStream is;
381    private Path path;
382    private short permission;
383    private boolean override;
384    private short replication;
385    private long blockSize;
386
387    /**
388     * Creates a Create executor.
389     *
390     * @param is input stream to for the file to create.
391     * @param path path of the file to create.
392     * @param perm permission for the file.
393     * @param override if the file should be overriden if it already exist.
394     * @param repl the replication factor for the file.
395     * @param blockSize the block size for the file.
396     */
397    public FSCreate(InputStream is, String path, short perm, boolean override,
398                    short repl, long blockSize) {
399      this.is = is;
400      this.path = new Path(path);
401      this.permission = perm;
402      this.override = override;
403      this.replication = repl;
404      this.blockSize = blockSize;
405    }
406
407    /**
408     * Executes the filesystem operation.
409     *
410     * @param fs filesystem instance to use.
411     *
412     * @return The URI of the created file.
413     *
414     * @throws IOException thrown if an IO error occured.
415     */
416    @Override
417    public Void execute(FileSystem fs) throws IOException {
418      if (replication == -1) {
419        replication = fs.getDefaultReplication(path);
420      }
421      if (blockSize == -1) {
422        blockSize = fs.getDefaultBlockSize(path);
423      }
424      FsPermission fsPermission = new FsPermission(permission);
425      int bufferSize = fs.getConf().getInt("httpfs.buffer.size", 4096);
426      OutputStream os = fs.create(path, fsPermission, override, bufferSize, replication, blockSize, null);
427      IOUtils.copyBytes(is, os, bufferSize, true);
428      os.close();
429      return null;
430    }
431
432  }
433
434  /**
435   * Executor that performs a delete FileSystemAccess files system operation.
436   */
437  @InterfaceAudience.Private
438  public static class FSDelete implements FileSystemAccess.FileSystemExecutor<JSONObject> {
439    private Path path;
440    private boolean recursive;
441
442    /**
443     * Creates a Delete executor.
444     *
445     * @param path path to delete.
446     * @param recursive if the delete should be recursive or not.
447     */
448    public FSDelete(String path, boolean recursive) {
449      this.path = new Path(path);
450      this.recursive = recursive;
451    }
452
453    /**
454     * Executes the filesystem operation.
455     *
456     * @param fs filesystem instance to use.
457     *
458     * @return <code>true</code> if the delete operation was successful,
459     *         <code>false</code> otherwise.
460     *
461     * @throws IOException thrown if an IO error occured.
462     */
463    @Override
464    public JSONObject execute(FileSystem fs) throws IOException {
465      boolean deleted = fs.delete(path, recursive);
466      return toJSON(HttpFSFileSystem.DELETE_JSON.toLowerCase(), deleted);
467    }
468
469  }
470
471  /**
472   * Executor that performs a file-checksum FileSystemAccess files system operation.
473   */
474  @InterfaceAudience.Private
475  public static class FSFileChecksum implements FileSystemAccess.FileSystemExecutor<Map> {
476    private Path path;
477
478    /**
479     * Creates a file-checksum executor.
480     *
481     * @param path the path to retrieve the checksum.
482     */
483    public FSFileChecksum(String path) {
484      this.path = new Path(path);
485    }
486
487    /**
488     * Executes the filesystem operation.
489     *
490     * @param fs filesystem instance to use.
491     *
492     * @return a Map object (JSON friendly) with the file checksum.
493     *
494     * @throws IOException thrown if an IO error occured.
495     */
496    @Override
497    public Map execute(FileSystem fs) throws IOException {
498      FileChecksum checksum = fs.getFileChecksum(path);
499      return fileChecksumToJSON(checksum);
500    }
501
502  }
503
504  /**
505   * Executor that performs a file-status FileSystemAccess files system operation.
506   */
507  @InterfaceAudience.Private
508  public static class FSFileStatus implements FileSystemAccess.FileSystemExecutor<Map> {
509    private Path path;
510
511    /**
512     * Creates a file-status executor.
513     *
514     * @param path the path to retrieve the status.
515     */
516    public FSFileStatus(String path) {
517      this.path = new Path(path);
518    }
519
520    /**
521     * Executes the filesystem getFileStatus operation and returns the
522     * result in a JSONish Map.
523     *
524     * @param fs filesystem instance to use.
525     *
526     * @return a Map object (JSON friendly) with the file status.
527     *
528     * @throws IOException thrown if an IO error occurred.
529     */
530    @Override
531    public Map execute(FileSystem fs) throws IOException {
532      FileStatus status = fs.getFileStatus(path);
533      return toJson(status);
534    }
535
536  }
537
538  /**
539   * Executor that performs a home-dir FileSystemAccess files system operation.
540   */
541  @InterfaceAudience.Private
542  public static class FSHomeDir implements FileSystemAccess.FileSystemExecutor<JSONObject> {
543
544    /**
545     * Executes the filesystem operation.
546     *
547     * @param fs filesystem instance to use.
548     *
549     * @return a JSON object with the user home directory.
550     *
551     * @throws IOException thrown if an IO error occured.
552     */
553    @Override
554    @SuppressWarnings("unchecked")
555    public JSONObject execute(FileSystem fs) throws IOException {
556      Path homeDir = fs.getHomeDirectory();
557      JSONObject json = new JSONObject();
558      json.put(HttpFSFileSystem.HOME_DIR_JSON, homeDir.toUri().getPath());
559      return json;
560    }
561
562  }
563
564  /**
565   * Executor that performs a list-status FileSystemAccess files system operation.
566   */
567  @InterfaceAudience.Private
568  public static class FSListStatus implements FileSystemAccess.FileSystemExecutor<Map>, PathFilter {
569    private Path path;
570    private PathFilter filter;
571
572    /**
573     * Creates a list-status executor.
574     *
575     * @param path the directory to retrieve the status of its contents.
576     * @param filter glob filter to use.
577     *
578     * @throws IOException thrown if the filter expression is incorrect.
579     */
580    public FSListStatus(String path, String filter) throws IOException {
581      this.path = new Path(path);
582      this.filter = (filter == null) ? this : new GlobFilter(filter);
583    }
584
585    /**
586     * Returns data for a JSON Map containing the information for
587     * the set of files in 'path' that match 'filter'.
588     *
589     * @param fs filesystem instance to use.
590     *
591     * @return a Map with the file status of the directory
592     *         contents that match the filter
593     *
594     * @throws IOException thrown if an IO error occurred.
595     */
596    @Override
597    public Map execute(FileSystem fs) throws IOException {
598      FileStatus[] fileStatuses = fs.listStatus(path, filter);
599      return toJson(fileStatuses, fs.getFileStatus(path).isFile());
600    }
601
602    @Override
603    public boolean accept(Path path) {
604      return true;
605    }
606
607  }
608
609  /**
610   * Executor that performs a batched directory listing.
611   */
612  @InterfaceAudience.Private
613  public static class FSListStatusBatch implements FileSystemAccess
614      .FileSystemExecutor<Map> {
615    private final Path path;
616    private final byte[] token;
617
618    public FSListStatusBatch(String path, byte[] token) throws IOException {
619      this.path = new Path(path);
620      this.token = token.clone();
621    }
622
623    /**
624     * Simple wrapper filesystem that exposes the protected batched
625     * listStatus API so we can use it.
626     */
627    private static class WrappedFileSystem extends FilterFileSystem {
628      public WrappedFileSystem(FileSystem f) {
629        super(f);
630      }
631
632      @Override
633      public DirectoryEntries listStatusBatch(Path f, byte[] token) throws
634          FileNotFoundException, IOException {
635        return super.listStatusBatch(f, token);
636      }
637    }
638
639    @Override
640    public Map execute(FileSystem fs) throws IOException {
641      WrappedFileSystem wrappedFS = new WrappedFileSystem(fs);
642      FileSystem.DirectoryEntries entries =
643          wrappedFS.listStatusBatch(path, token);
644      return toJson(entries, wrappedFS.getFileStatus(path).isFile());
645    }
646  }
647
648  /**
649   * Executor that performs a mkdirs FileSystemAccess files system operation.
650   */
651  @InterfaceAudience.Private
652  public static class FSMkdirs implements FileSystemAccess.FileSystemExecutor<JSONObject> {
653
654    private Path path;
655    private short permission;
656
657    /**
658     * Creates a mkdirs executor.
659     *
660     * @param path directory path to create.
661     * @param permission permission to use.
662     */
663    public FSMkdirs(String path, short permission) {
664      this.path = new Path(path);
665      this.permission = permission;
666    }
667
668    /**
669     * Executes the filesystem operation.
670     *
671     * @param fs filesystem instance to use.
672     *
673     * @return <code>true</code> if the mkdirs operation was successful,
674     *         <code>false</code> otherwise.
675     *
676     * @throws IOException thrown if an IO error occured.
677     */
678    @Override
679    public JSONObject execute(FileSystem fs) throws IOException {
680      FsPermission fsPermission = new FsPermission(permission);
681      boolean mkdirs = fs.mkdirs(path, fsPermission);
682      return toJSON(HttpFSFileSystem.MKDIRS_JSON, mkdirs);
683    }
684
685  }
686
687  /**
688   * Executor that performs a open FileSystemAccess files system operation.
689   */
690  @InterfaceAudience.Private
691  public static class FSOpen implements FileSystemAccess.FileSystemExecutor<InputStream> {
692    private Path path;
693
694    /**
695     * Creates a open executor.
696     *
697     * @param path file to open.
698     */
699    public FSOpen(String path) {
700      this.path = new Path(path);
701    }
702
703    /**
704     * Executes the filesystem operation.
705     *
706     * @param fs filesystem instance to use.
707     *
708     * @return The inputstream of the file.
709     *
710     * @throws IOException thrown if an IO error occured.
711     */
712    @Override
713    public InputStream execute(FileSystem fs) throws IOException {
714      int bufferSize = HttpFSServerWebApp.get().getConfig().getInt("httpfs.buffer.size", 4096);
715      return fs.open(path, bufferSize);
716    }
717
718  }
719
720  /**
721   * Executor that performs a rename FileSystemAccess files system operation.
722   */
723  @InterfaceAudience.Private
724  public static class FSRename implements FileSystemAccess.FileSystemExecutor<JSONObject> {
725    private Path path;
726    private Path toPath;
727
728    /**
729     * Creates a rename executor.
730     *
731     * @param path path to rename.
732     * @param toPath new name.
733     */
734    public FSRename(String path, String toPath) {
735      this.path = new Path(path);
736      this.toPath = new Path(toPath);
737    }
738
739    /**
740     * Executes the filesystem operation.
741     *
742     * @param fs filesystem instance to use.
743     *
744     * @return <code>true</code> if the rename operation was successful,
745     *         <code>false</code> otherwise.
746     *
747     * @throws IOException thrown if an IO error occured.
748     */
749    @Override
750    public JSONObject execute(FileSystem fs) throws IOException {
751      boolean renamed = fs.rename(path, toPath);
752      return toJSON(HttpFSFileSystem.RENAME_JSON, renamed);
753    }
754
755  }
756
757  /**
758   * Executor that performs a set-owner FileSystemAccess files system operation.
759   */
760  @InterfaceAudience.Private
761  public static class FSSetOwner implements FileSystemAccess.FileSystemExecutor<Void> {
762    private Path path;
763    private String owner;
764    private String group;
765
766    /**
767     * Creates a set-owner executor.
768     *
769     * @param path the path to set the owner.
770     * @param owner owner to set.
771     * @param group group to set.
772     */
773    public FSSetOwner(String path, String owner, String group) {
774      this.path = new Path(path);
775      this.owner = owner;
776      this.group = group;
777    }
778
779    /**
780     * Executes the filesystem operation.
781     *
782     * @param fs filesystem instance to use.
783     *
784     * @return void.
785     *
786     * @throws IOException thrown if an IO error occured.
787     */
788    @Override
789    public Void execute(FileSystem fs) throws IOException {
790      fs.setOwner(path, owner, group);
791      return null;
792    }
793
794  }
795
796  /**
797   * Executor that performs a set-permission FileSystemAccess files system operation.
798   */
799  @InterfaceAudience.Private
800  public static class FSSetPermission implements FileSystemAccess.FileSystemExecutor<Void> {
801
802    private Path path;
803    private short permission;
804
805    /**
806     * Creates a set-permission executor.
807     *
808     * @param path path to set the permission.
809     * @param permission permission to set.
810     */
811    public FSSetPermission(String path, short permission) {
812      this.path = new Path(path);
813      this.permission = permission;
814    }
815
816    /**
817     * Executes the filesystem operation.
818     *
819     * @param fs filesystem instance to use.
820     *
821     * @return void.
822     *
823     * @throws IOException thrown if an IO error occured.
824     */
825    @Override
826    public Void execute(FileSystem fs) throws IOException {
827      FsPermission fsPermission = new FsPermission(permission);
828      fs.setPermission(path, fsPermission);
829      return null;
830    }
831
832  }
833
834  /**
835   * Executor that sets the acl for a file in a FileSystem
836   */
837  @InterfaceAudience.Private
838  public static class FSSetAcl implements FileSystemAccess.FileSystemExecutor<Void> {
839
840    private Path path;
841    private List<AclEntry> aclEntries;
842
843    /**
844     * Creates a set-acl executor.
845     *
846     * @param path path to set the acl.
847     * @param aclSpec acl to set.
848     */
849    public FSSetAcl(String path, String aclSpec) {
850      this.path = new Path(path);
851      this.aclEntries = AclEntry.parseAclSpec(aclSpec, true);
852    }
853
854    /**
855     * Executes the filesystem operation.
856     *
857     * @param fs filesystem instance to use.
858     *
859     * @return void.
860     *
861     * @throws IOException thrown if an IO error occurred.
862     */
863    @Override
864    public Void execute(FileSystem fs) throws IOException {
865      fs.setAcl(path, aclEntries);
866      return null;
867    }
868
869  }
870
871  /**
872   * Executor that removes all acls from a file in a FileSystem
873   */
874  @InterfaceAudience.Private
875  public static class FSRemoveAcl implements FileSystemAccess.FileSystemExecutor<Void> {
876
877    private Path path;
878
879    /**
880     * Creates a remove-acl executor.
881     *
882     * @param path path from which to remove the acl.
883     */
884    public FSRemoveAcl(String path) {
885      this.path = new Path(path);
886    }
887
888    /**
889     * Executes the filesystem operation.
890     *
891     * @param fs filesystem instance to use.
892     *
893     * @return void.
894     *
895     * @throws IOException thrown if an IO error occurred.
896     */
897    @Override
898    public Void execute(FileSystem fs) throws IOException {
899      fs.removeAcl(path);
900      return null;
901    }
902
903  }
904
905  /**
906   * Executor that modifies acl entries for a file in a FileSystem
907   */
908  @InterfaceAudience.Private
909  public static class FSModifyAclEntries implements FileSystemAccess.FileSystemExecutor<Void> {
910
911    private Path path;
912    private List<AclEntry> aclEntries;
913
914    /**
915     * Creates a modify-acl executor.
916     *
917     * @param path path to set the acl.
918     * @param aclSpec acl to set.
919     */
920    public FSModifyAclEntries(String path, String aclSpec) {
921      this.path = new Path(path);
922      this.aclEntries = AclEntry.parseAclSpec(aclSpec, true);
923    }
924
925    /**
926     * Executes the filesystem operation.
927     *
928     * @param fs filesystem instance to use.
929     *
930     * @return void.
931     *
932     * @throws IOException thrown if an IO error occurred.
933     */
934    @Override
935    public Void execute(FileSystem fs) throws IOException {
936      fs.modifyAclEntries(path, aclEntries);
937      return null;
938    }
939
940  }
941
942  /**
943   * Executor that removes acl entries from a file in a FileSystem
944   */
945  @InterfaceAudience.Private
946  public static class FSRemoveAclEntries implements FileSystemAccess.FileSystemExecutor<Void> {
947
948    private Path path;
949    private List<AclEntry> aclEntries;
950
951    /**
952     * Creates a remove acl entry executor.
953     *
954     * @param path path to set the acl.
955     * @param aclSpec acl parts to remove.
956     */
957    public FSRemoveAclEntries(String path, String aclSpec) {
958      this.path = new Path(path);
959      this.aclEntries = AclEntry.parseAclSpec(aclSpec, false);
960    }
961
962    /**
963     * Executes the filesystem operation.
964     *
965     * @param fs filesystem instance to use.
966     *
967     * @return void.
968     *
969     * @throws IOException thrown if an IO error occurred.
970     */
971    @Override
972    public Void execute(FileSystem fs) throws IOException {
973      fs.removeAclEntries(path, aclEntries);
974      return null;
975    }
976
977  }
978
979  /**
980   * Executor that removes the default acl from a directory in a FileSystem
981   */
982  @InterfaceAudience.Private
983  public static class FSRemoveDefaultAcl implements FileSystemAccess.FileSystemExecutor<Void> {
984
985    private Path path;
986
987    /**
988     * Creates an executor for removing the default acl.
989     *
990     * @param path path to set the acl.
991     */
992    public FSRemoveDefaultAcl(String path) {
993      this.path = new Path(path);
994    }
995
996    /**
997     * Executes the filesystem operation.
998     *
999     * @param fs filesystem instance to use.
1000     *
1001     * @return void.
1002     *
1003     * @throws IOException thrown if an IO error occurred.
1004     */
1005    @Override
1006    public Void execute(FileSystem fs) throws IOException {
1007      fs.removeDefaultAcl(path);
1008      return null;
1009    }
1010
1011  }
1012
1013  /**
1014   * Executor that performs getting trash root FileSystemAccess
1015   * files system operation.
1016   */
1017  @InterfaceAudience.Private
1018  public static class FSTrashRoot
1019      implements FileSystemAccess.FileSystemExecutor<JSONObject> {
1020    private Path path;
1021    public FSTrashRoot(String path) {
1022      this.path = new Path(path);
1023    }
1024
1025    @Override
1026    @SuppressWarnings("unchecked")
1027    public JSONObject execute(FileSystem fs) throws IOException {
1028      Path trashRoot = fs.getTrashRoot(this.path);
1029      JSONObject json = new JSONObject();
1030      json.put(HttpFSFileSystem.TRASH_DIR_JSON, trashRoot.toUri().getPath());
1031      return json;
1032    }
1033
1034  }
1035
1036  /**
1037   * Executor that gets the ACL information for a given file.
1038   */
1039  @InterfaceAudience.Private
1040  public static class FSAclStatus implements FileSystemAccess.FileSystemExecutor<Map> {
1041    private Path path;
1042
1043    /**
1044     * Creates an executor for getting the ACLs for a file.
1045     *
1046     * @param path the path to retrieve the ACLs.
1047     */
1048    public FSAclStatus(String path) {
1049      this.path = new Path(path);
1050    }
1051
1052    /**
1053     * Executes the filesystem operation.
1054     *
1055     * @param fs filesystem instance to use.
1056     *
1057     * @return a Map object (JSON friendly) with the file status.
1058     *
1059     * @throws IOException thrown if an IO error occurred.
1060     */
1061    @Override
1062    public Map execute(FileSystem fs) throws IOException {
1063      AclStatus status = fs.getAclStatus(path);
1064      return aclStatusToJSON(status);
1065    }
1066
1067  }
1068
1069  /**
1070   * Executor that performs a set-replication FileSystemAccess files system operation.
1071   */
1072  @InterfaceAudience.Private
1073  public static class FSSetReplication implements FileSystemAccess.FileSystemExecutor<JSONObject> {
1074    private Path path;
1075    private short replication;
1076
1077    /**
1078     * Creates a set-replication executor.
1079     *
1080     * @param path path to set the replication factor.
1081     * @param replication replication factor to set.
1082     */
1083    public FSSetReplication(String path, short replication) {
1084      this.path = new Path(path);
1085      this.replication = replication;
1086    }
1087
1088    /**
1089     * Executes the filesystem operation.
1090     *
1091     * @param fs filesystem instance to use.
1092     *
1093     * @return <code>true</code> if the replication value was set,
1094     *         <code>false</code> otherwise.
1095     *
1096     * @throws IOException thrown if an IO error occured.
1097     */
1098    @Override
1099    @SuppressWarnings("unchecked")
1100    public JSONObject execute(FileSystem fs) throws IOException {
1101      boolean ret = fs.setReplication(path, replication);
1102      JSONObject json = new JSONObject();
1103      json.put(HttpFSFileSystem.SET_REPLICATION_JSON, ret);
1104      return json;
1105    }
1106
1107  }
1108
1109  /**
1110   * Executor that performs a set-times FileSystemAccess files system operation.
1111   */
1112  @InterfaceAudience.Private
1113  public static class FSSetTimes implements FileSystemAccess.FileSystemExecutor<Void> {
1114    private Path path;
1115    private long mTime;
1116    private long aTime;
1117
1118    /**
1119     * Creates a set-times executor.
1120     *
1121     * @param path path to set the times.
1122     * @param mTime modified time to set.
1123     * @param aTime access time to set.
1124     */
1125    public FSSetTimes(String path, long mTime, long aTime) {
1126      this.path = new Path(path);
1127      this.mTime = mTime;
1128      this.aTime = aTime;
1129    }
1130
1131    /**
1132     * Executes the filesystem operation.
1133     *
1134     * @param fs filesystem instance to use.
1135     *
1136     * @return void.
1137     *
1138     * @throws IOException thrown if an IO error occured.
1139     */
1140    @Override
1141    public Void execute(FileSystem fs) throws IOException {
1142      fs.setTimes(path, mTime, aTime);
1143      return null;
1144    }
1145
1146  }
1147
1148  /**
1149   * Executor that performs a setxattr FileSystemAccess files system operation.
1150   */
1151  @InterfaceAudience.Private
1152  public static class FSSetXAttr implements 
1153      FileSystemAccess.FileSystemExecutor<Void> {
1154
1155    private Path path;
1156    private String name;
1157    private byte[] value;
1158    private EnumSet<XAttrSetFlag> flag;
1159
1160    public FSSetXAttr(String path, String name, String encodedValue, 
1161        EnumSet<XAttrSetFlag> flag) throws IOException {
1162      this.path = new Path(path);
1163      this.name = name;
1164      this.value = XAttrCodec.decodeValue(encodedValue);
1165      this.flag = flag;
1166    }
1167
1168    @Override
1169    public Void execute(FileSystem fs) throws IOException {
1170      fs.setXAttr(path, name, value, flag);
1171      return null;
1172    }
1173  }
1174
1175  /**
1176   * Executor that performs a removexattr FileSystemAccess files system 
1177   * operation.
1178   */
1179  @InterfaceAudience.Private
1180  public static class FSRemoveXAttr implements 
1181      FileSystemAccess.FileSystemExecutor<Void> {
1182
1183    private Path path;
1184    private String name;
1185
1186    public FSRemoveXAttr(String path, String name) {
1187      this.path = new Path(path);
1188      this.name = name;
1189    }
1190
1191    @Override
1192    public Void execute(FileSystem fs) throws IOException {
1193      fs.removeXAttr(path, name);
1194      return null;
1195    }
1196  }
1197
1198  /**
1199   * Executor that performs listing xattrs FileSystemAccess files system 
1200   * operation.
1201   */
1202  @SuppressWarnings("rawtypes")
1203  @InterfaceAudience.Private
1204  public static class FSListXAttrs implements 
1205      FileSystemAccess.FileSystemExecutor<Map> {
1206    private Path path;
1207
1208    /**
1209     * Creates listing xattrs executor.
1210     *
1211     * @param path the path to retrieve the xattrs.
1212     */
1213    public FSListXAttrs(String path) {
1214      this.path = new Path(path);
1215    }
1216
1217    /**
1218     * Executes the filesystem operation.
1219     *
1220     * @param fs filesystem instance to use.
1221     *
1222     * @return Map a map object (JSON friendly) with the xattr names.
1223     *
1224     * @throws IOException thrown if an IO error occured.
1225     */
1226    @Override
1227    public Map execute(FileSystem fs) throws IOException {
1228      List<String> names = fs.listXAttrs(path);
1229      return xAttrNamesToJSON(names);
1230    }
1231  }
1232
1233  /**
1234   * Executor that performs getting xattrs FileSystemAccess files system 
1235   * operation.
1236   */
1237  @SuppressWarnings("rawtypes")
1238  @InterfaceAudience.Private
1239  public static class FSGetXAttrs implements 
1240      FileSystemAccess.FileSystemExecutor<Map> {
1241    private Path path;
1242    private List<String> names;
1243    private XAttrCodec encoding;
1244
1245    /**
1246     * Creates getting xattrs executor.
1247     *
1248     * @param path the path to retrieve the xattrs.
1249     */
1250    public FSGetXAttrs(String path, List<String> names, XAttrCodec encoding) {
1251      this.path = new Path(path);
1252      this.names = names;
1253      this.encoding = encoding;
1254    }
1255
1256    /**
1257     * Executes the filesystem operation.
1258     *
1259     * @param fs filesystem instance to use.
1260     *
1261     * @return Map a map object (JSON friendly) with the xattrs.
1262     *
1263     * @throws IOException thrown if an IO error occured.
1264     */
1265    @Override
1266    public Map execute(FileSystem fs) throws IOException {
1267      Map<String, byte[]> xattrs = null;
1268      if (names != null && !names.isEmpty()) {
1269        xattrs = fs.getXAttrs(path, names);
1270      } else {
1271        xattrs = fs.getXAttrs(path);
1272      }
1273      return xAttrsToJSON(xattrs, encoding);
1274    }
1275  }
1276}