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