├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── README.md ├── Vagrantfile ├── catlogs.sh ├── glusterfs-java-filesystem-example ├── pom.xml └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── peircean │ │ │ └── glusterfs │ │ │ └── example │ │ │ └── Example.java │ └── resources │ │ └── example.properties │ └── test │ └── java │ └── ExampleTest.java ├── glusterfs-java-filesystem ├── NOTICE.txt ├── pom.xml ├── sonar.txt └── src │ ├── main │ ├── java │ │ └── com │ │ │ └── peircean │ │ │ └── glusterfs │ │ │ ├── GlusterDirectoryIterator.java │ │ │ ├── GlusterDirectoryStream.java │ │ │ ├── GlusterFileAttributes.java │ │ │ ├── GlusterFileChannel.java │ │ │ ├── GlusterFileStore.java │ │ │ ├── GlusterFileSystem.java │ │ │ ├── GlusterFileSystemProvider.java │ │ │ ├── GlusterPath.java │ │ │ ├── GlusterPathMatcher.java │ │ │ ├── GlusterWatchEvent.java │ │ │ ├── GlusterWatchKey.java │ │ │ ├── GlusterWatchService.java │ │ │ └── borrowed │ │ │ └── GlobPattern.java │ └── resources │ │ └── META-INF │ │ └── services │ │ └── java.nio.file.spi.FileSystemProvider │ └── test │ └── java │ └── com │ └── peircean │ └── glusterfs │ ├── GlusterDirectoryIteratorTest.java │ ├── GlusterDirectoryStreamTest.java │ ├── GlusterFileAttributesTest.java │ ├── GlusterFileChannelTest.java │ ├── GlusterFileStoreTest.java │ ├── GlusterFileSystemProviderTest.java │ ├── GlusterFileSystemTest.java │ ├── GlusterPathMatcherTest.java │ ├── GlusterPathPowerMockTest.java │ ├── GlusterPathTest.java │ ├── GlusterWatchKeyTest.java │ └── GlusterWatchServiceTest.java ├── pom.xml └── vagrant-provisioner.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | .idea 4 | .idea/* 5 | *.iml 6 | *.ipr 7 | *.iws 8 | target 9 | .DS_Store 10 | .project 11 | .classpath 12 | .settings 13 | eclipse-classes 14 | hs_err_pid*.log 15 | .vagrant 16 | .clover 17 | dependency-reduced-pom.xml 18 | release.properties -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk7 4 | before_install: 5 | - sudo add-apt-repository ppa:gluster/glusterfs-3.4 -y 6 | - sudo apt-get update -qq -y 7 | - sudo apt-get install glusterfs-common -y 8 | after_script: bash catlogs.sh 9 | notifications: 10 | email: false 11 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Louis Zuckerman All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the names of the authors nor the names of 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # glusterfs-java-filesystem 2 | 3 | This project aims to be a complete implementation of a Java7/NIO.2 File System Provider backed by 4 | [GlusterFS](http://www.gluster.org/) via [libgfapi-jni](https://github.com/semiosis/libgfapi-jni) 5 | 6 | [![Build Status](https://travis-ci.org/semiosis/glusterfs-java-filesystem.png?branch=master)](https://travis-ci.org/semiosis/glusterfs-java-filesystem) 7 | 8 | [![Test coverage](http://sonar.peircean.com:8008/sonar-status-image/?resource=com.peircean.glusterfs:glusterfs-java-filesystem)](http://sonar.peircean.com/dashboard/index/com.peircean.glusterfs:glusterfs-java-filesystem) 9 | 10 | [Test Coverage & Code Quality](http://sonar.peircean.com/dashboard/index/com.peircean.glusterfs:glusterfs-java-filesystem) 11 | 12 | Please let me know if you use this project, even if you're just checking it out, I'd like to hear from you. 13 | 14 | I prefer to be contacted on IRC, Twitter, or a Github issue. You can find me, semiosis, in #gluster on Freenode IRC. My twitter handle is @pragmaticism. 15 | 16 | Thanks! 17 | 18 | # Use 19 | 20 | ## Adding to your maven project 21 | 22 | 23 | 24 | com.peircean.glusterfs 25 | glusterfs-java-filesystem 26 | 1.0.4 27 | 28 | 29 | 30 | ## Adding to your non-maven project 31 | 32 | The maven shade plugin can build a unified (shaded) JAR suitable for dropping in to the classpath of any JVM application. 33 | 34 | You can build a "shaded" JAR by cloning the project and running the following command in the glusterfs-java-filesystem subdirectory: 35 | 36 | cd glusterfs-java-filesystem 37 | mvn package shade:shade 38 | 39 | Maven will report the path of this shaded JAR. You can run `export CLASSPATH=` in a terminal before running your other application. 40 | 41 | Contact me (on IRC, Twitter, or in a Github issue) if you need help obtaining a JAR, if you can't, or don't want to, build it with maven yourself. 42 | 43 | ## Access GlusterFS volumes with the NIO.2 API 44 | 45 | Once this library is in your classpath all you need to do in your code is access a GlusterFS URI, for example 46 | 47 | gluster://server:volume/path 48 | 49 | ## Example usage 50 | 51 | A Vagrantfile in the root of this repository sets up a VM with a volume called *foo* at IP address *172.31.31.31* on a 52 | private network. 53 | 54 | The [Example.java](glusterfs-java-filesystem-example/src/main/java/com/peircean/glusterfs/example/Example.java) file in 55 | the glusterfs-java-filesystem-example project provides a demonstration of the capabilities of this project from a high 56 | level consumer's point of view, it connects to the volume on the vagrant VM. 57 | 58 | To run: 59 | 60 | cd glusterfs-java-filesystem-example 61 | vagrant up 62 | mvn exec:exec 63 | 64 | # Roadmap 65 | 66 | ### TODO: 67 | 68 | - Replace Example program with formal integration test suite 69 | - Align project versions with glusterfs (this project & libgfapi-jni) 70 | - Update watch service to use [libgfchangelog](https://github.com/gluster/glusterfs/blob/master/xlators/features/changelog/lib/examples/c/get-changes.c) (instead of polling) 71 | - Finish attribute support 72 | Owner/group names & ability to change 73 | More ways to set permissions 74 | - Finish integration testing for advanced synchronous file IO (reading/writing portion of file) 75 | - Asychronous file I/O 76 | - Better error reporting & handling (utilize UtilJNI.strerror() as part of IOException throws) 77 | - Finish readSymbolicLink unit tests 78 | - Publish test coverage report to Coveralls.io 79 | [Blocked](https://github.com/trautonen/coveralls-maven-plugin/issues/36) due to use of Atlassian Clover 80 | - Create hard links 81 | 82 | ### Completed: 83 | 84 | - Connect to a GlusterFS volume using the NIO.2 API 85 | - Basic synchronous file I/O 86 | Read the contents of a file all at once 87 | Write a chunk of bytes to a file all at once 88 | - File attributes 89 | See owner/group id, size, permissions, and last modified timestamp on files and directories 90 | Set permissions 91 | - Filesystem/volume stats 92 | See the total, free, and usable bytes in a volume 93 | - Directory listing (with filtering) 94 | - Move/rename files 95 | - Watch files for changes 96 | Complete except for GlusterWatchKeyTest, in progress 97 | - Create & Read symlinks (read tests incomplete) 98 | - Publish test coverage & code quality reports to [SonarQube](http://sonar.peircean.com/dashboard/index/com.peircean.glusterfs:glusterfs-java-filesystem) 99 | - Delete files 100 | - Copy files 101 | - Advanced synchronous file IO 102 | 103 | # Contributing/Development 104 | 105 | I'd appreciate your help with this project. If you have any feedback at all please get in touch. I'm interested in everything from gripes to pull requests. 106 | 107 | # Project License 108 | 109 | Until further notice (made here and in LICENSE.txt) this project is licensed under the terms of the 110 | 3-clause BSD license, as written in LICENSE.txt. 111 | 112 | The licensing is likely to change in the near future as the project matures. 113 | 114 | # Acknowledgements 115 | 116 | - May G. & Ian H. for their hard work & dedication to improving this project. 117 | - Atlassian for providing a free license for their most excellent Java code quality analyzer, [Clover](https://www.atlassian.com/software/clover/overview). 118 | - All the open source projects we depend on: [GlusterFS](http://gluster.org/), [HawtJNI](https://github.com/fusesource/hawtjni), [Lombok](http://projectlombok.org/), [JUnit](http://junit.org/), [Mockito](https://code.google.com/p/mockito/), [PowerMock](https://code.google.com/p/powermock/), [TestNG](http://testng.org/doc/index.html), [sonarqube](http://www.sonarqube.org/), [Hadoop](http://hadoop.apache.org/) (whose Glob to Regex converter we borrowed), [Maven](http://maven.apache.org/) and all the Maven plugins, and of course [Java](https://www.java.com/). 119 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | # All Vagrant configuration is done here. The most common configuration 9 | # options are documented and commented below. For a complete reference, 10 | # please see the online documentation at vagrantup.com. 11 | 12 | # Every Vagrant virtual environment requires a box to build off of. 13 | config.vm.box = "xenial64" 14 | 15 | # The url from where the 'config.vm.box' box will be fetched if it 16 | # doesn't already exist on the user's system. 17 | config.vm.box_url = "https://cloud-images.ubuntu.com/xenial/current/xenial-server-cloudimg-amd64-vagrant.box" 18 | 19 | # Create a forwarded port mapping which allows access to a specific port 20 | # within the machine from a port on the host machine. In the example below, 21 | # accessing "localhost:8080" will access port 80 on the guest machine. 22 | # config.vm.network :forwarded_port, guest: 80, host: 8080 23 | 24 | # Create a private network, which allows host-only access to the machine 25 | # using a specific IP. 26 | private_ip = "172.31.31.31" 27 | 28 | config.vm.network :private_network, ip: private_ip 29 | 30 | config.vm.provision "shell" do |s| 31 | s.path = "vagrant-provisioner.sh" 32 | s.args = [private_ip] 33 | end 34 | 35 | # Create a public network, which generally matched to bridged network. 36 | # Bridged networks make the machine appear as another physical device on 37 | # your network. 38 | # config.vm.network :public_network 39 | 40 | # If true, then any SSH connections made will enable agent forwarding. 41 | # Default value: false 42 | # config.ssh.forward_agent = true 43 | 44 | # Share an additional folder to the guest VM. The first argument is 45 | # the path on the host to the actual folder. The second argument is 46 | # the path on the guest to mount the folder. And the optional third 47 | # argument is a set of non-required options. 48 | # config.vm.synced_folder "../data", "/vagrant_data" 49 | 50 | # Provider-specific configuration so you can fine-tune various 51 | # backing providers for Vagrant. These expose provider-specific options. 52 | # Example for VirtualBox: 53 | # 54 | # config.vm.provider :virtualbox do |vb| 55 | # # Don't boot with headless mode 56 | # vb.gui = true 57 | # 58 | # # Use VBoxManage to customize the VM. For example to change memory: 59 | # vb.customize ["modifyvm", :id, "--memory", "1024"] 60 | # end 61 | # 62 | # View the documentation for the provider you're using for more 63 | # information on available options. 64 | 65 | # Enable provisioning with Puppet stand alone. Puppet manifests 66 | # are contained in a directory path relative to this Vagrantfile. 67 | # You will need to create the manifests directory and a manifest in 68 | # the file base.pp in the manifests_path directory. 69 | # 70 | # An example Puppet manifest to provision the message of the day: 71 | # 72 | # # group { "puppet": 73 | # # ensure => "present", 74 | # # } 75 | # # 76 | # # File { owner => 0, group => 0, mode => 0644 } 77 | # # 78 | # # file { '/etc/motd': 79 | # # content => "Welcome to your Vagrant-built virtual machine! 80 | # # Managed by Puppet.\n" 81 | # # } 82 | # 83 | # config.vm.provision :puppet do |puppet| 84 | # puppet.manifests_path = "manifests" 85 | # puppet.manifest_file = "site.pp" 86 | # end 87 | 88 | # Enable provisioning with chef solo, specifying a cookbooks path, roles 89 | # path, and data_bags path (all relative to this Vagrantfile), and adding 90 | # some recipes and/or roles. 91 | # 92 | # config.vm.provision :chef_solo do |chef| 93 | # chef.cookbooks_path = "../my-recipes/cookbooks" 94 | # chef.roles_path = "../my-recipes/roles" 95 | # chef.data_bags_path = "../my-recipes/data_bags" 96 | # chef.add_recipe "mysql" 97 | # chef.add_role "web" 98 | # 99 | # # You may also specify custom JSON attributes: 100 | # chef.json = { :mysql_password => "foo" } 101 | # end 102 | 103 | # Enable provisioning with chef server, specifying the chef server URL, 104 | # and the path to the validation key (relative to this Vagrantfile). 105 | # 106 | # The Opscode Platform uses HTTPS. Substitute your organization for 107 | # ORGNAME in the URL and validation key. 108 | # 109 | # If you have your own Chef Server, use the appropriate URL, which may be 110 | # HTTP instead of HTTPS depending on your configuration. Also change the 111 | # validation key to validation.pem. 112 | # 113 | # config.vm.provision :chef_client do |chef| 114 | # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME" 115 | # chef.validation_key_path = "ORGNAME-validator.pem" 116 | # end 117 | # 118 | # If you're using the Opscode platform, your validator client is 119 | # ORGNAME-validator, replacing ORGNAME with your organization name. 120 | # 121 | # If you have your own Chef Server, the default validation client name is 122 | # chef-validator, unless you changed the configuration. 123 | # 124 | # chef.validation_client_name = "ORGNAME-validator" 125 | end 126 | -------------------------------------------------------------------------------- /catlogs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for i in `find . -path '**/surefire-reports/**'`; do 3 | echo 4 | echo 5 | echo $i 6 | cat $i 7 | done -------------------------------------------------------------------------------- /glusterfs-java-filesystem-example/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | glusterfs-java-filesystem-project 5 | com.peircean.glusterfs 6 | 1.0.5-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | com.peircean.glusterfs 11 | glusterfs-java-filesystem-example 12 | 1.0.5-SNAPSHOT 13 | 14 | 15 | 172.31.31.31 16 | foo 17 | 18 | 19 | 20 | 21 | 22 | ${project.basedir}/src/main/resources 23 | true 24 | 25 | **/* 26 | 27 | 28 | 29 | 30 | 31 | org.codehaus.mojo 32 | exec-maven-plugin 33 | 1.2.1 34 | 35 | 36 | 37 | exec 38 | 39 | 40 | 41 | 42 | java 43 | 44 | -classpath 45 | 47 | 48 | com.peircean.glusterfs.example.Example 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | com.peircean.glusterfs 58 | glusterfs-java-filesystem 59 | 1.0.5-SNAPSHOT 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem-example/src/main/java/com/peircean/glusterfs/example/Example.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs.example; 2 | 3 | import com.peircean.glusterfs.GlusterFileSystem; 4 | import com.peircean.glusterfs.GlusterPath; 5 | 6 | import java.io.IOException; 7 | import java.net.URI; 8 | import java.net.URISyntaxException; 9 | import java.nio.file.*; 10 | import java.nio.file.attribute.FileAttribute; 11 | import java.nio.file.attribute.PosixFilePermission; 12 | import java.nio.file.attribute.PosixFilePermissions; 13 | import java.nio.file.spi.FileSystemProvider; 14 | import java.util.List; 15 | import java.util.Properties; 16 | import java.util.Set; 17 | import java.util.concurrent.TimeUnit; 18 | 19 | /** 20 | * @author Louis Zuckerman 21 | */ 22 | public class Example { 23 | public static FileSystemProvider getProvider(String scheme) { 24 | for (FileSystemProvider fsp : FileSystemProvider.installedProviders()) { 25 | if (fsp.getScheme().equals(scheme)) { 26 | return fsp; 27 | } 28 | } 29 | throw new IllegalArgumentException("No provider found for scheme: " + scheme); 30 | } 31 | 32 | public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException { 33 | Properties properties = new Properties(); 34 | properties.load(Example.class.getClassLoader().getResourceAsStream("example.properties")); 35 | 36 | String vagrantBox = properties.getProperty("glusterfs.server"); 37 | String volname = properties.getProperty("glusterfs.volume"); 38 | 39 | System.out.println(getProvider("gluster").toString()); 40 | 41 | String mountUri = "gluster://" + vagrantBox + ":" + volname + "/"; 42 | String testUri = "gluster://" + vagrantBox + ":" + volname + "/baz"; 43 | Path mountPath = Paths.get(new URI(mountUri)); 44 | 45 | FileSystem fileSystem = FileSystems.newFileSystem(new URI(mountUri), null); 46 | FileStore store = fileSystem.getFileStores().iterator().next(); 47 | System.out.println("TOTAL SPACE: " + store.getTotalSpace()); 48 | System.out.println("USABLE SPACE: " + store.getUsableSpace()); 49 | System.out.println("UNALLOCATED SPACE: " + store.getUnallocatedSpace()); 50 | System.out.println(fileSystem.toString()); 51 | 52 | String hidden = "/foo/.bar"; 53 | boolean isHidden = fileSystem.provider().isHidden(new GlusterPath(((GlusterFileSystem) fileSystem), hidden)); 54 | System.out.println("Is " + hidden + " hidden? " + isHidden); 55 | 56 | hidden = "/foo/bar"; 57 | isHidden = fileSystem.provider().isHidden(new GlusterPath(((GlusterFileSystem) fileSystem), hidden)); 58 | System.out.println("Is " + hidden + " hidden? " + isHidden); 59 | 60 | Set posixFilePermissions = PosixFilePermissions.fromString("rw-rw-rw-"); 61 | FileAttribute> attrs = PosixFilePermissions.asFileAttribute(posixFilePermissions); 62 | 63 | Path glusterPath = Paths.get(new URI(testUri)); 64 | System.out.println(glusterPath.getClass()); 65 | System.out.println(glusterPath); 66 | System.out.println(glusterPath.getFileSystem().toString()); 67 | 68 | try { 69 | Files.createFile(glusterPath, attrs); 70 | System.out.println("File created"); 71 | } catch (IOException e) { 72 | System.out.println("File exists, created at " + Files.getLastModifiedTime(glusterPath)); 73 | } 74 | String hello = "Hello, "; 75 | Files.write(glusterPath, hello.getBytes(), StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING); 76 | String world = "world!"; 77 | Files.write(glusterPath, world.getBytes(), StandardOpenOption.WRITE, StandardOpenOption.APPEND); 78 | long bazSize = Files.size(glusterPath); 79 | System.out.println("SIZE: " + bazSize); 80 | byte[] readBytes = Files.readAllBytes(glusterPath); 81 | System.out.println(hello + world + " == " + new String(readBytes)); 82 | System.out.println("Last modified: " + Files.getLastModifiedTime(glusterPath) + " (should be now)"); 83 | fileSystem.provider().checkAccess(glusterPath, AccessMode.READ, AccessMode.WRITE); 84 | System.out.println("Can read & write file"); 85 | try { 86 | fileSystem.provider().checkAccess(glusterPath, AccessMode.EXECUTE); 87 | System.out.println("Uh oh, file is executable, that's bad."); 88 | } catch (AccessDeniedException e) { 89 | System.out.println("Can't execute file, that's good."); 90 | } 91 | 92 | Path symlinkPath = Paths.get(new URI(mountUri + "symlink")); 93 | Path symlinkTarget = Paths.get(new URI(mountUri + "symlinktarget")); 94 | Files.createSymbolicLink(symlinkPath, symlinkTarget); 95 | System.out.println("SYMLINK: " + symlinkPath.toString() + " => " + Files.readSymbolicLink(symlinkPath)); 96 | 97 | Path copyPath = glusterPath.resolveSibling("copy"); 98 | // Files.createFile(copyPath, PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rw-rw-rw-"))); 99 | Files.copy(glusterPath, copyPath, StandardCopyOption.REPLACE_EXISTING); 100 | long copySize = Files.size(copyPath); 101 | System.out.println("Source and copy are " + (bazSize == copySize ? "" : "NOT") + " equal."); 102 | 103 | try { 104 | Files.newDirectoryStream(mountPath.resolve("bazzzzz")); 105 | } catch (NotDirectoryException e) { 106 | System.out.println("Can't list directory of a file, good."); 107 | } 108 | DirectoryStream.Filter filter = new DirectoryStream.Filter() { 109 | @Override 110 | public boolean accept(Path entry) throws IOException { 111 | return entry.endsWith("1"); 112 | } 113 | }; 114 | DirectoryStream stream = Files.newDirectoryStream(mountPath, filter); 115 | System.out.println("Mount contents:"); 116 | 117 | for (Path p : stream) { 118 | System.out.println(p.toString()); 119 | } 120 | 121 | filter = new DirectoryStream.Filter() { 122 | @Override 123 | public boolean accept(Path entry) throws IOException { 124 | return entry.endsWith("a"); 125 | } 126 | }; 127 | stream = Files.newDirectoryStream(mountPath, filter); 128 | System.out.println("Mount contents:"); 129 | 130 | for (Path p : stream) { 131 | System.out.println(p.toString()); 132 | } 133 | 134 | stream = Files.newDirectoryStream(mountPath); 135 | System.out.println("Mount contents:"); 136 | 137 | PathMatcher matcher = fileSystem.getPathMatcher("glob:**/*z"); 138 | for (Path p : stream) { 139 | System.out.println(p.toString()); 140 | if (matcher.matches(p)) { 141 | System.out.println(" **** MATCH ****"); 142 | } 143 | } 144 | 145 | stream = Files.newDirectoryStream(mountPath, "*z"); 146 | System.out.println("Mount contents:"); 147 | 148 | for (Path p : stream) { 149 | System.out.println(p.toString()); 150 | } 151 | 152 | WatchService watchService = fileSystem.newWatchService(); 153 | Path one = Paths.get(new URI("gluster://" + vagrantBox + ":" + volname + "/one")); 154 | 155 | System.out.println("STARTSWITH empty: " + one.startsWith("/")); 156 | one.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY, StandardWatchEventKinds.ENTRY_DELETE); 157 | for (int i = 0; i < 10; i++) { 158 | WatchKey take = watchService.poll(1, TimeUnit.SECONDS); 159 | if (null == take) { 160 | continue; 161 | } 162 | List> events = take.pollEvents(); 163 | for (WatchEvent e : events) { 164 | Path path = (Path) e.context(); 165 | Path absolutePath = one.resolve(path).toAbsolutePath(); 166 | boolean exists = Files.exists(absolutePath); 167 | System.out.println("EXISTS? " + exists); 168 | if (exists) { 169 | System.out.println("SIZE: " + Files.size(absolutePath)); 170 | } 171 | System.out.println(absolutePath); 172 | System.out.println(e.toString()); 173 | } 174 | take.reset(); 175 | } 176 | 177 | fileSystem.close(); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem-example/src/main/resources/example.properties: -------------------------------------------------------------------------------- 1 | glusterfs.server=${glusterfs.server} 2 | glusterfs.volume=${glusterfs.volume} 3 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem-example/src/test/java/ExampleTest.java: -------------------------------------------------------------------------------- 1 | import com.peircean.glusterfs.example.Example; 2 | import junit.framework.TestCase; 3 | import org.junit.Test; 4 | 5 | /** 6 | * @author Louis Zuckerman 7 | */ 8 | public class ExampleTest extends TestCase { 9 | 10 | @Test 11 | public void testGetProvider() { 12 | Example.getProvider("gluster"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/NOTICE.txt: -------------------------------------------------------------------------------- 1 | This product includes software developed by The Apache Software 2 | Foundation (http://www.apache.org/). 3 | 4 | [ src/main/java/com/peircean/glusterfs/borrowed/GlobPattern.java: 5 | Trivial changes only. See comments in top of that file for details. 6 | -Louis Zuckerman ] -------------------------------------------------------------------------------- /glusterfs-java-filesystem/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | glusterfs-java-filesystem-project 5 | com.peircean.glusterfs 6 | 1.0.5-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | glusterfs-java-filesystem 11 | 12 | 13 | 1.5.2 14 | 15 | 16 | 17 | 18 | 19 | org.apache.maven.plugins 20 | maven-surefire-plugin 21 | 2.16 22 | 23 | true 24 | once 25 | -ea 26 | true 27 | ${project.build.directory} 28 | 29 | **/*Test.java 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | com.peircean.libgfapi-jni 39 | libgfapi-all 40 | 1.0.5-SNAPSHOT 41 | 42 | 43 | 44 | org.projectlombok 45 | lombok 46 | 1.16.10 47 | provided 48 | 49 | 50 | 51 | org.mockito 52 | mockito-core 53 | 1.9.5 54 | test 55 | 56 | 57 | 58 | org.powermock 59 | powermock-module-junit4 60 | ${powermock.version} 61 | test 62 | 63 | 64 | 65 | org.powermock 66 | powermock-api-mockito 67 | ${powermock.version} 68 | test 69 | 70 | 71 | 72 | 73 | 74 | sonar 75 | 76 | 77 | 78 | com.atlassian.maven.plugins 79 | maven-clover2-plugin 80 | 4.0.2 81 | 82 | ${clover.licenseLocation} 83 | 84 | **/*.java 85 | 86 | 87 | **/borrowed/*.java 88 | **/*Test.java 89 | 90 | ${project.build.directory}/site/clover/clover.xml 91 | ${project.build.directory}/surefire-reports 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/sonar.txt: -------------------------------------------------------------------------------- 1 | # Run clover & sonar analyses separately 2 | mvn -Psonar clean clover2:setup test clover2:aggregate clover2:clover 3 | mvn -Psonar sonar:sonar 4 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/java/com/peircean/glusterfs/GlusterDirectoryIterator.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import com.peircean.libgfapi_jni.internal.GLFS; 4 | import com.peircean.libgfapi_jni.internal.structs.dirent; 5 | import lombok.Data; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.DirectoryStream; 9 | import java.nio.file.Path; 10 | import java.util.Iterator; 11 | import java.util.NoSuchElementException; 12 | 13 | @Data 14 | class GlusterDirectoryIterator implements Iterator { 15 | private GlusterDirectoryStream stream; 16 | private DirectoryStream.Filter filter; 17 | private dirent current, next; 18 | private GlusterPath nextPath; 19 | 20 | @Override 21 | public boolean hasNext() { 22 | advance(); 23 | if (null != filter) { 24 | try { 25 | while (next.d_ino != 0 && !filter.accept(nextPath)) { 26 | advance(); 27 | } 28 | } catch (IOException e) { 29 | current = null; 30 | next = null; 31 | return false; 32 | } 33 | } 34 | 35 | if (next != null && next.d_ino == 0) { 36 | current = null; 37 | next = null; 38 | return false; 39 | } 40 | 41 | return true; 42 | } 43 | 44 | void advance() { 45 | String name; 46 | do { 47 | current = new dirent(); 48 | long nextPtr = dirent.malloc(dirent.SIZE_OF); 49 | GLFS.glfs_readdir_r(stream.getDirHandle(), current, nextPtr); 50 | 51 | next = new dirent(); 52 | dirent.memmove(next, nextPtr, dirent.SIZE_OF); 53 | dirent.free(nextPtr); 54 | 55 | name = current.getName(); 56 | nextPath = (GlusterPath) stream.getDir().resolve(name); 57 | } while (name.equals(".") || name.equals("..")); 58 | } 59 | 60 | @Override 61 | public GlusterPath next() { 62 | if (nextPath == null) { 63 | throw new NoSuchElementException("No more entries"); 64 | } 65 | 66 | return nextPath; 67 | } 68 | 69 | @Override 70 | public void remove() { 71 | throw new UnsupportedOperationException(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/java/com/peircean/glusterfs/GlusterDirectoryStream.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import com.peircean.libgfapi_jni.internal.GLFS; 4 | import lombok.Data; 5 | 6 | import java.io.IOException; 7 | import java.nio.file.DirectoryStream; 8 | import java.nio.file.Path; 9 | import java.util.Iterator; 10 | 11 | @Data 12 | public class GlusterDirectoryStream implements DirectoryStream { 13 | private GlusterFileSystem fileSystem; 14 | private long dirHandle = 0; 15 | private GlusterDirectoryIterator iterator; 16 | private boolean closed = false; 17 | private GlusterPath dir; 18 | private DirectoryStream.Filter filter; 19 | 20 | @Override 21 | public Iterator iterator() { 22 | if (null != iterator || closed) { 23 | throw new IllegalStateException("Already iterating!"); 24 | } 25 | GlusterDirectoryIterator iterator = new GlusterDirectoryIterator(); 26 | iterator.setStream(this); 27 | iterator.setFilter(filter); 28 | this.iterator = iterator; 29 | return iterator; 30 | } 31 | 32 | @Override 33 | public void close() throws IOException { 34 | if (!closed) { 35 | GLFS.glfs_close(dirHandle); 36 | closed = true; 37 | } 38 | } 39 | 40 | public void open(GlusterPath path) { 41 | dir = path; 42 | if (dirHandle == 0) { 43 | dirHandle = GLFS.glfs_opendir(path.getFileSystem().getVolptr(), path.getString()); 44 | } else { 45 | throw new IllegalStateException("Already open!"); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/java/com/peircean/glusterfs/GlusterFileAttributes.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import lombok.Data; 4 | import com.peircean.libgfapi_jni.internal.structs.stat; 5 | 6 | import java.nio.file.attribute.*; 7 | import java.util.HashMap; 8 | import java.util.HashSet; 9 | import java.util.Map; 10 | import java.util.Set; 11 | 12 | @Data 13 | public class GlusterFileAttributes implements PosixFileAttributes { 14 | private static Map modeToPerms = new HashMap(); 15 | private static Map permsToMode; 16 | 17 | static { 18 | modeToPerms.put(0001, PosixFilePermission.OTHERS_EXECUTE); 19 | modeToPerms.put(0002, PosixFilePermission.OTHERS_WRITE); 20 | modeToPerms.put(0004, PosixFilePermission.OTHERS_READ); 21 | modeToPerms.put(0010, PosixFilePermission.GROUP_EXECUTE); 22 | modeToPerms.put(0020, PosixFilePermission.GROUP_WRITE); 23 | modeToPerms.put(0040, PosixFilePermission.GROUP_READ); 24 | modeToPerms.put(0100, PosixFilePermission.OWNER_EXECUTE); 25 | modeToPerms.put(0200, PosixFilePermission.OWNER_WRITE); 26 | modeToPerms.put(0400, PosixFilePermission.OWNER_READ); 27 | 28 | permsToMode = invertModeMap(modeToPerms); 29 | } 30 | 31 | private final int mode, uid, gid; 32 | private final long size, atime, ctime, mtime, inode; 33 | 34 | public static GlusterFileAttributes fromStat(stat stat) { 35 | return new GlusterFileAttributes(stat.st_mode, stat.st_uid, stat.st_gid, stat.st_size, 36 | stat.atime, stat.ctime, stat.mtime, stat.st_ino); 37 | } 38 | 39 | public static int parseAttrs(FileAttribute... attrs) { 40 | int mode = 0; 41 | for (FileAttribute a : attrs) { 42 | for (PosixFilePermission p : (Set) a.value()) { 43 | 44 | Integer perm = permsToMode.get(p); 45 | 46 | if (null != perm) { 47 | mode |= perm; 48 | } 49 | } 50 | } 51 | return mode; 52 | } 53 | 54 | private static Map invertModeMap(Map modeToPerms) { 55 | 56 | HashMap permsToMode = new HashMap<>(); 57 | 58 | for(Map.Entry entry : modeToPerms.entrySet()) { 59 | permsToMode.put(entry.getValue(), entry.getKey()); 60 | } 61 | 62 | return permsToMode; 63 | } 64 | 65 | @Override 66 | public UserPrincipal owner() { 67 | return new UserPrincipal() { 68 | @Override 69 | public String getName() { 70 | return String.valueOf(uid); 71 | } 72 | }; 73 | } 74 | 75 | @Override 76 | public GroupPrincipal group() { 77 | return new GroupPrincipal() { 78 | @Override 79 | public String getName() { 80 | return String.valueOf(gid); 81 | } 82 | }; 83 | } 84 | 85 | @Override 86 | public Set permissions() { 87 | Set permissions = new HashSet(); 88 | for (int mask : modeToPerms.keySet()) { 89 | if (mask == (mode & mask)) { 90 | permissions.add(modeToPerms.get(mask)); 91 | } 92 | } 93 | return permissions; 94 | } 95 | 96 | @Override 97 | public FileTime lastModifiedTime() { 98 | return FileTime.fromMillis(mtime * 1000); 99 | } 100 | 101 | @Override 102 | public FileTime lastAccessTime() { 103 | return FileTime.fromMillis(atime * 1000); 104 | } 105 | 106 | @Override 107 | public FileTime creationTime() { 108 | return FileTime.fromMillis(ctime * 1000); 109 | } 110 | 111 | @Override 112 | public boolean isRegularFile() { 113 | int mask = 0100000; 114 | return mask == (mode & mask); 115 | } 116 | 117 | @Override 118 | public boolean isDirectory() { 119 | int mask = 0040000; 120 | return mask == (mode & mask); 121 | } 122 | 123 | @Override 124 | public boolean isSymbolicLink() { 125 | int mask = 0120000; 126 | return mask == (mode & mask); 127 | } 128 | 129 | @Override 130 | public boolean isOther() { 131 | return !(isDirectory() || isRegularFile() || isSymbolicLink()); 132 | } 133 | 134 | @Override 135 | public long size() { 136 | return size; 137 | } 138 | 139 | @Override 140 | public Object fileKey() { 141 | return inode; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/java/com/peircean/glusterfs/GlusterFileChannel.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import com.peircean.libgfapi_jni.internal.GLFS; 4 | import com.peircean.libgfapi_jni.internal.GlusterOpenOption; 5 | import com.peircean.libgfapi_jni.internal.UtilJNI; 6 | import com.peircean.libgfapi_jni.internal.structs.stat; 7 | import lombok.AccessLevel; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | import java.io.IOException; 12 | import java.nio.ByteBuffer; 13 | import java.nio.MappedByteBuffer; 14 | import java.nio.channels.*; 15 | import java.nio.file.FileAlreadyExistsException; 16 | import java.nio.file.OpenOption; 17 | import java.nio.file.Path; 18 | import java.nio.file.StandardOpenOption; 19 | import java.nio.file.attribute.FileAttribute; 20 | import java.nio.file.attribute.PosixFilePermission; 21 | import java.util.*; 22 | 23 | /** 24 | * @author Louis Zuckerman 25 | */ 26 | @Data 27 | @NoArgsConstructor(access = AccessLevel.PACKAGE) 28 | public class GlusterFileChannel extends FileChannel { 29 | public static final Map optionMap = new HashMap(); 30 | public static final Map perms = new HashMap(); 31 | 32 | static { 33 | optionMap.put(StandardOpenOption.APPEND, GlusterOpenOption.O_APPEND); 34 | optionMap.put(StandardOpenOption.CREATE, GlusterOpenOption.O_CREAT); 35 | optionMap.put(StandardOpenOption.CREATE_NEW, GlusterOpenOption.O_CREAT | GlusterOpenOption.O_EXCL); 36 | optionMap.put(StandardOpenOption.DSYNC, GlusterOpenOption.O_DSYNC); 37 | optionMap.put(StandardOpenOption.READ, GlusterOpenOption.O_RDONLY); 38 | optionMap.put(StandardOpenOption.WRITE, GlusterOpenOption.O_RDWR); 39 | optionMap.put(StandardOpenOption.TRUNCATE_EXISTING, GlusterOpenOption.O_TRUNC); 40 | 41 | perms.put(PosixFilePermission.OTHERS_EXECUTE, 0001); 42 | perms.put(PosixFilePermission.OTHERS_WRITE, 0002); 43 | perms.put(PosixFilePermission.OTHERS_READ, 0004); 44 | perms.put(PosixFilePermission.GROUP_EXECUTE, 0010); 45 | perms.put(PosixFilePermission.GROUP_WRITE, 0020); 46 | perms.put(PosixFilePermission.GROUP_READ, 0040); 47 | perms.put(PosixFilePermission.OWNER_EXECUTE, 0100); 48 | perms.put(PosixFilePermission.OWNER_WRITE, 0200); 49 | perms.put(PosixFilePermission.OWNER_READ, 0400); 50 | } 51 | 52 | private GlusterFileSystem fileSystem; 53 | private GlusterPath path; 54 | private Set options = new HashSet<>(); 55 | private FileAttribute attrs[] = null; 56 | private long fileptr; 57 | private long position; 58 | private boolean closed = false; 59 | 60 | void init(GlusterFileSystem fileSystem, Path path, Set options, FileAttribute... attrs) throws IOException { 61 | this.fileSystem = fileSystem; 62 | if (!path.isAbsolute()) { 63 | throw new IllegalStateException("Only absolute paths are supported at this time"); 64 | } 65 | this.path = (GlusterPath) path; 66 | this.options = options; 67 | 68 | int flags = parseOptions(options); 69 | int mode = GlusterFileAttributes.parseAttrs(attrs); 70 | 71 | String pathString = path.toUri().getPath(); 72 | boolean createNew = options.contains(StandardOpenOption.CREATE_NEW); 73 | if (options.contains(StandardOpenOption.CREATE) || createNew) { 74 | fileptr = GLFS.glfs_creat(fileSystem.getVolptr(), pathString, flags, mode); 75 | } 76 | 77 | if (createNew && 0 == fileptr) { 78 | throw new FileAlreadyExistsException(path.toString()); 79 | } 80 | 81 | if (0 >= fileptr) { 82 | fileptr = GLFS.glfs_open(fileSystem.getVolptr(), pathString, flags); 83 | } 84 | 85 | if (0 >= fileptr) { 86 | throw new IOException("Unable to create or open file '" + pathString + "' on volume '" + fileSystem.toString() + "'"); 87 | } 88 | } 89 | 90 | int parseOptions(Set options) { 91 | int opt = 0; 92 | for (OpenOption o : options) { 93 | if (!optionMap.containsKey(o)) { 94 | throw new UnsupportedOperationException("Option " + o + " is not supported at this time"); 95 | } 96 | opt |= optionMap.get(o); 97 | } 98 | return opt; 99 | } 100 | 101 | @Override 102 | public int read(ByteBuffer byteBuffer) throws IOException { 103 | guardClosed(); 104 | byte[] bytes = byteBuffer.array(); 105 | long read = GLFS.glfs_read(fileptr, bytes, bytes.length, 0); 106 | if (read < 0) { 107 | throw new IOException(UtilJNI.strerror()); 108 | } 109 | position += read; 110 | byteBuffer.position((int)read); 111 | return (int) read; 112 | } 113 | 114 | @Override 115 | public long read(ByteBuffer[] byteBuffers, int offset, int length) throws IOException { 116 | guardClosed(); 117 | if (length < 0 || length > byteBuffers.length - offset) { 118 | throw new IndexOutOfBoundsException("Length provided is invalid."); 119 | } 120 | if (offset < 0 || offset > byteBuffers.length) { 121 | throw new IndexOutOfBoundsException("Offset provided is invalid."); 122 | } 123 | if (!options.contains(StandardOpenOption.READ)) { 124 | throw new NonReadableChannelException(); 125 | } 126 | 127 | long totalRead = 0L; 128 | try { 129 | totalRead = readHelper(byteBuffers, offset, length); 130 | } finally { 131 | if (totalRead > 0) { 132 | position += totalRead; 133 | } 134 | } 135 | return totalRead; 136 | } 137 | 138 | long readHelper(ByteBuffer[] byteBuffers, int offset, int length) throws IOException { 139 | long totalRead = 0L; 140 | boolean endOfStream = false; 141 | for (int i = offset; i < length + offset && !endOfStream; i++) { 142 | byte[] bytes = byteBuffers[i].array(); 143 | int remaining; 144 | while ((remaining = byteBuffers[i].remaining()) > 0) { 145 | long read = GLFS.glfs_read(fileptr, bytes, remaining, 0); 146 | if (read < 0) { 147 | throw new IOException(UtilJNI.strerror()); 148 | } 149 | totalRead += read; 150 | byteBuffers[i].position((int) read); 151 | if (0 == read) { 152 | endOfStream = true; 153 | break; 154 | } 155 | } 156 | } 157 | 158 | if (endOfStream && totalRead == 0) { 159 | return -1; 160 | } 161 | 162 | return totalRead; 163 | } 164 | 165 | @Override 166 | public int write(ByteBuffer byteBuffer) throws IOException { 167 | guardClosed(); 168 | byte[] buf = byteBuffer.array(); 169 | int written = GLFS.glfs_write(fileptr, buf, buf.length, 0); 170 | if (written < 0) { 171 | throw new IOException(UtilJNI.strerror()); 172 | } 173 | position += written; 174 | byteBuffer.position(written); 175 | return written; 176 | } 177 | 178 | @Override 179 | public long write(ByteBuffer[] byteBuffers, int offset, int length) throws IOException { 180 | guardClosed(); 181 | if (offset < 0 || offset > byteBuffers.length) { 182 | throw new IndexOutOfBoundsException("Offset provided is invalid."); 183 | } 184 | if (length < 0 || length > byteBuffers.length - offset) { 185 | throw new IndexOutOfBoundsException("Length provided is invalid"); 186 | } 187 | if (!options.contains(StandardOpenOption.WRITE)) { 188 | throw new NonWritableChannelException(); 189 | } 190 | 191 | long totalWritten = 0L; 192 | 193 | for (int i = offset; i < length + offset; i++) { 194 | int remaining = byteBuffers[i].remaining(); 195 | while (remaining > 0) { 196 | byte[] bytes = byteBuffers[i].array(); 197 | int written = GLFS.glfs_write(fileptr, bytes, remaining, 0); 198 | if (written < 0) { 199 | throw new IOException(); 200 | } 201 | position += written; 202 | byteBuffers[i].position(written); 203 | totalWritten += written; 204 | remaining = byteBuffers[i].remaining(); 205 | } 206 | } 207 | return totalWritten; 208 | } 209 | 210 | @Override 211 | public long position() throws IOException { 212 | guardClosed(); 213 | return position; 214 | } 215 | 216 | @Override 217 | public FileChannel position(long offset) throws IOException { 218 | guardClosed(); 219 | if (offset < 0) { 220 | throw new IllegalArgumentException("offset can't be negative"); 221 | } 222 | int whence = 0; //SEEK_SET 223 | int seek = GLFS.glfs_lseek(fileptr, offset, whence); 224 | position = offset; 225 | return this; 226 | } 227 | 228 | void guardClosed() throws ClosedChannelException { 229 | if (closed) { 230 | throw new ClosedChannelException(); 231 | } 232 | } 233 | 234 | @Override 235 | public long size() throws IOException { 236 | stat stat = new stat(); 237 | int retval = GLFS.glfs_fstat(fileptr, stat); 238 | if (0 != retval) { 239 | throw new IOException("fstat failed"); 240 | } 241 | return stat.st_size; 242 | } 243 | 244 | @Override 245 | public FileChannel truncate(long l) throws IOException { 246 | return null; //To change body of implemented methods use File | Settings | File Templates. 247 | } 248 | 249 | @Override 250 | public void force(boolean b) throws IOException { 251 | guardClosed(); 252 | int fsync = GLFS.glfs_fsync(fileptr); 253 | if (0 != fsync) { 254 | throw new IOException("Unable to fsync"); 255 | } 256 | } 257 | 258 | @Override 259 | public long transferTo(long l, long l2, WritableByteChannel writableByteChannel) throws IOException { 260 | return 0; //To change body of implemented methods use File | Settings | File Templates. 261 | } 262 | 263 | @Override 264 | public long transferFrom(ReadableByteChannel readableByteChannel, long l, long l2) throws IOException { 265 | return 0; //To change body of implemented methods use File | Settings | File Templates. 266 | } 267 | 268 | @Override 269 | public int read(ByteBuffer byteBuffer, long position) throws IOException { 270 | guardClosed(); 271 | if (position < 0) { 272 | throw new IllegalArgumentException(); 273 | } 274 | if (!options.contains(StandardOpenOption.READ)) { 275 | throw new NonReadableChannelException(); 276 | } 277 | if (position >= size()) { 278 | return -1; 279 | } 280 | int whence = 0; //SEEK_SET 281 | int seek = GLFS.glfs_lseek(fileptr, position, whence); 282 | if (seek < 0) { 283 | throw new IOException(); 284 | } 285 | byte[] bytes = byteBuffer.array(); 286 | long read = GLFS.glfs_read(fileptr, bytes, bytes.length, 0); 287 | 288 | if (0 > read) { 289 | throw new IOException(); 290 | } 291 | 292 | seek = GLFS.glfs_lseek(fileptr, this.position, whence); 293 | 294 | if (0 > seek) { 295 | throw new IOException(UtilJNI.strerror()); 296 | } 297 | 298 | return (int) read; 299 | } 300 | 301 | @Override 302 | public int write(ByteBuffer byteBuffer, long position) throws IOException { 303 | guardClosed(); 304 | if (position < 0) { 305 | throw new IllegalArgumentException(); 306 | } 307 | if (!options.contains(StandardOpenOption.WRITE)) { 308 | throw new NonWritableChannelException(); 309 | } 310 | if (position >= size()) { 311 | byte[] bytes = byteBuffer.array(); 312 | byte[] temp = Arrays.copyOf(bytes, bytes.length + (int) (position - size())); 313 | byteBuffer = ByteBuffer.wrap(temp); 314 | } 315 | int whence = 0; //SEEK_SET 316 | int seek = GLFS.glfs_lseek(fileptr, position, whence); 317 | if (seek < 0) { 318 | throw new IOException(); 319 | } 320 | byte[] bytes = byteBuffer.array(); 321 | long written = GLFS.glfs_write(fileptr, bytes, bytes.length, 0); 322 | seek = GLFS.glfs_lseek(fileptr, this.position, whence); 323 | if (seek < 0) { 324 | throw new IOException(); 325 | } 326 | return (int) written; 327 | } 328 | 329 | @Override 330 | public MappedByteBuffer map(MapMode mapMode, long l, long l2) throws IOException { 331 | return null; //To change body of implemented methods use File | Settings | File Templates. 332 | } 333 | 334 | @Override 335 | public FileLock lock(long l, long l2, boolean b) throws IOException { 336 | return null; //To change body of implemented methods use File | Settings | File Templates. 337 | } 338 | 339 | @Override 340 | public FileLock tryLock(long l, long l2, boolean b) throws IOException { 341 | return null; //To change body of implemented methods use File | Settings | File Templates. 342 | } 343 | 344 | @Override 345 | protected void implCloseChannel() throws IOException { 346 | if (!closed) { 347 | int close = GLFS.glfs_close(fileptr); 348 | if (0 != close) { 349 | throw new IOException("Close returned nonzero"); 350 | } 351 | closed = true; 352 | } 353 | } 354 | 355 | } 356 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/java/com/peircean/glusterfs/GlusterFileStore.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import lombok.AccessLevel; 4 | import lombok.Data; 5 | import lombok.NonNull; 6 | import lombok.RequiredArgsConstructor; 7 | 8 | import java.io.IOException; 9 | import java.nio.file.FileStore; 10 | import java.nio.file.attribute.FileAttributeView; 11 | import java.nio.file.attribute.FileStoreAttributeView; 12 | 13 | /** 14 | * @author Louis Zuckerman 15 | */ 16 | @Data 17 | @RequiredArgsConstructor(access = AccessLevel.PACKAGE) 18 | public class GlusterFileStore extends FileStore { 19 | public static final String GLUSTERFS = "glusterfs"; 20 | @NonNull 21 | private GlusterFileSystem fileSystem; 22 | 23 | @Override 24 | public String name() { 25 | return fileSystem.getVolname(); 26 | } 27 | 28 | @Override 29 | public String type() { 30 | return GLUSTERFS; 31 | } 32 | 33 | @Override 34 | public boolean isReadOnly() { 35 | return false; 36 | } 37 | 38 | @Override 39 | public long getTotalSpace() throws IOException { 40 | GlusterFileSystemProvider provider = (GlusterFileSystemProvider) fileSystem.provider(); 41 | return provider.getTotalSpace(fileSystem.getVolptr()); 42 | } 43 | 44 | @Override 45 | public long getUsableSpace() throws IOException { 46 | GlusterFileSystemProvider provider = (GlusterFileSystemProvider) fileSystem.provider(); 47 | return provider.getUsableSpace(fileSystem.getVolptr()); 48 | } 49 | 50 | @Override 51 | public long getUnallocatedSpace() throws IOException { 52 | GlusterFileSystemProvider provider = (GlusterFileSystemProvider) fileSystem.provider(); 53 | return provider.getUnallocatedSpace(fileSystem.getVolptr()); 54 | } 55 | 56 | @Override 57 | public boolean supportsFileAttributeView(Class aClass) { 58 | throw new UnsupportedOperationException(); 59 | } 60 | 61 | @Override 62 | public boolean supportsFileAttributeView(String s) { 63 | throw new UnsupportedOperationException(); 64 | } 65 | 66 | @Override 67 | public V getFileStoreAttributeView(Class vClass) { 68 | throw new UnsupportedOperationException(); 69 | } 70 | 71 | @Override 72 | public Object getAttribute(String s) throws IOException { 73 | throw new UnsupportedOperationException(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/java/com/peircean/glusterfs/GlusterFileSystem.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import com.peircean.glusterfs.borrowed.GlobPattern; 4 | import lombok.*; 5 | 6 | import java.io.IOException; 7 | import java.nio.file.*; 8 | import java.nio.file.attribute.UserPrincipalLookupService; 9 | import java.nio.file.spi.FileSystemProvider; 10 | import java.util.ArrayList; 11 | import java.util.Collections; 12 | import java.util.List; 13 | import java.util.Set; 14 | import java.util.regex.Pattern; 15 | 16 | /** 17 | * @author Louis Zuckerman 18 | */ 19 | @Getter(AccessLevel.PACKAGE) 20 | @Setter(AccessLevel.PACKAGE) 21 | @EqualsAndHashCode(exclude = {"provider", "volptr"}, callSuper = false) 22 | @RequiredArgsConstructor(access = AccessLevel.PACKAGE) 23 | public class GlusterFileSystem extends FileSystem { 24 | private static final String SEPARATOR = "/"; 25 | @NonNull 26 | private final GlusterFileSystemProvider provider; 27 | @NonNull 28 | private final String host; 29 | @NonNull 30 | private final String volname; 31 | @NonNull 32 | private long volptr; 33 | 34 | @Override 35 | public FileSystemProvider provider() { 36 | return provider; 37 | } 38 | 39 | @Override 40 | public void close() throws IOException { 41 | if (isOpen()) { 42 | int fini = provider.close(volptr); 43 | if (0 != fini) { 44 | throw new IOException("Unable to close filesystem: " + volname); 45 | } 46 | volptr = -1; 47 | } 48 | } 49 | 50 | @Override 51 | public boolean isOpen() { 52 | return volptr > 0; 53 | } 54 | 55 | @Override 56 | public boolean isReadOnly() { 57 | return false; 58 | } 59 | 60 | @Override 61 | public String getSeparator() { 62 | return SEPARATOR; 63 | } 64 | 65 | @Override 66 | public Iterable getRootDirectories() { 67 | GlusterPath root = new GlusterPath(this, "/"); 68 | List list = new ArrayList(1); 69 | list.add(root); 70 | return Collections.unmodifiableList(list); 71 | } 72 | 73 | @Override 74 | public Iterable getFileStores() { 75 | GlusterFileStore store = new GlusterFileStore(this); 76 | List stores = new ArrayList(1); 77 | stores.add(store); 78 | return Collections.unmodifiableList(stores); 79 | } 80 | 81 | @Override 82 | public Set supportedFileAttributeViews() { 83 | throw new UnsupportedOperationException(); 84 | } 85 | 86 | @Override 87 | public Path getPath(String s, String... strings) { 88 | boolean absolute = s.startsWith("/"); 89 | if (absolute) { 90 | s = s.substring(1); 91 | } 92 | String[] parts; 93 | if (null != strings && strings.length > 0) { 94 | parts = new String[1 + strings.length]; 95 | parts[0] = s; 96 | System.arraycopy(strings, 0, parts, 1, strings.length); 97 | } else { 98 | parts = new String[]{s}; 99 | } 100 | return new GlusterPath(this, parts, absolute); 101 | } 102 | 103 | @Override 104 | public PathMatcher getPathMatcher(String s) { 105 | if (!s.contains(":")) { 106 | throw new IllegalArgumentException("PathMatcher requires input syntax:expression"); 107 | } 108 | String[] parts = s.split(":", 2); 109 | Pattern pattern; 110 | if ("glob".equals(parts[0])) { 111 | pattern = GlobPattern.compile(parts[1]); 112 | } else if ("regex".equals(parts[0])) { 113 | pattern = Pattern.compile(parts[1]); 114 | } else { 115 | throw new UnsupportedOperationException("Unknown PathMatcher syntax: " + parts[0]); 116 | } 117 | 118 | return new GlusterPathMatcher(pattern); 119 | } 120 | 121 | @Override 122 | public UserPrincipalLookupService getUserPrincipalLookupService() { 123 | throw new UnsupportedOperationException(); 124 | } 125 | 126 | @Override 127 | public WatchService newWatchService() throws IOException { 128 | return new GlusterWatchService(); 129 | } 130 | 131 | public String toString() { 132 | return provider.getScheme() + "://" + host + ":" + volname; 133 | } 134 | } 135 | 136 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/java/com/peircean/glusterfs/GlusterFileSystemProvider.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import com.peircean.libgfapi_jni.internal.GLFS; 4 | import com.peircean.libgfapi_jni.internal.structs.stat; 5 | import com.peircean.libgfapi_jni.internal.structs.statvfs; 6 | import lombok.AccessLevel; 7 | import lombok.Getter; 8 | 9 | import java.io.IOException; 10 | import java.net.URI; 11 | import java.nio.ByteBuffer; 12 | import java.nio.channels.FileChannel; 13 | import java.nio.channels.SeekableByteChannel; 14 | import java.nio.file.*; 15 | import java.nio.file.attribute.*; 16 | import java.nio.file.spi.FileSystemProvider; 17 | import java.util.*; 18 | 19 | import static com.peircean.libgfapi_jni.internal.GLFS.*; 20 | 21 | /** 22 | * @author Louis Zuckerman 23 | */ 24 | public class GlusterFileSystemProvider extends FileSystemProvider { 25 | 26 | public static final String GLUSTER = "gluster"; 27 | public static final int GLUSTERD_PORT = 24007; 28 | public static final String TCP = "tcp"; 29 | @Getter(AccessLevel.PACKAGE) 30 | private static Map cache = new HashMap(); 31 | 32 | @Override 33 | public String getScheme() { 34 | return GLUSTER; 35 | } 36 | 37 | @Override 38 | public FileSystem newFileSystem(URI uri, Map stringMap) throws IOException { 39 | String authorityString = uri.getAuthority(); 40 | String[] authority = parseAuthority(authorityString); 41 | 42 | String volname = authority[1]; 43 | long volptr = glfsNew(volname); 44 | 45 | glfsSetVolfileServer(authority[0], volptr); 46 | 47 | glfsInit(authorityString, volptr); 48 | 49 | // GLFS.glfs_set_logging(volptr, "/tmp/gluster-java.log", 9); 50 | 51 | GlusterFileSystem fileSystem = new GlusterFileSystem(this, authority[0], volname, volptr); 52 | cache.put(authorityString, fileSystem); 53 | 54 | return fileSystem; 55 | } 56 | 57 | String[] parseAuthority(String authority) { 58 | if (!authority.contains(":")) { 59 | throw new IllegalArgumentException("URI must be of the form 'gluster://server:volume/path"); 60 | } 61 | String[] aarr = authority.split(":"); 62 | if (aarr.length != 2 || aarr[0].isEmpty() || aarr[1].isEmpty()) { 63 | throw new IllegalArgumentException("URI must be of the form 'gluster://server:volume/path"); 64 | } 65 | 66 | return aarr; 67 | } 68 | 69 | long glfsNew(String volname) { 70 | long volptr = glfs_new(volname); 71 | if (0 == volptr) { 72 | throw new IllegalArgumentException("Failed to create new client for volume: " + volname); 73 | } 74 | return volptr; 75 | } 76 | 77 | void glfsSetVolfileServer(String host, long volptr) { 78 | int setServer = glfs_set_volfile_server(volptr, TCP, host, GLUSTERD_PORT); 79 | if (0 != setServer) { 80 | throw new IllegalArgumentException("Failed to set server address: " + host); 81 | } 82 | } 83 | 84 | void glfsInit(String authorityString, long volptr) { 85 | int init = glfs_init(volptr); 86 | if (0 != init) { 87 | throw new IllegalArgumentException("Failed to initialize glusterfs client: " + authorityString); 88 | } 89 | } 90 | 91 | @Override 92 | public FileSystem getFileSystem(URI uri) { 93 | if (!cache.containsKey(uri.getAuthority())) { 94 | throw new FileSystemNotFoundException("No cached filesystem for: " + uri.getAuthority()); 95 | } 96 | return cache.get(uri.getAuthority()); 97 | } 98 | 99 | @Override 100 | public Path getPath(URI uri) { 101 | if (!uri.getScheme().equals(getScheme())) { 102 | throw new IllegalArgumentException("No support for scheme: " + uri.getScheme()); 103 | } 104 | try { 105 | FileSystem fileSystem = getFileSystem(uri); 106 | return fileSystem.getPath(uri.getPath()); 107 | } catch (FileSystemNotFoundException e) { 108 | } 109 | 110 | try { 111 | return newFileSystem(uri, null).getPath(uri.getPath()); 112 | } catch (IOException e) { 113 | throw new FileSystemNotFoundException("Unable to open a connection to " + uri.getAuthority()); 114 | } 115 | } 116 | 117 | @Override 118 | public SeekableByteChannel newByteChannel(Path path, Set openOptions, FileAttribute... fileAttributes) throws IOException { 119 | return newFileChannelHelper(path, openOptions, fileAttributes); 120 | } 121 | 122 | @Override 123 | public FileChannel newFileChannel(Path path, Set options, FileAttribute... attrs) throws IOException { 124 | return newFileChannelHelper(path, options, attrs); 125 | } 126 | 127 | FileChannel newFileChannelHelper(Path path, Set options, FileAttribute[] attrs) throws IOException { 128 | GlusterFileChannel channel = new GlusterFileChannel(); 129 | channel.init((GlusterFileSystem) getFileSystem(path.toUri()), path, options, attrs); 130 | return channel; 131 | } 132 | 133 | @Override 134 | public DirectoryStream newDirectoryStream(Path path, DirectoryStream.Filter filter) throws IOException { 135 | if (!Files.isDirectory(path)) { 136 | throw new NotDirectoryException("Not a directory! " + path.toString()); 137 | } 138 | GlusterPath glusterPath = (GlusterPath) path; 139 | GlusterDirectoryStream stream = new GlusterDirectoryStream(); 140 | stream.setFileSystem(glusterPath.getFileSystem()); 141 | stream.open(glusterPath); 142 | stream.setFilter(filter); 143 | 144 | return stream; 145 | } 146 | 147 | @Override 148 | public void createDirectory(Path path, FileAttribute... fileAttributes) throws IOException { 149 | if (Files.exists(path)) { 150 | throw new FileAlreadyExistsException(path.toString()); 151 | } 152 | if (!Files.exists(path.getParent())) { 153 | throw new IOException(); 154 | } 155 | 156 | int mode = 0775; 157 | 158 | if (fileAttributes.length > 0) { 159 | mode = GlusterFileAttributes.parseAttrs(fileAttributes); 160 | } 161 | 162 | int ret = GLFS.glfs_mkdir(((GlusterFileSystem) path.getFileSystem()).getVolptr(), path.toString(), mode); 163 | 164 | if (ret < 0) { 165 | throw new IOException(path.toString()); 166 | } 167 | } 168 | 169 | @Override 170 | public void delete(Path path) throws IOException { 171 | if (!Files.exists(path)) { 172 | throw new NoSuchFileException(path.toString()); 173 | } 174 | if (Files.isDirectory(path)) { 175 | if(!directoryIsEmpty(path)) { 176 | throw new DirectoryNotEmptyException(path.toString()); 177 | } 178 | 179 | int ret = GLFS.glfs_rmdir(((GlusterFileSystem)path.getFileSystem()).getVolptr(), path.toString()); 180 | 181 | if (ret < 0) { 182 | throw new IOException(path.toString()); 183 | } 184 | } else { 185 | int ret = GLFS.glfs_unlink(((GlusterFileSystem) path.getFileSystem()).getVolptr(), path.toString()); 186 | 187 | if (ret < 0) { 188 | throw new IOException(path.toString()); 189 | } 190 | } 191 | } 192 | 193 | @Override 194 | public void copy(Path path, Path path2, CopyOption... copyOptions) throws IOException { 195 | guardAbsolutePath(path); 196 | guardAbsolutePath(path2); 197 | guardFileExists(path); 198 | 199 | boolean targetExists = Files.exists(path2); 200 | if (targetExists && isSameFile(path, path2)) { 201 | return; 202 | } 203 | 204 | boolean overwrite = false; 205 | boolean copyAttributes = false; 206 | for (CopyOption co : copyOptions) { 207 | if (StandardCopyOption.ATOMIC_MOVE.equals(co)) { 208 | throw new UnsupportedOperationException("Atomic move not supported"); 209 | } 210 | if (StandardCopyOption.REPLACE_EXISTING.equals(co)) { 211 | overwrite = true; 212 | } 213 | if (StandardCopyOption.COPY_ATTRIBUTES.equals(co)) { 214 | copyAttributes = true; 215 | } 216 | } 217 | 218 | if (!overwrite && targetExists) { 219 | throw new FileAlreadyExistsException("Target " + path2 + " exists and REPLACE_EXISTING not specified"); 220 | } 221 | if (Files.isDirectory(path2) && !directoryIsEmpty(path2)) { 222 | throw new DirectoryNotEmptyException("Target not empty: " + path2); 223 | } 224 | if (Files.isDirectory(path)) { 225 | Files.createDirectory(path2); 226 | } else { 227 | Files.createFile(path2, PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rw-rw-r--"))); 228 | copyFileContent(path, path2); 229 | if (copyAttributes) { 230 | copyFileAttributes(path, path2); 231 | } 232 | } 233 | } 234 | 235 | void copyFileAttributes(Path path, Path path2) throws IOException { 236 | stat stat = new stat(); 237 | long volptr = ((GlusterFileSystem) path.getFileSystem()).getVolptr(); 238 | int retStat = glfs_stat(volptr, path.toString(), stat); 239 | int retChmod = 0; 240 | if (0664 != stat.st_mode) { 241 | retChmod = GLFS.glfs_chmod(volptr, path2.toString(), stat.st_mode); 242 | } 243 | if (retStat < 0 || retChmod < 0) { 244 | throw new IOException("Could not copy file attributes."); 245 | } 246 | } 247 | 248 | void copyFileContent(Path path, Path path2) throws IOException { 249 | Set options = new HashSet<>(); 250 | options.add(StandardOpenOption.READ); 251 | 252 | //TODO: investigate 8kB byte array limitation 253 | byte[] readBytes = new byte[8192]; 254 | FileChannel channel = newFileChannel(path, options); 255 | ByteBuffer readBuffer = ByteBuffer.wrap(readBytes); 256 | 257 | boolean writtenTo = false; 258 | int read = channel.read(readBuffer); 259 | 260 | while (read > 0) { 261 | byte[] writeBytes = Arrays.copyOf(readBytes, read); 262 | if (!writtenTo) { 263 | Files.write(path2, writeBytes, StandardOpenOption.TRUNCATE_EXISTING); 264 | writtenTo = true; 265 | } else { 266 | Files.write(path2, writeBytes, StandardOpenOption.APPEND); 267 | } 268 | read = channel.read(readBuffer); 269 | } 270 | channel.close(); 271 | } 272 | 273 | boolean directoryIsEmpty(Path path) throws IOException { 274 | try (DirectoryStream stream = newDirectoryStream(path, null)) { 275 | if (stream.iterator().hasNext()) { 276 | return false; 277 | } 278 | return true; 279 | } 280 | } 281 | 282 | @Override 283 | public void move(Path path, Path path2, CopyOption... copyOptions) throws IOException { 284 | guardAbsolutePath(path); 285 | guardAbsolutePath(path2); 286 | guardFileExists(path); 287 | if (Files.exists(path2) && isSameFile(path, path2)) { 288 | return; 289 | } 290 | 291 | boolean overwrite = false; 292 | for (CopyOption co : copyOptions) { 293 | if (StandardCopyOption.ATOMIC_MOVE.equals(co)) { 294 | throw new AtomicMoveNotSupportedException(path.toString(), path2.toString(), "Atomic move not supported"); 295 | } 296 | if (StandardCopyOption.REPLACE_EXISTING.equals(co)) { 297 | overwrite = true; 298 | } 299 | } 300 | 301 | FileSystem fileSystem = path.getFileSystem(); 302 | 303 | if (!overwrite && Files.exists(path2)) { 304 | throw new FileAlreadyExistsException("Target " + path2 + " exists and REPLACE_EXISTING not specified"); 305 | } 306 | if (Files.isDirectory(path2) && !directoryIsEmpty(path2)) { 307 | throw new DirectoryNotEmptyException("Target not empty: " + path2); 308 | } 309 | if (!fileSystem.equals(path2.getFileSystem())) { 310 | throw new UnsupportedOperationException("Can not move file to a different file system"); 311 | } 312 | GLFS.glfs_rename(((GlusterFileSystem) fileSystem).getVolptr(), ((GlusterPath) path).getString(), ((GlusterPath) path2).getString()); 313 | } 314 | 315 | void guardFileExists(Path path) throws NoSuchFileException { 316 | if (!Files.exists(path)) { 317 | throw new NoSuchFileException(path.toString()); 318 | } 319 | } 320 | 321 | void guardAbsolutePath(Path p) { 322 | if (!p.isAbsolute()) { 323 | throw new UnsupportedOperationException("Relative paths not supported: " + p); 324 | } 325 | } 326 | 327 | @Override 328 | public boolean isSameFile(Path path, Path path2) throws IOException { 329 | if (path.equals(path2)) { 330 | return true; 331 | } 332 | if (!path.getFileSystem().equals(path2.getFileSystem())) { //if file system differs, then we don't need to check provider; we know the files differ 333 | return false; 334 | } 335 | guardFileExists(path); 336 | guardFileExists(path2); 337 | 338 | stat stat1 = statPath(path); 339 | stat stat2 = statPath(path2); 340 | 341 | return stat1.st_ino == stat2.st_ino; 342 | } 343 | 344 | stat statPath(Path path) throws IOException { 345 | stat stat = new stat(); 346 | String pathString = ((GlusterPath) path).getString(); 347 | int ret = GLFS.glfs_stat(((GlusterFileSystem) path.getFileSystem()).getVolptr(), 348 | pathString, stat); 349 | if (ret != 0) { 350 | throw new IOException("Stat failed for " + pathString); 351 | } 352 | return stat; 353 | } 354 | 355 | @Override 356 | public boolean isHidden(Path path) throws IOException { 357 | return ((GlusterPath) path.getFileName()).getParts()[0].startsWith("."); 358 | } 359 | 360 | @Override 361 | public FileStore getFileStore(Path path) throws IOException { 362 | if (Files.exists(path)) { 363 | return path.getFileSystem().getFileStores().iterator().next(); 364 | } else { 365 | throw new NoSuchFileException(path.toString()); 366 | } 367 | } 368 | 369 | @Override 370 | public void checkAccess(Path path, AccessMode... accessModes) throws IOException { 371 | long volptr = ((GlusterFileSystem) path.getFileSystem()).getVolptr(); 372 | String pathString = ((GlusterPath) path).getString(); 373 | 374 | stat stat = new stat(); 375 | int ret = GLFS.glfs_lstat(volptr, pathString, stat); 376 | 377 | if (-1 == ret) { 378 | throw new NoSuchFileException(""); 379 | } 380 | 381 | for (AccessMode m : accessModes) { 382 | int access = GLFS.glfs_access(volptr, pathString, modeInt(m)); 383 | if (-1 == access) { 384 | throw new AccessDeniedException(pathString); 385 | } 386 | } 387 | 388 | } 389 | 390 | private int modeInt(AccessMode m) { 391 | switch (m) { 392 | case EXECUTE: 393 | return 1; 394 | case WRITE: 395 | return 2; 396 | case READ: 397 | return 4; 398 | } 399 | return -1; 400 | } 401 | 402 | @Override 403 | public V getFileAttributeView(Path path, Class vClass, LinkOption... linkOptions) { 404 | return null; 405 | } 406 | 407 | @Override 408 | public A readAttributes(Path path, Class type, LinkOption... linkOptions) throws IOException { 409 | // if (!type.isAssignableFrom(GlusterFileAttributes.class)) { // Why doesn't this work when type is GlusterFileAttributes.class?! 410 | if (type.equals(DosFileAttributes.class)) { 411 | throw new UnsupportedOperationException(type + " attribute type is not supported, only PosixFileAttributes & its superinterfaces"); 412 | } 413 | stat stat = new stat(); 414 | 415 | boolean followSymlinks = true; 416 | for (LinkOption lo : linkOptions) { 417 | if (lo.equals(LinkOption.NOFOLLOW_LINKS)) { 418 | followSymlinks = false; 419 | break; 420 | } 421 | } 422 | int ret; 423 | String pathString = ((GlusterPath) path).getString(); 424 | if (followSymlinks) { 425 | ret = GLFS.glfs_stat(((GlusterFileSystem) path.getFileSystem()).getVolptr(), pathString, stat); 426 | } else { 427 | ret = GLFS.glfs_lstat(((GlusterFileSystem) path.getFileSystem()).getVolptr(), pathString, stat); 428 | } 429 | 430 | if (-1 == ret) { 431 | throw new NoSuchFileException(""); 432 | } 433 | 434 | return (A) GlusterFileAttributes.fromStat(stat); 435 | } 436 | 437 | @Override 438 | public Map readAttributes(Path path, String s, LinkOption... linkOptions) throws IOException { 439 | return null; 440 | } 441 | 442 | @Override 443 | public void setAttribute(Path path, String s, Object o, LinkOption... linkOptions) throws IOException { 444 | 445 | } 446 | 447 | @Override 448 | public Path readSymbolicLink(Path link) throws IOException { 449 | String pathString = link.toString(); 450 | if (!Files.isSymbolicLink(link)) { 451 | throw new NotLinkException(pathString); 452 | } 453 | 454 | stat stat = new stat(); 455 | GlusterFileSystem fileSystem = (GlusterFileSystem) link.getFileSystem(); 456 | long volptr = fileSystem.getVolptr(); 457 | int statReturn = GLFS.glfs_lstat(volptr, pathString, stat); 458 | if (0 != statReturn) { 459 | throw new IOException("Unable to get size of symlink " + pathString); 460 | } 461 | 462 | long length = (int) stat.st_size; 463 | byte[] content = new byte[(int)length]; 464 | int readReturn = GLFS.glfs_readlink(volptr, pathString, content, content.length); 465 | if (-1 == readReturn) { 466 | throw new IOException("Unable to read symlink " + pathString); 467 | } 468 | 469 | return new GlusterPath(fileSystem, new String(content)); 470 | } 471 | 472 | @Override 473 | public void createSymbolicLink(Path link, Path target, FileAttribute... attrs) throws IOException { 474 | String linkPath = link.toString(); 475 | if (Files.exists(link, LinkOption.NOFOLLOW_LINKS)) { 476 | throw new FileAlreadyExistsException(linkPath); 477 | } 478 | if (null != attrs && attrs.length > 0) { 479 | throw new UnsupportedOperationException("glfs_symlink does not support atomic mode/perms"); 480 | } 481 | GlusterFileSystem fileSystem = (GlusterFileSystem) link.getFileSystem(); 482 | long volptr = fileSystem.getVolptr(); 483 | int ret = GLFS.glfs_symlink(volptr, target.toString(), linkPath); 484 | if (0 != ret) { 485 | throw new IOException("Unknown error creating symlink: " + linkPath); 486 | } 487 | } 488 | 489 | int close(long volptr) { 490 | return glfs_fini(volptr); 491 | } 492 | 493 | long getTotalSpace(long volptr) throws IOException { 494 | statvfs buf = new statvfs(); 495 | GLFS.glfs_statvfs(volptr, "/", buf); 496 | return buf.f_bsize * buf.f_blocks; 497 | } 498 | 499 | long getUsableSpace(long volptr) throws IOException { 500 | statvfs buf = new statvfs(); 501 | GLFS.glfs_statvfs(volptr, "/", buf); 502 | return buf.f_bsize * buf.f_bavail; 503 | } 504 | 505 | long getUnallocatedSpace(long volptr) throws IOException { 506 | statvfs buf = new statvfs(); 507 | GLFS.glfs_statvfs(volptr, "/", buf); 508 | return buf.f_bsize * buf.f_bfree; 509 | } 510 | } 511 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/java/com/peircean/glusterfs/GlusterPath.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.net.URI; 9 | import java.net.URISyntaxException; 10 | import java.nio.file.*; 11 | import java.util.*; 12 | 13 | /** 14 | * @author Louis Zuckerman 15 | */ 16 | @Data 17 | @EqualsAndHashCode(exclude = "pathString") 18 | public class GlusterPath implements Path { 19 | private GlusterFileSystem fileSystem; 20 | private String[] parts; 21 | private String pathString; 22 | private boolean absolute; 23 | 24 | public GlusterPath(GlusterFileSystem fileSystem, String path) { 25 | if (null == fileSystem) { 26 | throw new IllegalArgumentException("fileSystem can not be empty"); 27 | } 28 | if (null == path) { 29 | throw new InvalidPathException("", "path can not be null"); 30 | } 31 | this.fileSystem = fileSystem; 32 | this.pathString = path; 33 | 34 | String stripped = path; 35 | if (path.startsWith(fileSystem.getSeparator())) { 36 | absolute = true; 37 | stripped = stripped.substring(1); 38 | } 39 | if (stripped.endsWith(fileSystem.getSeparator())) { 40 | stripped = stripped.substring(0, stripped.length() - 1); 41 | } 42 | parts = stripped.split(fileSystem.getSeparator()); 43 | } 44 | 45 | GlusterPath(GlusterFileSystem fileSystem, String[] parts, boolean absolute) { 46 | this.fileSystem = fileSystem; 47 | this.parts = parts; 48 | this.absolute = absolute; 49 | } 50 | 51 | @Override 52 | public boolean isAbsolute() { 53 | return absolute; 54 | } 55 | 56 | @Override 57 | public Path getRoot() { 58 | if (absolute) { 59 | return fileSystem.getRootDirectories().iterator().next(); 60 | } else { 61 | return null; 62 | } 63 | } 64 | 65 | @Override 66 | public Path getFileName() { 67 | if (parts.length == 0 || parts[0].isEmpty()) { 68 | return null; 69 | } else { 70 | return new GlusterPath(fileSystem, parts[parts.length - 1]); 71 | } 72 | } 73 | 74 | @Override 75 | public Path getParent() { 76 | if (parts.length <= 1 || parts[0].isEmpty()) { 77 | if (absolute) { 78 | return getRoot(); 79 | } else { 80 | return null; 81 | } 82 | } else { 83 | return new GlusterPath(fileSystem, Arrays.copyOfRange(parts, 0, parts.length - 1), absolute); 84 | } 85 | } 86 | 87 | @Override 88 | public int getNameCount() { 89 | if (parts.length <= 1 && parts[0].isEmpty()) { 90 | if (absolute) { 91 | return 0; 92 | } else { 93 | throw new IllegalStateException("Only the root path should have one empty part"); 94 | } 95 | } else { 96 | return parts.length; 97 | } 98 | } 99 | 100 | @Override 101 | public Path getName(int i) { 102 | if (i < 0 || i >= parts.length || (0 == i && parts.length <= 1 && parts[0].isEmpty())) { 103 | throw new IllegalArgumentException("invalid name index"); 104 | } 105 | return new GlusterPath(fileSystem, Arrays.copyOfRange(parts, 0, i + 1), absolute); 106 | } 107 | 108 | @Override 109 | public Path subpath(int i, int i2) { 110 | if ((0 == i && parts.length <= 1 && parts[0].isEmpty()) 111 | || i < 0 || i2 < 0 112 | || i >= parts.length || i2 > parts.length 113 | || i > i2) { 114 | throw new IllegalArgumentException("invalid indices"); 115 | } 116 | return new GlusterPath(fileSystem, Arrays.copyOfRange(parts, i, i2), absolute); 117 | } 118 | 119 | @Override 120 | public boolean startsWith(Path path) { 121 | GlusterPath otherPath = (GlusterPath) path; 122 | if (this.equals(otherPath)) { 123 | return true; 124 | } 125 | if (otherPath.getParts().length > parts.length) { 126 | return false; 127 | } 128 | if (absolute && otherPath.isAbsolute() && otherPath.getParts()[0].isEmpty()) { 129 | return true; 130 | } 131 | String[] thisPrefix = Arrays.copyOfRange(parts, 0, otherPath.getParts().length); 132 | return ((absolute == otherPath.isAbsolute()) 133 | && (Arrays.equals(thisPrefix, otherPath.getParts()))); 134 | } 135 | 136 | @Override 137 | public boolean startsWith(String s) { 138 | return startsWith(new GlusterPath(fileSystem, s)); 139 | } 140 | 141 | @Override 142 | public boolean endsWith(Path path) { 143 | GlusterPath otherPath = (GlusterPath) path; 144 | if (this.equals(otherPath)) { 145 | return true; 146 | } 147 | if (otherPath.getParts().length > parts.length) { 148 | return false; 149 | } 150 | if (absolute && otherPath.isAbsolute() && otherPath.getParts()[0].isEmpty()) { 151 | return true; 152 | } 153 | String[] thisSuffix = Arrays.copyOfRange(parts, parts.length - otherPath.getParts().length, parts.length); 154 | return ((!otherPath.isAbsolute()) 155 | && (Arrays.equals(thisSuffix, otherPath.getParts()))); 156 | } 157 | 158 | @Override 159 | public boolean endsWith(String s) { 160 | return toString().endsWith(s); 161 | } 162 | 163 | @Override 164 | public Path normalize() { 165 | List newParts = new LinkedList(); 166 | for (String part : parts) { 167 | if (part.equals("..")) { 168 | newParts.remove(newParts.size() - 1); 169 | } else if (!part.equals(".") && !part.isEmpty()) { 170 | newParts.add(part); 171 | } 172 | } 173 | return new GlusterPath(fileSystem, newParts.toArray(new String[]{}), absolute); 174 | } 175 | 176 | @Override 177 | public Path resolve(Path path) { 178 | GlusterPath otherPath = (GlusterPath) path; 179 | if (!fileSystem.equals(otherPath.getFileSystem())) { 180 | throw new IllegalArgumentException("Can not resolve other path because it's on a different filesystem"); 181 | } 182 | 183 | if (otherPath.isAbsolute() || (absolute && parts.length == 1 && parts[0].isEmpty())) { 184 | return new GlusterPath(fileSystem, otherPath.getParts(), true); 185 | } 186 | 187 | if (otherPath.getParts().length == 1 && otherPath.getParts()[0].isEmpty()) { 188 | return this; 189 | } 190 | String[] newParts = Arrays.copyOf(parts, parts.length + otherPath.getParts().length); 191 | System.arraycopy(otherPath.getParts(), 0, newParts, parts.length, otherPath.getParts().length); 192 | return new GlusterPath(fileSystem, newParts, absolute); 193 | } 194 | 195 | @Override 196 | public Path resolve(String s) { 197 | return resolve(new GlusterPath(fileSystem, s)); 198 | } 199 | 200 | @Override 201 | public Path resolveSibling(Path path) { 202 | return getParent().resolve(path); 203 | } 204 | 205 | @Override 206 | public Path resolveSibling(String s) { 207 | return getParent().resolve(s); 208 | } 209 | 210 | @Override 211 | public Path relativize(Path path) { 212 | if (!fileSystem.equals(path.getFileSystem())) { 213 | throw new IllegalArgumentException("Can not relativize other path because it's on a different filesystem"); 214 | } 215 | 216 | if (!this.isAbsolute() || !path.isAbsolute()) { 217 | throw new IllegalArgumentException("Can only relativize when both paths are absolute"); 218 | } 219 | GlusterPath other = (GlusterPath) path; 220 | List relativeParts = new LinkedList(); 221 | boolean stillCommon = true; 222 | int lastCommonName = -1; 223 | for (int i = 0; i < parts.length; i++) { 224 | if (i >= other.getParts().length) { 225 | for (int r = 0; r < other.getParts().length; r++) { 226 | relativeParts.add(".."); 227 | } 228 | break; 229 | } 230 | if (stillCommon && parts[i].equals(other.getParts()[i])) { 231 | lastCommonName = i; 232 | } else { 233 | stillCommon = false; 234 | relativeParts.add(".."); 235 | } 236 | } 237 | for (int i = lastCommonName + 1; i < other.getParts().length; i++) { 238 | relativeParts.add(other.getParts()[i]); 239 | } 240 | return new GlusterPath(fileSystem, relativeParts.toArray(new String[]{}), false); 241 | } 242 | 243 | @Override 244 | public URI toUri() { 245 | try { 246 | GlusterFileSystem fs = getFileSystem(); 247 | String authority = fs.getHost() + ":" + fs.getVolname(); 248 | return new URI(fs.provider().getScheme(), authority, toString(), null, null); 249 | } catch (URISyntaxException e) { 250 | throw new IllegalStateException(e); 251 | } 252 | } 253 | 254 | @Override 255 | public Path toAbsolutePath() { 256 | if (!absolute) { 257 | throw new UnsupportedOperationException(); 258 | } else { 259 | return this; 260 | } 261 | } 262 | 263 | @Override 264 | public Path toRealPath(LinkOption... linkOptions) throws IOException { 265 | throw new UnsupportedOperationException(); 266 | } 267 | 268 | @Override 269 | public File toFile() { 270 | throw new UnsupportedOperationException(); 271 | } 272 | 273 | @Override 274 | public WatchKey register(WatchService watchService, WatchEvent.Kind[] kinds, WatchEvent.Modifier... modifiers) throws IOException { 275 | throw new UnsupportedOperationException("GlusterWatchService does not support modifiers at this time."); 276 | } 277 | 278 | @Override 279 | public WatchKey register(WatchService watchService, WatchEvent.Kind... kinds) throws IOException { 280 | guardRegisterWatchService(watchService); 281 | guardRegisterWatchDirectory(); 282 | 283 | return ((GlusterWatchService) watchService).registerPath(this, kinds); 284 | } 285 | 286 | void guardRegisterWatchDirectory() throws NotDirectoryException { 287 | if (!Files.isDirectory(this)) { 288 | throw new NotDirectoryException("GlusterWatchService can only watch directories. Not a directory: " + this); 289 | } 290 | } 291 | 292 | void guardRegisterWatchService(WatchService watchService) { 293 | Class watchServiceClass = watchService.getClass(); 294 | if (!GlusterWatchService.class.equals(watchServiceClass)) { 295 | throw new UnsupportedOperationException("GlusterPaths can only be watched by GlusterWatchServices. WatchService given: " + watchServiceClass); 296 | } 297 | } 298 | 299 | @Override 300 | public Iterator iterator() { 301 | List list = new ArrayList(parts.length); 302 | if (parts.length >= 1 && !parts[0].isEmpty()) { 303 | for (String p : parts) { 304 | list.add(new GlusterPath(fileSystem, p)); 305 | } 306 | } 307 | return Collections.unmodifiableList(list).iterator(); 308 | } 309 | 310 | @Override 311 | public int compareTo(Path path) { 312 | if (!getClass().equals(path.getClass())) { 313 | throw new ClassCastException(); 314 | } 315 | if (!fileSystem.equals(path.getFileSystem())) { 316 | throw new IllegalArgumentException("Can not compare other path because it's on a different filesystem"); 317 | } 318 | GlusterPath other = (GlusterPath) path; 319 | String[] otherParts = other.getParts(); 320 | for (int i = 0; i < Math.min(parts.length, otherParts.length); i++) { 321 | int c = parts[i].compareTo(otherParts[i]); 322 | if (c != 0) { 323 | return c; 324 | } 325 | } 326 | return parts.length - otherParts.length; 327 | } 328 | 329 | public String toString() { 330 | return /*fileSystem.toString() +*/ getString(); 331 | } 332 | 333 | public String getString() { 334 | if (null != pathString) { 335 | return pathString; 336 | } else { 337 | StringBuilder sb = new StringBuilder((absolute ? fileSystem.getSeparator() : "")); 338 | for (String p : parts) { 339 | sb.append(p).append(fileSystem.getSeparator()); 340 | } 341 | sb.deleteCharAt(sb.length() - 1); 342 | return sb.toString(); 343 | } 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/java/com/peircean/glusterfs/GlusterPathMatcher.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import java.nio.file.Path; 4 | import java.nio.file.PathMatcher; 5 | import java.util.regex.Pattern; 6 | 7 | class GlusterPathMatcher implements PathMatcher { 8 | Pattern pattern; 9 | 10 | public GlusterPathMatcher(Pattern pattern) { 11 | this.pattern = pattern; 12 | } 13 | 14 | @Override 15 | public boolean matches(Path path) { 16 | return pattern.matcher(path.toString()).matches(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/java/com/peircean/glusterfs/GlusterWatchEvent.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import lombok.Data; 4 | 5 | import java.nio.file.Path; 6 | import java.nio.file.StandardWatchEventKinds; 7 | import java.nio.file.WatchEvent; 8 | 9 | @Data 10 | public class GlusterWatchEvent implements WatchEvent { 11 | final private Path path; 12 | private Kind kind = StandardWatchEventKinds.ENTRY_CREATE; 13 | private int count = 0; 14 | private long lastModified; 15 | 16 | public Kind kind() { 17 | return kind; 18 | } 19 | 20 | @Override 21 | public int count() { 22 | return count; 23 | } 24 | 25 | @Override 26 | public Path context() { 27 | return path; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/java/com/peircean/glusterfs/GlusterWatchKey.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.NonNull; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.*; 9 | import java.util.*; 10 | 11 | @Data 12 | @EqualsAndHashCode(of = "path") 13 | public class GlusterWatchKey implements WatchKey { 14 | private boolean valid = true; 15 | private boolean ready = true; 16 | Map events = new HashMap<>(); 17 | final private GlusterPath path; 18 | @NonNull 19 | private WatchEvent.Kind[] kinds; 20 | private long lastPolled = (new Date()).getTime(); 21 | 22 | @Override 23 | public boolean isValid() { 24 | return valid; 25 | } 26 | 27 | public boolean update() { 28 | DirectoryStream paths; 29 | try { 30 | paths = Files.newDirectoryStream(path); 31 | } catch (IOException e) { 32 | return false; 33 | } 34 | List files = new LinkedList<>(); 35 | boolean newEvents = false; 36 | for (Path f : paths) { 37 | newEvents |= processExistingFile(files, f); 38 | } 39 | for (Path f : events.keySet()) { 40 | newEvents |= checkDeleted(files, f); 41 | } 42 | return newEvents; 43 | } 44 | 45 | boolean processExistingFile(List files, Path f) { 46 | if (Files.isDirectory(f)) { 47 | return false; 48 | } 49 | files.add(f); 50 | 51 | long lastModified; 52 | try { 53 | lastModified = Files.getLastModifiedTime(f).toMillis(); 54 | } catch (IOException e) { 55 | return false; 56 | } 57 | 58 | GlusterWatchEvent event = events.get(f); 59 | if (null != event) { 60 | return checkModified(event, lastModified); 61 | } else { 62 | return checkCreated(f, lastModified); 63 | } 64 | } 65 | 66 | boolean checkDeleted(List files, Path f) { 67 | GlusterWatchEvent event = events.get(f); 68 | if (!files.contains(f) && 69 | !StandardWatchEventKinds.ENTRY_DELETE.name().equals(event.kind().name())) { 70 | event.setLastModified((new Date()).getTime()); 71 | event.setKind(StandardWatchEventKinds.ENTRY_DELETE); 72 | event.setCount(event.getCount() + 1); 73 | return true; 74 | } 75 | return false; 76 | } 77 | 78 | boolean checkCreated(Path f, long lastModified) { 79 | GlusterWatchEvent event = new GlusterWatchEvent(f.getFileName()); 80 | event.setLastModified(lastModified); 81 | events.put(f, event); 82 | return (lastModified > lastPolled); 83 | } 84 | 85 | boolean checkModified(GlusterWatchEvent event, long lastModified) { 86 | if (lastModified > event.getLastModified()) { 87 | event.setLastModified(lastModified); 88 | if (event.kind().name().equals(StandardWatchEventKinds.ENTRY_DELETE.name())) { 89 | event.setKind(StandardWatchEventKinds.ENTRY_CREATE); 90 | event.setCount(0); 91 | } else { 92 | event.setKind(StandardWatchEventKinds.ENTRY_MODIFY); 93 | event.setCount(event.getCount() + 1); 94 | } 95 | return true; 96 | } 97 | return false; 98 | } 99 | 100 | boolean kindsContains(WatchEvent.Kind kind) { 101 | for (WatchEvent.Kind k : kinds) { 102 | if (k.name().equals(kind.name())) { 103 | return true; 104 | } 105 | } 106 | return false; 107 | } 108 | 109 | @Override 110 | synchronized public List> pollEvents() { 111 | if (!ready) { 112 | return new LinkedList<>(); 113 | } 114 | ready = false; 115 | return findPendingEvents(); 116 | } 117 | 118 | LinkedList> findPendingEvents() { 119 | long maxModifiedTime = lastPolled; 120 | LinkedList> pendingEvents = new LinkedList<>(); 121 | for (Path p : events.keySet()) { 122 | long lastModified = queueEventIfPending(pendingEvents, p); 123 | maxModifiedTime = Math.max(maxModifiedTime, lastModified); 124 | } 125 | lastPolled = maxModifiedTime; 126 | return pendingEvents; 127 | } 128 | 129 | private long queueEventIfPending(LinkedList> pendingEvents, Path p) { 130 | GlusterWatchEvent e = events.get(p); 131 | long lastModified = e.getLastModified(); 132 | if (lastModified > lastPolled && kindsContains(e.kind())) { 133 | pendingEvents.add(e); 134 | } 135 | return lastModified; 136 | } 137 | 138 | @Override 139 | synchronized public boolean reset() { 140 | if (!valid || ready) { 141 | return false; 142 | } else { 143 | ready = true; 144 | return true; 145 | } 146 | } 147 | 148 | @Override 149 | public void cancel() { 150 | valid = false; 151 | } 152 | 153 | @Override 154 | public Watchable watchable() { 155 | return path; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/java/com/peircean/glusterfs/GlusterWatchService.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.IOException; 6 | import java.nio.file.ClosedWatchServiceException; 7 | import java.nio.file.WatchEvent; 8 | import java.nio.file.WatchKey; 9 | import java.nio.file.WatchService; 10 | import java.util.HashSet; 11 | import java.util.Iterator; 12 | import java.util.NoSuchElementException; 13 | import java.util.Set; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | @Data 17 | public class GlusterWatchService implements WatchService { 18 | public static final int MILLIS_PER_SECOND = 1000; 19 | public static final int MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND; 20 | public static final int MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE; 21 | public static final int MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR; 22 | public static long PERIOD = 100L; 23 | 24 | private Set paths = new HashSet<>(); 25 | private Set pendingPaths = new HashSet<>(); 26 | private boolean running = true; 27 | 28 | public WatchKey registerPath(GlusterPath path, WatchEvent.Kind... kinds) { 29 | if (!running) { 30 | throw new ClosedWatchServiceException(); 31 | } 32 | for (GlusterWatchKey k : paths) { 33 | if (k.getPath().equals(path)) { 34 | k.setKinds(kinds); 35 | return k; 36 | } 37 | } 38 | GlusterWatchKey key = new GlusterWatchKey(path, kinds); 39 | paths.add(key); 40 | return key; 41 | } 42 | 43 | @Override 44 | public void close() throws IOException { 45 | if (running) { 46 | running = false; 47 | for (GlusterWatchKey k : paths) { 48 | k.cancel(); 49 | } 50 | } 51 | } 52 | 53 | WatchKey popPending() { 54 | Iterator iterator = pendingPaths.iterator(); 55 | try { 56 | GlusterWatchKey key = iterator.next(); 57 | iterator.remove(); 58 | return key; 59 | } catch (NoSuchElementException e) { 60 | return null; 61 | } 62 | } 63 | 64 | @Override 65 | public WatchKey poll() { 66 | if (!running) { 67 | throw new ClosedWatchServiceException(); 68 | } 69 | WatchKey pending = popPending(); 70 | if (null != pending) { 71 | return pending; 72 | } 73 | for (GlusterWatchKey k : paths) { 74 | if (k.isValid() && k.isReady() && k.update()) { 75 | pendingPaths.add(k); 76 | } 77 | } 78 | return popPending(); 79 | } 80 | 81 | @Override 82 | public WatchKey poll(long timeout, TimeUnit unit) { 83 | long timeoutMillis = timeoutToMillis(timeout, unit); 84 | long loops = 0; 85 | while (running) { 86 | WatchKey key = poll(); 87 | if (key != null) { 88 | return key; 89 | } 90 | if ((loops * PERIOD) > timeoutMillis) { 91 | return null; 92 | } 93 | loops++; 94 | try { 95 | Thread.sleep(PERIOD); 96 | } catch (InterruptedException e) { 97 | } 98 | } 99 | throw new ClosedWatchServiceException(); 100 | } 101 | 102 | @Override 103 | public WatchKey take() { 104 | while (running) { 105 | WatchKey key = poll(); 106 | if (key != null) { 107 | return key; 108 | } 109 | try { 110 | Thread.sleep(PERIOD); 111 | } catch (InterruptedException e) { 112 | } 113 | } 114 | throw new ClosedWatchServiceException(); 115 | } 116 | 117 | long timeoutToMillis(long timeout, TimeUnit unit) { 118 | switch (unit) { 119 | case DAYS: 120 | return (timeout * MILLIS_PER_DAY); 121 | case HOURS: 122 | return (timeout * MILLIS_PER_HOUR); 123 | case MINUTES: 124 | return (timeout * MILLIS_PER_MINUTE); 125 | case SECONDS: 126 | return (timeout * MILLIS_PER_SECOND); 127 | case MILLISECONDS: 128 | return timeout; 129 | default: //MICROS & NANOS 130 | return -1; 131 | } 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/java/com/peircean/glusterfs/borrowed/GlobPattern.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | /* 20 | This file was borrowed for glusterfs-java-filesystem from Apache Hadoop 21 | by Louis Zuckerman. Original source URL: 22 | https://svn.apache.org/repos/asf/hadoop/common/tags/release-2.3.0-rc0/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/GlobPattern.java 23 | */ 24 | 25 | // Changed package name. -LZ 26 | package com.peircean.glusterfs.borrowed; 27 | 28 | import java.util.regex.Pattern; 29 | import java.util.regex.PatternSyntaxException; 30 | 31 | /* Commented these lines. -LZ 32 | import org.apache.hadoop.classification.InterfaceAudience; 33 | import org.apache.hadoop.classification.InterfaceStability; 34 | @InterfaceAudience.Private 35 | @InterfaceStability.Evolving 36 | 37 | No other modifications made to the file. -LZ 38 | */ 39 | 40 | 41 | /** 42 | * A class for POSIX glob pattern with brace expansions. 43 | */ 44 | public class GlobPattern { 45 | private static final char BACKSLASH = '\\'; 46 | private Pattern compiled; 47 | private boolean hasWildcard = false; 48 | 49 | /** 50 | * Construct the glob pattern object with a glob pattern string 51 | * 52 | * @param globPattern the glob pattern string 53 | */ 54 | public GlobPattern(String globPattern) { 55 | set(globPattern); 56 | } 57 | 58 | /** 59 | * @return the compiled pattern 60 | */ 61 | public Pattern compiled() { 62 | return compiled; 63 | } 64 | 65 | /** 66 | * Compile glob pattern string 67 | * 68 | * @param globPattern the glob pattern 69 | * @return the pattern object 70 | */ 71 | public static Pattern compile(String globPattern) { 72 | return new GlobPattern(globPattern).compiled(); 73 | } 74 | 75 | /** 76 | * Match input against the compiled glob pattern 77 | * 78 | * @param s input chars 79 | * @return true for successful matches 80 | */ 81 | public boolean matches(CharSequence s) { 82 | return compiled.matcher(s).matches(); 83 | } 84 | 85 | /** 86 | * Set and compile a glob pattern 87 | * 88 | * @param glob the glob pattern string 89 | */ 90 | public void set(String glob) { 91 | StringBuilder regex = new StringBuilder(); 92 | int setOpen = 0; 93 | int curlyOpen = 0; 94 | int len = glob.length(); 95 | hasWildcard = false; 96 | 97 | for (int i = 0; i < len; i++) { 98 | char c = glob.charAt(i); 99 | 100 | switch (c) { 101 | case BACKSLASH: 102 | if (++i >= len) { 103 | error("Missing escaped character", glob, i); 104 | } 105 | regex.append(c).append(glob.charAt(i)); 106 | continue; 107 | case '.': 108 | case '$': 109 | case '(': 110 | case ')': 111 | case '|': 112 | case '+': 113 | // escape regex special chars that are not glob special chars 114 | regex.append(BACKSLASH); 115 | break; 116 | case '*': 117 | regex.append('.'); 118 | hasWildcard = true; 119 | break; 120 | case '?': 121 | regex.append('.'); 122 | hasWildcard = true; 123 | continue; 124 | case '{': // start of a group 125 | regex.append("(?:"); // non-capturing 126 | curlyOpen++; 127 | hasWildcard = true; 128 | continue; 129 | case ',': 130 | regex.append(curlyOpen > 0 ? '|' : c); 131 | continue; 132 | case '}': 133 | if (curlyOpen > 0) { 134 | // end of a group 135 | curlyOpen--; 136 | regex.append(")"); 137 | continue; 138 | } 139 | break; 140 | case '[': 141 | if (setOpen > 0) { 142 | error("Unclosed character class", glob, i); 143 | } 144 | setOpen++; 145 | hasWildcard = true; 146 | break; 147 | case '^': // ^ inside [...] can be unescaped 148 | if (setOpen == 0) { 149 | regex.append(BACKSLASH); 150 | } 151 | break; 152 | case '!': // [! needs to be translated to [^ 153 | regex.append(setOpen > 0 && '[' == glob.charAt(i - 1) ? '^' : '!'); 154 | continue; 155 | case ']': 156 | // Many set errors like [][] could not be easily detected here, 157 | // as []], []-] and [-] are all valid POSIX glob and java regex. 158 | // We'll just let the regex compiler do the real work. 159 | setOpen = 0; 160 | break; 161 | default: 162 | } 163 | regex.append(c); 164 | } 165 | 166 | if (setOpen > 0) { 167 | error("Unclosed character class", glob, len); 168 | } 169 | if (curlyOpen > 0) { 170 | error("Unclosed group", glob, len); 171 | } 172 | compiled = Pattern.compile(regex.toString()); 173 | } 174 | 175 | /** 176 | * @return true if this is a wildcard pattern (with special chars) 177 | */ 178 | public boolean hasWildcard() { 179 | return hasWildcard; 180 | } 181 | 182 | private static void error(String message, String pattern, int pos) { 183 | throw new PatternSyntaxException(message, pattern, pos); 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider: -------------------------------------------------------------------------------- 1 | com.peircean.glusterfs.GlusterFileSystemProvider 2 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/test/java/com/peircean/glusterfs/GlusterDirectoryIteratorTest.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import com.peircean.libgfapi_jni.internal.GLFS; 4 | import com.peircean.libgfapi_jni.internal.structs.dirent; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.Mock; 8 | import org.mockito.Mockito; 9 | import org.mockito.Spy; 10 | import org.powermock.api.mockito.PowerMockito; 11 | import org.powermock.core.classloader.annotations.PrepareForTest; 12 | import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor; 13 | import org.powermock.modules.junit4.PowerMockRunner; 14 | 15 | import java.nio.file.DirectoryStream; 16 | import java.nio.file.Path; 17 | import java.util.NoSuchElementException; 18 | 19 | import static org.junit.Assert.*; 20 | import static org.mockito.Mockito.*; 21 | 22 | @RunWith(PowerMockRunner.class) 23 | @PrepareForTest({GLFS.class, GlusterDirectoryIterator.class, dirent.class}) 24 | @SuppressStaticInitializationFor("com.peircean.libgfapi_jni.internal.structs.dirent") 25 | public class GlusterDirectoryIteratorTest { 26 | 27 | @Mock 28 | private GlusterPath mockPath; 29 | @Mock 30 | private GlusterPath fakeResultPath; 31 | @Mock 32 | private GlusterFileSystem mockFileSystem; 33 | @Mock 34 | private GlusterDirectoryStream mockStream; 35 | @Mock 36 | private dirent mockCurrentDirent; 37 | @Mock 38 | private dirent mockNextDirent; 39 | @Mock 40 | private DirectoryStream.Filter mockFilter; 41 | @Spy 42 | private GlusterDirectoryIterator iterator = new GlusterDirectoryIterator(); 43 | private long dirHandle = 12345l; 44 | 45 | @Test 46 | public void testHasNext_whenFilter() throws Exception { 47 | iterator.setFilter(mockFilter); 48 | iterator.setNextPath(fakeResultPath); 49 | when(mockFilter.accept(fakeResultPath)).thenReturn(false).thenReturn(true); 50 | mockNextDirent.d_ino = 1; 51 | iterator.setNext(mockNextDirent); 52 | doNothing().when(iterator).advance(); 53 | assertTrue(iterator.hasNext()); 54 | verify(iterator, times(2)).advance(); 55 | verify(mockFilter, times(2)).accept(fakeResultPath); 56 | } 57 | 58 | @Test 59 | public void testHasNext_whenFilter_andNoNext() throws Exception { 60 | iterator.setFilter(mockFilter); 61 | iterator.setNextPath(fakeResultPath); 62 | when(mockFilter.accept(fakeResultPath)).thenReturn(false).thenReturn(true); 63 | mockNextDirent.d_ino = 0; 64 | iterator.setNext(mockNextDirent); 65 | doNothing().when(iterator).advance(); 66 | assertFalse(iterator.hasNext()); 67 | verify(iterator).advance(); 68 | verify(mockFilter, never()).accept(fakeResultPath); 69 | } 70 | 71 | @Test 72 | public void testHasNext_whenNoFilter_andNoNext() throws Exception { 73 | mockNextDirent.d_ino = 0; 74 | iterator.setNext(mockNextDirent); 75 | doNothing().when(iterator).advance(); 76 | assertFalse(iterator.hasNext()); 77 | } 78 | 79 | @Test 80 | public void testHasNext_whenNoFilter() throws Exception { 81 | mockNextDirent.d_ino = 1; 82 | iterator.setNext(mockNextDirent); 83 | doNothing().when(iterator).advance(); 84 | assertTrue(iterator.hasNext()); 85 | } 86 | 87 | @Test 88 | public void testAdvance_whenNormalEntry() throws Exception { 89 | advanceHelper(0, true); 90 | } 91 | 92 | @Test 93 | public void testAdvance_skipSpecialEntryCurrent() throws Exception { 94 | advanceHelper(1, true); 95 | } 96 | 97 | @Test 98 | public void testAdvance_skipSpecialEntryParent() throws Exception { 99 | advanceHelper(1, false); 100 | } 101 | 102 | @Test 103 | public void testAdvance_skipSpecialEntryCurrentAndParent() throws Exception { 104 | advanceHelper(2, true); 105 | } 106 | 107 | /* 108 | * Type 0 = advancing on normal entry, so the boolean current value does not matter 109 | * Type 1 = advancing on either special entry "." or "..", differentiated by boolean current 110 | * Type 2 = advancing on both special entries "." and "..", so boolean current value does not matter 111 | */ 112 | private void advanceHelper(int type, boolean current) throws Exception { 113 | doReturn(dirHandle).when(mockStream).getDirHandle(); 114 | iterator.setStream(mockStream); 115 | switch (type) { 116 | case 1: 117 | PowerMockito.whenNew(dirent.class).withNoArguments().thenReturn(mockCurrentDirent, mockNextDirent, 118 | mockCurrentDirent, mockNextDirent); 119 | break; 120 | case 2: 121 | PowerMockito.whenNew(dirent.class).withNoArguments().thenReturn(mockCurrentDirent, mockNextDirent, 122 | mockCurrentDirent, mockNextDirent, 123 | mockCurrentDirent, mockNextDirent); 124 | break; 125 | default: 126 | PowerMockito.whenNew(dirent.class).withNoArguments().thenReturn(mockCurrentDirent, mockNextDirent); 127 | break; 128 | } 129 | long nextPtr = 4444l; 130 | PowerMockito.mockStatic(dirent.class); 131 | Mockito.when(dirent.malloc(dirent.SIZE_OF)).thenReturn(nextPtr); 132 | 133 | PowerMockito.doNothing().when(dirent.class); 134 | dirent.memmove(mockNextDirent, nextPtr, dirent.SIZE_OF); 135 | PowerMockito.doNothing().when(dirent.class); 136 | dirent.free(nextPtr); 137 | 138 | PowerMockito.mockStatic(GLFS.class); 139 | PowerMockito.when(GLFS.glfs_readdir_r(dirHandle, mockCurrentDirent, nextPtr)).thenReturn(0); 140 | 141 | Mockito.doReturn(mockPath).when(mockStream).getDir(); 142 | String stringPath = "foo"; 143 | switch (type) { 144 | case 1: 145 | if (current) { 146 | when(mockCurrentDirent.getName()).thenReturn(".", stringPath); 147 | } else { 148 | when(mockCurrentDirent.getName()).thenReturn("..", stringPath); 149 | } 150 | break; 151 | case 2: 152 | when(mockCurrentDirent.getName()).thenReturn(".", "..", stringPath); 153 | break; 154 | default: 155 | when(mockCurrentDirent.getName()).thenReturn(stringPath); 156 | break; 157 | } 158 | when(mockPath.resolve(any(String.class))).thenReturn(fakeResultPath); 159 | 160 | iterator.advance(); 161 | 162 | assertEquals(fakeResultPath, iterator.getNextPath()); 163 | 164 | verify(mockPath).resolve(stringPath); 165 | switch (type) { 166 | case 1: 167 | verify(mockCurrentDirent, times(2)).getName(); 168 | if (current) { 169 | verify(mockPath).resolve("."); 170 | } else { 171 | verify(mockPath).resolve(".."); 172 | } 173 | verify(mockStream, times(2)).getDir(); 174 | verify(mockStream, times(2)).getDirHandle(); 175 | 176 | PowerMockito.verifyNew(dirent.class, times(4)).withNoArguments(); 177 | PowerMockito.verifyStatic(times(2)); 178 | dirent.memmove(mockNextDirent, nextPtr, dirent.SIZE_OF); 179 | PowerMockito.verifyStatic(times(2)); 180 | dirent.free(nextPtr); 181 | break; 182 | case 2: 183 | verify(mockCurrentDirent, times(3)).getName(); 184 | verify(mockPath).resolve("."); 185 | verify(mockPath).resolve(".."); 186 | verify(mockStream, times(3)).getDir(); 187 | verify(mockStream, times(3)).getDirHandle(); 188 | 189 | PowerMockito.verifyNew(dirent.class, times(6)).withNoArguments(); 190 | PowerMockito.verifyStatic(times(3)); 191 | dirent.memmove(mockNextDirent, nextPtr, dirent.SIZE_OF); 192 | PowerMockito.verifyStatic(times(3)); 193 | dirent.free(nextPtr); 194 | break; 195 | default: 196 | verify(mockCurrentDirent).getName(); 197 | verify(mockStream).getDir(); 198 | verify(mockStream).getDirHandle(); 199 | 200 | PowerMockito.verifyNew(dirent.class, times(2)).withNoArguments(); 201 | PowerMockito.verifyStatic(); 202 | dirent.memmove(mockNextDirent, nextPtr, dirent.SIZE_OF); 203 | PowerMockito.verifyStatic(); 204 | dirent.free(nextPtr); 205 | break; 206 | } 207 | } 208 | 209 | @Test(expected = NoSuchElementException.class) 210 | public void testNext_whenNoNext() { 211 | iterator.next(); 212 | } 213 | 214 | @Test 215 | public void testNext() { 216 | iterator.setNextPath(fakeResultPath); 217 | GlusterPath path = iterator.next(); 218 | assertEquals(fakeResultPath, path); 219 | } 220 | 221 | @Test(expected = UnsupportedOperationException.class) 222 | public void testRemove() { 223 | iterator.remove(); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/test/java/com/peircean/glusterfs/GlusterDirectoryStreamTest.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import com.peircean.libgfapi_jni.internal.GLFS; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.mockito.Mock; 7 | import org.mockito.Mockito; 8 | import org.mockito.Spy; 9 | import org.powermock.core.classloader.annotations.PrepareForTest; 10 | import org.powermock.modules.junit4.PowerMockRunner; 11 | 12 | import java.io.IOException; 13 | import java.nio.file.DirectoryStream; 14 | import java.nio.file.Path; 15 | import java.util.Iterator; 16 | 17 | import static org.junit.Assert.assertEquals; 18 | import static org.mockito.Mockito.doReturn; 19 | import static org.mockito.Mockito.verify; 20 | import static org.powermock.api.mockito.PowerMockito.*; 21 | 22 | @RunWith(PowerMockRunner.class) 23 | @PrepareForTest({GlusterDirectoryStream.class, GLFS.class}) 24 | public class GlusterDirectoryStreamTest { 25 | private long dirHandle = 12345l; 26 | 27 | private long fsHandle = 56789l; 28 | 29 | @Mock 30 | private GlusterDirectoryIterator mockIterator; 31 | 32 | @Mock 33 | private GlusterPath mockPath; 34 | 35 | @Mock 36 | private GlusterFileSystem mockFileSystem; 37 | 38 | @Mock 39 | private DirectoryStream.Filter mockFilter; 40 | 41 | @Spy 42 | private GlusterDirectoryStream stream = new GlusterDirectoryStream(); 43 | 44 | @Test(expected = IllegalStateException.class) 45 | public void testOpenDirectory_whenAlreadyOpen() { 46 | stream.setDirHandle(1); 47 | stream.open(mockPath); 48 | } 49 | 50 | @Test 51 | public void testOpenDirectory() { 52 | String fakePath = "/foo/baz"; 53 | doReturn(mockFileSystem).when(mockPath).getFileSystem(); 54 | doReturn(fsHandle).when(mockFileSystem).getVolptr(); 55 | doReturn(fakePath).when(mockPath).getString(); 56 | 57 | mockStatic(GLFS.class); 58 | when(GLFS.glfs_opendir(fsHandle, fakePath)).thenReturn(dirHandle); 59 | 60 | stream.open(mockPath); 61 | 62 | assertEquals(dirHandle, stream.getDirHandle()); 63 | assertEquals(false, stream.isClosed()); 64 | assertEquals(mockPath, stream.getDir()); 65 | verify(mockPath).getFileSystem(); 66 | verify(mockFileSystem).getVolptr(); 67 | verify(mockPath).getString(); 68 | verifyStatic(); 69 | GLFS.glfs_opendir(fsHandle, fakePath); 70 | } 71 | 72 | @Test(expected = IllegalStateException.class) 73 | public void testIterator_whenAlreadyIterating() { 74 | stream.setIterator(mockIterator); 75 | stream.iterator(); 76 | } 77 | 78 | @Test(expected = IllegalStateException.class) 79 | public void testIterator_whenClosed() { 80 | stream.setClosed(true); 81 | stream.iterator(); 82 | } 83 | 84 | @Test 85 | public void testIterator() throws Exception { 86 | stream.setClosed(false); 87 | stream.setFilter(mockFilter); 88 | whenNew(GlusterDirectoryIterator.class). 89 | withNoArguments().thenReturn(mockIterator); 90 | stream.setDirHandle(dirHandle); 91 | Iterator iterator = stream.iterator(); 92 | verifyNew(GlusterDirectoryIterator.class).withNoArguments(); 93 | assertEquals(mockIterator, iterator); 94 | assertEquals(mockIterator, stream.getIterator()); 95 | verify(mockIterator).setStream(stream); 96 | verify(mockIterator).setFilter(mockFilter); 97 | } 98 | 99 | @Test 100 | public void testClose_whenAlreadyClosed() throws IOException { 101 | stream.setDirHandle(dirHandle); 102 | stream.setClosed(true); 103 | mockStatic(GLFS.class); 104 | 105 | stream.close(); 106 | 107 | verifyStatic(Mockito.never()); 108 | GLFS.glfs_close(dirHandle); 109 | } 110 | 111 | @Test 112 | public void testClose() throws IOException { 113 | stream.setDirHandle(dirHandle); 114 | mockStatic(GLFS.class); 115 | Mockito.when(GLFS.glfs_close(dirHandle)).thenReturn(0); 116 | 117 | stream.close(); 118 | 119 | verifyStatic(); 120 | GLFS.glfs_close(dirHandle); 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/test/java/com/peircean/glusterfs/GlusterFileAttributesTest.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import junit.framework.TestCase; 4 | import com.peircean.libgfapi_jni.internal.structs.stat; 5 | import org.junit.Test; 6 | 7 | import java.nio.file.attribute.FileAttribute; 8 | import java.nio.file.attribute.FileTime; 9 | import java.nio.file.attribute.PosixFilePermission; 10 | import java.nio.file.attribute.PosixFilePermissions; 11 | import java.util.Set; 12 | 13 | public class GlusterFileAttributesTest extends TestCase { 14 | 15 | public static final int MODE = 0100777; 16 | public static final int UID = 1000; 17 | public static final int GID = 1001; 18 | public static final long SIZE = 1234l; 19 | public static final long ATIME = 1373464796l; 20 | public static final long CTIME = 1373464797l; 21 | public static final long MTIME = 1373464798l; 22 | public static final long INODE = 4452352l; 23 | private GlusterFileAttributes attrib = new GlusterFileAttributes(MODE, UID, GID, SIZE, ATIME, CTIME, MTIME, INODE); 24 | 25 | @Test 26 | public void testOwner() { 27 | assertEquals(String.valueOf(UID), attrib.owner().getName()); 28 | } 29 | 30 | @Test 31 | public void testGroup() { 32 | assertEquals(String.valueOf(GID), attrib.group().getName()); 33 | } 34 | 35 | @Test 36 | public void testModified() { 37 | assertEquals(FileTime.fromMillis(MTIME * 1000), attrib.lastModifiedTime()); 38 | } 39 | 40 | @Test 41 | public void testCreated() { 42 | assertEquals(FileTime.fromMillis(CTIME * 1000), attrib.creationTime()); 43 | } 44 | 45 | @Test 46 | public void testAccessed() { 47 | assertEquals(FileTime.fromMillis(ATIME * 1000), attrib.lastAccessTime()); 48 | } 49 | 50 | @Test 51 | public void testPermissions() { 52 | Set expected = PosixFilePermissions.fromString("rwxrwxrwx"); 53 | Set permissions = attrib.permissions(); 54 | assertEquals(expected, permissions); 55 | } 56 | 57 | @Test 58 | public void testParseAttributes() { 59 | Set posixFilePermissions = PosixFilePermissions.fromString("rwxrwxrwx"); 60 | FileAttribute> attrs = PosixFilePermissions.asFileAttribute(posixFilePermissions); 61 | int mode = GlusterFileAttributes.parseAttrs(attrs); 62 | assertEquals(0777, mode); 63 | } 64 | 65 | 66 | @Test 67 | public void testIsRegular() { 68 | assertTrue(attrib.isRegularFile()); 69 | } 70 | 71 | @Test 72 | public void testIsSymbolic() { 73 | attrib = new GlusterFileAttributes(0120777, UID, GID, SIZE, ATIME, CTIME, MTIME, INODE); 74 | assertTrue(attrib.isSymbolicLink()); 75 | } 76 | 77 | @Test 78 | public void testIsDirectory() { 79 | attrib = new GlusterFileAttributes(0040777, UID, GID, SIZE, ATIME, CTIME, MTIME, INODE); 80 | assertTrue(attrib.isDirectory()); 81 | } 82 | 83 | @Test 84 | public void testIsOther() { 85 | attrib = new GlusterFileAttributes(0000777, UID, GID, SIZE, ATIME, CTIME, MTIME, INODE); 86 | assertTrue(attrib.isOther()); 87 | } 88 | 89 | @Test 90 | public void testSize() { 91 | assertEquals(SIZE, attrib.size()); 92 | } 93 | 94 | @Test 95 | public void testFileKey() { 96 | assertEquals(INODE, attrib.fileKey()); 97 | } 98 | 99 | @Test 100 | public void testFromStat() { 101 | stat stat = new stat(); 102 | stat.st_size = SIZE; 103 | stat.st_gid = GID; 104 | stat.st_uid = UID; 105 | stat.st_mode = MODE; 106 | stat.st_ino = INODE; 107 | stat.atime = ATIME; 108 | stat.mtime = MTIME; 109 | stat.ctime = CTIME; 110 | 111 | GlusterFileAttributes attr = GlusterFileAttributes.fromStat(stat); 112 | 113 | assertEquals(stat.st_size, attr.getSize()); 114 | assertEquals(stat.st_uid, attr.getUid()); 115 | assertEquals(stat.st_gid, attr.getGid()); 116 | assertEquals(stat.st_mode, attr.getMode()); 117 | assertEquals(stat.st_ino, attr.getInode()); 118 | assertEquals(stat.atime, attr.getAtime()); 119 | assertEquals(stat.mtime, attr.getMtime()); 120 | assertEquals(stat.ctime, attr.getCtime()); 121 | 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/test/java/com/peircean/glusterfs/GlusterFileChannelTest.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import com.peircean.libgfapi_jni.internal.GLFS; 4 | import com.peircean.libgfapi_jni.internal.GlusterOpenOption; 5 | import com.peircean.libgfapi_jni.internal.structs.stat; 6 | import junit.framework.TestCase; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.mockito.Mock; 10 | import org.mockito.Mockito; 11 | import org.mockito.Spy; 12 | import org.powermock.api.mockito.PowerMockito; 13 | import org.powermock.core.classloader.annotations.PrepareForTest; 14 | import org.powermock.modules.junit4.PowerMockRunner; 15 | 16 | import java.io.IOException; 17 | import java.net.URI; 18 | import java.net.URISyntaxException; 19 | import java.nio.ByteBuffer; 20 | import java.nio.channels.ClosedChannelException; 21 | import java.nio.channels.FileChannel; 22 | import java.nio.channels.NonReadableChannelException; 23 | import java.nio.channels.NonWritableChannelException; 24 | import java.nio.file.FileAlreadyExistsException; 25 | import java.nio.file.OpenOption; 26 | import java.nio.file.StandardOpenOption; 27 | import java.nio.file.attribute.FileAttribute; 28 | import java.nio.file.attribute.PosixFilePermission; 29 | import java.nio.file.attribute.PosixFilePermissions; 30 | import java.util.Arrays; 31 | import java.util.HashSet; 32 | import java.util.Set; 33 | 34 | import static org.mockito.Mockito.*; 35 | import static org.powermock.api.mockito.PowerMockito.mockStatic; 36 | import static org.powermock.api.mockito.PowerMockito.verifyStatic; 37 | 38 | /** 39 | * @author Louis Zuckerman 40 | */ 41 | @RunWith(PowerMockRunner.class) 42 | @PrepareForTest({GLFS.class, GlusterFileChannel.class, GlusterFileAttributes.class}) 43 | public class GlusterFileChannelTest extends TestCase { 44 | 45 | @Mock 46 | private GlusterPath mockPath; 47 | @Mock 48 | private GlusterFileSystem mockFileSystem; 49 | @Mock 50 | private ByteBuffer mockBuffer; 51 | @Spy 52 | private GlusterFileChannel channel = new GlusterFileChannel(); 53 | 54 | @Test(expected = IllegalStateException.class) 55 | public void testNewFileChannel_whenNotAbsolutePath() throws IOException, URISyntaxException { 56 | doReturn(false).when(mockPath).isAbsolute(); 57 | initTestHelper(null, false, false); 58 | } 59 | 60 | @Test 61 | public void testNewFileChannel_whenCreate() throws IOException, URISyntaxException { 62 | doReturn(true).when(mockPath).isAbsolute(); 63 | initTestHelper(StandardOpenOption.CREATE, true, false); 64 | } 65 | 66 | @Test 67 | public void testNewFileChannel_whenCreateNew() throws IOException, URISyntaxException { 68 | doReturn(true).when(mockPath).isAbsolute(); 69 | initTestHelper(StandardOpenOption.CREATE_NEW, true, false); 70 | } 71 | 72 | @Test(expected = FileAlreadyExistsException.class) 73 | public void testNewFileChannel_whenCreateNew_andFileAlreadyExists() throws IOException, URISyntaxException { 74 | doReturn(true).when(mockPath).isAbsolute(); 75 | initTestHelper(StandardOpenOption.CREATE_NEW, false, false); 76 | } 77 | 78 | @Test 79 | public void testNewFileChannel_whenNotCreating() throws IOException, URISyntaxException { 80 | doReturn(true).when(mockPath).isAbsolute(); 81 | initTestHelper(null, false, true); 82 | } 83 | 84 | @Test(expected = IOException.class) 85 | public void testNewFileChannel_whenFailing() throws IOException, URISyntaxException { 86 | doReturn(true).when(mockPath).isAbsolute(); 87 | initTestHelper(null, false, false); 88 | } 89 | 90 | private void initTestHelper(StandardOpenOption option, boolean created, boolean opened) throws IOException, URISyntaxException { 91 | Set options = new HashSet(); 92 | options.add(StandardOpenOption.WRITE); 93 | if (null != option) { 94 | options.add(option); 95 | } 96 | 97 | Set posixFilePermissions = PosixFilePermissions.fromString("rw-rw-rw-"); 98 | FileAttribute> attrs = PosixFilePermissions.asFileAttribute(posixFilePermissions); 99 | 100 | int mode = 0666; 101 | int flags = GlusterOpenOption.WRITE().create().getValue(); 102 | long volptr = 1234l; 103 | String path = "/foo/bar"; 104 | long createptr = created ? 4321l : 0; 105 | long openptr = opened ? 4321l : 0; 106 | URI pathUri = new URI("gluster://server:volume" + path); 107 | doReturn(volptr).when(mockFileSystem).getVolptr(); 108 | doReturn(pathUri).when(mockPath).toUri(); 109 | doReturn(flags).when(channel).parseOptions(options); 110 | mockStatic(GlusterFileAttributes.class); 111 | when(GlusterFileAttributes.parseAttrs(attrs)).thenReturn(mode); 112 | 113 | mockStatic(GLFS.class); 114 | if (null != option) { 115 | when(GLFS.glfs_creat(volptr, path, flags, mode)).thenReturn(createptr); 116 | } else { 117 | when(GLFS.glfs_open(volptr, path, flags)).thenReturn(openptr); 118 | } 119 | 120 | channel.init(mockFileSystem, mockPath, options, attrs); 121 | 122 | assertEquals(mockFileSystem, channel.getFileSystem()); 123 | assertEquals(mockPath, channel.getPath()); 124 | assertEquals(options, channel.getOptions()); 125 | assertEquals(null, channel.getAttrs()); 126 | 127 | verify(mockFileSystem).getVolptr(); 128 | verify(mockPath).toUri(); 129 | verify(channel).parseOptions(options); 130 | verifyStatic(); 131 | GlusterFileAttributes.parseAttrs(attrs); 132 | 133 | if (null != option) { 134 | verifyStatic(); 135 | GLFS.glfs_creat(volptr, path, flags, mode); 136 | } else { 137 | verifyStatic(); 138 | GLFS.glfs_open(volptr, path, flags); 139 | } 140 | } 141 | 142 | @Test 143 | public void testParseOptions() { 144 | Set options = new HashSet(); 145 | options.add(StandardOpenOption.APPEND); 146 | options.add(StandardOpenOption.WRITE); 147 | 148 | int result = channel.parseOptions(options); 149 | 150 | assertEquals(GlusterOpenOption.O_RDWR | GlusterOpenOption.O_APPEND, result); 151 | } 152 | 153 | @Test 154 | public void testRead1Arg() throws IOException { 155 | doNothing().when(channel).guardClosed(); 156 | long fileptr = 1234l; 157 | channel.setFileptr(fileptr); 158 | 159 | byte[] bytes = new byte[]{'a', 'b', 'c'}; 160 | long bufferLength = bytes.length; 161 | long offset = 4; 162 | channel.setPosition(offset); 163 | 164 | mockStatic(GLFS.class); 165 | when(GLFS.glfs_read(fileptr, bytes, bufferLength, 0)).thenReturn(bufferLength); 166 | 167 | doReturn(bytes).when(mockBuffer).array(); 168 | doReturn(mockBuffer).when(mockBuffer).position((int) bufferLength); 169 | 170 | int read = channel.read(mockBuffer); 171 | 172 | assertEquals(bufferLength, read); 173 | 174 | verify(channel).guardClosed(); 175 | verify(mockBuffer).array(); 176 | verify(mockBuffer).position((int) bufferLength); 177 | assertEquals(bufferLength + offset, channel.getPosition()); 178 | 179 | verifyStatic(); 180 | GLFS.glfs_read(fileptr, bytes, bufferLength, 0); 181 | } 182 | 183 | @Test(expected = IOException.class) 184 | public void testRead1Arg_whenReadFails() throws IOException { 185 | doNothing().when(channel).guardClosed(); 186 | long fileptr = 1234l; 187 | channel.setFileptr(fileptr); 188 | 189 | byte[] bytes = new byte[]{'a', 'b', 'c'}; 190 | long bufferLength = bytes.length; 191 | long offset = 4; 192 | channel.setPosition(offset); 193 | 194 | mockStatic(GLFS.class); 195 | when(GLFS.glfs_read(fileptr, bytes, bufferLength, 0)).thenReturn(-1L); 196 | 197 | doReturn(bytes).when(mockBuffer).array(); 198 | 199 | channel.read(mockBuffer); 200 | } 201 | 202 | @Test 203 | public void testRead3Arg() throws IOException { 204 | doNothing().when(channel).guardClosed(); 205 | Set options = new HashSet<>(); 206 | options.add(StandardOpenOption.READ); 207 | channel.setOptions(options); 208 | ByteBuffer[] buffers = new ByteBuffer[2]; 209 | buffers[0] = mockBuffer; 210 | buffers[1] = mockBuffer; 211 | channel.setPosition(0); 212 | int offset = 0; 213 | int length = 2; 214 | doReturn(10L).when(channel).readHelper(buffers, offset, length); 215 | long read = channel.read(buffers, offset, length); 216 | 217 | verify(channel).readHelper(buffers, offset, length); 218 | verify(channel).guardClosed(); 219 | assertEquals(channel.position(), read); 220 | } 221 | 222 | @Test(expected = ClosedChannelException.class) 223 | public void testRead3Arg_whenClosed() throws IOException { 224 | channel.setClosed(true); 225 | ByteBuffer[] buffers = new ByteBuffer[2]; 226 | buffers[0] = mockBuffer; 227 | buffers[1] = mockBuffer; 228 | int offset = 0; 229 | int length = 2; 230 | channel.read(buffers, offset, length); 231 | } 232 | 233 | @Test(expected = IndexOutOfBoundsException.class) 234 | public void testRead3Arg_whenLengthTooSmall() throws IOException { 235 | read3ArgLengthOffsetTestHelper(true, false); 236 | } 237 | 238 | @Test(expected = IndexOutOfBoundsException.class) 239 | public void testRead3Arg_whenLengthTooBig() throws IOException { 240 | read3ArgLengthOffsetTestHelper(true, true); 241 | } 242 | 243 | @Test(expected = IndexOutOfBoundsException.class) 244 | public void testRead3Arg_whenOffsetTooSmall() throws IOException { 245 | read3ArgLengthOffsetTestHelper(false, false); 246 | } 247 | 248 | @Test(expected = IndexOutOfBoundsException.class) 249 | public void testRead3Arg_whenOffsetTooBig() throws IOException { 250 | read3ArgLengthOffsetTestHelper(false, true); 251 | } 252 | 253 | private void read3ArgLengthOffsetTestHelper(boolean testingLength, boolean testingTooBig) throws IOException { 254 | doNothing().when(channel).guardClosed(); 255 | ByteBuffer[] buffers = new ByteBuffer[2]; 256 | buffers[0] = mockBuffer; 257 | buffers[1] = mockBuffer; 258 | int offset = 0; 259 | int length = 2; 260 | 261 | if (testingLength) { 262 | if (testingTooBig) { 263 | length = 3; 264 | } else { 265 | length = -1; 266 | } 267 | } else { 268 | if (testingTooBig) { 269 | offset = 3; 270 | } else { 271 | offset = 1; 272 | } 273 | } 274 | 275 | channel.read(buffers, offset, length); 276 | } 277 | 278 | @Test(expected = NonReadableChannelException.class) 279 | public void testRead3Arg_whenNonReadableChannel() throws IOException { 280 | doNothing().when(channel).guardClosed(); 281 | ByteBuffer[] buffers = new ByteBuffer[2]; 282 | buffers[0] = mockBuffer; 283 | buffers[1] = mockBuffer; 284 | int offset = 0; 285 | int length = 2; 286 | channel.read(buffers, offset, length); 287 | } 288 | 289 | @Test 290 | public void testReadHelper() throws IOException { 291 | long fileptr = 1234L; 292 | channel.setFileptr(fileptr); 293 | 294 | ByteBuffer[] buffers = new ByteBuffer[2]; 295 | buffers[0] = mockBuffer; 296 | buffers[1] = mockBuffer; 297 | int offset = 0; 298 | int length = 2; 299 | 300 | byte[] bytes = new byte[]{'h', 'e', 'l', 'l', 'o'}; 301 | doReturn(bytes).when(mockBuffer).array(); 302 | 303 | when(mockBuffer.remaining()).thenReturn(5, 0, 5, 0); 304 | 305 | mockStatic(GLFS.class); 306 | when(GLFS.glfs_read(fileptr, bytes, 5, 0)).thenReturn(5L); 307 | doReturn(mockBuffer).when(mockBuffer).position(5); 308 | 309 | channel.readHelper(buffers, offset, length); 310 | 311 | verify(mockBuffer, times(2)).position(5); 312 | verifyStatic(times(2)); 313 | GLFS.glfs_read(fileptr, bytes, 5, 0); 314 | verify(mockBuffer, times(4)).remaining(); 315 | verify(mockBuffer, times(2)).array(); 316 | } 317 | 318 | @Test(expected = IOException.class) 319 | public void testReadHelper_whenReadFails() throws IOException { 320 | long fileptr = 1234L; 321 | channel.setFileptr(fileptr); 322 | 323 | ByteBuffer[] buffers = new ByteBuffer[2]; 324 | buffers[0] = mockBuffer; 325 | buffers[1] = mockBuffer; 326 | int offset = 0; 327 | int length = 2; 328 | 329 | byte[] bytes = new byte[]{'h', 'e', 'l', 'l', 'o'}; 330 | doReturn(bytes).when(mockBuffer).array(); 331 | 332 | when(mockBuffer.remaining()).thenReturn(5, 0, 5, 0); 333 | 334 | mockStatic(GLFS.class); 335 | when(GLFS.glfs_read(fileptr, bytes, 5, 0)).thenReturn(-1L); 336 | 337 | channel.readHelper(buffers, offset, length); 338 | } 339 | 340 | @Test 341 | public void testReadHelper_whenEndOfStreamAndTotalReadZero() throws IOException { 342 | long fileptr = 1234L; 343 | channel.setFileptr(fileptr); 344 | 345 | ByteBuffer[] buffers = new ByteBuffer[2]; 346 | buffers[0] = mockBuffer; 347 | buffers[1] = mockBuffer; 348 | int offset = 0; 349 | int length = 2; 350 | 351 | byte[] bytes = new byte[]{'h', 'e', 'l', 'l', 'o'}; 352 | doReturn(bytes).when(mockBuffer).array(); 353 | 354 | when(mockBuffer.remaining()).thenReturn(5, 0, 5, 0); 355 | 356 | mockStatic(GLFS.class); 357 | when(GLFS.glfs_read(fileptr, bytes, 5, 0)).thenReturn(0L); 358 | doReturn(mockBuffer).when(mockBuffer).position(0); 359 | 360 | long ret = channel.readHelper(buffers, offset, length); 361 | 362 | assertEquals(ret, -1); 363 | verify(mockBuffer).position(0); 364 | verifyStatic(); 365 | GLFS.glfs_read(fileptr, bytes, 5, 0); 366 | verify(mockBuffer).remaining(); 367 | verify(mockBuffer).array(); 368 | } 369 | 370 | @Test 371 | public void testRead2Arg() throws IOException { 372 | long position = 5L; 373 | long fileptr = 1234L; 374 | long defaultPosition = 0L; 375 | channel.setFileptr(fileptr); 376 | channel.setPosition(defaultPosition); 377 | 378 | doNothing().when(channel).guardClosed(); 379 | 380 | Set options = new HashSet<>(); 381 | options.add(StandardOpenOption.READ); 382 | channel.setOptions(options); 383 | 384 | doReturn(10L).when(channel).size(); 385 | 386 | mockStatic(GLFS.class); 387 | when(GLFS.glfs_lseek(fileptr, position, 0)).thenReturn(0); 388 | 389 | byte[] bytes = new byte[]{'h', 'e', 'l', 'l', 'o'}; 390 | doReturn(bytes).when(mockBuffer).array(); 391 | 392 | long expectedRet = 7L; 393 | when(GLFS.glfs_read(fileptr, bytes, bytes.length, 0)).thenReturn(expectedRet); 394 | 395 | when(GLFS.glfs_lseek(fileptr, defaultPosition, 0)).thenReturn(0); 396 | 397 | long ret = channel.read(mockBuffer, position); 398 | 399 | assertEquals(ret, expectedRet); 400 | verifyStatic(); 401 | GLFS.glfs_lseek(fileptr, defaultPosition, 0); 402 | verifyStatic(); 403 | GLFS.glfs_read(fileptr, bytes, bytes.length, 0); 404 | verify(mockBuffer).array(); 405 | verifyStatic(); 406 | GLFS.glfs_lseek(fileptr, position, 0); 407 | verify(channel).size(); 408 | verify(channel).guardClosed(); 409 | } 410 | 411 | @Test(expected = ClosedChannelException.class) 412 | public void testRead2Arg_whenClosed() throws IOException { 413 | long position = 5L; 414 | channel.setClosed(true); 415 | channel.read(mockBuffer, position); 416 | } 417 | 418 | @Test(expected = IllegalArgumentException.class) 419 | public void testRead2Arg_whenPositionNegative() throws IOException { 420 | long position = -1L; 421 | 422 | doNothing().when(channel).guardClosed(); 423 | 424 | channel.read(mockBuffer, position); 425 | } 426 | 427 | @Test(expected = NonReadableChannelException.class) 428 | public void testRead2Arg_whenNonReadableChannel() throws IOException { 429 | long position = 5L; 430 | 431 | doNothing().when(channel).guardClosed(); 432 | 433 | channel.read(mockBuffer, position); 434 | } 435 | 436 | @Test 437 | public void testRead2Arg_whenPositionTooBig() throws IOException { 438 | long position = 5L; 439 | 440 | doNothing().when(channel).guardClosed(); 441 | 442 | Set options = new HashSet<>(); 443 | options.add(StandardOpenOption.READ); 444 | channel.setOptions(options); 445 | 446 | doReturn(4L).when(channel).size(); 447 | 448 | long expectedRet = -1L; 449 | 450 | long ret = channel.read(mockBuffer, position); 451 | 452 | assertEquals(ret, expectedRet); 453 | verify(channel).size(); 454 | verify(channel).guardClosed(); 455 | } 456 | 457 | @Test(expected = IOException.class) 458 | public void testRead2Arg_whenFirstSeekFails() throws IOException { 459 | long position = 5L; 460 | long fileptr = 1234L; 461 | channel.setFileptr(fileptr); 462 | 463 | doNothing().when(channel).guardClosed(); 464 | 465 | Set options = new HashSet<>(); 466 | options.add(StandardOpenOption.READ); 467 | channel.setOptions(options); 468 | 469 | doReturn(10L).when(channel).size(); 470 | 471 | mockStatic(GLFS.class); 472 | when(GLFS.glfs_lseek(fileptr, position, 0)).thenReturn(-1); 473 | 474 | channel.read(mockBuffer, position); 475 | 476 | verifyStatic(); 477 | GLFS.glfs_lseek(fileptr, position, 0); 478 | verify(channel).size(); 479 | verify(channel).guardClosed(); 480 | } 481 | 482 | @Test(expected = IOException.class) 483 | public void testRead2Arg_whenReadFails() throws IOException { 484 | long position = 5L; 485 | long fileptr = 1234L; 486 | long defaultPosition = 0L; 487 | channel.setFileptr(fileptr); 488 | channel.setPosition(defaultPosition); 489 | 490 | doNothing().when(channel).guardClosed(); 491 | 492 | Set options = new HashSet<>(); 493 | options.add(StandardOpenOption.READ); 494 | channel.setOptions(options); 495 | 496 | doReturn(10L).when(channel).size(); 497 | 498 | mockStatic(GLFS.class); 499 | when(GLFS.glfs_lseek(fileptr, position, 0)).thenReturn(0); 500 | 501 | byte[] bytes = new byte[]{'h', 'e', 'l', 'l', 'o'}; 502 | doReturn(bytes).when(mockBuffer).array(); 503 | 504 | long expectedRet = -1; 505 | when(GLFS.glfs_read(fileptr, bytes, bytes.length, 0)).thenReturn(expectedRet); 506 | 507 | channel.read(mockBuffer, position); 508 | 509 | verifyStatic(); 510 | GLFS.glfs_read(fileptr, bytes, bytes.length, 0); 511 | verify(mockBuffer).array(); 512 | verifyStatic(); 513 | GLFS.glfs_lseek(fileptr, position, 0); 514 | verify(channel).size(); 515 | verify(channel).guardClosed(); 516 | } 517 | 518 | @Test(expected = IOException.class) 519 | public void testRead2Arg_whenSecondSeekFails() throws IOException { 520 | long position = 5L; 521 | long fileptr = 1234L; 522 | long defaultPosition = 0L; 523 | channel.setFileptr(fileptr); 524 | channel.setPosition(defaultPosition); 525 | 526 | doNothing().when(channel).guardClosed(); 527 | 528 | Set options = new HashSet<>(); 529 | options.add(StandardOpenOption.READ); 530 | channel.setOptions(options); 531 | 532 | doReturn(10L).when(channel).size(); 533 | 534 | mockStatic(GLFS.class); 535 | when(GLFS.glfs_lseek(fileptr, position, 0)).thenReturn(0); 536 | 537 | byte[] bytes = new byte[]{'h', 'e', 'l', 'l', 'o'}; 538 | doReturn(bytes).when(mockBuffer).array(); 539 | 540 | long expectedRet = 7L; 541 | when(GLFS.glfs_read(fileptr, bytes, bytes.length, 0)).thenReturn(expectedRet); 542 | 543 | when(GLFS.glfs_lseek(fileptr, defaultPosition, 0)).thenReturn(-1); 544 | 545 | channel.read(mockBuffer, position); 546 | 547 | verifyStatic(); 548 | GLFS.glfs_lseek(fileptr, defaultPosition, 0); 549 | verifyStatic(); 550 | GLFS.glfs_read(fileptr, bytes, bytes.length, 0); 551 | verify(mockBuffer).array(); 552 | verifyStatic(); 553 | GLFS.glfs_lseek(fileptr, position, 0); 554 | verify(channel).size(); 555 | verify(channel).guardClosed(); 556 | } 557 | 558 | @Test 559 | public void testWrite1Arg() throws IOException { 560 | doNothing().when(channel).guardClosed(); 561 | long fileptr = 1234L; 562 | channel.setFileptr(fileptr); 563 | 564 | byte[] bytes = new byte[]{'a', 'b'}; 565 | int bufferLength = bytes.length; 566 | 567 | mockStatic(GLFS.class); 568 | when(GLFS.glfs_write(fileptr, bytes, bufferLength, 0)).thenReturn(bufferLength); 569 | 570 | doReturn(bytes).when(mockBuffer).array(); 571 | doReturn(null).when(mockBuffer).position(bufferLength); 572 | 573 | int written = channel.write(mockBuffer); 574 | 575 | assertEquals(bufferLength, written); 576 | 577 | verify(channel).guardClosed(); 578 | verify(mockBuffer).array(); 579 | verify(mockBuffer).position(bufferLength); 580 | 581 | verifyStatic(); 582 | GLFS.glfs_write(fileptr, bytes, bufferLength, 0); 583 | } 584 | 585 | @Test 586 | public void testWrite3Arg() throws IOException { 587 | doNothing().when(channel).guardClosed(); 588 | long fileptr = 1234L; 589 | channel.setFileptr(fileptr); 590 | Set mockOptions = Mockito.mock(Set.class); 591 | channel.setOptions(mockOptions); 592 | doReturn(true).when(mockOptions).contains(StandardOpenOption.WRITE); 593 | 594 | byte[] bytes1 = new byte[10]; 595 | byte[] bytes2 = new byte[10]; 596 | ByteBuffer buffer1 = ByteBuffer.wrap(bytes1); 597 | ByteBuffer buffer2 = ByteBuffer.wrap(bytes2); 598 | ByteBuffer[] buffers = new ByteBuffer[]{buffer1, buffer2}; 599 | int length = 2; 600 | int offset = 0; 601 | 602 | mockStatic(GLFS.class); 603 | when(GLFS.glfs_write(fileptr, bytes1, 10, 0)).thenReturn(10); 604 | when(GLFS.glfs_write(fileptr, bytes2, 10, 0)).thenReturn(10); 605 | 606 | long ret = channel.write(buffers, offset, length); 607 | 608 | assertEquals(ret, 20); 609 | 610 | verifyStatic(times(2)); 611 | GLFS.glfs_write(fileptr, bytes1, 10, 0); 612 | 613 | verify(mockOptions).contains(StandardOpenOption.WRITE); 614 | verify(channel).guardClosed(); 615 | } 616 | 617 | @Test(expected = NonWritableChannelException.class) 618 | public void testWrite3Arg_whenChannelNotOpenedForWrite() throws IOException { 619 | doNothing().when(channel).guardClosed(); 620 | long fileptr = 1234L; 621 | channel.setFileptr(fileptr); 622 | Set mockOptions = Mockito.mock(Set.class); 623 | channel.setOptions(mockOptions); 624 | doReturn(false).when(mockOptions).contains(StandardOpenOption.WRITE); 625 | 626 | int length = 2; 627 | int offset = 0; 628 | ByteBuffer buffer1 = Mockito.mock(ByteBuffer.class); 629 | ByteBuffer buffer2 = Mockito.mock(ByteBuffer.class); 630 | ByteBuffer[] buffers = new ByteBuffer[]{buffer1, buffer2}; 631 | 632 | channel.write(buffers, offset, length); 633 | } 634 | 635 | @Test(expected = IndexOutOfBoundsException.class) 636 | public void testWrite3Arg_whenLengthNegative() throws IOException { 637 | testWrite3Arg_helper(true, false); 638 | } 639 | 640 | @Test(expected = IndexOutOfBoundsException.class) 641 | public void testWrite3Arg_whenLengthTooBig() throws IOException { 642 | testWrite3Arg_helper(true, true); 643 | } 644 | 645 | @Test(expected = IndexOutOfBoundsException.class) 646 | public void testWrite3Arg_whenOffsetNegative() throws IOException { 647 | testWrite3Arg_helper(false, false); 648 | } 649 | 650 | @Test(expected = IndexOutOfBoundsException.class) 651 | public void testWrite3Arg_whenOffsetTooBig() throws IOException { 652 | testWrite3Arg_helper(false, true); 653 | } 654 | 655 | private void testWrite3Arg_helper(boolean testingLength, boolean testingTooBig) throws IOException { 656 | doNothing().when(channel).guardClosed(); 657 | 658 | int length; 659 | int offset; 660 | if (testingLength) { 661 | offset = 0; 662 | if (testingTooBig) { 663 | length = 3; 664 | } else { 665 | length = -1; 666 | } 667 | } else { 668 | length = 2; 669 | if (testingTooBig) { 670 | offset = 3; 671 | } else { 672 | offset = -1; 673 | } 674 | } 675 | ByteBuffer buffer1 = Mockito.mock(ByteBuffer.class); 676 | ByteBuffer buffer2 = Mockito.mock(ByteBuffer.class); 677 | ByteBuffer[] buffers = new ByteBuffer[]{buffer1, buffer2}; 678 | 679 | channel.write(buffers, offset, length); 680 | } 681 | 682 | @Test(expected = ClosedChannelException.class) 683 | public void testWrite3Arg_whenClosedChannel() throws IOException { 684 | channel.setClosed(true); 685 | 686 | int length = 2; 687 | int offset = 0; 688 | ByteBuffer buffer1 = Mockito.mock(ByteBuffer.class); 689 | ByteBuffer buffer2 = Mockito.mock(ByteBuffer.class); 690 | ByteBuffer[] buffers = new ByteBuffer[]{buffer1, buffer2}; 691 | 692 | channel.write(buffers, offset, length); 693 | } 694 | 695 | @Test 696 | public void testWrite2Arg_positionLessThanSize() throws IOException { 697 | testWrite2Arg_helper(false); 698 | } 699 | 700 | @Test 701 | public void testWrite2Arg_positionGreaterThanSize() throws IOException { 702 | testWrite2Arg_helper(true); 703 | } 704 | 705 | private void testWrite2Arg_helper(boolean testingPositionSize) throws IOException { 706 | doNothing().when(channel).guardClosed(); 707 | long fileptr = 1234L; 708 | channel.setFileptr(fileptr); 709 | Set mockOptions = Mockito.mock(Set.class); 710 | channel.setOptions(mockOptions); 711 | doReturn(true).when(mockOptions).contains(StandardOpenOption.WRITE); 712 | channel.setPosition(0L); 713 | doReturn(3L).when(channel).size(); 714 | 715 | byte[] bytes = new byte[10]; 716 | ByteBuffer buffer = ByteBuffer.wrap(bytes); 717 | long position = 2L; 718 | 719 | if (testingPositionSize) { 720 | position = 3L; 721 | mockStatic(Arrays.class); 722 | when(Arrays.copyOf(bytes, bytes.length)).thenReturn(bytes); 723 | mockStatic(ByteBuffer.class); 724 | when(ByteBuffer.wrap(bytes)).thenReturn(buffer); 725 | } 726 | 727 | mockStatic(GLFS.class); 728 | when(GLFS.glfs_write(fileptr, bytes, 10, 0)).thenReturn(10); 729 | when(GLFS.glfs_lseek(fileptr, position, 0)).thenReturn(0); 730 | when(GLFS.glfs_lseek(fileptr, 0L, 0)).thenReturn(0); 731 | 732 | int ret = channel.write(buffer, position); 733 | 734 | assertEquals(ret, 10); 735 | 736 | verifyStatic(); 737 | GLFS.glfs_lseek(fileptr, 0L, 0); 738 | verifyStatic(); 739 | GLFS.glfs_lseek(fileptr, position, 0); 740 | verifyStatic(); 741 | GLFS.glfs_write(fileptr, bytes, 10, 0); 742 | 743 | if (testingPositionSize) { 744 | verifyStatic(); 745 | ByteBuffer.wrap(bytes); 746 | verifyStatic(); 747 | Arrays.copyOf(bytes, bytes.length); 748 | } 749 | 750 | verify(channel, times(testingPositionSize ? 2 : 1)).size(); 751 | verify(mockOptions).contains(StandardOpenOption.WRITE); 752 | verify(channel).guardClosed(); 753 | } 754 | 755 | @Test(expected = IOException.class) 756 | public void testWrite2Arg_firstSeekFails() throws IOException { 757 | doNothing().when(channel).guardClosed(); 758 | long fileptr = 1234L; 759 | channel.setFileptr(fileptr); 760 | Set mockOptions = Mockito.mock(Set.class); 761 | channel.setOptions(mockOptions); 762 | doReturn(true).when(mockOptions).contains(StandardOpenOption.WRITE); 763 | channel.setPosition(0L); 764 | doReturn(3L).when(channel).size(); 765 | 766 | byte[] bytes = new byte[10]; 767 | ByteBuffer buffer = ByteBuffer.wrap(bytes); 768 | long position = 2L; 769 | 770 | mockStatic(GLFS.class); 771 | when(GLFS.glfs_write(fileptr, bytes, 10, 0)).thenReturn(10); 772 | when(GLFS.glfs_lseek(fileptr, position, 0)).thenReturn(0); 773 | when(GLFS.glfs_lseek(fileptr, 0L, 0)).thenReturn(-1); 774 | 775 | channel.write(buffer, position); 776 | } 777 | 778 | @Test(expected = IOException.class) 779 | public void testWrite2Arg_secondSeekFails() throws IOException { 780 | doNothing().when(channel).guardClosed(); 781 | long fileptr = 1234L; 782 | channel.setFileptr(fileptr); 783 | Set mockOptions = Mockito.mock(Set.class); 784 | channel.setOptions(mockOptions); 785 | doReturn(true).when(mockOptions).contains(StandardOpenOption.WRITE); 786 | channel.setPosition(0L); 787 | doReturn(3L).when(channel).size(); 788 | 789 | byte[] bytes = new byte[10]; 790 | ByteBuffer buffer = ByteBuffer.wrap(bytes); 791 | long position = 2L; 792 | 793 | mockStatic(GLFS.class); 794 | when(GLFS.glfs_write(fileptr, bytes, 10, 0)).thenReturn(10); 795 | when(GLFS.glfs_lseek(fileptr, position, 0)).thenReturn(-1); 796 | 797 | channel.write(buffer, position); 798 | } 799 | 800 | @Test(expected = NonWritableChannelException.class) 801 | public void testWrite2Arg_whenChannelNonWritable() throws IOException { 802 | doNothing().when(channel).guardClosed(); 803 | Set mockOptions = Mockito.mock(Set.class); 804 | channel.setOptions(mockOptions); 805 | doReturn(false).when(mockOptions).contains(StandardOpenOption.WRITE); 806 | 807 | ByteBuffer buffer = Mockito.mock(ByteBuffer.class); 808 | long position = 2L; 809 | 810 | channel.write(buffer, position); 811 | } 812 | 813 | @Test(expected = IllegalArgumentException.class) 814 | public void testWrite2Arg_positionInvalid() throws IOException { 815 | doNothing().when(channel).guardClosed(); 816 | 817 | ByteBuffer buffer = Mockito.mock(ByteBuffer.class); 818 | long position = -1L; 819 | 820 | channel.write(buffer, position); 821 | } 822 | 823 | @Test(expected = ClosedChannelException.class) 824 | public void testWrite2Arg_whenChannelClosed() throws IOException { 825 | channel.setClosed(true); 826 | 827 | ByteBuffer buffer = Mockito.mock(ByteBuffer.class); 828 | long position = 0L; 829 | 830 | channel.write(buffer, position); 831 | } 832 | 833 | @Test 834 | public void testGuardClosed_whenNotClosed() throws ClosedChannelException { 835 | channel.setClosed(false); 836 | channel.guardClosed(); 837 | } 838 | 839 | @Test(expected = ClosedChannelException.class) 840 | public void testGuardClosed_whenClosed() throws ClosedChannelException { 841 | channel.setClosed(true); 842 | channel.guardClosed(); 843 | } 844 | 845 | @Test 846 | public void testGetPosition() throws IOException { 847 | doNothing().when(channel).guardClosed(); 848 | long position = 12345l; 849 | channel.setPosition(position); 850 | long returnedPosition = channel.position(); 851 | verify(channel).guardClosed(); 852 | assertEquals(position, returnedPosition); 853 | } 854 | 855 | @Test(expected = IllegalArgumentException.class) 856 | public void testSetPosition_whenNegative() throws IOException { 857 | long position = -1l; 858 | channel.position(position); 859 | } 860 | 861 | @Test 862 | public void testSetPosition() throws IOException { 863 | doNothing().when(channel).guardClosed(); 864 | long fileptr = 123l; 865 | channel.setFileptr(fileptr); 866 | long position = 12345l; 867 | 868 | mockStatic(GLFS.class); 869 | when(GLFS.glfs_lseek(fileptr, position, 0)).thenReturn(0); 870 | FileChannel returnedChannel = channel.position(position); 871 | 872 | verify(channel).guardClosed(); 873 | assertEquals(channel, returnedChannel); 874 | assertEquals(position, channel.getPosition()); 875 | 876 | verifyStatic(); 877 | GLFS.glfs_lseek(fileptr, position, 0); 878 | } 879 | 880 | @Test(expected = IOException.class) 881 | public void testForce_whenFailing() throws IOException { 882 | long fileptr = 1234l; 883 | channel.setFileptr(fileptr); 884 | 885 | mockStatic(GLFS.class); 886 | when(GLFS.glfs_fsync(fileptr)).thenReturn(-1); 887 | 888 | channel.force(true); 889 | } 890 | 891 | @Test 892 | public void testForce() throws IOException { 893 | doNothing().when(channel).guardClosed(); 894 | long fileptr = 1234l; 895 | channel.setFileptr(fileptr); 896 | 897 | mockStatic(GLFS.class); 898 | when(GLFS.glfs_fsync(fileptr)).thenReturn(0); 899 | 900 | channel.force(true); 901 | verify(channel).guardClosed(); 902 | verifyStatic(); 903 | GLFS.glfs_fsync(fileptr); 904 | } 905 | 906 | @Test(expected = IOException.class) 907 | public void testImplCloseChannel_whenFailing() throws IOException { 908 | long fileptr = 1234l; 909 | channel.setFileptr(fileptr); 910 | 911 | mockStatic(GLFS.class); 912 | when(GLFS.glfs_close(fileptr)).thenReturn(1); 913 | 914 | channel.implCloseChannel(); 915 | } 916 | 917 | @Test 918 | public void testImplCloseChannel_whenAlreadyClosed() throws IOException { 919 | channel.setClosed(true); 920 | long fileptr = 1234l; 921 | channel.setFileptr(fileptr); 922 | 923 | mockStatic(GLFS.class); 924 | when(GLFS.glfs_close(fileptr)).thenReturn(0); 925 | 926 | channel.implCloseChannel(); 927 | 928 | assertTrue(channel.isClosed()); 929 | 930 | verifyStatic(never()); 931 | GLFS.glfs_close(fileptr); 932 | } 933 | 934 | @Test 935 | public void testImplCloseChannel() throws IOException { 936 | long fileptr = 1234l; 937 | channel.setFileptr(fileptr); 938 | 939 | mockStatic(GLFS.class); 940 | when(GLFS.glfs_close(fileptr)).thenReturn(0); 941 | 942 | channel.implCloseChannel(); 943 | 944 | assertTrue(channel.isClosed()); 945 | 946 | verifyStatic(); 947 | GLFS.glfs_close(fileptr); 948 | } 949 | 950 | @Test 951 | public void testSize() throws Exception { 952 | long fileptr = 1234l; 953 | channel.setFileptr(fileptr); 954 | 955 | long actualSize = 321l; 956 | stat stat = new stat(); 957 | stat.st_size = actualSize; 958 | 959 | PowerMockito.whenNew(stat.class).withNoArguments().thenReturn(stat); 960 | 961 | mockStatic(GLFS.class); 962 | when(GLFS.glfs_fstat(fileptr, stat)).thenReturn(0); 963 | 964 | long size = channel.size(); 965 | 966 | assertEquals(actualSize, size); 967 | 968 | verifyStatic(); 969 | GLFS.glfs_fstat(fileptr, stat); 970 | } 971 | 972 | @Test(expected = IOException.class) 973 | public void testSize_whenFailing() throws Exception { 974 | long fileptr = 1234l; 975 | channel.setFileptr(fileptr); 976 | 977 | long actualSize = 321l; 978 | stat stat = new stat(); 979 | stat.st_size = actualSize; 980 | 981 | PowerMockito.whenNew(stat.class).withNoArguments().thenReturn(stat); 982 | 983 | mockStatic(GLFS.class); 984 | when(GLFS.glfs_fstat(fileptr, stat)).thenReturn(-1); 985 | 986 | long size = channel.size(); 987 | } 988 | 989 | } 990 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/test/java/com/peircean/glusterfs/GlusterFileStoreTest.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import junit.framework.TestCase; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.Mock; 8 | import org.mockito.runners.MockitoJUnitRunner; 9 | 10 | import java.io.IOException; 11 | 12 | import static org.mockito.Mockito.doReturn; 13 | import static org.mockito.Mockito.verify; 14 | 15 | /** 16 | * @author Louis Zuckerman 17 | */ 18 | @RunWith(MockitoJUnitRunner.class) 19 | public class GlusterFileStoreTest extends TestCase { 20 | 21 | @Mock 22 | private GlusterFileSystem mockFileSystem; 23 | @Mock 24 | private GlusterFileSystemProvider mockProvider; 25 | private GlusterFileStore fileStore; 26 | 27 | @Before 28 | public void setUp() { 29 | doReturn(mockProvider).when(mockFileSystem).provider(); 30 | fileStore = new GlusterFileStore(mockFileSystem); 31 | } 32 | 33 | @Test 34 | public void testName() { 35 | String volname = "testvol"; 36 | doReturn(volname).when(mockFileSystem).getVolname(); 37 | assertEquals(volname, fileStore.name()); 38 | } 39 | 40 | @Test 41 | public void testType() { 42 | assertEquals("glusterfs", fileStore.type()); 43 | } 44 | 45 | @Test 46 | public void testIsReadOnly() { 47 | assertFalse(fileStore.isReadOnly()); 48 | } 49 | 50 | @Test 51 | public void testGetTotalSpace() throws IOException { 52 | long volptr = 4321l; 53 | long space = 1234l; 54 | doReturn(volptr).when(mockFileSystem).getVolptr(); 55 | doReturn(space).when(mockProvider).getTotalSpace(volptr); 56 | long totalSpace = fileStore.getTotalSpace(); 57 | verify(mockProvider).getTotalSpace(volptr); 58 | verify(mockFileSystem).getVolptr(); 59 | assertEquals(space, totalSpace); 60 | } 61 | 62 | @Test 63 | public void testGetUsableSpace() throws IOException { 64 | long volptr = 4321l; 65 | long space = 1234l; 66 | doReturn(volptr).when(mockFileSystem).getVolptr(); 67 | doReturn(space).when(mockProvider).getUsableSpace(volptr); 68 | long usableSpace = fileStore.getUsableSpace(); 69 | verify(mockProvider).getUsableSpace(volptr); 70 | verify(mockFileSystem).getVolptr(); 71 | assertEquals(space, usableSpace); 72 | } 73 | 74 | @Test 75 | public void testGetUnallocatedSpace() throws IOException { 76 | long volptr = 4321l; 77 | long space = 1234l; 78 | doReturn(volptr).when(mockFileSystem).getVolptr(); 79 | doReturn(space).when(mockProvider).getUnallocatedSpace(volptr); 80 | long unallocatedSpace = fileStore.getUnallocatedSpace(); 81 | verify(mockProvider).getUnallocatedSpace(volptr); 82 | verify(mockFileSystem).getVolptr(); 83 | assertEquals(space, unallocatedSpace); 84 | } 85 | 86 | 87 | } 88 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/test/java/com/peircean/glusterfs/GlusterFileSystemTest.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import com.peircean.glusterfs.borrowed.GlobPattern; 4 | import junit.framework.TestCase; 5 | import org.junit.Before; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.mockito.Mock; 9 | import org.powermock.api.mockito.PowerMockito; 10 | import org.powermock.core.classloader.annotations.PrepareForTest; 11 | import org.powermock.modules.junit4.PowerMockRunner; 12 | 13 | import java.io.IOException; 14 | import java.nio.file.FileStore; 15 | import java.nio.file.Path; 16 | import java.nio.file.PathMatcher; 17 | import java.util.Iterator; 18 | import java.util.regex.Matcher; 19 | import java.util.regex.Pattern; 20 | import java.util.regex.PatternSyntaxException; 21 | 22 | import static org.mockito.Mockito.doReturn; 23 | import static org.mockito.Mockito.*; 24 | import static org.mockito.Mockito.when; 25 | import static org.powermock.api.mockito.PowerMockito.*; 26 | 27 | /** 28 | * @author Louis Zuckerman 29 | */ 30 | @RunWith(PowerMockRunner.class) 31 | @PrepareForTest({Pattern.class, GlobPattern.class, GlusterFileSystem.class}) 32 | public class GlusterFileSystemTest extends TestCase { 33 | public static final long VOLPTR = 1234l; 34 | public static final String VOLNAME = "testvol"; 35 | public static final String HOST = "123.45.67.89"; 36 | @Mock 37 | private GlusterFileSystemProvider mockFileSystemProvider; 38 | @Mock 39 | private GlusterPath mockPath; 40 | @Mock 41 | private GlusterPathMatcher mockMatcher; 42 | 43 | private GlusterFileSystem fileSystem; 44 | 45 | @Before 46 | public void setUp() { 47 | fileSystem = new GlusterFileSystem(mockFileSystemProvider, HOST, VOLNAME, VOLPTR); 48 | } 49 | 50 | @Test 51 | public void testProvider() { 52 | assertEquals(mockFileSystemProvider, fileSystem.provider()); 53 | } 54 | 55 | @Test(expected = IOException.class) 56 | public void testClose_whenFailing() throws IOException { 57 | doReturn(11).when(mockFileSystemProvider).close(VOLPTR); 58 | fileSystem.close(); 59 | } 60 | 61 | @Test 62 | public void testClose() throws IOException { 63 | doReturn(0).when(mockFileSystemProvider).close(VOLPTR); 64 | fileSystem.close(); 65 | verify(mockFileSystemProvider).close(VOLPTR); 66 | assertEquals(-1, fileSystem.getVolptr()); 67 | } 68 | 69 | @Test 70 | public void testIsOpen_whenOpen() { 71 | assertEquals(true, fileSystem.isOpen()); 72 | } 73 | 74 | @Test 75 | public void testIsOpen_whenClosed() { 76 | fileSystem.setVolptr(-1); 77 | assertEquals(false, fileSystem.isOpen()); 78 | } 79 | 80 | @Test 81 | public void testIsReadOnly() { 82 | assertFalse(fileSystem.isReadOnly()); 83 | } 84 | 85 | @Test 86 | public void testGetSeparator() { 87 | assertEquals("/", fileSystem.getSeparator()); 88 | } 89 | 90 | @Test 91 | public void testGetRootDirectories() { 92 | Iterable pi = fileSystem.getRootDirectories(); 93 | Iterator iterator = pi.iterator(); 94 | Path path = new GlusterPath(fileSystem, "/"); 95 | assertEquals(path, iterator.next()); 96 | assertFalse(iterator.hasNext()); 97 | } 98 | 99 | @Test 100 | public void testGetFileStores() { 101 | GlusterFileStore correctStore = new GlusterFileStore(fileSystem); 102 | Iterable stores = fileSystem.getFileStores(); 103 | Iterator iterator = stores.iterator(); 104 | assertEquals(correctStore, iterator.next()); 105 | assertFalse(iterator.hasNext()); 106 | } 107 | 108 | @Test 109 | public void testGetPath() { 110 | Path correctPath = new GlusterPath(fileSystem, "/foo/bar/baz"); 111 | Path returnedPath = fileSystem.getPath("/foo", "bar", "baz"); 112 | assertEquals(correctPath, returnedPath); 113 | } 114 | 115 | @Test 116 | public void testToString() { 117 | doReturn("gluster").when(mockFileSystemProvider).getScheme(); 118 | String string = "gluster://" + HOST + ":" + VOLNAME; 119 | assertEquals(string, fileSystem.toString()); 120 | verify(mockFileSystemProvider).getScheme(); 121 | } 122 | 123 | @Test(expected = IllegalArgumentException.class) 124 | public void testGetPathMatcher_whenBadInput() { 125 | fileSystem.getPathMatcher("foo"); 126 | } 127 | 128 | @Test(expected = UnsupportedOperationException.class) 129 | public void testGetPathMatcher_whenBadSyntax() { 130 | fileSystem.getPathMatcher("foo:bar"); 131 | } 132 | 133 | @Test(expected = PatternSyntaxException.class) 134 | public void testGetPathMatcher_whenBadExpression() { 135 | fileSystem.getPathMatcher("regex:["); 136 | } 137 | 138 | @Test 139 | public void testGetPathMatcher_whenGlob() throws Exception { 140 | pathMatcherHelper(true); 141 | } 142 | 143 | @Test 144 | public void testGetPathMatcher_whenRegex() throws Exception { 145 | pathMatcherHelper(false); 146 | } 147 | 148 | void pathMatcherHelper(boolean glob) throws Exception { 149 | PowerMockito.mockStatic(GlobPattern.class); 150 | Pattern pattern = PowerMockito.mock(Pattern.class); 151 | Matcher matcher = PowerMockito.mock(Matcher.class); 152 | 153 | String globPattern = "someglob"; 154 | if (glob) { 155 | mockStatic(GlobPattern.class); 156 | when(GlobPattern.compile(globPattern)).thenReturn(pattern); 157 | } else { 158 | mockStatic(Pattern.class); 159 | when(Pattern.compile(globPattern)).thenReturn(pattern); 160 | } 161 | 162 | when(pattern.matcher(globPattern)).thenReturn(matcher); 163 | PowerMockito.whenNew(GlusterPathMatcher.class).withArguments(pattern).thenReturn(mockMatcher); 164 | 165 | String syntax; 166 | if (glob) { 167 | syntax = "glob"; 168 | } else { 169 | syntax = "regex"; 170 | } 171 | PathMatcher pathMatcher = fileSystem.getPathMatcher(syntax + ":" + globPattern); 172 | 173 | assertEquals(mockMatcher, pathMatcher); 174 | 175 | verifyNew(GlusterPathMatcher.class).withArguments(pattern); 176 | 177 | if (glob) { 178 | verifyStatic(); 179 | GlobPattern.compile(globPattern); 180 | } else { 181 | verifyStatic(); 182 | Pattern.compile(globPattern); 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/test/java/com/peircean/glusterfs/GlusterPathMatcherTest.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import com.peircean.glusterfs.borrowed.GlobPattern; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.mockito.Mock; 7 | import org.powermock.api.mockito.PowerMockito; 8 | import org.powermock.core.classloader.annotations.PrepareForTest; 9 | import org.powermock.modules.junit4.PowerMockRunner; 10 | 11 | import java.net.URISyntaxException; 12 | import java.util.regex.Matcher; 13 | import java.util.regex.Pattern; 14 | 15 | import static org.junit.Assert.assertEquals; 16 | import static org.mockito.Mockito.doReturn; 17 | import static org.mockito.Mockito.when; 18 | 19 | @RunWith(PowerMockRunner.class) 20 | @PrepareForTest({Pattern.class, Matcher.class, GlusterPathMatcher.class, GlobPattern.class}) 21 | public class GlusterPathMatcherTest { 22 | @Mock 23 | private GlusterPath mockPath; 24 | 25 | @Test 26 | public void testPathMatcherMatches_whenTrue() throws URISyntaxException { 27 | matchesHelper(true); 28 | } 29 | 30 | @Test 31 | public void testPathMatcherMatches_whenFalse() throws URISyntaxException { 32 | matchesHelper(false); 33 | } 34 | 35 | void matchesHelper(boolean result) throws URISyntaxException { 36 | Pattern pattern = PowerMockito.mock(Pattern.class); 37 | Matcher matcher = PowerMockito.mock(Matcher.class); 38 | String foo = "/foo"; 39 | 40 | doReturn(foo).when(mockPath).toString(); 41 | 42 | when(pattern.matcher(foo)).thenReturn(matcher); 43 | when(matcher.matches()).thenReturn(result); 44 | 45 | GlusterPathMatcher pathMatcher = new GlusterPathMatcher(pattern); 46 | 47 | boolean matches = pathMatcher.matches(mockPath); 48 | 49 | assertEquals(result, matches); 50 | 51 | // verify doesn't work with toString or PowerMock objects 52 | // verify(mockPath).toString(); 53 | 54 | // verify(pattern).matcher(foo); 55 | // verify(matcher).matches(); 56 | 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/test/java/com/peircean/glusterfs/GlusterPathPowerMockTest.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.mockito.Mock; 6 | import org.powermock.api.mockito.PowerMockito; 7 | import org.powermock.core.classloader.annotations.PrepareForTest; 8 | import org.powermock.modules.junit4.PowerMockRunner; 9 | 10 | import java.nio.file.Files; 11 | import java.nio.file.NotDirectoryException; 12 | 13 | import static org.mockito.Mockito.when; 14 | 15 | @RunWith(PowerMockRunner.class) 16 | @PrepareForTest({GlusterPath.class, Files.class}) 17 | public class GlusterPathPowerMockTest { 18 | @Mock 19 | private GlusterFileSystem mockFileSystem; 20 | 21 | @Test(expected = NotDirectoryException.class) 22 | public void testGuardRegisterWatchDirectory_whenNotDirectory() throws NotDirectoryException { 23 | GlusterPath path = new GlusterPath(mockFileSystem, new String[]{"/foo"}, false); 24 | 25 | PowerMockito.mockStatic(Files.class); 26 | when(Files.isDirectory(path)).thenReturn(false); 27 | 28 | path.guardRegisterWatchDirectory(); 29 | } 30 | 31 | @Test 32 | public void testGuardRegisterWatchDirectory() throws NotDirectoryException { 33 | GlusterPath path = new GlusterPath(mockFileSystem, new String[]{"/foo"}, false); 34 | 35 | PowerMockito.mockStatic(Files.class); 36 | when(Files.isDirectory(path)).thenReturn(true); 37 | 38 | path.guardRegisterWatchDirectory(); 39 | 40 | PowerMockito.verifyStatic(); 41 | Files.isDirectory(path); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/test/java/com/peircean/glusterfs/GlusterPathTest.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import junit.framework.TestCase; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.Mock; 8 | import org.mockito.runners.MockitoJUnitRunner; 9 | 10 | import java.io.IOException; 11 | import java.net.URI; 12 | import java.net.URISyntaxException; 13 | import java.nio.file.*; 14 | import java.util.Iterator; 15 | import java.util.LinkedList; 16 | import java.util.List; 17 | 18 | import static org.mockito.Mockito.*; 19 | 20 | /** 21 | * @author Louis Zuckerman 22 | */ 23 | @RunWith(MockitoJUnitRunner.class) 24 | public class GlusterPathTest extends TestCase { 25 | @Mock 26 | private GlusterFileSystem mockFileSystem; 27 | @Mock 28 | private GlusterPath mockGlusterPath; 29 | 30 | @Before 31 | public void setUp() { 32 | doReturn("/").when(mockFileSystem).getSeparator(); 33 | } 34 | 35 | @Test 36 | public void testConstruct() { 37 | GlusterPath p = new GlusterPath(mockFileSystem, "/foo/bar"); 38 | assertEquals(2, p.getParts().length); 39 | 40 | p = new GlusterPath(mockFileSystem, "foo/bar"); 41 | assertEquals(2, p.getParts().length); 42 | 43 | p = new GlusterPath(mockFileSystem, "foo/bar/"); 44 | assertEquals(2, p.getParts().length); 45 | 46 | p = new GlusterPath(mockFileSystem, "/foo/bar/"); 47 | assertEquals(2, p.getParts().length); 48 | } 49 | 50 | @Test(expected = IllegalArgumentException.class) 51 | public void testConstruct_noFileSystem() { 52 | Path p = new GlusterPath(null, ""); 53 | } 54 | 55 | // @Test(expected = InvalidPathException.class) 56 | // public void testConstruct_emptyPath() { 57 | // Path p = new GlusterPath(mockFileSystem, ""); 58 | // } 59 | 60 | @Test(expected = InvalidPathException.class) 61 | public void testConstruct_nullPath() { 62 | Path p = new GlusterPath(mockFileSystem, null); 63 | } 64 | 65 | @Test 66 | public void testConstruct_internal() { 67 | String[] parts = {"a", "b"}; 68 | GlusterPath p = new GlusterPath(mockFileSystem, parts, true); 69 | assertEquals(parts, p.getParts()); 70 | assertEquals(mockFileSystem, p.getFileSystem()); 71 | } 72 | 73 | @Test 74 | public void testIsAbsolute() { 75 | Path p = new GlusterPath(mockFileSystem, "foo/bar"); 76 | assertFalse(p.isAbsolute()); 77 | 78 | p = new GlusterPath(mockFileSystem, "/foo/bar"); 79 | assertTrue(p.isAbsolute()); 80 | } 81 | 82 | @Test 83 | public void testGetRoot_absolute() { 84 | List paths = new LinkedList(); 85 | GlusterPath root = new GlusterPath(mockFileSystem, "/"); 86 | paths.add(root); 87 | doReturn(paths).when(mockFileSystem).getRootDirectories(); 88 | 89 | Path p = new GlusterPath(mockFileSystem, "/foo/bar"); 90 | Path returned = p.getRoot(); 91 | 92 | verify(mockFileSystem).getRootDirectories(); 93 | assertEquals(root, returned); 94 | } 95 | 96 | @Test 97 | public void testGetRoot_relative() { 98 | Path p = new GlusterPath(mockFileSystem, "foo/bar"); 99 | Path returned = p.getRoot(); 100 | 101 | verify(mockFileSystem, times(0)).getRootDirectories(); 102 | assertEquals(null, returned); 103 | } 104 | 105 | @Test 106 | public void testGetFilesystem() { 107 | Path p = new GlusterPath(mockFileSystem, "foo/bar"); 108 | assertTrue(p.getFileSystem() == mockFileSystem); 109 | } 110 | 111 | @Test 112 | public void testToUri() throws URISyntaxException { 113 | GlusterFileSystemProvider mockProvider = mock(GlusterFileSystemProvider.class); 114 | String scheme = GlusterFileSystemProvider.GLUSTER; 115 | doReturn(scheme).when(mockProvider).getScheme(); 116 | doReturn(mockProvider).when(mockFileSystem).provider(); 117 | 118 | String host = "localhost"; 119 | doReturn(host).when(mockFileSystem).getHost(); 120 | 121 | String volname = "foo"; 122 | doReturn(volname).when(mockFileSystem).getVolname(); 123 | 124 | String path = "/foo/bar"; 125 | GlusterPath p = spy(new GlusterPath(mockFileSystem, path)); 126 | doReturn(mockFileSystem).when(p).getFileSystem(); 127 | doReturn(path).when(p).toString(); 128 | 129 | String authority = host + ":" + volname; 130 | URI expected = new URI(scheme, authority, path, null, null); 131 | assertEquals(expected, p.toUri()); 132 | } 133 | 134 | @Test 135 | public void testGetFileName() { 136 | Path p = new GlusterPath(mockFileSystem, "/"); 137 | assertEquals(null, p.getFileName()); 138 | 139 | p = new GlusterPath(mockFileSystem, "/bar"); 140 | assertEquals(new GlusterPath(mockFileSystem, "bar"), p.getFileName()); 141 | 142 | p = new GlusterPath(mockFileSystem, "foo/bar"); 143 | assertEquals(new GlusterPath(mockFileSystem, "bar"), p.getFileName()); 144 | } 145 | 146 | @Test 147 | public void testGetParent() { 148 | List roots = new LinkedList(); 149 | roots.add(mockGlusterPath); 150 | doReturn(roots).when(mockFileSystem).getRootDirectories(); 151 | 152 | Path p = new GlusterPath(mockFileSystem, "/"); 153 | assertEquals(mockGlusterPath, p.getParent()); 154 | 155 | p = new GlusterPath(mockFileSystem, "/bar"); 156 | assertEquals(mockGlusterPath, p.getParent()); 157 | 158 | p = new GlusterPath(mockFileSystem, "/bar/baz"); 159 | assertEquals(new GlusterPath(mockFileSystem, "/bar"), p.getParent()); 160 | 161 | p = new GlusterPath(mockFileSystem, "foo/bar"); 162 | assertEquals(new GlusterPath(mockFileSystem, "foo"), p.getParent()); 163 | 164 | p = new GlusterPath(mockFileSystem, "bar"); 165 | assertEquals(null, p.getParent()); 166 | 167 | verify(mockFileSystem, times(2)).getRootDirectories(); 168 | } 169 | 170 | @Test 171 | public void testGetNameCount() { 172 | Path p = new GlusterPath(mockFileSystem, "/"); 173 | assertEquals(0, p.getNameCount()); 174 | 175 | p = new GlusterPath(mockFileSystem, "/bar"); 176 | assertEquals(1, p.getNameCount()); 177 | 178 | p = new GlusterPath(mockFileSystem, "/foo/bar"); 179 | assertEquals(2, p.getNameCount()); 180 | 181 | p = new GlusterPath(mockFileSystem, "foo/bar"); 182 | assertEquals(2, p.getNameCount()); 183 | } 184 | 185 | @Test(expected = IllegalArgumentException.class) 186 | public void testGetName_noName() { 187 | Path p = new GlusterPath(mockFileSystem, "/"); 188 | p.getName(0); 189 | } 190 | 191 | @Test(expected = IllegalArgumentException.class) 192 | public void testGetName_negative() { 193 | Path p = new GlusterPath(mockFileSystem, "/foo"); 194 | p.getName(-1); 195 | } 196 | 197 | @Test(expected = IllegalArgumentException.class) 198 | public void testGetName_excessive() { 199 | Path p = new GlusterPath(mockFileSystem, "/foo"); 200 | p.getName(4); 201 | } 202 | 203 | @Test 204 | public void testGetName() { 205 | Path p = new GlusterPath(mockFileSystem, "/foo/bar/baz"); 206 | assertEquals(new GlusterPath(mockFileSystem, new String[]{"foo"}, true), p.getName(0)); 207 | assertEquals(new GlusterPath(mockFileSystem, new String[]{"foo", "bar"}, true), p.getName(1)); 208 | assertEquals(new GlusterPath(mockFileSystem, new String[]{"foo", "bar", "baz"}, true), p.getName(2)); 209 | 210 | p = new GlusterPath(mockFileSystem, "foo/bar/baz"); 211 | assertEquals(new GlusterPath(mockFileSystem, new String[]{"foo"}, false), p.getName(0)); 212 | assertEquals(new GlusterPath(mockFileSystem, new String[]{"foo", "bar"}, false), p.getName(1)); 213 | assertEquals(new GlusterPath(mockFileSystem, new String[]{"foo", "bar", "baz"}, false), p.getName(2)); 214 | } 215 | 216 | @Test(expected = IllegalArgumentException.class) 217 | public void testSubpath_noName() { 218 | Path p = new GlusterPath(mockFileSystem, "/"); 219 | p.getName(0); 220 | } 221 | 222 | @Test(expected = IllegalArgumentException.class) 223 | public void testSubpath_negativeStart() { 224 | Path p = new GlusterPath(mockFileSystem, "/foo"); 225 | p.subpath(-1, 0); 226 | } 227 | 228 | @Test(expected = IllegalArgumentException.class) 229 | public void testSubpath_negativeEnd() { 230 | Path p = new GlusterPath(mockFileSystem, "/foo"); 231 | p.subpath(0, -1); 232 | } 233 | 234 | @Test(expected = IllegalArgumentException.class) 235 | public void testSubpath_excessiveStart() { 236 | Path p = new GlusterPath(mockFileSystem, "/foo"); 237 | p.subpath(4, 5); 238 | } 239 | 240 | @Test(expected = IllegalArgumentException.class) 241 | public void testSubpath_excessiveEnd() { 242 | Path p = new GlusterPath(mockFileSystem, "/foo"); 243 | p.subpath(0, 4); 244 | } 245 | 246 | @Test(expected = IllegalArgumentException.class) 247 | public void testSubpath_Inverted() { 248 | Path p = new GlusterPath(mockFileSystem, "/foo"); 249 | p.subpath(2, 1); 250 | } 251 | 252 | @Test 253 | public void testSubpath() { 254 | Path p = new GlusterPath(mockFileSystem, "/foo/bar/baz"); 255 | assertEquals(new GlusterPath(mockFileSystem, new String[]{"foo"}, true), p.subpath(0, 1)); 256 | assertEquals(new GlusterPath(mockFileSystem, new String[]{"bar"}, true), p.subpath(1, 2)); 257 | assertEquals(new GlusterPath(mockFileSystem, new String[]{"bar", "baz"}, true), p.subpath(1, 3)); 258 | 259 | p = new GlusterPath(mockFileSystem, "foo/bar/baz"); 260 | assertEquals(new GlusterPath(mockFileSystem, new String[]{"foo"}, false), p.subpath(0, 1)); 261 | assertEquals(new GlusterPath(mockFileSystem, new String[]{"bar"}, false), p.subpath(1, 2)); 262 | assertEquals(new GlusterPath(mockFileSystem, new String[]{"bar", "baz"}, false), p.subpath(1, 3)); 263 | } 264 | 265 | @Test 266 | public void testStartsWith() { 267 | Path p1 = new GlusterPath(mockFileSystem, "/"); 268 | assertTrue(p1.startsWith(p1)); 269 | 270 | Path p2 = new GlusterPath(mockFileSystem, "/foo"); 271 | assertFalse(p1.startsWith(p2)); 272 | 273 | assertTrue(p2.startsWith(p1)); 274 | 275 | p1 = new GlusterPath(mockFileSystem, "/bar"); 276 | assertFalse(p1.startsWith(p2)); 277 | } 278 | 279 | @Test 280 | public void testStartsWith_String() { 281 | Path p1 = new GlusterPath(mockFileSystem, "/"); 282 | assertTrue(p1.startsWith("/")); 283 | 284 | Path p2 = new GlusterPath(mockFileSystem, "/foo"); 285 | assertFalse(p1.startsWith("/foo")); 286 | 287 | assertTrue(p2.startsWith("/")); 288 | 289 | p1 = new GlusterPath(mockFileSystem, "/bar"); 290 | assertFalse(p1.startsWith("/foo")); 291 | } 292 | 293 | @Test 294 | public void testEndsWith() { 295 | Path p1 = new GlusterPath(mockFileSystem, "/"); 296 | assertTrue(p1.endsWith(p1)); 297 | 298 | Path p2 = new GlusterPath(mockFileSystem, "/foo"); 299 | assertFalse(p1.endsWith(p2)); 300 | 301 | p1 = new GlusterPath(mockFileSystem, "/foo"); 302 | assertTrue(p1.endsWith(p2)); 303 | 304 | p2 = new GlusterPath(mockFileSystem, "foo"); 305 | assertTrue(p1.endsWith(p2)); 306 | 307 | p1 = new GlusterPath(mockFileSystem, "foo/bar"); 308 | p2 = new GlusterPath(mockFileSystem, "bar"); 309 | assertTrue(p1.endsWith(p2)); 310 | 311 | p2 = new GlusterPath(mockFileSystem, "/bar"); 312 | assertFalse(p1.endsWith(p2)); 313 | } 314 | 315 | @Test 316 | public void testNormalize() { 317 | Path p = new GlusterPath(mockFileSystem, "foo//bar"); 318 | assertEquals(new GlusterPath(mockFileSystem, "foo/bar"), p.normalize()); 319 | 320 | p = new GlusterPath(mockFileSystem, "foo/./bar"); 321 | assertEquals(new GlusterPath(mockFileSystem, "foo/bar"), p.normalize()); 322 | 323 | p = new GlusterPath(mockFileSystem, "foo/././bar"); 324 | assertEquals(new GlusterPath(mockFileSystem, "foo/bar"), p.normalize()); 325 | 326 | p = new GlusterPath(mockFileSystem, "foo/baz/../bar"); 327 | assertEquals(new GlusterPath(mockFileSystem, "foo/bar"), p.normalize()); 328 | 329 | p = new GlusterPath(mockFileSystem, "foo/baz/../baz/../bar"); 330 | assertEquals(new GlusterPath(mockFileSystem, "foo/bar"), p.normalize()); 331 | } 332 | 333 | @Test 334 | public void testResolve() { 335 | Path path = new GlusterPath(mockFileSystem, "/"); 336 | Path otherPath = new GlusterPath(mockFileSystem, "/bar"); 337 | 338 | assertEquals(otherPath, path.resolve(otherPath)); 339 | 340 | otherPath = new GlusterPath(mockFileSystem, "bar"); 341 | assertEquals(new GlusterPath(mockFileSystem, "/bar"), path.resolve(otherPath)); 342 | 343 | otherPath = new GlusterPath(mockFileSystem, ""); 344 | assertEquals(path, path.resolve(otherPath)); 345 | } 346 | 347 | @Test 348 | public void testResolve_string() { 349 | Path path = new GlusterPath(mockFileSystem, "/"); 350 | 351 | assertEquals(new GlusterPath(mockFileSystem, "/bar"), path.resolve("/bar")); 352 | 353 | assertEquals(new GlusterPath(mockFileSystem, "/bar"), path.resolve("bar")); 354 | 355 | assertEquals(path, path.resolve("")); 356 | 357 | path = new GlusterPath(mockFileSystem, "/foo"); 358 | assertEquals(new GlusterPath(mockFileSystem, "/foo/bar"), path.resolve("bar")); 359 | } 360 | 361 | @Test 362 | public void testResolveSibling() { 363 | Path p = new GlusterPath(mockFileSystem, "foo/bar"); 364 | Path other = new GlusterPath(mockFileSystem, "baz"); 365 | Path finalPath = p.resolveSibling(other); 366 | assertEquals(new GlusterPath(mockFileSystem, "foo/baz"), finalPath); 367 | } 368 | 369 | @Test 370 | public void testResolveSibling_string() { 371 | Path p = new GlusterPath(mockFileSystem, "foo/bar"); 372 | Path finalPath = p.resolveSibling("baz"); 373 | assertEquals(new GlusterPath(mockFileSystem, "foo/baz"), finalPath); 374 | } 375 | 376 | @Test(expected = IllegalArgumentException.class) 377 | public void testRelativize_otherIsRelative() { 378 | Path p = new GlusterPath(mockFileSystem, "/foo/bar"); 379 | Path other = new GlusterPath(mockFileSystem, "foo/baz"); 380 | p.relativize(other); 381 | } 382 | 383 | @Test(expected = IllegalArgumentException.class) 384 | public void testRelativize_thisIsRelative() { 385 | Path p = new GlusterPath(mockFileSystem, "foo/bar"); 386 | Path other = new GlusterPath(mockFileSystem, "/foo/baz"); 387 | p.relativize(other); 388 | } 389 | 390 | @Test(expected = IllegalArgumentException.class) 391 | public void testRelativize_bothAreRelative() { 392 | Path p = new GlusterPath(mockFileSystem, "foo/bar"); 393 | Path other = new GlusterPath(mockFileSystem, "foo/baz"); 394 | p.relativize(other); 395 | } 396 | 397 | @Test 398 | public void testRelativize() { 399 | Path p = new GlusterPath(mockFileSystem, "/foo/bar"); 400 | Path other = new GlusterPath(mockFileSystem, "/foo/baz"); 401 | 402 | Path relativePath = p.relativize(other); 403 | assertEquals(new GlusterPath(mockFileSystem, "../baz"), relativePath); 404 | 405 | other = new GlusterPath(mockFileSystem, "/foo/bar/baz"); 406 | relativePath = p.relativize(other); 407 | assertEquals(new GlusterPath(mockFileSystem, "baz"), relativePath); 408 | 409 | p = new GlusterPath(mockFileSystem, "/foo/bar"); 410 | other = new GlusterPath(mockFileSystem, "/foo"); 411 | relativePath = p.relativize(other); 412 | assertEquals(new GlusterPath(mockFileSystem, ".."), relativePath); 413 | 414 | p = new GlusterPath(mockFileSystem, "/foo/bar"); 415 | other = new GlusterPath(mockFileSystem, "/baz"); 416 | relativePath = p.relativize(other); 417 | assertEquals(new GlusterPath(mockFileSystem, "../../baz"), relativePath); 418 | 419 | p = new GlusterPath(mockFileSystem, "/foo"); 420 | other = new GlusterPath(mockFileSystem, "/bar/baz"); 421 | relativePath = p.relativize(other); 422 | assertEquals(new GlusterPath(mockFileSystem, "../bar/baz"), relativePath); 423 | } 424 | 425 | @Test(expected = UnsupportedOperationException.class) 426 | public void testToAbsolutePath_whenRelative() { 427 | Path p = new GlusterPath(mockFileSystem, "foo"); 428 | p.toAbsolutePath(); 429 | } 430 | 431 | @Test 432 | public void testToAbsolutePath() { 433 | Path p = new GlusterPath(mockFileSystem, "/foo"); 434 | assertEquals(p, p.toAbsolutePath()); 435 | } 436 | 437 | @Test 438 | public void testIterator() { 439 | Path p = new GlusterPath(mockFileSystem, "/foo/bar"); 440 | Iterator it = p.iterator(); 441 | assertEquals(new GlusterPath(mockFileSystem, "foo"), it.next()); 442 | assertEquals(new GlusterPath(mockFileSystem, "bar"), it.next()); 443 | 444 | p = new GlusterPath(mockFileSystem, "/"); 445 | it = p.iterator(); 446 | assertFalse(it.hasNext()); 447 | } 448 | 449 | @Test(expected = ClassCastException.class) 450 | public void testCompareTo_differentProvider() { 451 | Path path = new GlusterPath(mockFileSystem, ""); 452 | Path zipfile = Paths.get("/codeSamples/zipfs/zipfstest.zip"); 453 | path.compareTo(zipfile); 454 | } 455 | 456 | @Test 457 | public void testCompareTo() { 458 | Path p = new GlusterPath(mockFileSystem, "/foo"); 459 | Path other = new GlusterPath(mockFileSystem, "/bar"); 460 | assertTrue(p.compareTo(other) > 0); 461 | 462 | p = new GlusterPath(mockFileSystem, "/bar"); 463 | other = new GlusterPath(mockFileSystem, "/foo"); 464 | assertTrue(p.compareTo(other) < 0); 465 | 466 | p = new GlusterPath(mockFileSystem, "/foo"); 467 | other = new GlusterPath(mockFileSystem, "/foo"); 468 | assertEquals(0, p.compareTo(other)); 469 | 470 | p = new GlusterPath(mockFileSystem, "/"); 471 | other = new GlusterPath(mockFileSystem, "/foo"); 472 | assertTrue(p.compareTo(other) < 0); 473 | 474 | p = new GlusterPath(mockFileSystem, "/foo"); 475 | other = new GlusterPath(mockFileSystem, "/"); 476 | assertTrue(p.compareTo(other) > 0); 477 | } 478 | 479 | @Test 480 | public void testToString_whenPathString() { 481 | String pathString = "/bar/baz"; 482 | Path p = new GlusterPath(mockFileSystem, pathString); 483 | String filesystemString = "gluster://127.0.2.1:foo"; 484 | doReturn(filesystemString).when(mockFileSystem).toString(); 485 | assertEquals(pathString, p.toString()); 486 | } 487 | 488 | @Test 489 | public void testToString_whenNoPathString() { 490 | Path p = new GlusterPath(mockFileSystem, new String[]{"a", "b"}, true); 491 | assertEquals("/a/b", p.toString()); 492 | } 493 | 494 | @Test 495 | public void testGetString_whenPathString() { 496 | String string = "/foo/bar"; 497 | GlusterPath path = new GlusterPath(mockFileSystem, string); 498 | path.setParts(new String[]{"a", "b"}); 499 | assertEquals(string, path.getString()); 500 | } 501 | 502 | @Test 503 | public void testGetString_whenNoPathString() { 504 | GlusterPath path = new GlusterPath(mockFileSystem, new String[]{"a", "b"}, false); 505 | assertEquals("a/b", path.getString()); 506 | } 507 | 508 | @Test 509 | public void testRegisterWatchService() throws IOException { 510 | GlusterPath path = spy(new GlusterPath(mockFileSystem, new String[]{"foo", "bar"}, true)); 511 | GlusterWatchService mockWatchService = mock(GlusterWatchService.class); 512 | doNothing().when(path).guardRegisterWatchService(mockWatchService); 513 | 514 | WatchEvent.Kind[] kinds = new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}; 515 | 516 | doNothing().when(path).guardRegisterWatchDirectory(); 517 | 518 | WatchKey mockKey = mock(WatchKey.class); 519 | doReturn(mockKey).when(mockWatchService).registerPath(path, kinds); 520 | 521 | WatchKey watchKey = path.register(mockWatchService, kinds); 522 | 523 | assertEquals(mockKey, watchKey); 524 | 525 | verify(path).guardRegisterWatchService(mockWatchService); 526 | verify(path).guardRegisterWatchDirectory(); 527 | verify(mockWatchService).registerPath(path, kinds); 528 | } 529 | 530 | @Test(expected = UnsupportedOperationException.class) 531 | public void testGuardRegisterWatchService() { 532 | GlusterPath path = spy(new GlusterPath(mockFileSystem, new String[]{}, false)); 533 | WatchService mockWatchService = mock(WatchService.class); 534 | 535 | path.guardRegisterWatchService(mockWatchService); 536 | } 537 | 538 | } 539 | 540 | -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/test/java/com/peircean/glusterfs/GlusterWatchKeyTest.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.mockito.Mock; 8 | import org.powermock.api.mockito.PowerMockito; 9 | import org.powermock.core.classloader.annotations.PrepareForTest; 10 | import org.powermock.modules.junit4.PowerMockRunner; 11 | 12 | import java.io.IOException; 13 | import java.nio.file.DirectoryStream; 14 | import java.nio.file.Files; 15 | import java.nio.file.Path; 16 | import java.nio.file.WatchEvent; 17 | import java.util.*; 18 | 19 | import static junit.framework.Assert.*; 20 | import static org.mockito.Matchers.isA; 21 | import static org.mockito.Mockito.*; 22 | import static org.powermock.api.mockito.PowerMockito.doReturn; 23 | import static org.powermock.api.mockito.PowerMockito.when; 24 | 25 | @RunWith(PowerMockRunner.class) 26 | @PrepareForTest({GlusterWatchKey.class, Files.class, LinkedList.class}) 27 | public class GlusterWatchKeyTest { 28 | @Mock 29 | GlusterPath mockPath; 30 | 31 | @Mock 32 | WatchEvent.Kind mockKind; 33 | 34 | WatchEvent.Kind[] mockKinds = new WatchEvent.Kind[]{mockKind}; 35 | 36 | GlusterWatchKey key; 37 | 38 | @Before 39 | public void setUp() { 40 | key = PowerMockito.spy(new GlusterWatchKey(mockPath, mockKinds)); 41 | } 42 | 43 | @Test 44 | public void testUpdate_whenIOException() throws IOException { 45 | PowerMockito.mockStatic(Files.class); 46 | when(Files.newDirectoryStream(mockPath)).thenThrow(new IOException()); 47 | 48 | assertFalse(key.update()); 49 | 50 | PowerMockito.verifyStatic(); 51 | Files.newDirectoryStream(mockPath); 52 | } 53 | 54 | @Test 55 | public void testUpdate_whenNoEvents() throws Exception { 56 | updateHelper(UpdateTestMode.NONE); 57 | } 58 | 59 | @Test 60 | public void testUpdate_whenExistingEvent() throws Exception { 61 | updateHelper(UpdateTestMode.CHANGED); 62 | } 63 | 64 | @Test 65 | public void testUpdate_whenDeletedEvent() throws Exception { 66 | updateHelper(UpdateTestMode.DELETED); 67 | } 68 | 69 | void updateHelper(UpdateTestMode mode) throws Exception { 70 | boolean deleted = false; 71 | boolean existing = false; 72 | switch (mode) { 73 | case CHANGED: 74 | existing = true; 75 | break; 76 | case DELETED: 77 | deleted = true; 78 | break; 79 | } 80 | LinkedList mockPaths = new LinkedList<>(); 81 | Path onePath = mock(Path.class); 82 | mockPaths.add(onePath); 83 | 84 | LinkedList mockFiles = mock(LinkedList.class); 85 | PowerMockito.whenNew(LinkedList.class).withNoArguments().thenReturn(mockFiles); 86 | 87 | Path mockEventPath = mock(Path.class); 88 | Set eventsKeys = new HashSet(); 89 | eventsKeys.add(mockEventPath); 90 | HashMap mockEvents = mock(HashMap.class); 91 | doReturn(eventsKeys).when(mockEvents).keySet(); 92 | doReturn(deleted).when(key).checkDeleted(mockFiles, mockEventPath); 93 | key.setEvents(mockEvents); 94 | 95 | doReturn(existing).when(key).processExistingFile(mockFiles, onePath); 96 | 97 | DirectoryStream mockDirectoryStream = mock(DirectoryStream.class); 98 | doReturn(mockPaths.iterator()).when(mockDirectoryStream).iterator(); 99 | 100 | PowerMockito.mockStatic(Files.class); 101 | when(Files.newDirectoryStream(mockPath)).thenReturn(mockDirectoryStream); 102 | 103 | switch (mode) { 104 | case CHANGED: 105 | case DELETED: 106 | assertTrue(key.update()); 107 | break; 108 | default: 109 | assertFalse(key.update()); 110 | } 111 | 112 | verify(mockFiles, never()).add(isA(Path.class)); 113 | verify(mockDirectoryStream).iterator(); 114 | verify(key).processExistingFile(mockFiles, onePath); 115 | verify(mockEvents).keySet(); 116 | verify(key).checkDeleted(mockFiles, mockEventPath); 117 | 118 | PowerMockito.verifyStatic(); 119 | Files.newDirectoryStream(mockPath); 120 | 121 | PowerMockito.verifyNew(LinkedList.class); 122 | } 123 | 124 | @Test 125 | public void testProcessExistingFile() { 126 | 127 | } 128 | 129 | @Test 130 | public void testCheckDeleted() { 131 | 132 | } 133 | 134 | @Test 135 | public void testCheckCreated() { 136 | 137 | } 138 | 139 | @Test 140 | public void testCheckModified() { 141 | 142 | } 143 | 144 | @Test 145 | public void testKindsContains() { 146 | 147 | } 148 | 149 | @Test 150 | public void testPollEvents_whenNotReady() { 151 | key.setReady(false); 152 | assertTrue(key.pollEvents().isEmpty()); 153 | } 154 | 155 | @Test 156 | public void testPollEvents_whenReady() { 157 | key.setReady(true); 158 | 159 | LinkedList> mockEvents = mock(LinkedList.class); 160 | doReturn(mockEvents).when(key).findPendingEvents(); 161 | 162 | List> events = key.pollEvents(); 163 | assertEquals(mockEvents, events); 164 | 165 | assertFalse(key.isReady()); 166 | 167 | verify(key).findPendingEvents(); 168 | } 169 | 170 | @Test 171 | public void testFindPendingEvents() { 172 | 173 | } 174 | 175 | @Test 176 | public void testReset_whenInvalid() { 177 | key.setValid(false); 178 | Assert.assertFalse(key.reset()); 179 | } 180 | 181 | @Test 182 | public void testReset_whenReady() { 183 | WatchEvent.Kind[] kinds = new WatchEvent.Kind[]{mock(WatchEvent.Kind.class)}; 184 | GlusterPath path = mock(GlusterPath.class); 185 | 186 | GlusterWatchKey key = new GlusterWatchKey(path, kinds); 187 | key.setValid(true); 188 | key.setReady(true); 189 | Assert.assertFalse(key.reset()); 190 | } 191 | 192 | @Test 193 | public void testCancel() { 194 | WatchEvent.Kind[] kinds = new WatchEvent.Kind[]{mock(WatchEvent.Kind.class)}; 195 | GlusterPath path = mock(GlusterPath.class); 196 | 197 | GlusterWatchKey key = new GlusterWatchKey(path, kinds); 198 | key.setValid(true); 199 | key.setReady(false); 200 | assertTrue(key.reset()); 201 | assertTrue(key.isReady()); 202 | } 203 | 204 | } 205 | 206 | enum UpdateTestMode { 207 | NONE, CHANGED, DELETED 208 | } -------------------------------------------------------------------------------- /glusterfs-java-filesystem/src/test/java/com/peircean/glusterfs/GlusterWatchServiceTest.java: -------------------------------------------------------------------------------- 1 | package com.peircean.glusterfs; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.mockito.Mockito; 7 | import org.powermock.api.mockito.PowerMockito; 8 | import org.powermock.core.classloader.annotations.PrepareForTest; 9 | import org.powermock.modules.junit4.PowerMockRunner; 10 | 11 | import java.io.IOException; 12 | import java.nio.file.ClosedWatchServiceException; 13 | import java.nio.file.StandardWatchEventKinds; 14 | import java.nio.file.WatchEvent; 15 | import java.nio.file.WatchKey; 16 | import java.util.concurrent.TimeUnit; 17 | 18 | import static org.junit.Assert.assertEquals; 19 | import static org.junit.Assert.assertFalse; 20 | import static org.powermock.api.mockito.PowerMockito.doReturn; 21 | import static org.powermock.api.mockito.PowerMockito.mock; 22 | 23 | @RunWith(PowerMockRunner.class) 24 | @PrepareForTest({GlusterWatchService.class, Thread.class}) 25 | public class GlusterWatchServiceTest { 26 | 27 | GlusterWatchService watchService = PowerMockito.spy(new GlusterWatchService()); 28 | 29 | @Test(expected = ClosedWatchServiceException.class) 30 | public void testRegisterPath_whenNotRunning() { 31 | watchService.setRunning(false); 32 | GlusterPath mockPath = mock(GlusterPath.class); 33 | watchService.registerPath(mockPath); 34 | } 35 | 36 | @Test 37 | public void testRegisterPath() { 38 | watchService.setRunning(true); 39 | GlusterPath mockPath = mock(GlusterPath.class); 40 | WatchEvent.Kind mockKind = mock(WatchEvent.Kind.class); 41 | watchService.registerPath(mockPath, mockKind); 42 | GlusterWatchKey event = watchService.getPaths().iterator().next(); 43 | assertEquals(mockPath, event.getPath()); 44 | assertEquals(mockKind, event.getKinds()[0]); 45 | } 46 | 47 | @Test 48 | public void testRegisterPath_whenPathExists() { 49 | watchService.setRunning(true); 50 | GlusterPath mockPath = mock(GlusterPath.class); 51 | GlusterWatchKey keyFix = new GlusterWatchKey(mockPath, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}); 52 | watchService.getPaths().add(keyFix); 53 | watchService.registerPath(mockPath, StandardWatchEventKinds.ENTRY_CREATE); 54 | GlusterWatchKey event = watchService.getPaths().iterator().next(); 55 | assertEquals(mockPath, event.getPath()); 56 | assertEquals(StandardWatchEventKinds.ENTRY_CREATE, event.getKinds()[0]); 57 | 58 | } 59 | 60 | @Test 61 | public void testClose_whenNotRunning() throws IOException { 62 | watchService.setRunning(false); 63 | GlusterWatchKey mockKey = mock(GlusterWatchKey.class); 64 | watchService.getPaths().add(mockKey); 65 | watchService.close(); 66 | Mockito.verify(mockKey, Mockito.never()).cancel(); 67 | } 68 | 69 | @Test 70 | public void testClose() throws IOException { 71 | watchService.setRunning(true); 72 | GlusterWatchKey mockKey = mock(GlusterWatchKey.class); 73 | watchService.getPaths().add(mockKey); 74 | watchService.close(); 75 | Mockito.verify(mockKey).cancel(); 76 | assertFalse(watchService.isRunning()); 77 | } 78 | 79 | @Test 80 | public void testPopPending_whenNonePending() { 81 | WatchKey key = watchService.popPending(); 82 | assertEquals(null, key); 83 | assertEquals(0, watchService.getPendingPaths().size()); 84 | } 85 | 86 | @Test 87 | public void testPopPending() { 88 | GlusterPath mockPath = mock(GlusterPath.class); 89 | GlusterWatchKey keyFix = new GlusterWatchKey(mockPath, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_CREATE}); 90 | watchService.getPendingPaths().add(keyFix); 91 | WatchKey key = watchService.popPending(); 92 | assertEquals(keyFix, key); 93 | assertEquals(0, watchService.getPendingPaths().size()); 94 | } 95 | 96 | @Test(expected = ClosedWatchServiceException.class) 97 | public void testPoll_whenNotRunning() { 98 | watchService.setRunning(false); 99 | watchService.poll(); 100 | } 101 | 102 | @Test 103 | public void testPoll_whenPending() { 104 | watchService.setRunning(true); 105 | GlusterWatchKey mockKey = mock(GlusterWatchKey.class); 106 | watchService.getPendingPaths().add(mockKey); 107 | WatchKey key = watchService.poll(); 108 | assertEquals(mockKey, key); 109 | } 110 | 111 | @Test 112 | public void testPoll_whenReadyAndEvent() { 113 | watchService.setRunning(true); 114 | GlusterWatchKey mockKey = mock(GlusterWatchKey.class); 115 | watchService.getPaths().add(mockKey); 116 | doReturn(null).doReturn(mockKey).when(watchService).popPending(); 117 | doReturn(true).when(mockKey).isValid(); 118 | doReturn(true).when(mockKey).isReady(); 119 | doReturn(true).when(mockKey).update(); 120 | 121 | WatchKey key = watchService.poll(); 122 | 123 | assertEquals(mockKey, key); 124 | 125 | Mockito.verify(mockKey).isValid(); 126 | Mockito.verify(mockKey).isReady(); 127 | Mockito.verify(mockKey).update(); 128 | } 129 | 130 | @Test 131 | public void testPoll_whenReadyAndNoEvent() { 132 | watchService.setRunning(true); 133 | GlusterWatchKey mockKey = mock(GlusterWatchKey.class); 134 | watchService.getPaths().add(mockKey); 135 | doReturn(true).when(mockKey).isValid(); 136 | doReturn(true).when(mockKey).isReady(); 137 | doReturn(false).when(mockKey).update(); 138 | 139 | WatchKey key = watchService.poll(); 140 | 141 | assertEquals(null, key); 142 | 143 | Mockito.verify(mockKey).isValid(); 144 | Mockito.verify(mockKey).isReady(); 145 | Mockito.verify(mockKey).update(); 146 | } 147 | 148 | @Test(expected = ClosedWatchServiceException.class) 149 | public void testPollTimeout_whenClosed() { 150 | long timeout = 150L; 151 | TimeUnit unit = TimeUnit.MILLISECONDS; 152 | doReturn(timeout).when(watchService).timeoutToMillis(timeout, unit); 153 | watchService.setRunning(false); 154 | watchService.poll(timeout, unit); 155 | } 156 | 157 | @Test 158 | public void testPollTimeout() throws InterruptedException { 159 | long timeout = 150L; 160 | TimeUnit unit = TimeUnit.MILLISECONDS; 161 | // doReturn(timeout).when(watchService).timeoutToMillis(timeout, unit); 162 | 163 | WatchKey mockKey = mock(WatchKey.class); 164 | PowerMockito.when(watchService.poll()).thenReturn(null).thenReturn(mockKey); 165 | 166 | PowerMockito.spy(Thread.class); 167 | PowerMockito.doThrow(new InterruptedException()).when(Thread.class); 168 | Thread.sleep(GlusterWatchService.PERIOD); 169 | 170 | WatchKey key = watchService.poll(timeout, unit); 171 | 172 | assertEquals(mockKey, key); 173 | 174 | Mockito.verify(watchService, Mockito.times(2)).poll(); 175 | // Mockito.verify(watchService).timeoutToMillis(timeout, unit); 176 | 177 | PowerMockito.verifyStatic(Mockito.times(1)); 178 | Thread.sleep(GlusterWatchService.PERIOD); 179 | } 180 | 181 | @Test(expected = ClosedWatchServiceException.class) 182 | public void testTake_whenClosed() { 183 | watchService.setRunning(false); 184 | watchService.take(); 185 | } 186 | 187 | @Test 188 | public void testTake() throws Exception { 189 | WatchKey mockKey = mock(WatchKey.class); 190 | doReturn(null).doReturn(mockKey).when(watchService).poll(); 191 | 192 | PowerMockito.spy(Thread.class); 193 | PowerMockito.doThrow(new InterruptedException()).when(Thread.class); 194 | Thread.sleep(GlusterWatchService.PERIOD); 195 | 196 | WatchKey key = watchService.take(); 197 | 198 | assertEquals(mockKey, key); 199 | 200 | Mockito.verify(watchService, Mockito.times(2)).poll(); 201 | 202 | PowerMockito.verifyStatic(); 203 | Thread.sleep(GlusterWatchService.PERIOD); 204 | } 205 | 206 | @Test 207 | public void testTimeoutToMillis() { 208 | long time = 12345L; 209 | Assert.assertEquals(-1, 210 | watchService.timeoutToMillis(time, TimeUnit.NANOSECONDS)); 211 | Assert.assertEquals(-1, 212 | watchService.timeoutToMillis(time, TimeUnit.MICROSECONDS)); 213 | Assert.assertEquals(time, 214 | watchService.timeoutToMillis(time, TimeUnit.MILLISECONDS)); 215 | Assert.assertEquals(time * GlusterWatchService.MILLIS_PER_SECOND, 216 | watchService.timeoutToMillis(time, TimeUnit.SECONDS)); 217 | Assert.assertEquals(time * GlusterWatchService.MILLIS_PER_MINUTE, 218 | watchService.timeoutToMillis(time, TimeUnit.MINUTES)); 219 | Assert.assertEquals(time * GlusterWatchService.MILLIS_PER_HOUR, 220 | watchService.timeoutToMillis(time, TimeUnit.HOURS)); 221 | Assert.assertEquals(time * GlusterWatchService.MILLIS_PER_DAY, 222 | watchService.timeoutToMillis(time, TimeUnit.DAYS)); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | com.peircean.glusterfs 6 | glusterfs-java-filesystem-project 7 | 1.0.5-SNAPSHOT 8 | 9 | glusterfs-java-filesystem 10 | glusterfs-java-filesystem-example 11 | 12 | pom 13 | 14 | 15 | org.sonatype.oss 16 | oss-parent 17 | 7 18 | 19 | 20 | ${project.artifactId} 21 | glusterfs-java-filesystem aims to be a complete implementation of a Java7/NIO.2 File System Provider 22 | backed by GlusterFS via libgfapi-jni 23 | 24 | 25 | 26 | BSD-style 27 | https://github.com/semiosis/glusterfs-java-filesystem/blob/master/LICENSE.txt 28 | A permissive open source license 29 | repo 30 | 31 | 32 | 33 | 2013 34 | 35 | 36 | https://github.com/semiosis/glusterfs-java-filesystem 37 | scm:git:https://github.com/semiosis/glusterfs-java-filesystem.git 38 | scm:git:git@github.com:semiosis/glusterfs-java-filesystem.git 39 | HEAD 40 | 41 | 42 | 43 | 44 | zuckerman 45 | Louis Zuckerman 46 | me@louiszuckerman.com 47 | http://about.me/louiszuckerman 48 | GMT-5 49 | 50 | 51 | 52 | 53 | 54 | 55 | org.apache.maven.plugins 56 | maven-surefire-plugin 57 | 2.16 58 | 59 | true 60 | once 61 | -ea 62 | true 63 | ${project.build.directory} 64 | 65 | **/*Test.java 66 | 67 | 68 | 69 | 70 | org.apache.maven.plugins 71 | maven-compiler-plugin 72 | 3.1 73 | 74 | 1.7 75 | 1.7 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | junit 84 | junit 85 | 4.11 86 | test 87 | 88 | 89 | 90 | 91 | 92 | release 93 | 94 | 95 | 96 | org.apache.maven.plugins 97 | maven-release-plugin 98 | 2.5 99 | 100 | true 101 | 102 | 103 | 104 | org.apache.maven.plugins 105 | maven-gpg-plugin 106 | 1.5 107 | 108 | 109 | sign-artifacts 110 | verify 111 | 112 | sign 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /vagrant-provisioner.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | add-apt-repository -y ppa:gluster/glusterfs-3.8 4 | apt-get update 5 | # apt-get -y dist-upgrade 6 | apt-get -y install glusterfs-server 7 | sleep 2 8 | 9 | sed -i 's/\(end-volume\)/ option rpc-auth-allow-insecure on\n\1/' /etc/glusterfs/glusterd.vol 10 | service glusterfs-server restart 11 | sleep 2 12 | 13 | gluster volume create foo ${1}:/var/tmp/foo force 14 | gluster volume set foo server.allow-insecure on 15 | gluster volume start foo 16 | sleep 2 17 | 18 | mkdir -v /mnt/foo 19 | mount -t glusterfs localhost:foo /mnt/foo && echo Mounted glusterfs volume at /mnt/foo. 20 | chmod -v ugo+w /mnt/foo 21 | 22 | echo Provision complete. 23 | --------------------------------------------------------------------------------