/* * Copyright (c) 2013, the authors. * * This file is part of 'DXFS'. * * DXFS is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * DXFS is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DXFS. If not, see <http://www.gnu.org/licenses/>. */ /* * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package nextflow.fs.dx; import java.io.IOException; import java.nio.file.FileStore; import java.nio.file.FileSystem; import java.nio.file.Path; import java.nio.file.PathMatcher; import java.nio.file.WatchService; import java.nio.file.attribute.UserPrincipalLookupService; import java.nio.file.spi.FileSystemProvider; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import nextflow.fs.dx.api.DxApi; /** * A file system implementation for the DnaNexus storage * * @author Paolo Di Tommaso <paolo.ditommaso@gmail.com> * @author Beatriz San Juan <bmsanjuan@gmail.com> */ public class DxFileSystem extends FileSystem { static private Set<String> FILE_ATTRIBUTE_VIEWS = Collections.unmodifiableSet( new HashSet<String>(Arrays.asList( DxFileAttributeView.NAME )) ); private DxFileSystemProvider provider; private DxPath rootDirectory; private String contextId; private DxPath defaultDirectory; private String contextName; public DxFileSystem( DxFileSystemProvider provider ) { this(provider, provider.defaultContextId); } public DxFileSystem( DxFileSystemProvider provider, String contextId ) { this(provider, contextId, contextId); } public DxFileSystem( DxFileSystemProvider provider, String contextId, String label ) { init(provider,contextId,label); } private void init( DxFileSystemProvider provider, String contextId, String label ) { this.rootDirectory = new DxPath(this, "/"); this.defaultDirectory = new DxPath(this, "/"); this.provider = provider; this.contextId = contextId; this.contextName = label; } /** * @return The reference of the {@code DxApi} object */ DxApi remoter() { return provider.api; } public String getContextName() { return contextName; } // package private String getContextId() { return contextId; } /** * {@inheritDoc} */ @Override public FileSystemProvider provider() { return provider; } public DxPath rootDirectory() { return rootDirectory; } public DxPath defaultDirectory() { return defaultDirectory; } @Override public void close() throws IOException { // nothing to close } @Override public boolean isOpen() { return true; } @Override public boolean isReadOnly() { return false; } @Override public String getSeparator() { return "/"; } @Override public Iterable<Path> getRootDirectories() { final List<Path> result = Collections.unmodifiableList(Arrays.asList((Path) rootDirectory)); return new Iterable<Path>() { public Iterator<Path> iterator() { return result.iterator(); } }; } // TODO getFileStores @Override public Iterable<FileStore> getFileStores() { final List<FileStore> result = new ArrayList<FileStore>(1); result.add( new DxFileStore(this) ); return new Iterable<FileStore>() { @Override public Iterator<FileStore> iterator() { return result.iterator(); } }; } @Override public Set<String> supportedFileAttributeViews() { return FILE_ATTRIBUTE_VIEWS; } /** * Factory method for {@code DxPath} object * * @param first First element in the path * @param more Other path elements * * @return */ @Override public DxPath getPath(String first, String... more) { String path; if (more.length == 0) { path = first; } else { StringBuilder sb = new StringBuilder(); sb.append(first); for (String segment: more) { if (segment.length() > 0) { if (sb.length() > 0) sb.append('/'); sb.append(segment); } } path = sb.toString(); } return new DxPath(this, path); } // TODO getPathMatcher @Override public PathMatcher getPathMatcher(String syntaxAndPattern) { return null; //To change body of implemented methods use File | Settings | File Templates. } @Override public UserPrincipalLookupService getUserPrincipalLookupService() { throw new UnsupportedOperationException(); } @Override public WatchService newWatchService() throws IOException { throw new UnsupportedOperationException(); } /** * An iterator over the content of the specified folder * * @param path The path of the folder to iterate over * @return */ @SuppressWarnings("unchecked") Iterator<DxPath> folderIterator(DxPath path) throws IOException { String sPath = path.normalize().toAbsolutePath().toRawPath(); Map<String,Object> all = remoter().folderList(contextId, sPath, true); // the above method returns a map containing two lists: "folders" and "objects" (the files) final List<String> folders = (List<String>)all.get("folders"); final List<Map<String,Object>> files = (List<Map<String,Object>>)all.get("objects"); // total number of elements final int length = folders.size() + files.size(); return new Iterator<DxPath>() { int count = 0; @Override public boolean hasNext() { return count<length; } @Override public DxPath next() { int i = count++; if( i<files.size() ) { // find out the 'file' meta data and return a DxPath for it Map<String,Object> entry = files.get(i); String id = (String)entry.get("id"); Map<String,Object> attr = (Map<String,Object>)entry.get("describe"); String name = (String)attr.get("name"); String folder = (String)attr.get("folder"); // add the resulting path to the list return DxPath.file( DxFileSystem.this, endsWithSlash(folder)+name, id, attr); } else if( i < length ) { // return a DxPath for the folder return DxPath.directory( DxFileSystem.this, folders.get(i-files.size()) ); } else { throw new IllegalStateException(); } } @Override public void remove() { throw new UnsupportedOperationException(); } }; } /** * ListCmd the content of the specified folder * * @param path The path of the folder for which list the content * @return A list holding a collection of {@code DxPath} objects or a empty list if the folder contains nothing */ @SuppressWarnings("unchecked") List<DxPath> folderList(DxPath path) throws IOException { // the result list collecting all the results final List<DxPath> result = new LinkedList<>( ); Iterator<DxPath> it = folderIterator(path); while( it.hasNext() ) { result.add( it.next() ); } return result; } private static String endsWithSlash( String path ) { return path.endsWith("/") ? path : path + '/'; } @Deprecated List fileFind(String path) throws IOException { return remoter().fileFind(contextId, path); } public void createFolder(DxPath path) throws IOException { createFolder(path,true); } public void createFolder(DxPath path, boolean createParents) throws IOException { String str = path.normalize().toAbsolutePath().toRawPath(); remoter().folderCreate(contextId, str, createParents); path.type = DxPath.PathType.DIRECTORY; } public void deleteFolder( DxPath path, boolean recurse ) throws IOException { String str = path.normalize().toAbsolutePath().toRawPath(); remoter().folderDelete(contextId, str, recurse); } public void deleteFiles( DxPath... paths ) throws IOException { int i=0; if( paths == null ) { return; } String[] fileIds = new String[ paths.length ]; for( DxPath item : paths ) { fileIds[i++] = item.getFileId(); } remoter().fileDelete(contextId, fileIds); } public DxFileAttributes describeFile( DxPath path ) throws IOException { Map<String,Object> attr = remoter().fileDescribe(path.getFileId()); return DxFileAttributes.file(attr); } public void fileRename( DxPath path, String name) throws IOException { remoter().rename(contextId, path.getFileId(), name); } public void fileAddTags(DxPath path, String[] value) throws IOException { remoter().addFileTags(contextId, path.getFileId(), value); } public void fileAddTypes(DxPath path, String[] value) throws IOException { remoter().addFileTypes(contextId, path.getFileId(), value); } public String fileNew( DxPath path ) throws IOException { String str = path.normalize().toAbsolutePath().toRawPath(); String fileId = remoter().fileNew(contextId, str); path.fileId = fileId; path.type = DxPath.PathType.FILE; return fileId; } public Map<String,Object> fileCopy( DxPath source, DxPath target ) throws IOException { DxPath normalized = target.normalize().toAbsolutePath(); DxPath targetPath = normalized.getParent(); String targetStr = targetPath != null ? targetPath.toRawPath() : "/"; String targetCtxId = target.getFileSystem().contextId; Map<String,Object> result = remoter().fileClone(contextId, source.getFileId(), targetCtxId, targetStr ); return result; } }