content: /*
   *  Licensed to the Apache Software Foundation (ASF) under one or more
   *  contributor license agreements.  See the NOTICE file distributed with
   *  this work for additional information regarding copyright ownership.
   *  The ASF licenses this file to You under the Apache License, Version 2.0
   *  (the "License"); you may not use this file except in compliance with
   *  the License.  You may obtain a copy of the License at
   *
   *      https://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing, software
   *  distributed under the License is distributed on an "AS IS" BASIS,
   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   *  See the License for the specific language governing permissions and
   *  limitations under the License.
   *
   */
  
  package org.apache.ivy.plugins.repository.vfs;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.List;
  
  import org.apache.commons.vfs2.FileContent;
  import org.apache.commons.vfs2.FileObject;
  import org.apache.commons.vfs2.FileSystemException;
  import org.apache.commons.vfs2.FileSystemManager;
  import org.apache.commons.vfs2.FileType;
  import org.apache.commons.vfs2.impl.StandardFileSystemManager;
  import org.apache.ivy.core.settings.TimeoutConstraint;
  import org.apache.ivy.plugins.repository.AbstractRepository;
  import org.apache.ivy.plugins.repository.RepositoryCopyProgressListener;
  import org.apache.ivy.plugins.repository.Resource;
  import org.apache.ivy.plugins.repository.TransferEvent;
  import org.apache.ivy.util.CopyProgressListener;
  import org.apache.ivy.util.FileUtil;
  import org.apache.ivy.util.Message;
  
  /**
   * Implementation of a VFS repository
   */
  public class VfsRepository extends AbstractRepository {
      /**
       * Name of the resource defining the Ivy VFS Repo configuration.
       */
      private static final String IVY_VFS_CONFIG = "ivy_vfs.xml";
  
      private StandardFileSystemManager manager = null;
  
      private final CopyProgressListener progress = new RepositoryCopyProgressListener(this);
  
      /**
       * Create a new Ivy VFS Repository Instance
       */
      public VfsRepository() {
      }
  
      public VfsRepository(final TimeoutConstraint timeoutConstraint) {
          super(timeoutConstraint);
      }
  
      private FileSystemManager getVFSManager() throws IOException {
          synchronized (this) {
              if (manager == null) {
                  manager = createVFSManager();
              }
          }
          return manager;
      }
  
      private StandardFileSystemManager createVFSManager() throws IOException {
          StandardFileSystemManager result = null;
          try {
              /*
               * The DefaultFileSystemManager gets its configuration from the jakarta-vfs-common
               * implementation which includes the res and tmp schemes which are of no use to use
               * here. Using StandardFileSystemManager lets us specify which schemes to support as
               * well as providing a mechanism to change this support without recompilation.
               */
              result = new StandardFileSystemManager() {
                  protected void configurePlugins() throws FileSystemException {
                      // disable automatic loading potential unsupported extensions
                  }
              };
              result.setConfiguration(getClass().getResource(IVY_VFS_CONFIG));
              result.init();
  
              // Generate and print a list of available schemes
              Message.verbose("Available VFS schemes...");
              String[] schemes = result.getSchemes();
              Arrays.sort(schemes);
              for (String scheme : schemes) {
                  Message.verbose("VFS Supported Scheme: " + scheme);
              }
          } catch (FileSystemException e) {
              /*
               * If our attempt to initialize a VFS Repository fails we log the failure but continue
               * on. Given that an Ivy instance may involve numerous different repository types, it
               * seems overly cautious to throw a runtime exception on the initialization failure of
               * just one repository type.
               */
              Message.error("Unable to initialize VFS repository manager!");
              Message.error(e.getLocalizedMessage());
              throw new IOException(e.getLocalizedMessage(), e);
          }
  
          return result;
      }
  
      protected void finalize() {
          if (manager != null) {
              manager.close();
              manager = null;
          }
      }
  
      /**
       * Get a VfsResource
       *
       * @param vfsURI
       *            a <code>String</code> identifying a VFS Resource
       * @return Resource
       * @throws IOException on failure
       * @see "Supported File Systems in the jakarta-commons-vfs documentation"
       */
      public Resource getResource(String vfsURI) throws IOException {
          return new VfsResource(vfsURI, getVFSManager());
      }
  
      /**
       * Transfer a VFS Resource from the repository to the local file system.
       *
       * @param srcVfsURI
       *            a <code>String</code> identifying the VFS resource to be fetched
       * @param destination
       *            a <code>File</code> identifying the destination file
       * @throws IOException on failure
       * @see "Supported File Systems in the jakarta-commons-vfs documentation"
       */
      public void get(String srcVfsURI, File destination) throws IOException {
          VfsResource src = new VfsResource(srcVfsURI, getVFSManager());
          fireTransferInitiated(src, TransferEvent.REQUEST_GET);
          try {
              FileContent content = src.getContent();
              if (content == null) {
                  throw new IllegalArgumentException("invalid vfs uri " + srcVfsURI
                          + ": no content found");
              }
              FileUtil.copy(content.getInputStream(), destination, progress);
          } catch (IOException | RuntimeException ex) {
              fireTransferError(ex);
              throw ex;
          }
      }
  
      /**
       * Return a listing of the contents of a parent directory. Listing is a set of strings
       * representing VFS URIs.
       *
       * @param vfsURI
       *            providing identifying a VFS provided resource
       * @return List
       * @throws IOException
       *             on failure.
       * @see "Supported File Systems in the jakarta-commons-vfs documentation"
       */
      public List<String> list(String vfsURI) throws IOException {
          List<String> list = new ArrayList<>();
          Message.debug("list called for URI" + vfsURI);
          FileObject resourceImpl = getVFSManager().resolveFile(vfsURI);
          Message.debug("resourceImpl=" + resourceImpl.toString());
          Message.debug("resourceImpl.exists()" + resourceImpl.exists());
          Message.debug("resourceImpl.getType()" + resourceImpl.getType());
          Message.debug("FileType.FOLDER" + FileType.FOLDER);
          if (resourceImpl.exists() && resourceImpl.getType() == FileType.FOLDER) {
              List<FileObject> children = Arrays.asList(resourceImpl.getChildren());
              for (FileObject child : children) {
                  Message.debug("child " + children.indexOf(child) + child.getName().getURI());
                  list.add(VfsResource.normalize(child.getName().getURI()));
              }
          }
          return list;
      }
  
      /**
       * Transfer an Ivy resource to a VFS repository
       *
       * @param source
       *            a <code>File</code> identifying the local file to transfer to the repository
       * @param vfsURI
       *            a <code>String</code> identifying the destination VFS Resource.
       * @param overwrite
       *            whether to overwrite an existing resource.
       * @throws IOException on failure.
       * @see "Supported File Systems in the jakarta-commons-vfs documentation"
       */
      public void put(File source, String vfsURI, boolean overwrite) throws IOException {
          VfsResource dest = new VfsResource(vfsURI, getVFSManager());
          fireTransferInitiated(dest, TransferEvent.REQUEST_PUT);
          if (dest.physicallyExists() && !overwrite) {
              throw new IOException("Cannot copy. Destination file: " + dest.getName()
                      + " exists and overwrite not set.");
          }
          if (dest.getContent() == null) {
              throw new IllegalArgumentException("invalid vfs uri " + vfsURI
                      + " to put data to: resource has no content");
          }
  
          FileUtil.copy(new FileInputStream(source), dest.getContent().getOutputStream(), progress);
      }
  
  }
  