├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── task.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── build.yml │ ├── bump-snapshot-version.yaml │ ├── clear-cache.yaml │ ├── codeql.yml │ ├── command-deploy-pr-snapshot.yaml │ ├── command-dispatcher.yaml │ ├── command-help.yaml │ ├── command-merge-release.yaml │ ├── command-merge-snapshot.yaml │ ├── deploy-tag.yaml │ ├── docs.yaml │ └── prepare-release.yaml ├── .gitignore ├── .release-please-manifest.json ├── .run ├── build-and-sign.run.xml ├── build-sign-publish-internal.run.xml ├── build-sign-publish-maven-central.run.xml └── build-with-coverage.run.xml ├── CHANGELOG.md ├── CODE-OF-CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.Apache-2.0.md ├── LICENSE.MIT.md ├── README.md ├── build.gradle.kts ├── docs ├── README.md ├── content │ ├── assets │ │ ├── fixed-the-code-will-be.jpg │ │ └── resources │ │ │ ├── codestyles │ │ │ ├── s3fs-eclipse.xml │ │ │ └── s3fs-idea.xml │ │ │ ├── pdfs │ │ │ └── ICLA.pdf │ │ │ └── s3fs-nio-strict-policy.json │ ├── changelog │ │ ├── release │ │ │ └── notes-master.md │ │ └── upgrading │ │ │ ├── .gitkeep │ │ │ └── master.md │ ├── chat.md │ ├── contributing │ │ ├── code-of-conduct.md │ │ ├── developer-guide │ │ │ ├── building-the-code.md │ │ │ ├── coding-convention.md │ │ │ └── index.md │ │ ├── index.md │ │ ├── legal │ │ │ ├── ICLA.md │ │ │ ├── index.md │ │ │ ├── license-Apache-2.0.md │ │ │ └── license-MIT.md │ │ ├── pull-request-guidelines.md │ │ ├── security-advisers.md │ │ ├── who-can-help-and-how.md │ │ └── writing-documentation.md │ ├── history.md │ ├── index.md │ ├── license.md │ ├── reference │ │ ├── configuration-options.md │ │ └── examples │ │ │ ├── basic-example.md │ │ │ ├── mina-example.md │ │ │ └── spring-example.md │ └── versioning.md ├── docker-compose.yml ├── docs.Dockerfile ├── mkdocs.yml ├── requirements.txt ├── runtime.txt └── theme │ └── assets │ ├── custom.css │ ├── structor-menu.css │ └── structor-menu.js.gotmpl ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── release-please-config.json ├── settings.gradle.kts └── src ├── main ├── java │ └── org │ │ └── carlspring │ │ └── cloud │ │ └── storage │ │ └── s3fs │ │ ├── S3AccessControlList.java │ │ ├── S3AwsRegionProviderChain.java │ │ ├── S3ClientFactory.java │ │ ├── S3Factory.java │ │ ├── S3FileChannel.java │ │ ├── S3FileStore.java │ │ ├── S3FileStoreAttributeView.java │ │ ├── S3FileSystem.java │ │ ├── S3FileSystemConfigurationException.java │ │ ├── S3FileSystemProvider.java │ │ ├── S3FilteredIterator.java │ │ ├── S3Iterator.java │ │ ├── S3ObjectId.java │ │ ├── S3OutputStream.java │ │ ├── S3Path.java │ │ ├── S3SeekableByteChannel.java │ │ ├── attribute │ │ ├── S3BasicFileAttributeView.java │ │ ├── S3BasicFileAttributes.java │ │ ├── S3PosixFileAttributeView.java │ │ ├── S3PosixFileAttributes.java │ │ └── S3UserPrincipal.java │ │ ├── cache │ │ ├── S3FileAttributesCache.java │ │ └── S3FileAttributesCachePolicy.java │ │ └── util │ │ ├── AttributesUtils.java │ │ ├── Constants.java │ │ ├── IOUtils.java │ │ └── S3Utils.java └── resources │ └── META-INF │ └── services │ └── java.nio.file.spi.FileSystemProvider ├── test ├── java │ └── org │ │ └── carlspring │ │ └── cloud │ │ └── storage │ │ └── s3fs │ │ ├── AttributesUtilsTest.java │ │ ├── BaseTest.java │ │ ├── S3AwsRegionProviderChainTest.java │ │ ├── S3ClientFactoryTest.java │ │ ├── S3FileAttributesTest.java │ │ ├── S3FileChannelTest.java │ │ ├── S3FileStoreTest.java │ │ ├── S3FileSystemTest.java │ │ ├── S3IteratorTest.java │ │ ├── S3OutputStreamTest.java │ │ ├── S3SeekableByteChannelTest.java │ │ ├── S3UnitTestBase.java │ │ ├── S3UtilsTest.java │ │ ├── S3WalkerTest.java │ │ ├── fileSystemProvider │ │ ├── CheckAccessTest.java │ │ ├── CopyTest.java │ │ ├── CreateDirectoryTest.java │ │ ├── DeleteTest.java │ │ ├── GetFileAttributeViewTest.java │ │ ├── GetFileStoreTest.java │ │ ├── GetPathTest.java │ │ ├── IsHiddenTest.java │ │ ├── IsSameFileTest.java │ │ ├── MoveTest.java │ │ ├── NewAsynchronousFileChannelTest.java │ │ ├── NewDirectoryStreamTest.java │ │ ├── NewFileSystemTest.java │ │ ├── NewInputStreamTest.java │ │ ├── NewOutputStreamTest.java │ │ ├── ReadAttributesTest.java │ │ └── SetAttributeTest.java │ │ ├── path │ │ ├── EndsWithTest.java │ │ ├── EqualsTest.java │ │ ├── GetFileNameTest.java │ │ ├── GetKeyTest.java │ │ ├── GetNameTest.java │ │ ├── GetRootTest.java │ │ ├── IteratorTest.java │ │ ├── ResolveSiblingTest.java │ │ ├── ResolveTest.java │ │ ├── S3PathTest.java │ │ ├── StartsWithTest.java │ │ ├── SubpathTest.java │ │ └── ToUriTest.java │ │ ├── spike │ │ ├── FileStoreTest.java │ │ ├── InstallProviderTest.java │ │ ├── PathSpecTest.java │ │ ├── ProviderSpecTest.java │ │ ├── SpecTest.java │ │ └── URISpikeTest.java │ │ └── util │ │ ├── BrokenS3Factory.java │ │ ├── CopyDirVisitor.java │ │ ├── ExposingS3BaseClientBuilder.java │ │ ├── ExposingS3Client.java │ │ ├── ExposingS3ClientBuilder.java │ │ ├── ExposingS3ClientFactory.java │ │ ├── FileAttributeBuilder.java │ │ ├── MinioContainer.java │ │ ├── MockBucket.java │ │ ├── S3ClientMock.java │ │ ├── S3EndpointConstant.java │ │ ├── S3MockFactory.java │ │ └── UnsupportedFileStoreAttributeView.java └── resources │ ├── META-INF │ └── services │ │ └── java.nio.file.spi.FileSystemProvider │ ├── amazon-test-sample.properties │ ├── config-test │ └── logback-test.xml └── testIntegration └── java └── org └── carlspring └── cloud └── storage └── s3fs ├── BaseIntegrationTest.java ├── FileSystemsIT.java ├── FilesIT.java ├── S3ClientIT.java ├── S3UtilsIT.java ├── fileSystemProvider ├── CacheTestIT.java ├── GetFileSystemIT.java ├── NewAsynchronousFileChannelTestIT.java ├── NewByteChannelIT.java ├── NewByteChannelTest.java ├── NewFileSystemIT.java └── ReadAttributesIT.java ├── junit ├── annotations │ ├── BaseAnnotationTest.java │ ├── MinioIntegrationTest.java │ ├── MinioIntegrationTestAnnotationTest.java │ ├── S3IntegrationTest.java │ └── S3IntegrationTestAnnotationTest.java └── examples │ ├── CombinedIT.java │ ├── CombinedMinioS3IT.java │ ├── CombinedS3MinioIT.java │ ├── MinioClassAnnotationIT.java │ ├── MinioMethodAnnotationIT.java │ ├── S3ClassAnnotationIT.java │ └── S3MethodAnnotationIT.java ├── path └── ToURLIT.java ├── spike └── EnvironmentIT.java └── util └── EnvironmentBuilder.java /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug, needs triage 6 | assignees: '' 7 | --- 8 | 9 | # Bug Description 10 | 11 | Describe the bug/issue you have. 12 | 13 | # Steps To Reproduce 14 | 15 | 1. 16 | 2. 17 | 3. 18 | 4. 19 | 20 | # Expected Behavior 21 | 22 | # Environment 23 | 24 | * `s3fs-nio` version: 25 | * OS: 26 | * JDK: 27 | ``` 28 | full output of mvn -version (or java -version) 29 | ``` 30 | 31 | # Screenshots 32 | 33 | # Additional context 34 | 35 | # Proposed Solution 36 | 37 | # Known Workarounds 38 | 39 | # Useful Links 40 | 41 | * [link1]() 42 | * [link2]() 43 | * [link3]() 44 | 45 | # Task Relationships 46 | 47 | This bug: 48 | * Is caused by: 49 | * Relates to: 50 | * Depends on: 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/task.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Task 3 | about: Template for creating tasks 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Task Description 11 | 12 | 13 | # Tasks 14 | 15 | The following tasks will need to be carried out: 16 | * [ ] Task 1 17 | * [ ] Task 2 18 | * [ ] Task 3 19 | 20 | # Task Relationships 21 | 22 | This task: 23 | * Is a sub-task of: # 24 | * Depends on: # 25 | * Is a follow-up of: # 26 | * Relates to: # 27 | 28 | # Useful Links 29 | 30 | * [link1]() 31 | * [link2]() 32 | * [link3]() 33 | 34 | # Help 35 | 36 | * Our [chat channel](https://chat.carlspring.org/channel/s3fs-nio-community) 37 | * Points of contact: 38 | * @carlspring 39 | * @steve-todorov 40 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Pull Request Description 2 | 3 | This pull request closes # . 4 | 5 | # Acceptance Test 6 | 7 | * [ ] Building the code with `gradle clean build` still works. 8 | 9 | # Questions 10 | 11 | * Does this pull request break backward compatibility? 12 | * [ ] Yes and my commit follows the [Conventional Commits Specification](https://www.conventionalcommits.org/en/v1.0.0/) 13 | * [ ] No 14 | 15 | * Does this pull request require other pull requests to be merged first? 16 | * [ ] Yes, please see #... 17 | * [ ] No 18 | 19 | * Does this require an update of the documentation? 20 | * [ ] Yes, please see [provide details here] 21 | * [ ] No 22 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gradle" 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | # Check for updates to Gradle dependencies every day 12 | interval: "daily" 13 | commit-message: 14 | prefix: deps(upgrade) 15 | - package-ecosystem: "github-actions" 16 | directory: "/" 17 | schedule: 18 | # Check for updates to GitHub Actions every day 19 | interval: "daily" 20 | commit-message: 21 | prefix: deps(upgrade) 22 | -------------------------------------------------------------------------------- /.github/workflows/bump-snapshot-version.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | inputs: 4 | VERSION: 5 | required: true 6 | type: string 7 | description: Next snapshot version (i.e. 1.2.3, no SNAPSHOT suffix) 8 | 9 | name: Bump snapshot version 10 | run-name: Bump snapshot version to ${{ inputs.VERSION }}-SNAPSHOT 11 | 12 | jobs: 13 | bump-version: 14 | runs-on: ubuntu-latest 15 | if: | 16 | github.repository == vars.CURRENT_REPO && 17 | contains('["carlspring", "steve-todorov"]', github.triggering_actor) 18 | permissions: 19 | contents: write 20 | pull-requests: write 21 | steps: 22 | # git commit --allow-empty -m "Prepare for 1.0.3-SNAPSHOT" -m "Release-As: 1.0.3" && git push 23 | - name: Checkout 24 | uses: actions/checkout@v4 25 | with: 26 | fetch-depth: 0 27 | 28 | - name: Show tags. 29 | run: git tag -l 30 | 31 | - name: Ensure tag does not exist 32 | continue-on-error: false 33 | shell: bash 34 | run: | 35 | version="${{ inputs.VERSION }}" 36 | if [[ $(git tag --list | grep -Ei "^(v)?$version(-SNAPSHOT)?$") ]]; then 37 | echo "Tag for $version(-SNAPSHOT)? already exists!" 38 | exit 1 39 | fi 40 | if [[ $(git log --all -i --grep="Prepare for v$version-SNAPSHOT" --grep="Release v$version") ]]; then 41 | echo "Found commit messages matching 'Prepare for $version-SNAPSHOT' or 'Release v$version'"; 42 | exit 1 43 | fi 44 | 45 | - name: Configure git user. 46 | run: | 47 | git config user.name github-actions[bot] 48 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 49 | git remote add gh-token "https://${{ secrets.GITHUB_TOKEN}}@github.com/${{ github.action_repository }}.git" 50 | 51 | - name: Bump version 52 | run: | 53 | cat <<< $(jq '."." = "${{ inputs.VERSION }}-SNAPSHOT"' .release-please-manifest.json) > .release-please-manifest.json 54 | sed -Ei 's/version([ ]*)\=.*/version=${{ inputs.VERSION }}-SNAPSHOT/g' gradle.properties 55 | sed -Ei 's/POM_VERSION:.*/POM_VERSION: "${{ inputs.VERSION }}-SNAPSHOT"/g' docs/mkdocs.y*ml 56 | 57 | - name: Create PR 58 | uses: peter-evans/create-pull-request@v7 59 | id: pr 60 | with: 61 | token: ${{ secrets.GITHUB_TOKEN }} 62 | author: "cs-devops-bot " 63 | commit-message: "chore(snapshot): Prepare for v${{ inputs.VERSION }}\n\nRelease-As: ${{ inputs.VERSION }}" 64 | title: "chore(snapshot): Prepare for v${{ inputs.VERSION }}" 65 | body: "" 66 | labels: automated pr 67 | branch: chore/prepare-for-${{ inputs.VERSION }} 68 | delete-branch: true 69 | reviewers: carlspring, steve-todorov 70 | 71 | - name: Update comment 72 | uses: actions/github-script@v7 73 | with: 74 | script: | 75 | const pr = await github.rest.pulls.get({ 76 | owner: context.repo.owner, 77 | repo: context.repo.repo, 78 | pull_number: '${{ steps.pr.outputs.pull-request-number }}' 79 | }); 80 | const prTitle = pr.data.title 81 | 82 | console.log("PR: ", pr) 83 | 84 | const body = `## Next development cycle 85 | 86 | This pull request prepares for the next release cycle. 87 | Next snapshot version will become \`${{ inputs.VERSION }}-SNAPSHOT\`. 88 | 89 | Please use \`Squash and Merge\` with the following title: 90 | \`\`\` 91 | ${prTitle} (#${{ steps.pr.outputs.pull-request-number }}) 92 | \`\`\` 93 | Alternatively you can comment with \`/merge-snapshot\` which will do the same for you. 94 | 95 | ` 96 | 97 | await github.rest.pulls.update({ 98 | owner: context.repo.owner, 99 | repo: context.repo.repo, 100 | pull_number: '${{ steps.pr.outputs.pull-request-number }}', 101 | body: body, 102 | headers: { 'X-GitHub-Api-Version': '2022-11-28' } 103 | }); 104 | -------------------------------------------------------------------------------- /.github/workflows/clear-cache.yaml: -------------------------------------------------------------------------------- 1 | name: Clear Cache 2 | 3 | on: 4 | workflow_dispatch: 5 | # schedule: 6 | # - cron: '0 0 * * *' # Runs once a day (https://crontab.guru/once-a-day) 7 | 8 | permissions: 9 | actions: write 10 | 11 | jobs: 12 | clear: 13 | name: Clear cache 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: MyAlbum/purge-cache@v2 17 | with: 18 | max-age: 1 19 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | paths-ignore: 7 | - 'docs/**' 8 | - '*.md' 9 | - '**/*.md' 10 | - '.github/workflows/bump*.yaml' 11 | - '.github/workflows/docs.yaml' 12 | - '.github/workflows/command-*.yaml' 13 | - '.github/workflows/clear-cache.yaml' 14 | - '.github/workflows/deploy-tag.yaml' 15 | - '.github/workflows/prepare-release.yaml' 16 | pull_request: 17 | # The branches below must be a subset of the branches above 18 | branches: [ "master" ] 19 | # schedule: 20 | # - cron: '21 3 * * 4' 21 | paths-ignore: 22 | - 'docs/**' 23 | - '*.md' 24 | - '**/*.md' 25 | - '.github/workflows/bump*.yaml' 26 | - '.github/workflows/docs.yaml' 27 | - '.github/workflows/command-*.yaml' 28 | - '.github/workflows/clear-cache.yaml' 29 | - '.github/workflows/deploy-tag.yaml' 30 | - '.github/workflows/prepare-release.yaml' 31 | 32 | jobs: 33 | analyze: 34 | name: Analyze 35 | runs-on: ubuntu-latest 36 | permissions: 37 | actions: read 38 | contents: read 39 | security-events: write 40 | 41 | strategy: 42 | fail-fast: false 43 | matrix: 44 | language: [ 'java' ] 45 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 46 | # Use only 'java' to analyze code written in Java, Kotlin or both 47 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 48 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 49 | 50 | steps: 51 | - name: Checkout repository 52 | uses: actions/checkout@v4 53 | 54 | # Initializes the CodeQL tools for scanning. 55 | - name: Initialize CodeQL 56 | uses: github/codeql-action/init@v3 57 | with: 58 | languages: ${{ matrix.language }} 59 | # If you wish to specify custom queries, you can do so here or in a config file. 60 | # By default, queries listed here will override any specified in a config file. 61 | # Prefix the list here with "+" to use these queries and those in the config file. 62 | 63 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 64 | # queries: security-extended,security-and-quality 65 | 66 | 67 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 68 | # If this step fails, then you should remove it and run the build manually (see below) 69 | - name: Autobuild 70 | uses: github/codeql-action/autobuild@v3 71 | 72 | # ℹ️ Command-line programs to run using the OS shell. 73 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 74 | 75 | # If the Autobuild fails above, remove it and uncomment the following three lines. 76 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 77 | 78 | # - run: | 79 | # echo "Run, Build Application using script" 80 | # ./location_of_script_within_repo/buildscript.sh 81 | 82 | - name: Perform CodeQL Analysis 83 | uses: github/codeql-action/analyze@v3 84 | with: 85 | category: "/language:${{matrix.language}}" 86 | -------------------------------------------------------------------------------- /.github/workflows/command-dispatcher.yaml: -------------------------------------------------------------------------------- 1 | name: command-dispatch 2 | on: 3 | issue_comment: 4 | types: [created] 5 | permissions: 6 | contents: write 7 | pull-requests: write 8 | actions: write 9 | #permissions: write-all 10 | jobs: 11 | slashCommandDispatch: 12 | runs-on: ubuntu-latest 13 | if: | 14 | contains(FromJSON('["carlspring", "steve-todorov"]'), github.triggering_actor) && 15 | github.event.issue.pull_request 16 | steps: 17 | - name: Slash Command Dispatch 18 | uses: peter-evans/slash-command-dispatch@v4 19 | with: 20 | token: ${{ secrets.GITHUB_TOKEN }} 21 | config: > 22 | [ 23 | { 24 | "command": "merge-snapshot", 25 | "permission": "write", 26 | "issue_type": "pull-request" 27 | }, 28 | { 29 | "command": "merge-release", 30 | "permission": "write", 31 | "issue_type": "pull-request" 32 | }, 33 | { 34 | "command": "update-release", 35 | "permission": "write", 36 | "issue_type": "pull-request" 37 | }, 38 | { 39 | "command": "deploy-pr", 40 | "permission": "write", 41 | "issue_type": "pull-request" 42 | }, 43 | { 44 | "command": "help", 45 | "permission": "write", 46 | "issue_type": "pull-request" 47 | } 48 | ] 49 | -------------------------------------------------------------------------------- /.github/workflows/command-help.yaml: -------------------------------------------------------------------------------- 1 | name: command-help 2 | run-name: command-help PR-${{ github.event.client_payload.github.payload.issue.number }} 3 | 4 | on: 5 | repository_dispatch: 6 | types: [help-command] 7 | permissions: 8 | issues: write 9 | pull-requests: write 10 | contents: write 11 | jobs: 12 | help: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Dump the client payload context 16 | env: 17 | PAYLOAD_CONTEXT: ${{ toJson(github.event.client_payload) }} 18 | run: echo "$PAYLOAD_CONTEXT" 19 | - name: Update comment 20 | uses: peter-evans/create-or-update-comment@v4 21 | with: 22 | token: ${{ secrets.GITHUB_TOKEN }} 23 | repository: ${{ github.event.client_payload.github.payload.repository.full_name }} 24 | comment-id: ${{ github.event.client_payload.github.payload.comment.id }} 25 | body: | 26 | > Command | Description 27 | > --- | --- 28 | > /merge-snapshot | Squash and merge snapshot PR. 29 | > /merge-release | Squash and merge release PR. 30 | > /update-release | Updates the changelog of a release PR. 31 | > /deploy-pr | Deploys the PR as a snapshot version (i.e. `org.carlspring.cloud.aws:s3fs-nio:PR-${{ github.event.client_payload.github.payload.issue.number }}-SNAPSHOT` using ref `${{ github.event.client_payload.pull_request.head.sha }}`) 32 | reaction-type: hooray 33 | -------------------------------------------------------------------------------- /.github/workflows/deploy-tag.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | inputs: 4 | TAG: 5 | required: true 6 | description: The tag to build and publish (as is) 7 | 8 | name: Deploy tagged release 9 | run-name: Deploying tagged release ${{ inputs.TAG }} 10 | 11 | # gh/actions/runner/issues/1007#issuecomment-808904408 12 | jobs: 13 | build-and-deploy: 14 | runs-on: ubuntu-latest 15 | if: ${{ startsWith(github.ref, 'refs/tags/v') || inputs.TAG != '' }} 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v4 19 | with: 20 | ref: refs/tags/${{ inputs.TAG }} 21 | 22 | - name: Setup Java 23 | uses: actions/setup-java@v4 24 | with: 25 | java-version: 11 26 | distribution: "temurin" 27 | 28 | - name: Setup Gradle 29 | uses: gradle/actions/setup-gradle@v4 30 | with: 31 | # The Gradle wrapper's version (already the default, putting it here to clarity) 32 | gradle-version: wrapper 33 | # Removing unused files from Gradle User Home before saving to cache (i.e. older versions of gradle) 34 | gradle-home-cache-cleanup: true 35 | # Cache downloaded JDKs in addition to the default directories. 36 | gradle-home-cache-includes: | 37 | caches 38 | notifications 39 | jdks 40 | 41 | - name: Setup key 42 | uses: crazy-max/ghaction-import-gpg@v6 43 | with: 44 | fingerprint: ${{ vars.GPG_FINGERPRINT }} 45 | gpg_private_key: ${{ secrets.GPG_KEY }} 46 | passphrase: ${{ secrets.GPG_PASS }} 47 | trust_level: 5 48 | 49 | - name: Build and deploy 50 | env: 51 | S3FS_BUCKET_NAME: ${{ secrets.S3FS_BUCKET_NAME }} 52 | S3FS_ACCESS_KEY: ${{ secrets.S3FS_ACCESS_KEY }} 53 | S3FS_SECRET_KEY: ${{ secrets.S3FS_SECRET_KEY }} 54 | S3FS_REGION: ${{ secrets.S3FS_REGION }} 55 | S3FS_PROTOCOL: "https" 56 | S3FS_PUBLISH_SONATYPE_USER: "${{ secrets.S3FS_PUBLISH_SONATYPE_USER }}" 57 | S3FS_PUBLISH_SONATYPE_PASS: "${{ secrets.S3FS_PUBLISH_SONATYPE_PASS }}" 58 | run: ./gradlew build publish -PwithSignature=true --warn --stacktrace 59 | 60 | - name: Check file signature 61 | run: | 62 | ls -al ./build/libs/ 63 | for f in ./build/libs/*.asc; do 64 | echo "Verifying $f" 65 | gpg --verify $f 66 | done 67 | -------------------------------------------------------------------------------- /.github/workflows/docs.yaml: -------------------------------------------------------------------------------- 1 | name: Build docs 2 | on: 3 | # Build PRs 4 | pull_request_target: 5 | types: [ opened, synchronize, reopened ] 6 | paths: 7 | - 'docs/**' 8 | - '.github/workflows/docs.yaml' 9 | 10 | # On push event 11 | push: 12 | # Build only changes pushed into the `master` branch -- avoids double builds when you push in a branch which has a PR. 13 | branches: 14 | - master 15 | - pipeline 16 | paths: 17 | - 'docs/**' 18 | - '.github/workflows/docs.yaml' 19 | 20 | workflow_dispatch: 21 | 22 | permissions: 23 | statuses: write 24 | 25 | env: 26 | REPOSITORY: ${{ github.repository }} 27 | IS_FORK: ${{ github.event.pull_request.head.repo.full_name != vars.CURRENT_REPO || github.repository != vars.CURRENT_REPO }} 28 | IS_PR: ${{ github.event_name == 'pull_request' }} 29 | EVENT_NAME: ${{ toJSON(github.event_name) }} 30 | # format: username:branch 31 | PR_HEAD_LABEL: ${{ toJSON(github.event.pull_request.head.label) }} 32 | PR_NUMBER: ${{ github.event.number }} 33 | 34 | jobs: 35 | docs: 36 | environment: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.repo.full_name != vars.CURRENT_REPO && 'external-collaborators' || '' }} 37 | runs-on: ubuntu-latest 38 | steps: 39 | 40 | - name: Checkout 41 | uses: actions/checkout@v4 42 | with: 43 | ref: ${{ github.event.pull_request.head.sha || github.sha }} 44 | 45 | - name: Get current branch name 46 | id: get_branch 47 | run: | 48 | if [ "${{ github.event_name }}" == "pull_request_target" ]; then 49 | echo "branch_name=${{ github.event.pull_request.head.ref }}" >> $GITHUB_ENV 50 | else 51 | echo "branch_name=${GITHUB_REF#refs/heads/}" >> $GITHUB_ENV 52 | fi 53 | 54 | - name: Build docs 55 | working-directory: docs 56 | run: docker compose run build 57 | 58 | - name: Publish 59 | working-directory: docs 60 | id: publish 61 | env: 62 | NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }} 63 | NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} 64 | run: | 65 | npm install -g netlify 66 | is_master=${{ env.branch_name == 'master' }} 67 | is_fork=${{ github.event.pull_request.head.repo.fork == true }} 68 | [[ "$is_master" == "true" && "$is_fork" == "false" ]] && args=" --prod " 69 | netlify deploy -d site/ $args | tee build.log 70 | url=$(cat build.log | grep -iE "website(.+)url" | cut -f 2- -d":" | xargs) 71 | echo "Url: $url" 72 | echo "url=$url" >> $GITHUB_OUTPUT 73 | 74 | - name: Post job result 75 | uses: Sibz/github-status-action@v1 76 | if: always() 77 | with: 78 | authToken: ${{ secrets.GITHUB_TOKEN }} 79 | context: Build docs / docs / netlify preview 80 | repository: ${{ vars.CURRENT_REPO }} 81 | state: ${{ job.status }} 82 | sha: ${{ github.event.pull_request.head.sha || github.sha }} 83 | target_url: ${{ steps.publish.outputs.url }} 84 | 85 | -------------------------------------------------------------------------------- /.github/workflows/prepare-release.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | workflow_dispatch: 3 | workflow_call: 4 | repository_dispatch: 5 | types: [ update-release-command ] 6 | 7 | permissions: 8 | contents: write 9 | pull-requests: write 10 | 11 | name: Prepare release 12 | 13 | jobs: 14 | prepare-release: 15 | if: | 16 | ${{ github.repository == vars.CURRENT_REPO }} && 17 | ${{ (github.triggering_actor == 'carlspring' || github.triggering_actor == 'steve-todorov') == true }} 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | ref: 'master' 23 | fetch-depth: 1 24 | 25 | - uses: actions/setup-node@v4 26 | 27 | - name: Install release-please 28 | run: npm install -g release-please 29 | 30 | - name: Release 31 | run: | 32 | release-please --token=${{ secrets.GITHUB_TOKEN }} --repo-url=${{ vars.CURRENT_REPO }} release-pr --release-type simple --pull-request-title-pattern "Release v\${version}" --extra-files gradle.properties,docs/mkdocs.yaml,docs/mkdocs.yml --changelog-sections feat,fix,docs,deprecated,removed,deps,chore,build 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # common ignore 2 | *~ 3 | target/ 4 | tmp/ 5 | *.versionsBackup 6 | .m2/ 7 | site/ 8 | build/ 9 | .gradle 10 | 11 | # Java Files # 12 | *.jar 13 | *.war 14 | *.class 15 | 16 | # eclipse specific 17 | .settings 18 | .project 19 | .classpath 20 | .metadata 21 | .factorypath 22 | *.launch 23 | 24 | # idea specific 25 | .idea 26 | !.idea/codeStyleSettings.xml 27 | *.iml 28 | *.ipr 29 | *.iws 30 | 31 | # unix specific 32 | .DS_Store 33 | .directory 34 | 35 | # static plugins 36 | .pmd 37 | 38 | # log files 39 | logs/ 40 | *.log 41 | 42 | lib/* 43 | 44 | # Secrets file 45 | amazon-test.properties 46 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | ".": "3.0.1-SNAPSHOT" 3 | } 4 | -------------------------------------------------------------------------------- /.run/build-and-sign.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 18 | true 19 | true 20 | false 21 | false 22 | 23 | 24 | -------------------------------------------------------------------------------- /.run/build-sign-publish-internal.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 16 | 22 | 24 | true 25 | true 26 | false 27 | false 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /.run/build-sign-publish-maven-central.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 17 | 19 | true 20 | true 21 | false 22 | false 23 | 24 | 25 | -------------------------------------------------------------------------------- /.run/build-with-coverage.run.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 18 | 20 | true 21 | true 22 | false 23 | false 24 | 25 | 26 | -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | For our code of conduct, please check [here](https://s3fs-nio.carlspring.org/contributing/code-of-conduct/). 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | For details on how to contribute, please check [here](https://s3fs-nio.carlspring.org/contributing/). 2 | -------------------------------------------------------------------------------- /LICENSE.MIT.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014, Javier Arnáiz @arnaix 4 | Copyright (c) 2014, Better.be Application Services BV 5 | Copyright (c) 2020, Carlspring Consulting & Development Ltd. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # S3FS NIO 3 | 4 | [![Master Build Status](https://github.com/carlspring/s3fs-nio/workflows/Build%20and%20test/badge.svg)](https://github.com/carlspring/s3fs-nio/actions?query=branch%3Amaster) 5 | ![JDK support badge](https://img.shields.io/badge/JDK-8%20,%2011%20,%2017-blue) 6 | [![Maven Release Version](https://img.shields.io/maven-central/v/org.carlspring.cloud.aws/s3fs-nio)](https://repo.maven.apache.org/maven2/org/carlspring/cloud/aws/s3fs-nio/) 7 | [![Docs](https://img.shields.io/badge/docs-current-brightgreen.svg)](https://s3fs-nio.carlspring.org) 8 | [![License](https://img.shields.io/badge/License-Apache%202.0-brightgreen.svg)](https://github.com/carlspring/s3fs-nio/blob/master/LICENSE.Apache-2.0.md) 9 | [![Chat](https://img.shields.io/badge/chat-join-success)](https://chat.carlspring.org/channel/s3fs-nio-community) 10 | [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=carlspring_s3fs-nio&branch=master&metric=bugs)](https://sonarcloud.io/summary/overall?id=carlspring_s3fs-nio) 11 | [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=carlspring_s3fs-nio&branch=master&metric=coverage)](https://sonarcloud.io/summary/overall?id=carlspring_s3fs-nio) 12 | [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=carlspring_s3fs-nio&branch=master&metric=reliability_rating)](https://sonarcloud.io/summary/overall?id=carlspring_s3fs-nio) 13 | [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=carlspring_s3fs-nio&branch=master&metric=security_rating)](https://sonarcloud.io/summary/overall?id=carlspring_s3fs-nio) 14 | [![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=carlspring_s3fs-nio&branch=master&metric=vulnerabilities)](https://sonarcloud.io/summary/overall?id=carlspring_s3fs-nio) 15 | [![GitHub issues by-label](https://img.shields.io/github/issues-raw/carlspring/s3fs-nio/good%20first%20issue.svg?label=good%20first%20issue)](https://github.com/carlspring/s3fs-nio/issues?q=is%3Aissue+is%3Aopen+label%3A%22good%20first%20issue%22) 16 | [![GitHub issues by-label](https://img.shields.io/github/issues-raw/carlspring/s3fs-nio/help%20wanted.svg?label=help%20wanted&color=%23856bf9&)](https://github.com/carlspring/s3fs-nio/issues?q=is%3Aissue+is%3Aopen+label%3A%22help%20wanted%22) 17 | [![GitHub issues by-label](https://img.shields.io/github/issues-raw/carlspring/s3fs-nio/hacktoberfest.svg?label=hacktoberfest&color=orange)](https://github.com/carlspring/s3fs-nio/issues?q=is%3Aissue+is%3Aopen+label%3A%22hacktoberfest%22) 18 | [![GitHub issues by-label](https://img.shields.io/badge/stackoverflow-ask-orange.svg)](https://stackoverflow.com/tags/s3fs-nio/) 19 | 20 | This is an implementation of an **Amazon AWS S3** `FileSystem` provider using **[JSR-203]** (a.k.a. NIO2) for Java 8. 21 | 22 | Amazon Simple Storage Service provides a fully redundant data storage infrastructure for storing and retrieving any 23 | amount of data, at any time. 24 | 25 | [NIO2][JSR-203] is the new file management API, introduced in Java version 7. 26 | 27 | This project provides a complete API implementation, for managing files and folders directly in Amazon S3. 28 | 29 | [<--# Links -->]: # 30 | [JSR-203]: https://jcp.org/en/jsr/detail?id=203 31 | 32 | 33 | 34 | ## Compatibility 35 | 36 | We aim to support only the latest LTS versions. At the moment this includes: 37 | 38 | * JDK 8 39 | * JDK 11 40 | * JDK 17 41 | * JDK 21 42 | 43 | Please note that although we support JDK 8 we have plans to drop it in one of our next **major** release. 44 | 45 | ## Documentation 46 | 47 | You can check out our documentation [here](https://s3fs-nio.carlspring.org). 48 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ## Documentation 2 | 3 | This directory contains the S3FS NIO2 documentation. 4 | 5 | ## Pre-requisites 6 | 7 | * [Docker][docker-install] 8 | * [Docker Compose][docker-compose-install] 9 | 10 | ## Getting started 11 | 12 | 1. `docker-compose up` 13 | 2. Open [`http://localhost:8000`](http://localhost:8000) 14 | 15 | ## Debugging 16 | 17 | 1. Build image 18 | ``` 19 | docker-compose build 20 | ``` 21 | 22 | 2. Log into the container 23 | ``` 24 | docker-compose run --rm -p 8000:8000 -v $(pwd)/../:/workspace --entrypoint /bin/sh mkdocs 25 | ``` 26 | 27 | 2. Manually start the server (i.e. test plugins, cwd should be ./docs!) 28 | ``` 29 | mkdocs serve -a 0.0.0.0:8000 30 | ``` 31 | 32 | ## Notes 33 | 34 | * Imported snippets (`--8<-- "./filename"`) are using relative paths to `./docs` 35 | (i.e. `./content/something` would be searched for in `./docs/content/something`) 36 | 37 | ## Used tools and extensions 38 | 39 | | Tool | Documentation | Sources | 40 | | ------------------------------- | ------------------------------------- | ----------------------------------- | 41 | | mkdocs | [documentation][mkdocs] | [Sources][mkdocs-src] | 42 | | mkdocs-material | [documentation][mkdocs-material] | [Sources][mkdocs-material-src] | 43 | | pymdown-extensions | [documentation][pymdown-extensions] | [Sources][pymdown-extensions-src] | 44 | | mdx_gh_links | [documentation][mdx_gh_links] | [Sources][mdx_gh_links] | 45 | | mkdocs-redirects | [documentation][mkdocs-redirects] | [Sources][mkdocs-redirects] | 46 | | mkdocs-markdownextradata-plugin | [documentation][mkdocs-markdownextradata-plugin] | [Sources][mkdocs-markdownextradata-plugin] | 47 | | mkdocs-pom-parser-plugin | [documentation][mkdocs-pom-parser-plugin] | [Sources][mkdocs-pom-parser-plugin] | 48 | | mkdocs-minify-plugin | [documentation][mkdocs-minify-plugin] | [Sources][mkdocs-minify-plugin] | 49 | 50 | 51 | [docker-install]: https://docs.docker.com/get-docker/ "Docker Installation" 52 | [docker-compose-install]: https://docs.docker.com/compose/install/ "Docker Compose Installation" 53 | 54 | [mkdocs]: https://www.mkdocs.org "Mkdocs" 55 | [mkdocs-src]: https://github.com/mkdocs/mkdocs "Mkdocs - Sources" 56 | 57 | [mkdocs-material]: https://squidfunk.github.io/mkdocs-material/ "Material for MkDocs" 58 | [mkdocs-material-src]: https://github.com/squidfunk/mkdocs-material "Material for MkDocs - Sources" 59 | 60 | [pymdown-extensions]: https://facelessuser.github.io/pymdown-extensions "PyMdown Extensions" 61 | [pymdown-extensions-src]: https://github.com/facelessuser/pymdown-extensions "PyMdown Extensions - Sources" 62 | 63 | 64 | [mdx_gh_links]: https://github.com/Python-Markdown/github-links/ 65 | [mkdocs-redirects]: https://github.com/datarobot/mkdocs-redirects 66 | [mkdocs-markdownextradata-plugin]: https://github.com/rosscdh/mkdocs-markdownextradata-plugin 67 | [mkdocs-pom-parser-plugin]: https://github.com/steve-todorov/mkdocs-pom-parser-plugin "A pom.xml file parser for mkdocs" 68 | 69 | [mkdocs-minify-plugin]: https://github.com/byrnereese/mkdocs-minify-plugin 70 | -------------------------------------------------------------------------------- /docs/content/assets/fixed-the-code-will-be.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlspring/s3fs-nio/e76d7c2eff20e130fb17b87e8c9bdf229b112b9f/docs/content/assets/fixed-the-code-will-be.jpg -------------------------------------------------------------------------------- /docs/content/assets/resources/pdfs/ICLA.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlspring/s3fs-nio/e76d7c2eff20e130fb17b87e8c9bdf229b112b9f/docs/content/assets/resources/pdfs/ICLA.pdf -------------------------------------------------------------------------------- /docs/content/assets/resources/s3fs-nio-strict-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Sid": "VisualEditor0", 6 | "Effect": "Allow", 7 | "Action": [ 8 | "s3:PutObject", 9 | "s3:GetObjectAcl", 10 | "s3:GetObject", 11 | "s3:DeleteObjectVersion", 12 | "s3:PutReplicationConfiguration", 13 | "s3:ListBucket", 14 | "s3:DeleteObject", 15 | "s3:GetBucketAcl" 16 | ], 17 | "Resource": [ 18 | "arn:aws:s3:::YOUR_BUCKET_NAME", 19 | "arn:aws:s3:::YOUR_BUCKET_NAME/*" 20 | ] 21 | }, 22 | { 23 | "Sid": "VisualEditor1", 24 | "Effect": "Allow", 25 | "Action": "s3:ListAllMyBuckets", 26 | "Resource": "*" 27 | } 28 | ] 29 | } -------------------------------------------------------------------------------- /docs/content/changelog/release/notes-master.md: -------------------------------------------------------------------------------- 1 | # Release notes for next release -------------------------------------------------------------------------------- /docs/content/changelog/upgrading/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlspring/s3fs-nio/e76d7c2eff20e130fb17b87e8c9bdf229b112b9f/docs/content/changelog/upgrading/.gitkeep -------------------------------------------------------------------------------- /docs/content/changelog/upgrading/master.md: -------------------------------------------------------------------------------- 1 | # Upgrading notes and deprecation notices for next release 2 | -------------------------------------------------------------------------------- /docs/content/chat.md: -------------------------------------------------------------------------------- 1 | redirect: {{ chat_url }} -------------------------------------------------------------------------------- /docs/content/contributing/code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Team conduct 4 | 5 | * Be respectful 6 | * Keep a friendly tone 7 | * Be open to constructive criticism (we will be doing code reviews of every pull request) 8 | 9 | ## Work conduct 10 | 11 | * Before starting work on an issue from the issue tracker, please, make sure there are no active pull requests referencing it. 12 | * When you start working on an issue from issue tracker, please place a comment that you intend do so. 13 | * Timebox your expectations for this work and update the issue/pull request regularly, so that if you say it will take you 2 days and in two weeks there hasn't been apparent progress, if another developer decides they would like to work on this, they can pick it up 14 | * Be professional 15 | * Review your own work after creating a pull and try to resolve any: 16 | * Build failures 17 | * Code issues reported by Sonar (unless these are false-positives) 18 | * Code indentation issues 19 | * Other issues you spot with your code 20 | * When you open a new pull request: 21 | * Put the words `[in progress]` in the title, while the pull request is not yet ready for merging and remove these words, when done, so that it's clear when we can review and merge your pull 22 | * If this pull relates to an existing issue, please refer to it in your pull by mentioning it (for example, "This pull is for issue #123"), which will automatically link the pull to the issue. 23 | 24 | ## General 25 | 26 | * Do your homework (when possible) 27 | * Use our chat channel to ask for help [![RocketChat.Community.Channel](https://chat.carlspring.org/images/join-chat.svg)][chat] 28 | * If you're new to the project: 29 | * [Building The Code] 30 | * [Coding Convention] 31 | * Look around the Github issue tracker and find some ["good first issue" and "help wanted"] 32 | 33 | [chat]: {{ chat_url }} 34 | [Building The Code]: ./developer-guide/index.md 35 | [Coding Convention]: ./developer-guide/coding-convention.md 36 | ["good first issue" and "help wanted"]: https://github.com/carlspring/s3fs-nio/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22+label%3A%22help+wanted%22 37 | -------------------------------------------------------------------------------- /docs/content/contributing/developer-guide/building-the-code.md: -------------------------------------------------------------------------------- 1 | # Building the code 2 | 3 | !!! tip "Before continuing, please make sure you've read the [Getting Started](./getting-started.md) section." 4 | 5 | Running the commands below should be enough to end with a successful build: 6 | 7 | ```linenums="1" 8 | git clone https://github.com/carlspring/s3fs-nio/ 9 | cd s3fs-nio 10 | mvn clean install 11 | ``` 12 | 13 | ## Tests 14 | 15 | ### Skipping tests 16 | 17 | To skip the Maven tests and just build and install the code, run: 18 | 19 | mvn clean install -DskipTests 20 | 21 | ### Executing a particular test 22 | 23 | To execute a particular tests, run: 24 | 25 | mvn clean install -Dtest=MyTest 26 | 27 | To execute a test method of a test, run: 28 | 29 | mvn clean install -Dtest=MyTest#testMyMethod 30 | -------------------------------------------------------------------------------- /docs/content/contributing/index.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | This project a continuation of [martint/s3fs] and [Upplication/Amazon-S3-FileSystem-NIO2]'s hard work on implementing a 4 | Java `FileSystem` provider for Amazon's S3. 5 | 6 | We are also grateful to the countless contributors, who have helped shape this API! 7 | 8 | The current research and development effort is lead by Carlspring Consulting & Development Ltd. 9 | 10 | ## Coding Convention 11 | 12 | While we appreciate all the help we can get, here are some more details on the [coding convention]. 13 | Please, follow these guidelines and set up your IDE to use the respective code style configuration file. 14 | 15 | ## Code of Conduct 16 | 17 | If would like to contribute to our project, please follow our [Code of Conduct]. 18 | 19 | ## Pull Requests 20 | 21 | Please, check our [Pull Request Guidelines] for more details on how to organize your work on contributions. 22 | 23 | ## Contributing 24 | 25 | Thank you for your interest in helping out with our project! 26 | 27 | All efforts are key to the success of our project and we deeply appreciate all contributions. 28 | There is always something you can help with or fix. 29 | 30 | Before contributing to our project, please take a moment, to familiarize yourself with the [Legal Overview]. 31 | 32 | Click the button which best fits your abilities for further guidance: 33 |
34 | 35 | 41 | 42 | 43 | 44 | [martint/s3fs]: https://github.com/martint/s3fs 45 | [Upplication/Amazon-S3-FileSystem-NIO2]: https://github.com/Upplication/Amazon-S3-FileSystem-NIO2 46 | [Sonarcloud]: https://sonarcloud.io/dashboard?id=org.carlspring.cloud.aws%3As3fs-nio 47 | 48 | [chat]: https://chat.carlspring.org/channel/s3fs-nio-community 49 | [issue tracker]: https://github.com/carlspring/s3fs-nio/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22+label%3A%22good+first+issue%22 50 | [Code of Conduct]: ./code-of-conduct.md 51 | [coding convention]: ./developer-guide/coding-convention.md 52 | [Pull Request Guidelines]: ./pull-request-guidelines.md 53 | [who can help]: ./who-can-help-and-how.md 54 | [Individual Contributor's License Agreement (ICLA)]: ./legal/ICLA.md 55 | [Legal Overview]: ./legal/index.md 56 | [developer-guide]: ./developer-guide/index.md 57 | [security-advisers]: ./security-advisers.md 58 | [documentation]: ./writing-documentation.md 59 | [stackoverflow-link]: https://stackoverflow.com/tags/{{ POM_ARTIFACT_ID }} 60 | 61 | -------------------------------------------------------------------------------- /docs/content/contributing/legal/index.md: -------------------------------------------------------------------------------- 1 | # Legal Overview 2 | 3 | ## Legal Notes And Credits 4 | 5 | We appreciate all the help that we can get and are always open to cooperating with contributors. However, it is our 6 | responsibility towards both all the contributors as well as the users of this library to clarify the licensing and 7 | protect both sides' rights and interests. 8 | 9 | Developers who wish to contribute to this project will need to sign our Contributor License Agreement (either in a PDF, 10 | or by printing and signing it and sending it over regular mail). Contributors should only submit their own work product 11 | (either as an individual, or as a company). 12 | 13 | The work on this project is based on [martint/s3fs]'s and [Upplication/Amazon-S3-FileSystem-NIO2]'s projects. 14 | [Upplication/Amazon-S3-FileSystem-NIO2] is a fork of [martint/s3fs]. 15 | [martint/s3fs]'s project is under the Apache 2.0 license. 16 | [Upplication/Amazon-S3-FileSystem-NIO2]'s project is under the MIT license. 17 | 18 | As a further clarification: 19 | 20 | * The code that we've borrowed, which belongs to each of the above-mentioned projects will retain its original license. 21 | * New code will be under the Apache 2.0 license. 22 | * Contributors agree to have their code/fixes available under both the Apache 2.0 and MIT licenses. 23 | * Contributors representing a company or other organization (collectively referred to below as "organization"), should 24 | each sign a separate CLA and clarify that they're representing the organization. If this is not possible, then the 25 | lead contributors from the organization should sign the ICLA and clarify that they are making this contribution on 26 | behalf of the organization and that it may include works of other authors in their organization. 27 | 28 | ## License Details 29 | 30 | Below, you can find our license texts: 31 | 32 | * [Apache 2.0] 33 | * [MIT] 34 | 35 | ## Individual Contributor License Agreement (CLA) 36 | 37 | To accept, please: 38 | 39 | * Fill in all the mandatory fields 40 | 41 | * `Full name` (**mandatory**) 42 | * `Company/Organization/University` (**optional** -- please, only fill this, if you're contributing work on behalf of 43 | a company, organization, or are studying) 44 | * `E-mail` (**mandatory**) 45 | * `Mailing address` (**mandatory**) 46 | * `Country` (**mandatory**) 47 | * `Telephone` (**optional**) 48 | * `Signature` (**mandatory** -- please, use an actual signature) 49 | 50 | * Print, sign and scan the [Individual Contributor's License Agreement (ICLA)], or, alternatively, fill in the 51 | [ICLA PDF] file and mail it back to [carlspring@gmail.com](mailto:carlspring@gmail.com). 52 | 53 | **Notes:** Please, note that none of this information is shared with third-parties and is only required due to the legal 54 | agreement which you will be entering when contributing your code to the project. We require this minimal amount of 55 | information in order to be able to identify you, as we're not keeping record, or more sensitive information, such as 56 | passport/ID details. We will not send you any spam, or share your details with third parties. 57 | 58 | 59 | [martint/s3fs]: https://github.com/martint/s3fs 60 | [Upplication/Amazon-S3-FileSystem-NIO2]: https://github.com/Upplication/Amazon-S3-FileSystem-NIO2 61 | [Individual Contributor's License Agreement (ICLA)]: ICLA.md 62 | [ICLA PDF]: {{ resourcesPath }}/pdfs/ICLA.pdf 63 | [Individual Contributor's License Agreement (ICLA)]: ICLA.md 64 | [Apache 2.0]: ./license-Apache-2.0.md 65 | [MIT]: ./license-MIT.md 66 | 67 | -------------------------------------------------------------------------------- /docs/content/contributing/legal/license-Apache-2.0.md: -------------------------------------------------------------------------------- 1 | # Apache 2.0 License 2 | 3 | This is our copy of the Apache 2.0 license. 4 | 5 | ## License Text 6 | 7 | ``` 8 | --8<-- "../LICENSE.Apache-2.0.md" 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/content/contributing/legal/license-MIT.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | This is our copy of the MIT license. 4 | 5 | ## License Text 6 | 7 | ``` 8 | --8<-- "../LICENSE.MIT.md" 9 | ``` 10 | -------------------------------------------------------------------------------- /docs/content/contributing/pull-request-guidelines.md: -------------------------------------------------------------------------------- 1 | # Pull Requests 2 | 3 | Please, follow these basic rules when creating pull requests. Pull requests: 4 | 5 | !!! success "Must" 6 | * Be tidy 7 | * Easy to read 8 | * Have optimized imports 9 | * Contain a sufficient amount of comments, if this is a new feature 10 | * Where applicable, contain a reasonable, (even, if it's just a minimalistic), set of test cases, that cover the bases 11 | 12 | !!! danger "Should not" 13 | * Change the existing formatting of code, unless this is really required, especially of files that have no other 14 | changes, or are not related to the pull request at all. (Please, don't enable pre-commit features in IDE-s such as 15 | "Reformat code", "Re-arrange code" and so on, as this may add extra noise to the pull and make the diff harder to 16 | read. When adding, or changing code, apply the re-formatting, only to the respective changed code blocks). 17 | * Have unresolved merge conflicts with the base branch. 18 | * Have failing tests. 19 | * Contain unaddressed **critical** issues reported by Sonarcloud. 20 | * Have commented out dead code. (Commented out code is fine, just not blocks and blocks of it). 21 | * Contain `public static void(String[] args])` methods (as those would clearly have been used for the sake of quick 22 | testing without an actual test case). 23 | 24 | Once you've created a new pull request, kindly first review the diff thoroughly yourselves, before requesting it to be 25 | reviewed by others and merged. 26 | -------------------------------------------------------------------------------- /docs/content/contributing/security-advisers.md: -------------------------------------------------------------------------------- 1 | # Security Advisers 2 | 3 | !!! danger "DO NOT PUBLISH SECURITY REPORTS PUBLICLY" 4 | 5 | Please contact @carlspring or @steve-todorov in the chat if you have found a security issue. -------------------------------------------------------------------------------- /docs/content/contributing/who-can-help-and-how.md: -------------------------------------------------------------------------------- 1 | # Who Can Help 2 | 3 | We are always pleased to get help with our project! Both experienced and not so experienced developers are welcome! 4 | 5 | There are many tasks you can help with, so please feel free to look around our [issue tracker] for some good first issues that we need help with. 6 | 7 | You can also start by having a look at our code quality and coverage issues in [Sonarcloud]. 8 | 9 | Let us know what you're good at on out [chat] channel, as well as what sort of tasks you're typically interested in, or what areas of your skills you'd like to develop and we'll try to accomodate, by finding you the right tasks. It might be the case that we have tasks that are suitable for you, but just haven't yet been added to the issue tracker, so feel free to ask us how you can help! 10 | 11 | We try our best to help new-joiners get started and eased into the project. 12 | 13 | *Please, note that while a certain level of help will be provided to newcomers, anyone who wishes to contribute to this project must be able to work independently.* 14 | 15 | ## Academics 16 | 17 | We're all here to learn cool new things and share the knowledge. 18 | 19 | ### Professors 20 | 21 | Please, reach out to us, if you're teaching programming classes and would like your students to do some work on OSS projects! 22 | 23 | Either raise a question in the issue tracker, join our [chat], or contact @carlspring for more details. We would be happy to hear about your curriculum and expectations of your students. 24 | 25 | ### Students And Interns 26 | 27 | We welcome students from all backgrounds, who have sane knowledge of programming, a willingness to learn, an openness to constructive criticism and a passion to deliver working code! 28 | 29 | Finding your first few jobs might sometimes be challenging, but having contributed to an OSS project could give your CV quite a boost, as it shows initiative, dedication and self-drivenness, among other things. 30 | 31 | # How To Help 32 | 33 | We could use all the help we can get, so, please feel free to have a good look at our issue tracker and find something of interest, that you think you would be able to help with and just add a comment under the respective issue that you'll be looking into it. If somebody else was looking at it, but seems to have been inactive for more than a few days, please feel free to ask them if they've abandoned the task, if they're blocked, or waiting for information. They might still be researching the topic, but also, please keep in mind that sometimes people can no longer work on an issue (time constraints, change of circumstances, etc). 34 | 35 | 36 | [Sonarcloud]: https://sonarcloud.io/dashboard?id=org.carlspring.cloud.aws%3As3fs-nio 37 | 38 | [chat]: https://chat.carlspring.org/channel/s3fs-nio-community 39 | [issue tracker]: https://github.com/carlspring/s3fs-nio/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22+label%3A%22good+first+issue%22 40 | 41 | -------------------------------------------------------------------------------- /docs/content/contributing/writing-documentation.md: -------------------------------------------------------------------------------- 1 | # Writing Documentation 2 | 3 | // TODO - add guide. 4 | -------------------------------------------------------------------------------- /docs/content/history.md: -------------------------------------------------------------------------------- 1 | # History 2 | 3 | ## Yet another fork !?! 4 | 5 | ![fixed-the-code-will-be-meme]({{assetsPath}}/fixed-the-code-will-be.jpg) 6 | 7 | In the past, there have been several attempts to implement an S3 `FileSystem` provider. Initially, the work on this was 8 | started by Martin Traverso in his project [martint/s3fs]. This work was later continued by Javier Arnáiz and his fork 9 | [Upplication/Amazon-S3-FileSystem-NIO2] became the de facto upstream. Many other contributors helped shape up a working 10 | API. Unfortunately, this upstream became abandoned and issues and features were being reported, that were no longer 11 | being addressed. This lead to the creation of countless forks where either single people, or small teams would address 12 | their own needs and all these efforts became decentralized and created a plethora of other unmaintained forks, 13 | documentation sites and rebranded artifacts in Maven Central. 14 | 15 | The lack of a stable, well-maintained and fully documented S3 filesystem provider for Java was the reason 16 | for us to create this as new spin-off project -- not as a fork, but as a new project that is based on 17 | [Upplication/Amazon-S3-FileSystem-NIO2]'s `master`. We have preserved the commit history and continued from there on. 18 | The side-effect of this is that any forks of [Upplication/Amazon-S3-FileSystem-NIO2] that would like to contribute their 19 | fixes back to us, would have to fork from ours and re-apply their fixes against our repository. While this may be an 20 | inconvenience, we believe it'll be a small price to pay, as it will make it clearer which project is actively maintained. 21 | 22 | ## Goals 23 | 24 | Ultimately, we don't want this to become "yet another dead fork" which "fixes just that one thing that we need". 25 | On the long run, our goal is to: 26 | 27 | 1. Build a stable community of contributors who help with the implementation of new features, bug fixing, improvements, 28 | maintenance and keeping the documentation up-to-date. 29 | 30 | 2. Make regular releases with patches, fixes and feature updates. 31 | 32 | 3. Keep this project alive and kicking. 33 | 34 | 4. Engage with the official Amazon OSS community which develops the SDK for Java and seek their guidance and reviews 35 | when necessary. 36 | 37 | If you would like to get involved and help out, you can check our [Contributing] section and get in touch on our [chat] 38 | channel. 39 | 40 | 41 | [<--# Links -->]: # 42 | [Contributing]: /contributing/index.md "Contributing page" 43 | [martint/s3fs]: https://github.com/martint/s3fs 44 | [Upplication/Amazon-S3-FileSystem-NIO2]: https://github.com/Upplication/Amazon-S3-FileSystem-NIO2 45 | [chat]: {{ chat_url }} 46 | -------------------------------------------------------------------------------- /docs/content/index.md: -------------------------------------------------------------------------------- 1 | {! ../../README.md [ln:19-47] !} 2 | 3 | ## Installation 4 | 5 | === "Maven" 6 | 7 | ``` 8 | 9 | {{ POM_GROUP_ID }} 10 | {{ POM_ARTIFACT_ID }} 11 | {{ POM_VERSION }} 12 | 13 | ``` 14 | 15 | === "Gradle.build.kts" 16 | 17 | ``` 18 | implementation("{{ POM_GROUP_ID }}:{{ POM_ARTIFACT_ID }}:{{ POM_VERSION }}") 19 | ``` 20 | 21 | === "Gradle.build" 22 | 23 | ``` 24 | implementation '{{ POM_GROUP_ID }}:{{ POM_ARTIFACT_ID }}:{{ POM_VERSION }}' 25 | ``` 26 | 27 | === "SBT" 28 | 29 | ``` 30 | libraryDependencies += "{{ POM_GROUP_ID }}" % "{{ POM_ARTIFACT_ID }}" % "{{ POM_VERSION }}" 31 | ``` 32 | 33 | 34 | ## Quick start 35 | 36 | ### Amazon S3 Setup 37 | 38 | 1. Open [S3 Console] and add a bucket 39 | 2. Open [IAM] and go to `Add User` 40 | 3. Set `Access Type` to `Programmatic Access` or you will get `403 Forbidden` errors. 41 | 4. Select `Attach an existing policies directly` 42 | 5. Select `AmazonS3FullAccess` policy and `Create user` (if you prefer more fine-grained access [click here](./contributing/developer-guide/index.md#s3-advanced)) 43 | 6. Copy `Access key ID` and `Secret access key` - you will need them later! 44 | 45 | ### Example 46 | 47 | === "1. Configure" 48 | 49 | Create/load a properties file in your project which defines the following properties: 50 | 51 | ``` 52 | 53 | --8<-- "../src/test/resources/amazon-test-sample.properties" 54 | ``` 55 | 56 | These properties can also be exported as environment variables. 57 | A complete list is available in the [Configuration Options] 58 | 59 | === "2. Code" 60 | 61 | ```java 62 | FileSystems.newFileSystem(URI.create("s3:///"), 63 | new HashMap<>(), 64 | Thread.currentThread().getContextClassLoader()); 65 | 66 | ``` 67 | 68 | ## See also 69 | 70 | * [Contributing] 71 | * [Configuration Options] 72 | * [More examples] 73 | 74 | 75 | [<--# Links -->]: # 76 | [Contributing]: ./contributing/index.md "Contributing" 77 | [Configuration Options]: ./reference/configuration-options.md "Configuration Options" 78 | [More examples]: reference/examples/basic-example.md "More examples" 79 | [S3 Console]: https://s3.console.aws.amazon.com/s3/home "Amazon S3 Console" 80 | [IAM]: https://console.aws.amazon.com/iam/home "Amazon IAM" 81 | -------------------------------------------------------------------------------- /docs/content/license.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | This project would not have been possible without the initial implementation(s) done by: 4 | 5 | * @martint/s3fs (initial implementation, `Apache 2.0`) 6 | * @Upplication/Amazon-S3-FileSystem-NIO2 (fork with fixes, mainly `Apache 2.0`, but some `MIT` as well) 7 | * Other forks (only ones licensed under `Apache 2.0` or `MIT`) 8 | 9 | We are grateful to both the main authors and all of the contributors that helped shape the initial implementation 10 | that we are building on top of. Without their hard work, this library would not exist. 11 | 12 | As explained in [#2](https://github.com/carlspring/s3fs-nio/issues/2), [martint/s3fs]'s project was under an Apache 2.0 13 | license. However, it is unclear whether it's a matter of a mistake that in Javier Arnáiz's fork (under the 14 | [Upplication/Amazon-S3-FileSystem-NIO2], the license is defined as MIT, while at the same time the project's `pom.xml` 15 | still defines the license as being under Apache 2.0. This appears to have caused confusion for other forkers as well 16 | and some of them have come to the conclusion that it would be better off to dual-license the project. 17 | 18 | As MIT is a permissive license, and, since Apache 2.0 has not been removed as such from 19 | [Upplication/Amazon-S3-FileSystem-NIO2], the entire current and future codebase of `s3fs-nio` will also be dual-licensed 20 | under both Apache 2.0 and MIT and, any developers wishing to use our library, will be free to choose which of the two 21 | license would work for them. 22 | 23 | ## Apache 2.0 24 | 25 | A copy of our Apache 2.0 license can be found [here](https://github.com/carlspring/s3fs-nio2/blob/master/LICENSE.Apache-2.0.md). 26 | 27 | ## MIT 28 | 29 | A copy of the MIT license can be found [here](https://github.com/carlspring/s3fs-nio2/blob/master/LICENSE.MIT.md). 30 | 31 | 32 | [martint/s3fs]: https://github.com/martint/s3fs 33 | [Upplication/Amazon-S3-FileSystem-NIO2]: https://github.com/Upplication/Amazon-S3-FileSystem-NIO2 34 | -------------------------------------------------------------------------------- /docs/content/reference/examples/basic-example.md: -------------------------------------------------------------------------------- 1 | # Basic Example 2 | 3 | This is an absolutely minimalistic example of how to use our library. 4 | 5 | ## Required Dependencies 6 | 7 | === "Maven" 8 | 9 | ``` 10 | 11 | {{ POM_GROUP_ID }} 12 | {{ POM_ARTIFACT_ID }} 13 | {{ POM_VERSION }} 14 | 15 | ``` 16 | 17 | === "Gradle" 18 | 19 | ``` 20 | compile group: '{{ POM_GROUP_ID }}', name: '{{ POM_ARTIFACT_ID }}', version: '{{ POM_VERSION }}' 21 | ``` 22 | 23 | === "SBT" 24 | 25 | ``` 26 | libraryDependencies += "{{ POM_GROUP_ID }}" % "{{ POM_ARTIFACT_ID }}" % "{{ POM_VERSION }}" 27 | ``` 28 | 29 | ## Basic Code Example 30 | 31 | You can find some code examples below, along with explanations. 32 | 33 | ### `S3FileSystem` And AmazonS3 Settings 34 | 35 | All settings for `S3FileSystem` and the underlying AmazonS3 connector library can be set through system properties or 36 | environment variables. 37 | 38 | The possible configuration settings can be found [here][Configuration Options]. 39 | 40 | ### Using A Service Locator And System Variables 41 | 42 | Check that `s3fs.access.key` and `s3fs.secret.key` system vars are present with the correct values to have full access 43 | to your Amazon S3 bucket. 44 | 45 | Use the following code to create the `FileSystem` and set to a concrete end-point. 46 | 47 | ```java 48 | FileSystems.newFileSystem(URI.create("s3:///"), 49 | new HashMap<>(), 50 | Thread.currentThread().getContextClassLoader()); 51 | ``` 52 | 53 | ### Using A Service Locator And An `amazon.properties` file 54 | 55 | In your `src/main/resources/amazon.properties`, add the following settings: 56 | 57 | ``` 58 | s3fs.access.key=access-key 59 | s3fs.secret.key=secret-key 60 | ``` 61 | 62 | Use the following code to create the `FileSystem` and set it to a specific end-point. 63 | 64 | ```java 65 | FileSystems.newFileSystem(URI.create("s3:///"), 66 | new HashMap<>(), 67 | Thread.currentThread().getContextClassLoader()); 68 | ``` 69 | 70 | ### Using Service Locator With Authentication Settings 71 | 72 | Create a map with the authentication and use it to create the `FileSystem` and set to a concrete end-point. 73 | 74 | ```java 75 | import static org.carlspring.cloud.storage.s3fs.S3Factory.ACCESS_KEY; 76 | import static org.carlspring.cloud.storage.s3fs.S3Factory.SECRET_KEY; 77 | 78 | ... 79 | 80 | Map env = ImmutableMap. builder().put(ACCESS_KEY, "access key") 81 | .put(SECRET_KEY, "secret key") 82 | .build(); 83 | 84 | FileSystems.newFileSystem(URI.create("s3:///"), 85 | env, 86 | Thread.currentThread().getContextClassLoader()); 87 | ``` 88 | 89 | ### Set End-Point To Reduce Data Latency In Your Applications 90 | 91 | ```java 92 | // Northern Virginia or Pacific Northwest 93 | FileSystems.newFileSystem(URI.create("s3://s3.amazonaws.com/"), 94 | env, 95 | Thread.currentThread().getContextClassLoader()); 96 | 97 | // Northern Virginia only 98 | FileSystems.newFileSystem(URI.create("s3://s3-external-1.amazonaws.com/"), 99 | env, 100 | Thread.currentThread().getContextClassLoader()); 101 | 102 | // US West (Oregon) Region 103 | FileSystems.newFileSystem(URI.create("s3://s3-us-west-2.amazonaws.com/"), 104 | env, 105 | Thread.currentThread().getContextClassLoader()); 106 | 107 | // US West (Northern California) Region 108 | FileSystems.newFileSystem(URI.create("s3://s3-us-west-1.amazonaws.com/"), 109 | env, 110 | Thread.currentThread().getContextClassLoader()); 111 | 112 | // EU (Ireland) Region 113 | FileSystems.newFileSystem(URI.create("s3://s3-eu-west-1.amazonaws.com/"), 114 | env, 115 | Thread.currentThread().getContextClassLoader()); 116 | ``` 117 | 118 | For a complete list of available regions, you can check the [AWS S3 Reference]. 119 | 120 | 121 | [<--# Links -->]: # 122 | [Configuration Options]: ../configuration-options.md 123 | [AWS S3 Reference]: http://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region 124 | -------------------------------------------------------------------------------- /docs/content/reference/examples/mina-example.md: -------------------------------------------------------------------------------- 1 | # MINA Example 2 | 3 | This article illustrates how to use our library with Apache MINA. 4 | 5 | Please, get acquainted with the [Basic Example] first. 6 | 7 | The possible configuration settings can be found [here][Configuration Options]. 8 | 9 | ## Required Dependencies 10 | 11 | === "Maven" 12 | 13 | ``` 14 | 15 | {{ POM_GROUP_ID }} 16 | {{ POM_ARTIFACT_ID }} 17 | {{ POM_VERSION }} 18 | 19 | ``` 20 | 21 | === "Gradle" 22 | 23 | ``` 24 | compile group: '{{ POM_GROUP_ID }}', name: '{{ POM_ARTIFACT_ID }}', version: '{{ POM_VERSION }}' 25 | ``` 26 | 27 | === "SBT" 28 | 29 | ``` 30 | libraryDependencies += "{{ POM_GROUP_ID }}" % "{{ POM_ARTIFACT_ID }}" % "{{ POM_VERSION }}" 31 | ``` 32 | 33 | ## MINA Code Example 34 | 35 | You can use our library with MINA like this: 36 | 37 | ```java 38 | public FileSystemFactory createFileSystemFactory(final String bucketName) 39 | throws IOException 40 | { 41 | final FileSystem fileSystem = FileSystems.newFileSystem(URI.create("s3:///"), 42 | env, 43 | Thread.currentThread() 44 | .getContextClassLoader()); 45 | 46 | final Path bucketPath = fileSystem.getPath(bucketName); 47 | 48 | return new VirtualFileSystemFactory(bucketPath); 49 | } 50 | ``` 51 | 52 | [<--# Links -->]: # 53 | [Basic Example]: ./basic-example.md 54 | [Configuration Options]: ../configuration-options.md 55 | -------------------------------------------------------------------------------- /docs/content/reference/examples/spring-example.md: -------------------------------------------------------------------------------- 1 | # Spring Example 2 | 3 | This is an example of how to use our library with Spring. 4 | 5 | Please, get acquainted with the [Basic Example] first. 6 | 7 | The possible configuration settings can be found [here][Configuration Options]. 8 | 9 | ## Required Dependencies 10 | 11 | === "Maven" 12 | 13 | ``` 14 | 15 | {{ POM_GROUP_ID }} 16 | {{ POM_ARTIFACT_ID }} 17 | {{ POM_VERSION }} 18 | 19 | ``` 20 | 21 | === "Gradle" 22 | 23 | ``` 24 | compile group: '{{ POM_GROUP_ID }}', name: '{{ POM_ARTIFACT_ID }}', version: '{{ POM_VERSION }}' 25 | ``` 26 | 27 | === "SBT" 28 | 29 | ``` 30 | libraryDependencies += "{{ POM_GROUP_ID }}" % "{{ POM_ARTIFACT_ID }}" % "{{ POM_VERSION }}" 31 | ``` 32 | 33 | ## Spring Code Example 34 | 35 | ```java 36 | import static org.carlspring.cloud.storage.s3fs.S3Factory.ACCESS_KEY; 37 | import static org.carlspring.cloud.storage.s3fs.S3Factory.SECRET_KEY; 38 | 39 | @Configuration 40 | public class AwsConfig 41 | { 42 | 43 | @Value("${aws.accessKey}") 44 | private String accessKey; 45 | 46 | @Value("${aws.secretKey}") 47 | private String secretKey; 48 | 49 | 50 | @Bean 51 | public FileSystem s3FileSystem() 52 | throws IOException 53 | { 54 | Map env = new HashMap<>(); 55 | env.put(ACCESS_KEY, accessKey); 56 | env.put(SECRET_KEY, secretKey); 57 | 58 | return FileSystems.newFileSystem(URI.create("s3:///"), 59 | env, 60 | Thread.currentThread().getContextClassLoader()); 61 | } 62 | 63 | } 64 | ``` 65 | 66 | Now you can inject in any spring component: 67 | 68 | ```java 69 | @Inject 70 | private FileSystem s3FileSystem; 71 | ``` 72 | 73 | [<--# Links -->]: # 74 | [Basic Example]: ./basic-example.md 75 | [Configuration Options]: ../configuration-options.md 76 | -------------------------------------------------------------------------------- /docs/content/versioning.md: -------------------------------------------------------------------------------- 1 | # Versioning policy and Releases 2 | 3 | We recognize stability and usability is the most important thing for every project, especially, for libraries. We would 4 | also like to keep evolving this library. We intend to keep the library as stable, as possible and when there will be 5 | breaking changes, we will release new major versions. 6 | 7 | Here is an overview of the practices we will be following to ensure predictable changes, high-quality code and stable 8 | release. 9 | 10 | ## Versioning policy 11 | 12 | {{ project_name }} is following the [semantic versioning][semver] guide to ensure we introduce predictable changes. 13 | 14 | * Release version numbers consist of three parts: `major.minor.patch`. 15 | For example, version `1.2.3` indicates major version `1`, minor version `2`, and patch level `3`. 16 | 17 | * Release version numbers are incremented, based on the level of change included in the release: 18 | 19 | !!! danger "Major releases contain significant new features or changes" 20 | When updating to a new major release, you might need to refactor code, run additional tests, and/or 21 | learn new APIs. Major releases will be **backwards-incompatible**, unless otherwise stated in the release notes. 22 | 23 | !!! success "Minor releases contain new smaller features" 24 | Minor releases will be fully backward-compatible. 25 | Developer intervention might be required during updates, but you can optionally modify your apps and libraries 26 | to begin using new features, bug fixes and other improvements that were added to the release. 27 | 28 | !!! success "Patch releases are low risk, bug fix releases" 29 | Upgrading requires no developer assistance other than updating the dependency. 30 | 31 | ## Deprecation policy 32 | 33 | TODO 34 | 35 | ## Preview releases 36 | 37 | We will be doing pre-releases for each upcoming `major` version. 38 | 39 | ### Release Candidate 40 | 41 | `Release Candidate` (rc) versions are considered to be feature complete and in the final testing phase. 42 | All `rc` versions will end with `-rc-[N]` where `[N]` will equal a positive integer. 43 | We encourage the community to test and provide [feedback] regarding possible issues. 44 | 45 | Examples: `1.2.3-rc-1`, `1.2.3-rc-2` 46 | 47 | !!! warning "RC versions should be considered unstable" 48 | Although the `rc` versions should be near ready for production - keep in mind it's in the final testing phase. 49 | Some functionality might be broken without us being aware of just yet. Use with caution. 50 | 51 | 52 | ### Snapshots 53 | 54 | During active development we might deploy a `SNAPSHOT` version for either `major`, `minor` or `patch` versions. 55 | These versions are `Work In Progress` and under active development (and/or testing). For more information check [What is a SNAPSHOT version]. 56 | 57 | Examples: `1.2.3-SNAPSHOT`, `2.3.4-SNAPSHOT` 58 | 59 | !!! success "It is fine to use SNAPSHOT versions for TESTING purposes." 60 | 61 | !!! danger "DO NOT USE SNAPSHOT VERSIONS IN PRODUCTION!" 62 | 63 | These versions are usually in active development and **WILL** break something! 64 | 65 | 66 | ## Release frequency 67 | 68 | We would love to fix and release as fast and as often as possible, but we also have a daytime job. We will do our best 69 | to cut releases as frequently as possible, but will act within reason and depending on our free time. 70 | 71 | * `snapshot` builds - automatically - on every push to the `master` between releases. (DO NOT USE IN PRODUCTION!) 72 | * `major` releases will most-likely be tied with new releases of Amazon's SDK for Java, or when there are other 73 | significant changes in our codebase, or dependencies that introduce breaking changes. 74 | * `minor` releases will be cut as often as possible, as long as there enough approved changes. These should, hopefully, 75 | be monthly (or more frequently, should there be approved fixes); or at least once every three months. 76 | * `patch` releases will be on-demand and be cut, when there are critical issues/CVEs/bugfixes. 77 | 78 | 79 | [<--# Links -->]: # 80 | [semver]: https://semver.org/ "Semantic Versioning Specification" 81 | [What is a SNAPSHOT version]: https://maven.apache.org/guides/getting-started/index.html#What_is_a_SNAPSHOT_version "What is a SNAPSHOT version?" 82 | [feedback]: {{ repo_url }}/issues 83 | -------------------------------------------------------------------------------- /docs/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | serve: 4 | build: 5 | context: . 6 | dockerfile: ./docs.Dockerfile 7 | image: squidfunk/mkdocs-material:9.5.44-custom 8 | container_name: s3fs-nio-docs 9 | working_dir: /workspace/docs 10 | volumes: 11 | - ../:/workspace 12 | ports: 13 | - 8000:8000 14 | build: 15 | build: 16 | context: . 17 | dockerfile: ./docs.Dockerfile 18 | image: squidfunk/mkdocs-material:9.5.44-custom 19 | working_dir: /workspace/docs 20 | command: [ "build" ] 21 | volumes: 22 | - ../:/workspace 23 | -------------------------------------------------------------------------------- /docs/docs.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM squidfunk/mkdocs-material:9.5.44 2 | 3 | WORKDIR /workspace/docs 4 | COPY requirements.txt /workspace/docs 5 | 6 | RUN set -x \ 7 | && apk add --no-cache --virtual .build-deps gcc libc-dev make \ 8 | && pip3 install -r requirements.txt \ 9 | && apk del .build-deps 10 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs~=1.6 2 | mkdocs-material==9.5.44 3 | mkdocs-material-extensions~=1.3 4 | mdx_gh_links==0.4 5 | mdx_include==1.4.2 6 | mdx_include==1.4.2 7 | mkdocs-markdownextradata-plugin==0.2.5 8 | mkdocs-git-revision-date-plugin==0.3.2 9 | mkdocs-redirects==1.2.1 10 | mkdocs-htmlproofer-plugin==1.2.1 11 | mkdocs-minify-plugin==0.8.0 12 | # This dependency is necessary for older mkdocs versions. 13 | # TODO: Remove when upgrading mkdocs. 14 | jinja2~=3.0 15 | -------------------------------------------------------------------------------- /docs/runtime.txt: -------------------------------------------------------------------------------- 1 | 3.8 2 | -------------------------------------------------------------------------------- /docs/theme/assets/custom.css: -------------------------------------------------------------------------------- 1 | :root > * { 2 | --md-primary-danger-bg-color: #ff1744; 3 | --md-primary-danger-fg-color: rgba(255,23,68,.1); 4 | --md-primary-danger-bg-hover-color: #ff1744; 5 | --md-primary-danger-fg-hover-color: rgba(255,23,68,.15); 6 | } 7 | 8 | .grid-links p { 9 | display: grid; 10 | grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); 11 | grid-row-gap: 1em; 12 | grid-auto-flow: dense; 13 | grid-column-gap: 3em; 14 | } 15 | 16 | .md-content { 17 | text-align: justify; 18 | } 19 | 20 | .md-typeset table:not([class]) th, .md-typeset table:not([class]) tr, .md-typeset table:not([class]) td { 21 | vertical-align: middle; 22 | } 23 | 24 | .md-typeset table:not([class]) tr:hover { 25 | box-shadow: none; 26 | } 27 | 28 | .md-typeset table:not([class]) td small:before { 29 | white-space: pre-wrap; 30 | } 31 | 32 | .md-typeset table:not([class]) td small { 33 | font-size: 85%; 34 | } 35 | 36 | .md-typeset .md-typeset__table { 37 | width: 100%; 38 | } 39 | 40 | .md-typeset .md-typeset__table table { 41 | display: table; 42 | box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12), 0 3px 1px -2px rgba(0,0,0,.2); 43 | } 44 | 45 | .md-typeset .md-button { 46 | padding: .65em 1.25em !important; 47 | margin-top: 1em; 48 | margin-bottom: 1em; 49 | } 50 | 51 | .md-typeset .md-button .twemoji { 52 | margin-right: 0.5em; 53 | } 54 | 55 | .md-typeset .md-button--danger { 56 | color: var(--md-primary-danger-bg-color) !important; 57 | background-color: var(--md-primary-danger-fg-color) !important; 58 | border-color: var(--md-primary-danger-fg-color) !important; 59 | } 60 | 61 | .md-typeset .md-button--danger:focus, .md-typeset .md-button--danger:hover { 62 | color: var(--md-primary-danger-bg-hover-color) !important; 63 | background-color: var(--md-primary-danger-fg-hover-color) !important; 64 | border-color: var(--md-primary-danger-fg-hover-color) !important; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /docs/theme/assets/structor-menu.css: -------------------------------------------------------------------------------- 1 | @media only screen and (min-width:76.25em) { 2 | .md-nav__item--version { 3 | border-width: 0.09rem; 4 | border-style: solid; 5 | border-radius: .2rem; 6 | padding-left: .625em !important; 7 | padding-bottom: .425em !important; 8 | margin-top: 1.875rem; 9 | } 10 | } -------------------------------------------------------------------------------- /docs/theme/assets/structor-menu.js.gotmpl: -------------------------------------------------------------------------------- 1 | const versions = [ 2 | {{- range $version := .Versions }} 3 | {{- $text := $version.Text }} 4 | {{- if eq $version.State "EXPERIMENTAL" }} 5 | {{- $latest := semver $.Latest }} 6 | {{- $text = printf "v%d.%d (unreleased)" $latest.Major (int64 1 | add $latest.Minor) }} 7 | {{- end}} 8 | {path: "{{ $version.Path }}", text: "{{ $text }}", selected: {{ $version.Selected }} }, 9 | {{- end}} 10 | ]; 11 | 12 | 13 | // Material theme 14 | function addMaterialMenu(elt, versions) { 15 | const current = versions.find(function (value) { 16 | return value.selected 17 | }) 18 | 19 | const rootLi = document.createElement('li'); 20 | rootLi.classList.add('md-nav__item'); 21 | rootLi.classList.add('md-nav__item--version'); 22 | rootLi.classList.add('md-nav__item--nested'); 23 | 24 | const input = document.createElement('input'); 25 | input.classList.add('md-toggle'); 26 | input.classList.add('md-nav__toggle'); 27 | input.setAttribute('data-md-toggle', 'nav-10000000'); 28 | input.id = "nav-10000000"; 29 | input.type = 'checkbox'; 30 | 31 | rootLi.appendChild(input); 32 | 33 | const lbl01 = document.createElement('label') 34 | lbl01.classList.add('md-nav__link'); 35 | lbl01.setAttribute('for', 'nav-10000000'); 36 | lbl01.textContent = current.text + " "; 37 | 38 | rootLi.appendChild(lbl01); 39 | 40 | const nav = document.createElement('nav') 41 | nav.classList.add('md-nav'); 42 | nav.setAttribute('data-md-component','collapsible'); 43 | nav.setAttribute('data-md-level','1'); 44 | 45 | rootLi.appendChild(nav); 46 | 47 | const lbl02 = document.createElement('label') 48 | lbl02.classList.add('md-nav__title'); 49 | lbl02.setAttribute('for', 'nav-10000000'); 50 | lbl02.textContent = current.text + " "; 51 | 52 | nav.appendChild(lbl02); 53 | 54 | const ul = document.createElement('ul') 55 | ul.classList.add('md-nav__list'); 56 | ul.setAttribute('data-md-scrollfix',''); 57 | 58 | nav.appendChild(ul); 59 | 60 | for (let i = 0; i < versions.length; i++) { 61 | const li = document.createElement('li'); 62 | li.classList.add('md-nav__item'); 63 | 64 | ul.appendChild(li); 65 | 66 | const a = document.createElement('a'); 67 | a.classList.add('md-nav__link'); 68 | if (versions[i].selected) { 69 | a.classList.add('md-nav__link--active'); 70 | } 71 | a.href = window.location.protocol + "//" + window.location.host + "/"; 72 | if (window.location.host.includes(".github.io")) { 73 | a.href = a.href + window.location.pathname.split("/")[1] + "/"; 74 | } 75 | if (versions[i].path) { 76 | a.href = a.href + versions[i].path + "/" 77 | } 78 | a.title = versions[i].text; 79 | a.text = versions[i].text; 80 | 81 | li.appendChild(a); 82 | } 83 | 84 | elt.appendChild(rootLi); 85 | } 86 | 87 | // United theme 88 | function addMenu(elt, versions){ 89 | const li = document.createElement('li'); 90 | li.classList.add('md-nav__item'); 91 | li.style.cssText = 'padding-top: 1em;'; 92 | 93 | const select = document.createElement('select'); 94 | select.classList.add('md-nav__link'); 95 | select.style.cssText = 'background: white;border: none;color: #00BCD4;-webkit-border-radius: 5px;-moz-border-radius: 5px;border-radius: 5px;overflow: hidden;padding: 0.1em;' 96 | select.setAttribute('onchange', 'location = this.options[this.selectedIndex].value;'); 97 | 98 | for (let i = 0; i < versions.length; i++) { 99 | let opt = document.createElement('option'); 100 | opt.value = window.location.protocol + "//" + window.location.host + "/"; 101 | if (versions[i].path) { 102 | opt.value = opt.value + versions[i].path + "/" 103 | } 104 | opt.text = versions[i].text; 105 | opt.selected = versions[i].selected; 106 | select.appendChild(opt); 107 | } 108 | 109 | li.appendChild(select); 110 | elt.appendChild(li); 111 | } 112 | 113 | 114 | const unitedSelector = 'div.navbar.navbar-default.navbar-fixed-top div.container div.navbar-collapse.collapse ul.nav.navbar-nav.navbar-right'; 115 | const materialSelector = 'div.md-container main.md-main div.md-main__inner.md-grid div.md-sidebar.md-sidebar--primary div.md-sidebar__scrollwrap div.md-sidebar__inner nav.md-nav.md-nav--primary ul.md-nav__list'; 116 | 117 | let elt = document.querySelector(materialSelector); 118 | if (elt) { 119 | addMaterialMenu(elt, versions); 120 | } else { 121 | const elt = document.querySelector(unitedSelector); 122 | addMenu(elt, versions); 123 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## {x-release-please-start-version} 2 | version=3.0.1-SNAPSHOT 3 | ## {x-release-please-end} 4 | 5 | # Signature key id 6 | signing.gnupg.keyName=CEE144B17ECAC99A 7 | 8 | # Kotlin 9 | kotlin.caching.enabled=true 10 | kotlin.incremental=true 11 | kotlin.incremental.useClasspathSnapshot=true 12 | kotlin.parallel.tasks.in.project=true 13 | 14 | # Gradle 15 | org.gradle.parallel=true 16 | org.gradle.caching=true 17 | org.gradle.daemon.idletimeout=3600000 18 | # These cause issues. 19 | #org.gradle.configuration-cache=true 20 | #org.gradle.configureondemand=true 21 | 22 | ## 23 | ## Maven Central 24 | ## 25 | ## Upload to Maven Central takes only forever and connections timeout. 26 | ## The settings below help to mitigate that problem. 27 | ## 28 | 29 | # Set the socket timeout to 5 minutes 30 | systemProp.org.gradle.internal.http.connectionTimeout=300000 31 | systemProp.org.gradle.internal.http.socketTimeout=300000 32 | 33 | # the number of retries (initial included) (default 3) 34 | systemProp.org.gradle.internal.repository.max.tentative=10 35 | 36 | # the initial time before retrying, in milliseconds (default 125) 37 | systemProp.org.gradle.internal.repository.initial.backoff=1000 38 | 39 | # Gradle 40 | org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 41 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/carlspring/s3fs-nio/e76d7c2eff20e130fb17b87e8c9bdf229b112b9f/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | @rem SPDX-License-Identifier: Apache-2.0 17 | @rem 18 | 19 | @if "%DEBUG%"=="" @echo off 20 | @rem ########################################################################## 21 | @rem 22 | @rem Gradle startup script for Windows 23 | @rem 24 | @rem ########################################################################## 25 | 26 | @rem Set local scope for the variables with windows NT shell 27 | if "%OS%"=="Windows_NT" setlocal 28 | 29 | set DIRNAME=%~dp0 30 | if "%DIRNAME%"=="" set DIRNAME=. 31 | @rem This is normally unused 32 | set APP_BASE_NAME=%~n0 33 | set APP_HOME=%DIRNAME% 34 | 35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 37 | 38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 40 | 41 | @rem Find java.exe 42 | if defined JAVA_HOME goto findJavaFromJavaHome 43 | 44 | set JAVA_EXE=java.exe 45 | %JAVA_EXE% -version >NUL 2>&1 46 | if %ERRORLEVEL% equ 0 goto execute 47 | 48 | echo. 1>&2 49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 50 | echo. 1>&2 51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 52 | echo location of your Java installation. 1>&2 53 | 54 | goto fail 55 | 56 | :findJavaFromJavaHome 57 | set JAVA_HOME=%JAVA_HOME:"=% 58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 59 | 60 | if exist "%JAVA_EXE%" goto execute 61 | 62 | echo. 1>&2 63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 64 | echo. 1>&2 65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2 66 | echo location of your Java installation. 1>&2 67 | 68 | goto fail 69 | 70 | :execute 71 | @rem Setup the command line 72 | 73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 74 | 75 | 76 | @rem Execute Gradle 77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 78 | 79 | :end 80 | @rem End local scope for the variables with windows NT shell 81 | if %ERRORLEVEL% equ 0 goto mainEnd 82 | 83 | :fail 84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 85 | rem the _cmd.exe /c_ return code! 86 | set EXIT_CODE=%ERRORLEVEL% 87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 89 | exit /b %EXIT_CODE% 90 | 91 | :mainEnd 92 | if "%OS%"=="Windows_NT" endlocal 93 | 94 | :omega 95 | -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "bootstrap-sha": "01471b4b5db5274f7ba5469c63548079b5d0e369", 3 | "packages": { 4 | ".": { 5 | "changelog-path": "CHANGELOG.md", 6 | "release-type": "java", 7 | "bump-minor-pre-major": false, 8 | "bump-patch-for-minor-pre-major": true, 9 | "pull-request-title-pattern": "Release v${version}", 10 | "draft": false, 11 | "prerelease": false, 12 | "include-v-in-tag": true, 13 | "extra-files": [ 14 | "gradle.properties", 15 | "docs/mkdocs.yaml", 16 | "docs/mkdocs.yml" 17 | ] 18 | } 19 | }, 20 | "changelog-sections": [ 21 | {"type":"feat","section":"Features","hidden":false}, 22 | {"type":"fix","section":"Bug Fixes","hidden":false}, 23 | {"type":"docs","section":"Documentation","hidden":true}, 24 | {"type":"deprecated","section":"Deprecated","hidden":false}, 25 | {"type":"removed","section":"Removed","hidden":false}, 26 | {"type":"deps","section":"Dependency management","hidden":false}, 27 | {"type":"chore","section":"Miscellaneous","hidden":true} 28 | ], 29 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json" 30 | } 31 | -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "s3fs-nio" 2 | 3 | pluginManagement { 4 | repositories { 5 | mavenLocal() 6 | // Allows you to specify your own repository manager instance. 7 | if(extra.has("s3fs.proxy.url")) { 8 | maven { url = extra["s3fs.proxy.url"]?.let { uri(it) }!! } 9 | } 10 | gradlePluginPortal() 11 | mavenCentral() 12 | } 13 | } 14 | 15 | plugins { 16 | id("com.gradle.develocity") version "3.19.2" 17 | } 18 | 19 | develocity { 20 | buildScan { 21 | termsOfUseUrl = "https://gradle.com/terms-of-service" 22 | termsOfUseAgree = "yes" 23 | 24 | // Automatically publish scans in CI 25 | publishing.onlyIf { !System.getenv("CI").isNullOrEmpty() } 26 | 27 | capture { 28 | // https://docs.gradle.com/enterprise/gradle-plugin/current/#capturing_resource_usage 29 | resourceUsage.set(true) 30 | buildLogging.set(false) 31 | testLogging.set(false) 32 | } 33 | 34 | obfuscation { 35 | username { _ -> "__redacted__" } 36 | ipAddresses { addresses -> addresses.map { _ -> "0.0.0.0" } } 37 | externalProcessName { processName -> "__redacted__" } 38 | } 39 | 40 | // Add custom values 41 | value("Project Name", rootProject.name) 42 | value("Build Branch", System.getenv("GITHUB_REF")?.replace("refs/heads/", "") ?: "N/A") 43 | value("Build Commit", System.getenv("GITHUB_SHA") ?: "N/A") 44 | 45 | val ciLink = System.getenv("GITHUB_ACTIONS")?.let { "https://github.com/${System.getenv("GITHUB_REPOSITORY")}/actions/runs/${System.getenv("GITHUB_RUN_ID")}" } ?: "N/A" 46 | // Add tags to the build scan 47 | tag("CI") 48 | tag(System.getProperty("os.name")) 49 | // Add links to related information 50 | if(ciLink != "N/A") { 51 | link("GitHub Action", ciLink) 52 | } else { 53 | tag("local dev build") 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/S3AccessControlList.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs; 2 | 3 | import java.nio.file.AccessDeniedException; 4 | import java.nio.file.AccessMode; 5 | 6 | import software.amazon.awssdk.services.s3.model.Grant; 7 | import software.amazon.awssdk.services.s3.model.Owner; 8 | 9 | public class S3AccessControlList 10 | { 11 | 12 | private final String fileStoreName; 13 | 14 | private final String key; 15 | 16 | 17 | /** 18 | * Creates a new S3AccessControlList 19 | * 20 | * @param fileStoreName 21 | * @param key 22 | * @param grants unused 23 | * @param owner unused 24 | * @deprecated use {@link #S3AccessControlList(String, String)} 25 | */ 26 | @Deprecated 27 | public S3AccessControlList(final String fileStoreName, 28 | final String key, 29 | final Iterable grants, //unused, but keeping to preserve signature 30 | final Owner owner //unused, but keeping to preserve signature 31 | ) 32 | { 33 | this.fileStoreName = fileStoreName; 34 | this.key = key; 35 | } 36 | 37 | /** 38 | * Creates a new S3AccessControlList 39 | * 40 | * @param fileStoreName 41 | * @param key 42 | */ 43 | public S3AccessControlList(final String fileStoreName, final String key) 44 | { 45 | this.fileStoreName = fileStoreName; 46 | this.key = key; 47 | } 48 | 49 | public String getKey() 50 | { 51 | return key; 52 | } 53 | 54 | public void checkAccess(final AccessMode[] modes) 55 | throws AccessDeniedException 56 | { 57 | for (AccessMode accessMode : modes) 58 | { 59 | // Checking the ACL grants is not sufficient to determine access as bucket policy may override ACL. 60 | // Any permission problems will have to be handled at time of access. 61 | if (accessMode == AccessMode.EXECUTE) 62 | { 63 | throw new AccessDeniedException(fileName(), null, "file is not executable"); 64 | } 65 | } 66 | } 67 | 68 | private String fileName() 69 | { 70 | return fileStoreName + S3Path.PATH_SEPARATOR + key; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/S3AwsRegionProviderChain.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs; 2 | 3 | import java.util.function.Supplier; 4 | 5 | import software.amazon.awssdk.profiles.ProfileFile; 6 | import software.amazon.awssdk.regions.providers.AwsProfileRegionProvider; 7 | import software.amazon.awssdk.regions.providers.AwsRegionProviderChain; 8 | import software.amazon.awssdk.regions.providers.SystemSettingsRegionProvider; 9 | 10 | /** 11 | * AWS Region provider that looks for the region in this order: 12 | *
    13 | *
  1. Check the 'aws.region' system property for the region.
  2. 14 | *
  3. Check the 'AWS_REGION' environment variable for the region.
  4. 15 | *
  5. Check the {user.home}/.aws/credentials and {user.home}/.aws/config files for the region.
  6. 16 | *
17 | */ 18 | public class S3AwsRegionProviderChain 19 | extends AwsRegionProviderChain 20 | { 21 | 22 | private S3AwsRegionProviderChain(final S3AwsRegionProviderChain.Builder builder) 23 | { 24 | super(new SystemSettingsRegionProvider(), 25 | new AwsProfileRegionProvider(builder.profileFile, builder.profileName)); 26 | } 27 | 28 | public static S3AwsRegionProviderChain.Builder builder() 29 | { 30 | return new S3AwsRegionProviderChain.Builder(); 31 | } 32 | 33 | public static final class Builder 34 | { 35 | 36 | private Supplier profileFile; 37 | private String profileName; 38 | 39 | private Builder() 40 | { 41 | } 42 | 43 | public Builder profileFile(final Supplier profileFile) 44 | { 45 | this.profileFile = profileFile; 46 | return this; 47 | } 48 | 49 | public Builder profileName(final String profileName) 50 | { 51 | this.profileName = profileName; 52 | return this; 53 | } 54 | 55 | public S3AwsRegionProviderChain build() 56 | { 57 | return new S3AwsRegionProviderChain(this); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/S3ClientFactory.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs; 2 | 3 | import software.amazon.awssdk.services.s3.S3Client; 4 | import software.amazon.awssdk.services.s3.S3ClientBuilder; 5 | 6 | public class S3ClientFactory 7 | extends S3Factory 8 | { 9 | 10 | @Override 11 | protected S3Client createS3Client(final S3ClientBuilder builder) 12 | { 13 | return builder.build(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/S3FileStoreAttributeView.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs; 2 | 3 | import java.nio.file.attribute.FileStoreAttributeView; 4 | import java.util.Date; 5 | 6 | public class S3FileStoreAttributeView 7 | implements FileStoreAttributeView 8 | { 9 | 10 | public static final String ATTRIBUTE_VIEW_NAME = "S3FileStoreAttributeView"; 11 | 12 | private Date creationDate; 13 | 14 | private String name; 15 | 16 | private String ownerId; 17 | 18 | private String ownerDisplayName; 19 | 20 | 21 | public enum AttrID 22 | { 23 | creationDate, 24 | name, 25 | ownerId, 26 | ownerDisplayName 27 | } 28 | 29 | public S3FileStoreAttributeView(Date creationDate, String name, String ownerId, String ownerDisplayName) 30 | { 31 | this.creationDate = creationDate; 32 | this.name = name; 33 | this.ownerId = ownerId; 34 | this.ownerDisplayName = ownerDisplayName; 35 | } 36 | 37 | @Override 38 | public String name() 39 | { 40 | return ATTRIBUTE_VIEW_NAME; 41 | } 42 | 43 | public Object getAttribute(String attribute) 44 | { 45 | return getAttribute(AttrID.valueOf(attribute)); 46 | } 47 | 48 | private Object getAttribute(AttrID attrID) 49 | { 50 | switch (attrID) 51 | { 52 | case creationDate: 53 | return creationDate; 54 | case ownerDisplayName: 55 | return ownerDisplayName; 56 | case ownerId: 57 | return ownerId; 58 | default: 59 | return name; 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/S3FileSystemConfigurationException.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs; 2 | 3 | public class S3FileSystemConfigurationException 4 | extends RuntimeException 5 | { 6 | 7 | private static final long serialVersionUID = 1L; 8 | 9 | public S3FileSystemConfigurationException(String message, Throwable cause) 10 | { 11 | super(message, cause); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/S3FilteredIterator.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.DirectoryStream.Filter; 5 | import java.nio.file.Path; 6 | import java.util.Iterator; 7 | import java.util.NoSuchElementException; 8 | 9 | public class S3FilteredIterator implements Iterator 10 | { 11 | 12 | private final S3Iterator s3iterator; 13 | 14 | private final Filter filter; 15 | 16 | private Path cursor; 17 | 18 | private boolean cursorIsCurrent; 19 | 20 | public S3FilteredIterator(S3Path s3Path, 21 | Filter filter) 22 | { 23 | this.filter = filter; 24 | this.s3iterator = new S3Iterator(s3Path); 25 | clearCursor(); 26 | } 27 | 28 | @Override 29 | public boolean hasNext() 30 | { 31 | if (!cursorIsCurrent) 32 | { 33 | findNextFiltered(); 34 | } 35 | return cursorIsCurrent; 36 | } 37 | 38 | @Override 39 | public Path next() 40 | { 41 | if (!hasNext()) 42 | { 43 | throw new NoSuchElementException(); 44 | } 45 | Path next = cursor; 46 | clearCursor(); 47 | return next; 48 | } 49 | 50 | private void findNextFiltered() 51 | { 52 | try 53 | { 54 | while (s3iterator.hasNext()) 55 | { 56 | S3Path next = s3iterator.next(); 57 | if (filter.accept(next)) 58 | { 59 | cursor = next; 60 | cursorIsCurrent = true; 61 | return; 62 | } 63 | } 64 | clearCursor(); 65 | } 66 | catch (IOException e) 67 | { 68 | throw new RuntimeException(e); 69 | } 70 | } 71 | 72 | private void clearCursor() 73 | { 74 | cursor = null; 75 | cursorIsCurrent = false; 76 | } 77 | 78 | @Override 79 | public void remove() 80 | { 81 | throw new UnsupportedOperationException(); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/S3ObjectId.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * An Immutable S3 object identifier. Used to uniquely identify an S3 object. 7 | * Can be instantiated via the convenient builder {@link Builder}. 8 | */ 9 | public class S3ObjectId 10 | implements Serializable 11 | { 12 | 13 | private final String bucket; 14 | private final String key; 15 | 16 | /** 17 | * @param builder must not be null. 18 | */ 19 | private S3ObjectId(final Builder builder) 20 | { 21 | this.bucket = builder.getBucket(); 22 | this.key = builder.getKey(); 23 | } 24 | 25 | public static Builder builder() 26 | { 27 | return new Builder(); 28 | } 29 | 30 | public Builder cloneBuilder() 31 | { 32 | return new Builder(this); 33 | } 34 | 35 | public String getBucket() 36 | { 37 | return bucket; 38 | } 39 | 40 | public String getKey() 41 | { 42 | return key; 43 | } 44 | 45 | @Override 46 | public boolean equals(Object o) 47 | { 48 | if (this == o) return true; 49 | if (o == null || getClass() != o.getClass()) return false; 50 | 51 | S3ObjectId that = (S3ObjectId) o; 52 | 53 | if (getBucket() != null ? !getBucket().equals(that.getBucket()) : that.getBucket() != null) return false; 54 | return getKey() != null ? getKey().equals(that.getKey()) : that.getKey() == null; 55 | } 56 | 57 | @Override 58 | public int hashCode() 59 | { 60 | int result = getBucket() != null ? getBucket().hashCode() : 0; 61 | result = 31 * result + (getKey() != null ? getKey().hashCode() : 0); 62 | return result; 63 | } 64 | 65 | @Override 66 | public String toString() 67 | { 68 | return "bucket: " + bucket + ", key: " + key; 69 | } 70 | 71 | public static final class Builder 72 | { 73 | 74 | private String bucket; 75 | private String key; 76 | 77 | public Builder() 78 | { 79 | super(); 80 | } 81 | 82 | /** 83 | * @param src S3 object id, which must not be null. 84 | */ 85 | public Builder(final S3ObjectId src) 86 | { 87 | super(); 88 | this.bucket(src.getBucket()); 89 | this.key(src.getKey()); 90 | } 91 | 92 | public String getBucket() 93 | { 94 | return bucket; 95 | } 96 | 97 | public String getKey() 98 | { 99 | return key; 100 | } 101 | 102 | public void setBucket(final String bucket) 103 | { 104 | this.bucket = bucket; 105 | } 106 | 107 | public void setKey(final String key) 108 | { 109 | this.key = key; 110 | } 111 | 112 | public Builder bucket(final String bucket) 113 | { 114 | this.bucket = bucket; 115 | return this; 116 | } 117 | 118 | public Builder key(final String key) 119 | { 120 | this.key = key; 121 | return this; 122 | } 123 | 124 | public S3ObjectId build() 125 | { 126 | return new S3ObjectId(this); 127 | } 128 | 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/attribute/S3BasicFileAttributeView.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.attribute; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3Path; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.attribute.BasicFileAttributeView; 9 | import java.nio.file.attribute.BasicFileAttributes; 10 | import java.nio.file.attribute.FileTime; 11 | 12 | public class S3BasicFileAttributeView 13 | implements BasicFileAttributeView 14 | { 15 | 16 | private static final Logger log = LoggerFactory.getLogger(S3BasicFileAttributeView.class); 17 | private S3Path s3Path; 18 | 19 | 20 | public S3BasicFileAttributeView(S3Path s3Path) 21 | { 22 | this.s3Path = s3Path; 23 | } 24 | 25 | @Override 26 | public String name() 27 | { 28 | return "basic"; 29 | } 30 | 31 | @Override 32 | public BasicFileAttributes readAttributes() 33 | throws IOException 34 | { 35 | return s3Path.getFileSystem().provider().readAttributes(s3Path, BasicFileAttributes.class); 36 | } 37 | 38 | @Override 39 | public void setTimes(FileTime lastModifiedTime, 40 | FileTime lastAccessTime, 41 | FileTime createTime) 42 | { 43 | // TODO: Implement 44 | log.debug(getClass() + "#setTimes() is not supported yet."); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/attribute/S3BasicFileAttributes.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.attribute; 2 | 3 | import java.nio.file.attribute.BasicFileAttributes; 4 | import java.nio.file.attribute.FileTime; 5 | 6 | import static java.lang.String.format; 7 | 8 | public class S3BasicFileAttributes 9 | implements BasicFileAttributes 10 | { 11 | 12 | private final FileTime lastModifiedTime; 13 | 14 | private final long size; 15 | 16 | private final boolean directory; 17 | 18 | private final boolean regularFile; 19 | 20 | private final String key; 21 | 22 | private long cacheCreated; 23 | 24 | 25 | public S3BasicFileAttributes(final String key, 26 | final FileTime lastModifiedTime, 27 | final long size, 28 | final boolean isDirectory, 29 | final boolean isRegularFile) 30 | { 31 | this.key = key; 32 | this.lastModifiedTime = lastModifiedTime; 33 | this.size = size; 34 | this.directory = isDirectory; 35 | this.regularFile = isRegularFile; 36 | 37 | this.cacheCreated = System.currentTimeMillis(); 38 | } 39 | 40 | @Override 41 | public FileTime lastModifiedTime() 42 | { 43 | return lastModifiedTime; 44 | } 45 | 46 | @Override 47 | public FileTime lastAccessTime() 48 | { 49 | return lastModifiedTime; 50 | } 51 | 52 | @Override 53 | public FileTime creationTime() 54 | { 55 | return lastModifiedTime; 56 | } 57 | 58 | @Override 59 | public boolean isRegularFile() 60 | { 61 | return regularFile; 62 | } 63 | 64 | @Override 65 | public boolean isDirectory() 66 | { 67 | return directory; 68 | } 69 | 70 | @Override 71 | public boolean isSymbolicLink() 72 | { 73 | return false; 74 | } 75 | 76 | @Override 77 | public boolean isOther() 78 | { 79 | return false; 80 | } 81 | 82 | @Override 83 | public long size() 84 | { 85 | return size; 86 | } 87 | 88 | @Override 89 | public Object fileKey() 90 | { 91 | return key; 92 | } 93 | 94 | @Override 95 | public String toString() 96 | { 97 | return format("[%s: lastModified=%s, size=%s, isDirectory=%s, isRegularFile=%s]", 98 | key, 99 | lastModifiedTime, 100 | size, 101 | directory, 102 | regularFile); 103 | } 104 | 105 | public long getCacheCreated() 106 | { 107 | return cacheCreated; 108 | } 109 | 110 | // for testing 111 | public void setCacheCreated(long time) 112 | { 113 | this.cacheCreated = time; 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/attribute/S3PosixFileAttributeView.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.attribute; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3Path; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.attribute.*; 9 | import java.util.Set; 10 | 11 | 12 | public class S3PosixFileAttributeView 13 | implements PosixFileAttributeView 14 | { 15 | 16 | private static final Logger log = LoggerFactory.getLogger(S3PosixFileAttributeView.class); 17 | private S3Path s3Path; 18 | 19 | public S3PosixFileAttributeView(S3Path s3Path) 20 | { 21 | this.s3Path = s3Path; 22 | } 23 | 24 | @Override 25 | public String name() 26 | { 27 | return "posix"; 28 | } 29 | 30 | @Override 31 | public PosixFileAttributes readAttributes() 32 | throws IOException 33 | { 34 | return read(); 35 | } 36 | 37 | @Override 38 | public UserPrincipal getOwner() 39 | throws IOException 40 | { 41 | return read().owner(); 42 | } 43 | 44 | @Override 45 | public void setOwner(UserPrincipal owner) 46 | { 47 | // TODO: Implement 48 | log.debug(getClass() + "#setOwner() is not supported yet."); 49 | } 50 | 51 | @Override 52 | public void setPermissions(Set perms) 53 | { 54 | // TODO: Implement 55 | log.debug(getClass() + "#setPermissions() is not supported yet."); 56 | } 57 | 58 | @Override 59 | public void setGroup(GroupPrincipal group) 60 | { 61 | // TODO: Implement 62 | log.debug(getClass() + "#setGroup() is not supported yet."); 63 | } 64 | 65 | @Override 66 | public void setTimes(FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime) 67 | { 68 | // TODO: Implement 69 | log.debug(getClass() + "#setTimes() is not supported yet."); 70 | } 71 | 72 | public PosixFileAttributes read() 73 | throws IOException 74 | { 75 | return s3Path.getFileSystem().provider().readAttributes(s3Path, PosixFileAttributes.class); 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/attribute/S3PosixFileAttributes.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.attribute; 2 | 3 | import java.nio.file.attribute.*; 4 | import java.util.Set; 5 | 6 | public class S3PosixFileAttributes 7 | extends S3BasicFileAttributes 8 | implements PosixFileAttributes 9 | { 10 | 11 | private UserPrincipal userPrincipal; 12 | 13 | private GroupPrincipal groupPrincipal; 14 | 15 | private Set posixFilePermissions; 16 | 17 | 18 | public S3PosixFileAttributes(String key, 19 | FileTime lastModifiedTime, 20 | long size, 21 | boolean isDirectory, 22 | boolean isRegularFile, 23 | UserPrincipal userPrincipal, 24 | GroupPrincipal groupPrincipal, 25 | Set posixFilePermissionSet) 26 | { 27 | 28 | super(key, lastModifiedTime, size, isDirectory, isRegularFile); 29 | 30 | this.userPrincipal = userPrincipal; 31 | this.groupPrincipal = groupPrincipal; 32 | this.posixFilePermissions = posixFilePermissionSet; 33 | } 34 | 35 | @Override 36 | public UserPrincipal owner() 37 | { 38 | return this.userPrincipal; 39 | } 40 | 41 | @Override 42 | public GroupPrincipal group() 43 | { 44 | return this.groupPrincipal; 45 | } 46 | 47 | @Override 48 | public Set permissions() 49 | { 50 | return this.posixFilePermissions; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/attribute/S3UserPrincipal.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.attribute; 2 | 3 | import java.nio.file.attribute.UserPrincipal; 4 | 5 | public class S3UserPrincipal 6 | implements UserPrincipal 7 | { 8 | 9 | private String name; 10 | 11 | 12 | public S3UserPrincipal(String name) 13 | { 14 | this.name = name; 15 | } 16 | 17 | @Override 18 | public String getName() 19 | { 20 | return name; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/cache/S3FileAttributesCachePolicy.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.cache; 2 | 3 | import com.github.benmanes.caffeine.cache.Expiry; 4 | import org.carlspring.cloud.storage.s3fs.attribute.S3BasicFileAttributes; 5 | 6 | import java.util.Optional; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | public class S3FileAttributesCachePolicy implements Expiry> 10 | { 11 | 12 | private int cacheTTL; 13 | 14 | public S3FileAttributesCachePolicy(int cacheTTL) 15 | { 16 | this.cacheTTL = cacheTTL; 17 | } 18 | 19 | public int getTTL() 20 | { 21 | return cacheTTL; 22 | } 23 | 24 | public void setTTL(int cacheTTL) 25 | { 26 | this.cacheTTL = cacheTTL; 27 | } 28 | 29 | @Override 30 | public long expireAfterCreate(String key, Optional value, long currentTime) 31 | { 32 | // Set initial TTL upon creation 33 | return TimeUnit.MILLISECONDS.toNanos(cacheTTL); 34 | } 35 | 36 | @Override 37 | public long expireAfterUpdate(String key, Optional value, long currentTime, long currentDuration) 38 | { 39 | // Reset TTL on update 40 | return TimeUnit.MILLISECONDS.toNanos(cacheTTL); 41 | } 42 | 43 | @Override 44 | public long expireAfterRead(String key, Optional value, long currentTime, long currentDuration) 45 | { 46 | // Use already assigned TTL. 47 | return currentDuration; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/util/AttributesUtils.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | import java.nio.file.attribute.BasicFileAttributes; 4 | import java.nio.file.attribute.PosixFileAttributes; 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * Utilities to help transforming BasicFileAttributes to Map 10 | */ 11 | public abstract class AttributesUtils 12 | { 13 | 14 | 15 | /** 16 | * Given a BasicFileAttributes not null then return a Map 17 | * with the keys as the fields of the BasicFileAttributes or PosixFileAttributes and the values 18 | * with the content of the fields 19 | * 20 | * @param attr BasicFileAttributes 21 | * @return Map String Object never null 22 | */ 23 | public static Map fileAttributeToMap(BasicFileAttributes attr) 24 | { 25 | Map result = new HashMap<>(); 26 | result.put("creationTime", attr.creationTime()); 27 | result.put("fileKey", attr.fileKey()); 28 | result.put("isDirectory", attr.isDirectory()); 29 | result.put("isOther", attr.isOther()); 30 | result.put("isRegularFile", attr.isRegularFile()); 31 | result.put("isSymbolicLink", attr.isSymbolicLink()); 32 | result.put("lastAccessTime", attr.lastAccessTime()); 33 | result.put("lastModifiedTime", attr.lastModifiedTime()); 34 | result.put("size", attr.size()); 35 | 36 | if (attr instanceof PosixFileAttributes) 37 | { 38 | PosixFileAttributes posixAttr = (PosixFileAttributes) attr; 39 | result.put("permissions", posixAttr.permissions()); 40 | result.put("owner", posixAttr.owner()); 41 | result.put("group", posixAttr.group()); 42 | } 43 | 44 | return result; 45 | } 46 | 47 | /** 48 | * transform the java.nio.file.attribute.BasicFileAttributes to Map filtering by the keys 49 | * given in the filters param 50 | * 51 | * @param attr BasicFileAttributes not null to tranform to map 52 | * @param filters String[] filters 53 | * @return Map String Object with the same keys as the filters 54 | */ 55 | public static Map fileAttributeToMap(BasicFileAttributes attr, String[] filters) 56 | { 57 | Map result = new HashMap<>(); 58 | 59 | for (String filter : filters) 60 | { 61 | filter = filter.replace("basic:", ""); 62 | filter = filter.replace("posix:", ""); 63 | switch (filter) 64 | { 65 | case "creationTime": 66 | result.put("creationTime", attr.creationTime()); 67 | break; 68 | case "fileKey": 69 | result.put("fileKey", attr.fileKey()); 70 | break; 71 | case "isDirectory": 72 | result.put("isDirectory", attr.isDirectory()); 73 | break; 74 | case "isOther": 75 | result.put("isOther", attr.isOther()); 76 | break; 77 | case "isRegularFile": 78 | result.put("isRegularFile", attr.isRegularFile()); 79 | break; 80 | case "isSymbolicLink": 81 | result.put("isSymbolicLink", attr.isSymbolicLink()); 82 | break; 83 | case "lastAccessTime": 84 | result.put("lastAccessTime", attr.lastAccessTime()); 85 | break; 86 | case "lastModifiedTime": 87 | result.put("lastModifiedTime", attr.lastModifiedTime()); 88 | break; 89 | case "size": 90 | result.put("size", attr.size()); 91 | break; 92 | case "permissions": 93 | result.put("permissions", ((PosixFileAttributes) attr).permissions()); 94 | break; 95 | case "group": 96 | result.put("group", ((PosixFileAttributes) attr).group()); 97 | break; 98 | case "owner": 99 | result.put("owner", ((PosixFileAttributes) attr).owner()); 100 | break; 101 | } 102 | } 103 | 104 | return result; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/util/Constants.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | public final class Constants 4 | { 5 | 6 | private Constants() 7 | { 8 | throw new IllegalStateException("Utility class"); 9 | } 10 | 11 | public static final String S3_HOSTNAME = "s3.amazonaws.com"; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/carlspring/cloud/storage/s3fs/util/IOUtils.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | 7 | /** 8 | * Utilities for streams 9 | */ 10 | public abstract class IOUtils 11 | { 12 | 13 | /** 14 | * get the stream content and return as a byte array 15 | * 16 | * @param is InputStream 17 | * @return byte array 18 | * @throws IOException if the stream is closed 19 | */ 20 | public static byte[] toByteArray(InputStream is) 21 | throws IOException 22 | { 23 | ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 24 | 25 | int nRead; 26 | byte[] data = new byte[16384]; 27 | 28 | while ((nRead = is.read(data, 0, data.length)) != -1) 29 | { 30 | buffer.write(data, 0, nRead); 31 | } 32 | 33 | buffer.flush(); 34 | 35 | return buffer.toByteArray(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/java.nio.file.spi.FileSystemProvider: -------------------------------------------------------------------------------- 1 | # if not present, FileSystems.newFileSystem throw NotProviderFoundException 2 | org.carlspring.cloud.storage.s3fs.S3FileSystemProvider 3 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/AttributesUtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs; 2 | 3 | import org.carlspring.cloud.storage.s3fs.attribute.S3BasicFileAttributes; 4 | import org.carlspring.cloud.storage.s3fs.util.AttributesUtils; 5 | 6 | import java.nio.file.attribute.BasicFileAttributes; 7 | import java.nio.file.attribute.FileTime; 8 | import java.util.Map; 9 | import java.util.concurrent.TimeUnit; 10 | 11 | import org.junit.jupiter.api.Test; 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | 14 | 15 | class AttributesUtilsTest 16 | { 17 | 18 | @Test 19 | void filterAll() 20 | { 21 | 22 | final String key = "key"; 23 | 24 | final FileTime fileTime = FileTime.from(10L, TimeUnit.DAYS); 25 | 26 | final long size = 10L; 27 | 28 | final boolean isDirectory = true; 29 | 30 | final String[] filters = new String[]{ "isDirectory", 31 | "isRegularFile", 32 | "isOther", 33 | "creationTime", 34 | "fileKey", 35 | "isSymbolicLink", 36 | "lastAccessTime", 37 | "lastModifiedTime", 38 | "size" }; 39 | 40 | BasicFileAttributes attrs = new S3BasicFileAttributes(key, fileTime, size, isDirectory, !isDirectory); 41 | 42 | Map map = AttributesUtils.fileAttributeToMap(attrs, filters); 43 | 44 | assertEquals(filters.length, map.size()); 45 | assertEquals(key, map.get("fileKey")); 46 | assertEquals(fileTime, map.get("creationTime")); 47 | assertEquals(isDirectory, map.get("isDirectory")); 48 | assertEquals(false, map.get("isRegularFile")); 49 | assertEquals(false, map.get("isOther")); 50 | assertEquals(false, map.get("isSymbolicLink")); 51 | assertEquals(fileTime, map.get("lastAccessTime")); 52 | assertEquals(fileTime, map.get("lastModifiedTime")); 53 | assertEquals(size, map.get("size")); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/S3FileAttributesTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs; 2 | 3 | import org.carlspring.cloud.storage.s3fs.attribute.S3BasicFileAttributes; 4 | 5 | import java.nio.file.attribute.FileTime; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | import org.junit.jupiter.api.Test; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | class S3FileAttributesTest 12 | { 13 | 14 | 15 | @Test 16 | void toStringPrintsBasicInfo() 17 | { 18 | final String key = "a key"; 19 | final FileTime fileTime = FileTime.from(100, TimeUnit.SECONDS); 20 | 21 | final int size = 10; 22 | 23 | final boolean isDirectory = true; 24 | final boolean isRegularFile = true; 25 | 26 | S3BasicFileAttributes fileAttributes = new S3BasicFileAttributes(key, 27 | fileTime, 28 | size, 29 | isDirectory, 30 | isRegularFile); 31 | 32 | String print = fileAttributes.toString(); 33 | 34 | assertTrue(print.contains(isRegularFile + "")); 35 | assertTrue(print.contains(isDirectory + "")); 36 | assertTrue(print.contains(size + "")); 37 | assertTrue(print.contains(fileTime.toString())); 38 | assertTrue(print.contains(key)); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/S3UnitTestBase.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs; 2 | 3 | import org.carlspring.cloud.storage.s3fs.util.S3ClientMock; 4 | import org.carlspring.cloud.storage.s3fs.util.S3MockFactory; 5 | 6 | import java.io.IOException; 7 | import java.nio.file.FileSystem; 8 | import java.util.Properties; 9 | 10 | import org.junit.jupiter.api.AfterEach; 11 | import org.junit.jupiter.api.BeforeEach; 12 | import static org.carlspring.cloud.storage.s3fs.S3Factory.ACCESS_KEY; 13 | import static org.carlspring.cloud.storage.s3fs.S3Factory.SECRET_KEY; 14 | import static org.carlspring.cloud.storage.s3fs.S3FileSystemProvider.S3_FACTORY_CLASS; 15 | import static org.mockito.ArgumentMatchers.any; 16 | import static org.mockito.ArgumentMatchers.anyString; 17 | import static org.mockito.Mockito.doReturn; 18 | import static org.mockito.Mockito.spy; 19 | 20 | public abstract class S3UnitTestBase extends BaseTest 21 | { 22 | 23 | protected S3FileSystemProvider s3fsProvider; 24 | 25 | protected FileSystem fileSystem; 26 | 27 | @BeforeEach 28 | public void setProperties() 29 | { 30 | System.clearProperty(S3FileSystemProvider.S3_FACTORY_CLASS); 31 | System.clearProperty(ACCESS_KEY); 32 | System.clearProperty(SECRET_KEY); 33 | 34 | System.setProperty(S3_FACTORY_CLASS, "org.carlspring.cloud.storage.s3fs.util.S3MockFactory"); 35 | } 36 | 37 | @BeforeEach 38 | public void setupS3fsProvider() 39 | { 40 | s3fsProvider = spy(new S3FileSystemProvider()); 41 | 42 | // stub the possibility to add system envs var 43 | doReturn(false).when(s3fsProvider).overloadPropertiesWithSystemEnv(any(Properties.class), anyString()); 44 | doReturn(new Properties()).when(s3fsProvider).loadAmazonProperties(); 45 | } 46 | 47 | @AfterEach 48 | public void closeMemory() 49 | { 50 | S3ClientMock client = S3MockFactory.getS3ClientMock(); 51 | client.close(); 52 | 53 | for (S3FileSystem s3FileSystem : S3FileSystemProvider.getFilesystems().values()) 54 | { 55 | try 56 | { 57 | s3FileSystem.close(); 58 | } 59 | catch (Exception e) 60 | { 61 | //ignore 62 | } 63 | } 64 | } 65 | 66 | @AfterEach 67 | public void tearDown() 68 | throws IOException 69 | { 70 | try 71 | { 72 | if (s3fsProvider != null) 73 | { 74 | s3fsProvider.close((S3FileSystem) fileSystem); 75 | } 76 | 77 | if (fileSystem != null) 78 | { 79 | fileSystem.close(); 80 | } 81 | } 82 | catch (Throwable ignored) 83 | { 84 | } 85 | } 86 | 87 | public S3FileSystemProvider getS3fsProvider() 88 | { 89 | return this.s3fsProvider; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/S3UtilsTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs; 2 | 3 | import org.carlspring.cloud.storage.s3fs.util.S3ClientMock; 4 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 5 | import org.carlspring.cloud.storage.s3fs.util.S3MockFactory; 6 | import org.carlspring.cloud.storage.s3fs.util.S3Utils; 7 | 8 | import java.io.IOException; 9 | import java.io.OutputStream; 10 | import java.nio.file.FileSystems; 11 | import java.nio.file.Files; 12 | import java.nio.file.NoSuchFileException; 13 | import java.nio.file.StandardOpenOption; 14 | 15 | import org.junit.jupiter.api.BeforeEach; 16 | import org.junit.jupiter.api.Test; 17 | import software.amazon.awssdk.awscore.exception.AwsServiceException; 18 | import software.amazon.awssdk.services.s3.model.GetObjectAclRequest; 19 | import software.amazon.awssdk.services.s3.model.Owner; 20 | import software.amazon.awssdk.services.s3.model.S3Exception; 21 | import software.amazon.awssdk.services.s3.model.S3Object; 22 | import static org.junit.jupiter.api.Assertions.assertEquals; 23 | import static org.junit.jupiter.api.Assertions.assertNotNull; 24 | import static org.junit.jupiter.api.Assertions.assertNull; 25 | import static org.junit.jupiter.api.Assertions.assertThrows; 26 | import static org.mockito.Mockito.doThrow; 27 | import static software.amazon.awssdk.http.HttpStatusCode.INTERNAL_SERVER_ERROR; 28 | 29 | class S3UtilsTest 30 | extends S3UnitTestBase 31 | { 32 | 33 | 34 | @BeforeEach 35 | public void setup() 36 | throws IOException 37 | { 38 | fileSystem = FileSystems.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 39 | 40 | final S3ClientMock client = S3MockFactory.getS3ClientMock(); 41 | 42 | client.bucket("bucket"); 43 | } 44 | 45 | @Test 46 | void getS3Object() 47 | throws IOException 48 | { 49 | final S3Path root = (S3Path) fileSystem.getPath("/bucket"); 50 | final S3Path file1 = (S3Path) root.resolve("file1"); 51 | 52 | final String contentString = "Some content String"; 53 | 54 | final OutputStream outputStream = Files.newOutputStream(file1, StandardOpenOption.CREATE, StandardOpenOption.WRITE); 55 | outputStream.write(contentString.getBytes()); 56 | outputStream.close(); 57 | 58 | final S3Object file1Object = getS3Object(file1); 59 | 60 | assertNull(file1Object.eTag()); 61 | assertEquals("file1", file1Object.key()); 62 | assertNotNull(file1Object.lastModified()); 63 | 64 | final Owner owner = file1Object.owner(); 65 | 66 | assertNotNull(owner); 67 | assertEquals("Mock", owner.displayName()); 68 | assertEquals("1", owner.id()); 69 | assertEquals(19, file1Object.size()); 70 | } 71 | 72 | @Test 73 | void getS3Object404() 74 | { 75 | final S3Path root = (S3Path) fileSystem.getPath("/bucket"); 76 | final S3Path file1 = (S3Path) root.resolve("file1"); 77 | 78 | // We're expecting an exception here to be thrown 79 | final Exception exception = assertThrows(NoSuchFileException.class, () -> getS3Object(file1)); 80 | 81 | assertNotNull(exception); 82 | } 83 | 84 | @Test 85 | void getS3Object500() 86 | throws IOException 87 | { 88 | final S3ClientMock client = S3MockFactory.getS3ClientMock(); 89 | final AwsServiceException toBeThrown = S3Exception.builder().message("We messed up").statusCode( 90 | INTERNAL_SERVER_ERROR).build(); 91 | 92 | GetObjectAclRequest request = GetObjectAclRequest.builder().bucket("bucket").key("file2").build(); 93 | doThrow(toBeThrown).when(client).getObjectAcl(request); 94 | 95 | final S3Path root = (S3Path) fileSystem.getPath("/bucket"); 96 | final S3Path file2 = (S3Path) root.resolve("file2"); 97 | 98 | Files.createFile(file2); 99 | 100 | // We're expecting an exception here to be thrown 101 | final Exception exception = assertThrows(S3Exception.class, () -> getS3Object(file2)); 102 | 103 | assertNotNull(exception); 104 | } 105 | 106 | public S3Object getS3Object(S3Path s3Path) 107 | throws NoSuchFileException 108 | { 109 | return new S3Utils().getS3Object(s3Path); 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/fileSystemProvider/CreateDirectoryTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.fileSystemProvider; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3FileSystem; 4 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 5 | import org.carlspring.cloud.storage.s3fs.util.S3ClientMock; 6 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 7 | import org.carlspring.cloud.storage.s3fs.util.S3MockFactory; 8 | 9 | import java.io.IOException; 10 | import java.nio.file.FileSystemNotFoundException; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | 14 | import org.junit.jupiter.api.BeforeEach; 15 | import org.junit.jupiter.api.Test; 16 | import static org.junit.jupiter.api.Assertions.assertEquals; 17 | import static org.junit.jupiter.api.Assertions.assertTrue; 18 | 19 | class CreateDirectoryTest 20 | extends S3UnitTestBase 21 | { 22 | 23 | 24 | @BeforeEach 25 | public void setup() 26 | throws IOException 27 | { 28 | s3fsProvider = getS3fsProvider(); 29 | fileSystem = s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 30 | } 31 | 32 | @Test 33 | void createDirectory() 34 | throws IOException 35 | { 36 | // fixtures 37 | S3ClientMock client = S3MockFactory.getS3ClientMock(); 38 | client.bucket("bucketA"); 39 | 40 | // act 41 | Path base = createNewS3FileSystem().getPath("/bucketA/dir"); 42 | Files.createDirectory(base); 43 | 44 | // assertions 45 | assertTrue(Files.exists(base)); 46 | assertTrue(Files.isDirectory(base)); 47 | assertTrue(Files.exists(base)); 48 | } 49 | 50 | 51 | @Test 52 | void createDirectoryInNewBucket() 53 | throws IOException 54 | { 55 | Path root = createNewS3FileSystem().getPath("/newer-bucket"); 56 | 57 | Path resolve = root.resolve("folder"); 58 | Path path = Files.createDirectories(resolve); 59 | 60 | // assertions 61 | assertEquals("/newer-bucket/folder", path.toAbsolutePath().toString()); 62 | assertTrue(Files.exists(root)); 63 | assertTrue(Files.isDirectory(root)); 64 | assertTrue(Files.exists(resolve)); 65 | assertTrue(Files.isDirectory(resolve)); 66 | assertTrue(Files.exists(path)); 67 | assertTrue(Files.isDirectory(path)); 68 | } 69 | 70 | @Test 71 | void createDirectoryWithSpace() 72 | throws IOException 73 | { 74 | // fixtures 75 | S3ClientMock client = S3MockFactory.getS3ClientMock(); 76 | client.bucket("bucketA"); 77 | 78 | // act 79 | Path base = createNewS3FileSystem().getPath("/bucketA/dir with space/another space"); 80 | Files.createDirectories(base); 81 | 82 | // assertions 83 | assertTrue(Files.exists(base)); 84 | assertTrue(Files.isDirectory(base)); 85 | 86 | // parent 87 | assertTrue(Files.exists(base.getParent())); 88 | assertTrue(Files.isDirectory(base.getParent())); 89 | } 90 | 91 | /** 92 | * create a new file system for s3 scheme with fake credentials 93 | * and global endpoint 94 | * 95 | * @return FileSystem 96 | */ 97 | private S3FileSystem createNewS3FileSystem() 98 | { 99 | try 100 | { 101 | return s3fsProvider.getFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST); 102 | } 103 | catch (FileSystemNotFoundException e) 104 | { 105 | return (S3FileSystem) s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 106 | } 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/fileSystemProvider/GetFileAttributeViewTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.fileSystemProvider; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3FileSystem; 4 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 5 | import org.carlspring.cloud.storage.s3fs.attribute.S3BasicFileAttributes; 6 | import org.carlspring.cloud.storage.s3fs.attribute.S3PosixFileAttributes; 7 | import org.carlspring.cloud.storage.s3fs.util.S3ClientMock; 8 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 9 | import org.carlspring.cloud.storage.s3fs.util.S3MockFactory; 10 | 11 | import java.io.IOException; 12 | import java.nio.file.FileSystemNotFoundException; 13 | import java.nio.file.FileSystems; 14 | import java.nio.file.Path; 15 | import java.nio.file.attribute.BasicFileAttributeView; 16 | import java.nio.file.attribute.PosixFileAttributeView; 17 | 18 | import org.junit.jupiter.api.BeforeEach; 19 | import org.junit.jupiter.api.Test; 20 | import static org.junit.jupiter.api.Assertions.assertEquals; 21 | import static org.junit.jupiter.api.Assertions.assertNotNull; 22 | import static org.junit.jupiter.api.Assertions.assertTrue; 23 | 24 | class GetFileAttributeViewTest 25 | extends S3UnitTestBase 26 | { 27 | 28 | 29 | @BeforeEach 30 | public void setup() 31 | throws IOException 32 | { 33 | s3fsProvider = getS3fsProvider(); 34 | fileSystem = s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 35 | } 36 | 37 | @Test 38 | void getBasicFileAttributeView() 39 | throws IOException 40 | { 41 | S3ClientMock client = S3MockFactory.getS3ClientMock(); 42 | client.bucket("bucketA").dir("dir").file("dir/file"); 43 | Path file = createNewS3FileSystem().getPath("/bucketA/dir/file"); 44 | 45 | BasicFileAttributeView view = s3fsProvider.getFileAttributeView(file, BasicFileAttributeView.class); 46 | 47 | assertEquals("basic", view.name()); 48 | assertNotNull(view.readAttributes()); 49 | assertTrue(view.readAttributes() instanceof S3BasicFileAttributes); 50 | } 51 | 52 | @Test 53 | void getPosixFileAttributeView() 54 | throws IOException 55 | { 56 | S3ClientMock client = S3MockFactory.getS3ClientMock(); 57 | client.bucket("bucketA").dir("dir").file("dir/file"); 58 | 59 | Path file = createNewS3FileSystem().getPath("/bucketA/dir/file"); 60 | 61 | BasicFileAttributeView view = s3fsProvider.getFileAttributeView(file, PosixFileAttributeView.class); 62 | 63 | assertEquals("posix", view.name()); 64 | assertNotNull(view.readAttributes()); 65 | assertTrue(view.readAttributes() instanceof S3PosixFileAttributes); 66 | } 67 | 68 | /** 69 | * create a new file system for s3 scheme with fake credentials 70 | * and global endpoint 71 | * 72 | * @return FileSystem 73 | * @throws IOException 74 | */ 75 | private S3FileSystem createNewS3FileSystem() 76 | throws IOException 77 | { 78 | try 79 | { 80 | return s3fsProvider.getFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST); 81 | } 82 | catch (FileSystemNotFoundException e) 83 | { 84 | return (S3FileSystem) FileSystems.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/fileSystemProvider/GetFileStoreTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.fileSystemProvider; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3FileStore; 4 | import org.carlspring.cloud.storage.s3fs.S3FileSystem; 5 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 6 | import org.carlspring.cloud.storage.s3fs.util.S3ClientMock; 7 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 8 | import org.carlspring.cloud.storage.s3fs.util.S3MockFactory; 9 | 10 | import java.io.IOException; 11 | import java.nio.file.FileStore; 12 | import java.nio.file.FileSystemNotFoundException; 13 | import java.nio.file.FileSystems; 14 | import java.nio.file.Path; 15 | 16 | import org.junit.jupiter.api.BeforeEach; 17 | import org.junit.jupiter.api.Test; 18 | 19 | import static org.junit.jupiter.api.Assertions.assertInstanceOf; 20 | 21 | class GetFileStoreTest 22 | extends S3UnitTestBase 23 | { 24 | 25 | 26 | @BeforeEach 27 | public void setup() 28 | throws IOException 29 | { 30 | s3fsProvider = getS3fsProvider(); 31 | fileSystem = s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 32 | } 33 | 34 | @Test 35 | void shouldReturnS3FileStore() 36 | throws IOException 37 | { 38 | // fixtures 39 | S3ClientMock client = S3MockFactory.getS3ClientMock(); 40 | client.bucket("bucketA").dir("dir").file("dir/file1"); 41 | 42 | // act 43 | Path file1 = createNewS3FileSystem().getPath("/bucketA/dir/file1"); 44 | 45 | FileStore fileStore = s3fsProvider.getFileStore(file1); 46 | assertInstanceOf(S3FileStore.class, fileStore); 47 | } 48 | 49 | /** 50 | * create a new file system for s3 scheme with fake credentials 51 | * and global endpoint 52 | * 53 | * @return FileSystem 54 | * @throws IOException 55 | */ 56 | private S3FileSystem createNewS3FileSystem() 57 | throws IOException 58 | { 59 | try 60 | { 61 | return s3fsProvider.getFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST); 62 | } 63 | catch (FileSystemNotFoundException e) 64 | { 65 | return (S3FileSystem) FileSystems.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/fileSystemProvider/GetPathTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.fileSystemProvider; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 4 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 5 | 6 | import java.io.IOException; 7 | import java.net.URI; 8 | import java.nio.file.FileSystem; 9 | import java.nio.file.FileSystems; 10 | import java.nio.file.Path; 11 | import java.nio.file.spi.FileSystemProvider; 12 | 13 | import com.google.common.collect.ImmutableMap; 14 | import org.junit.jupiter.api.BeforeEach; 15 | import org.junit.jupiter.api.Test; 16 | import static org.junit.jupiter.api.Assertions.assertEquals; 17 | import static org.junit.jupiter.api.Assertions.assertNotNull; 18 | import static org.junit.jupiter.api.Assertions.assertSame; 19 | import static org.junit.jupiter.api.Assertions.assertThrows; 20 | 21 | class GetPathTest 22 | extends S3UnitTestBase 23 | { 24 | 25 | 26 | @BeforeEach 27 | public void setup() 28 | throws IOException 29 | { 30 | s3fsProvider = getS3fsProvider(); 31 | fileSystem = s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 32 | } 33 | 34 | @Test 35 | void getPathWithEmptyEndpoint() 36 | throws IOException 37 | { 38 | FileSystem fs = FileSystems.newFileSystem(URI.create("s3:///"), ImmutableMap.of()); 39 | Path path = fs.provider().getPath(URI.create("s3:///bucket/path/to/file")); 40 | 41 | assertEquals(path, fs.getPath("/bucket/path/to/file")); 42 | assertSame(path.getFileSystem(), fs); 43 | } 44 | 45 | @Test 46 | void getPath() 47 | throws IOException 48 | { 49 | FileSystem fs = FileSystems.newFileSystem(URI.create("s3://endpoint1/"), null); 50 | Path path = fs.provider().getPath(URI.create("s3://endpoint1/bucket/path/to/file")); 51 | 52 | assertEquals(path, fs.getPath("/bucket/path/to/file")); 53 | assertSame(path.getFileSystem(), fs); 54 | } 55 | 56 | @Test 57 | void getAnotherPath() 58 | throws IOException 59 | { 60 | FileSystem fs = FileSystems.newFileSystem(URI.create("s3://endpoint1/"), ImmutableMap.of()); 61 | Path path = fs.provider().getPath(URI.create("s3://endpoint1/bucket/path/to/file")); 62 | 63 | assertEquals(path, fs.getPath("/bucket/path/to/file")); 64 | assertSame(path.getFileSystem(), fs); 65 | } 66 | 67 | @Test 68 | void getPathWithEndpointAndWithoutBucket() 69 | throws IOException 70 | { 71 | FileSystem fs = FileSystems.newFileSystem(URI.create("s3://endpoint1/"), null); 72 | URI uri = URI.create("s3://endpoint1//missed-bucket"); 73 | FileSystemProvider provider = fs.provider(); 74 | 75 | Exception exception = assertThrows(IllegalArgumentException.class, 76 | () -> provider.getPath(uri)); 77 | 78 | assertNotNull(exception); 79 | } 80 | 81 | @Test 82 | void getPathWithDefaultEndpointAndWithoutBucket() 83 | throws IOException 84 | { 85 | FileSystem fs = FileSystems.newFileSystem(URI.create("s3:///"), ImmutableMap.of()); 86 | FileSystemProvider provider = fs.provider(); 87 | URI uri = URI.create("s3:////missed-bucket"); 88 | 89 | Exception exception = assertThrows(IllegalArgumentException.class, 90 | () -> provider.getPath(uri)); 91 | 92 | assertNotNull(exception); 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/fileSystemProvider/IsHiddenTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.fileSystemProvider; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3FileSystem; 4 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 5 | import org.carlspring.cloud.storage.s3fs.util.S3ClientMock; 6 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 7 | import org.carlspring.cloud.storage.s3fs.util.S3MockFactory; 8 | 9 | import java.io.IOException; 10 | import java.nio.file.FileSystemNotFoundException; 11 | import java.nio.file.FileSystems; 12 | import java.nio.file.Path; 13 | 14 | import org.junit.jupiter.api.BeforeEach; 15 | import org.junit.jupiter.api.Test; 16 | import static org.junit.jupiter.api.Assertions.assertFalse; 17 | 18 | class IsHiddenTest 19 | extends S3UnitTestBase 20 | { 21 | 22 | 23 | @BeforeEach 24 | public void setup() 25 | throws IOException 26 | { 27 | s3fsProvider = getS3fsProvider(); 28 | fileSystem = s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 29 | } 30 | 31 | @Test 32 | void isHidden() 33 | throws IOException 34 | { 35 | // fixtures 36 | S3ClientMock client = S3MockFactory.getS3ClientMock(); 37 | client.bucket("bucketA").dir("dir").file("dir/file1"); 38 | 39 | // act 40 | Path file1 = createNewS3FileSystem().getPath("/bucketA/dir/file1"); 41 | 42 | // assert 43 | assertFalse(s3fsProvider.isHidden(file1)); 44 | } 45 | 46 | /** 47 | * create a new file system for s3 scheme with fake credentials 48 | * and global endpoint 49 | * 50 | * @return FileSystem 51 | * @throws IOException 52 | */ 53 | private S3FileSystem createNewS3FileSystem() 54 | throws IOException 55 | { 56 | try 57 | { 58 | return s3fsProvider.getFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST); 59 | } 60 | catch (FileSystemNotFoundException e) 61 | { 62 | return (S3FileSystem) FileSystems.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/fileSystemProvider/IsSameFileTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.fileSystemProvider; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3FileSystem; 4 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 5 | import org.carlspring.cloud.storage.s3fs.util.S3ClientMock; 6 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 7 | import org.carlspring.cloud.storage.s3fs.util.S3MockFactory; 8 | 9 | import java.io.IOException; 10 | import java.nio.file.FileSystem; 11 | import java.nio.file.FileSystemNotFoundException; 12 | import java.nio.file.FileSystems; 13 | import java.nio.file.Path; 14 | 15 | import org.junit.jupiter.api.BeforeEach; 16 | import org.junit.jupiter.api.Test; 17 | import static org.junit.jupiter.api.Assertions.assertFalse; 18 | import static org.junit.jupiter.api.Assertions.assertTrue; 19 | 20 | class IsSameFileTest 21 | extends S3UnitTestBase 22 | { 23 | 24 | 25 | @BeforeEach 26 | public void setup() 27 | throws IOException 28 | { 29 | s3fsProvider = getS3fsProvider(); 30 | fileSystem = s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 31 | } 32 | 33 | @Test 34 | void isSameFileTrue() 35 | throws IOException 36 | { 37 | // fixtures 38 | S3ClientMock client = S3MockFactory.getS3ClientMock(); 39 | client.bucket("bucketA").dir("dir").file("dir/file1"); 40 | 41 | // act 42 | FileSystem fs = createNewS3FileSystem(); 43 | Path file1 = fs.getPath("/bucketA/dir/file1"); 44 | Path fileCopy = fs.getPath("/bucketA/dir/file1"); 45 | 46 | // assert 47 | assertTrue(s3fsProvider.isSameFile(file1, fileCopy)); 48 | } 49 | 50 | @Test 51 | void isSameFileFalse() 52 | throws IOException 53 | { 54 | // fixtures 55 | S3ClientMock client = S3MockFactory.getS3ClientMock(); 56 | client.bucket("bucketA").dir("dir", "dir2").file("dir/file1", "dir2/file13"); 57 | 58 | // act 59 | FileSystem fs = createNewS3FileSystem(); 60 | Path file1 = fs.getPath("/bucketA/dir/file1"); 61 | Path fileCopy = fs.getPath("/bucketA/dir2/file2"); 62 | 63 | // assert 64 | assertFalse(s3fsProvider.isSameFile(file1, fileCopy)); 65 | } 66 | 67 | /** 68 | * create a new file system for s3 scheme with fake credentials 69 | * and global endpoint 70 | * 71 | * @return FileSystem 72 | * @throws IOException 73 | */ 74 | private S3FileSystem createNewS3FileSystem() 75 | throws IOException 76 | { 77 | try 78 | { 79 | return s3fsProvider.getFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST); 80 | } 81 | catch (FileSystemNotFoundException e) 82 | { 83 | return (S3FileSystem) FileSystems.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 84 | } 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/fileSystemProvider/SetAttributeTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.fileSystemProvider; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 4 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 5 | 6 | import java.io.IOException; 7 | 8 | import org.junit.jupiter.api.BeforeEach; 9 | import org.junit.jupiter.api.Test; 10 | import static org.junit.jupiter.api.Assertions.assertNotNull; 11 | import static org.junit.jupiter.api.Assertions.assertThrows; 12 | 13 | class SetAttributeTest 14 | extends S3UnitTestBase 15 | { 16 | 17 | 18 | @BeforeEach 19 | public void setup() 20 | throws IOException 21 | { 22 | s3fsProvider = getS3fsProvider(); 23 | fileSystem = s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 24 | } 25 | 26 | @Test 27 | void readAttributesObject() 28 | { 29 | Object value = new Object(); 30 | 31 | // We're expecting an exception here to be thrown 32 | Exception exception = assertThrows(UnsupportedOperationException.class, 33 | () -> s3fsProvider.setAttribute(null, "", value)); 34 | 35 | assertNotNull(exception); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/path/EqualsTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.path; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3FileSystem; 4 | import org.carlspring.cloud.storage.s3fs.S3Path; 5 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 6 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 7 | 8 | import java.io.IOException; 9 | import java.net.URI; 10 | import java.nio.file.FileSystem; 11 | import java.nio.file.FileSystems; 12 | import java.nio.file.Path; 13 | 14 | import com.github.marschall.memoryfilesystem.MemoryFileSystemBuilder; 15 | import org.junit.jupiter.api.AfterEach; 16 | import org.junit.jupiter.api.BeforeEach; 17 | import org.junit.jupiter.api.Test; 18 | import static org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant.S3_GLOBAL_URI_TEST; 19 | import static org.junit.jupiter.api.Assertions.assertEquals; 20 | import static org.junit.jupiter.api.Assertions.assertNotEquals; 21 | import static org.junit.jupiter.api.Assertions.assertNotNull; 22 | 23 | class EqualsTest 24 | extends S3UnitTestBase 25 | { 26 | 27 | 28 | @BeforeEach 29 | public void setup() 30 | throws IOException 31 | { 32 | s3fsProvider = getS3fsProvider(); 33 | fileSystem = s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 34 | } 35 | 36 | @AfterEach 37 | public void tearDown() 38 | throws IOException 39 | { 40 | s3fsProvider.close((S3FileSystem) fileSystem); 41 | fileSystem.close(); 42 | } 43 | 44 | private S3Path getPath(String path) 45 | { 46 | return s3fsProvider.getFileSystem(S3_GLOBAL_URI_TEST).getPath(path); 47 | } 48 | 49 | @Test 50 | void equals() 51 | { 52 | Path path = getPath("/bucketA/dir/file"); 53 | Path path2 = getPath("/bucketA/dir/file"); 54 | 55 | assertEquals(path, path2); 56 | } 57 | 58 | @Test 59 | void equalsDir() 60 | { 61 | Path path = getPath("/bucketA/dir/"); 62 | Path path2 = getPath("/bucketA/dir/"); 63 | 64 | assertEquals(path, path2); 65 | } 66 | 67 | @Test 68 | void equalsBucket() 69 | { 70 | Path path = getPath("/bucketA/"); 71 | Path path2 = getPath("/bucketA/"); 72 | 73 | assertEquals(path, path2); 74 | } 75 | 76 | @Test 77 | void equalsBucketWithoutEndSlash() 78 | { 79 | Path path = getPath("/bucketA/"); 80 | Path path2 = getPath("/bucketA"); 81 | 82 | assertNotEquals(path, path2); 83 | } 84 | 85 | @Test 86 | void notEquals() 87 | { 88 | Path path = getPath("/bucketA/dir/file"); 89 | Path path2 = getPath("/bucketA/dir/file2"); 90 | 91 | assertNotEquals(path, path2); 92 | } 93 | 94 | @Test 95 | void notEqualsDirFile() 96 | { 97 | Path path = getPath("/bucketA/dir/asd/"); 98 | Path path2 = getPath("/bucketA/dir/asd"); 99 | 100 | assertNotEquals(path, path2); 101 | } 102 | 103 | @Test 104 | void notEqualsNull() 105 | { 106 | Path path = getPath("/bucketA/dir/file"); 107 | 108 | assertNotNull(path); 109 | } 110 | 111 | @Test 112 | void notEqualsDifferentProvider() 113 | throws IOException 114 | { 115 | Path path = getPath("/c/dir/file"); 116 | 117 | try (FileSystem linux = MemoryFileSystemBuilder.newLinux().build("linux")) 118 | { 119 | Path fileLinux = linux.getPath("/dir/file"); 120 | 121 | assertNotEquals(path, fileLinux); 122 | } 123 | 124 | try (FileSystem window = MemoryFileSystemBuilder.newWindows().build("window")) 125 | { 126 | Path file = window.getPath("c:/dir/file"); 127 | 128 | assertNotEquals(path, file); 129 | } 130 | 131 | Path pathS3EmptyEndpoint = FileSystems.newFileSystem(URI.create("s3:///"), null).getPath("/bucketA/dir/"); 132 | Path pathS3TestEndpoint = getPath("/bucketA/dir/"); 133 | 134 | assertNotEquals(pathS3EmptyEndpoint, pathS3TestEndpoint); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/path/GetFileNameTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.path; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3FileSystem; 4 | import org.carlspring.cloud.storage.s3fs.S3Path; 5 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.Path; 9 | 10 | import org.junit.jupiter.api.AfterEach; 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | import static org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant.S3_GLOBAL_URI_TEST; 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | import static org.junit.jupiter.api.Assertions.assertNull; 16 | 17 | class GetFileNameTest 18 | extends S3UnitTestBase 19 | { 20 | 21 | 22 | @BeforeEach 23 | public void setup() 24 | throws IOException 25 | { 26 | s3fsProvider = getS3fsProvider(); 27 | fileSystem = s3fsProvider.newFileSystem(S3_GLOBAL_URI_TEST, null); 28 | } 29 | 30 | @AfterEach 31 | public void tearDown() 32 | { 33 | s3fsProvider.close((S3FileSystem) fileSystem); 34 | } 35 | 36 | private S3Path getPath(String path) 37 | { 38 | return s3fsProvider.getFileSystem(S3_GLOBAL_URI_TEST).getPath(path); 39 | } 40 | 41 | @Test 42 | void getFileName() 43 | { 44 | Path path = getPath("/bucketA/file"); 45 | Path name = path.getFileName(); 46 | 47 | assertEquals(getPath("file"), name); 48 | } 49 | 50 | @Test 51 | void getFileNameInDir() 52 | { 53 | Path path = getPath("/bucketA/dir/another-file"); 54 | Path fileName = path.getFileName(); 55 | Path dirName = path.getParent().getFileName(); 56 | 57 | assertEquals(getPath("another-file"), fileName); 58 | assertEquals(getPath("dir"), dirName); 59 | } 60 | 61 | @Test 62 | void getBucket() 63 | { 64 | Path path = getPath("/bucket"); 65 | Path name = path.getFileName(); 66 | 67 | assertNull(name); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/path/GetKeyTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.path; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3Path; 4 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 5 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 6 | 7 | import java.io.IOException; 8 | 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.Test; 11 | import static org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant.S3_GLOBAL_URI_TEST; 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | 14 | class GetKeyTest 15 | extends S3UnitTestBase 16 | { 17 | 18 | 19 | @BeforeEach 20 | public void setup() 21 | throws IOException 22 | { 23 | s3fsProvider = getS3fsProvider(); 24 | fileSystem = s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 25 | } 26 | 27 | private S3Path getPath(String path) 28 | { 29 | return s3fsProvider.getFileSystem(S3_GLOBAL_URI_TEST).getPath(path); 30 | } 31 | 32 | @Test 33 | void getKeyBucket() 34 | { 35 | S3Path path = getPath("/bucket"); 36 | 37 | assertEquals("", path.getKey()); 38 | } 39 | 40 | @Test 41 | void getKeyFile() 42 | { 43 | S3Path path = getPath("/bucket/file"); 44 | 45 | assertEquals("file", path.getKey()); 46 | } 47 | 48 | @Test 49 | void getKeyFolder() 50 | { 51 | S3Path path = getPath("/bucket/folder/"); 52 | 53 | assertEquals("folder/", path.getKey()); 54 | } 55 | 56 | @Test 57 | void getKeyParent() 58 | { 59 | S3Path path = (S3Path) getPath("/bucket/folder/file").getParent(); 60 | 61 | assertEquals("folder/", path.getKey()); 62 | } 63 | 64 | @Test 65 | void getKeyRoot() 66 | { 67 | S3Path path = (S3Path) getPath("/bucket/folder/file").getRoot(); 68 | 69 | assertEquals("", path.getKey()); 70 | } 71 | 72 | @Test 73 | void getKeyEncodingPath() 74 | { 75 | S3Path path = getPath("/bucket/path with spaces/to/β ϐ"); 76 | 77 | assertEquals("path with spaces/to/β ϐ", path.getKey()); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/path/GetNameTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.path; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3FileSystem; 4 | import org.carlspring.cloud.storage.s3fs.S3Path; 5 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 6 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 7 | 8 | import java.io.IOException; 9 | 10 | import org.junit.jupiter.api.AfterEach; 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | import static org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant.S3_GLOBAL_URI_TEST; 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | import static org.junit.jupiter.api.Assertions.assertNotNull; 16 | import static org.junit.jupiter.api.Assertions.assertThrows; 17 | 18 | class GetNameTest 19 | extends S3UnitTestBase 20 | { 21 | 22 | 23 | @BeforeEach 24 | public void setup() 25 | throws IOException 26 | { 27 | s3fsProvider = getS3fsProvider(); 28 | fileSystem = s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 29 | } 30 | 31 | @AfterEach 32 | public void tearDown() 33 | throws IOException 34 | { 35 | s3fsProvider.close((S3FileSystem) fileSystem); 36 | fileSystem.close(); 37 | } 38 | 39 | private S3Path getPath(String path) 40 | { 41 | return s3fsProvider.getFileSystem(S3_GLOBAL_URI_TEST).getPath(path); 42 | } 43 | 44 | @Test 45 | void getNameBucket() 46 | { 47 | S3Path path = getPath("/bucket"); 48 | 49 | // We're expecting an exception here to be thrown 50 | Exception exception = assertThrows(IllegalArgumentException.class, () -> path.getName(0)); 51 | 52 | assertNotNull(exception); 53 | } 54 | 55 | @Test 56 | void getName0() 57 | { 58 | S3Path path = getPath("/bucket/file"); 59 | 60 | assertEquals(getPath("/bucket/file"), path.getName(0)); 61 | } 62 | 63 | @Test 64 | void getNames() 65 | { 66 | S3Path path = getPath("/bucket/path/to/file"); 67 | 68 | assertEquals(path.getName(0), getPath("/bucket/path/")); 69 | assertEquals(path.getName(1), getPath("to/")); 70 | assertEquals(path.getName(2), getPath("file")); 71 | } 72 | 73 | @Test 74 | void getNameOutOfIndex() 75 | { 76 | S3Path path = getPath("/bucket/path/to/file"); 77 | 78 | // We're expecting an exception here to be thrown 79 | Exception exception = assertThrows(IllegalArgumentException.class, () -> path.getName(3)); 80 | 81 | assertNotNull(exception); 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/path/GetRootTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.path; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3FileSystem; 4 | import org.carlspring.cloud.storage.s3fs.S3Path; 5 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 6 | 7 | import java.io.IOException; 8 | 9 | import org.junit.jupiter.api.AfterEach; 10 | import org.junit.jupiter.api.BeforeEach; 11 | import org.junit.jupiter.api.Test; 12 | import static org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant.S3_GLOBAL_URI_TEST; 13 | import static org.junit.jupiter.api.Assertions.assertEquals; 14 | import static org.junit.jupiter.api.Assertions.assertNull; 15 | 16 | class GetRootTest 17 | extends S3UnitTestBase 18 | { 19 | 20 | 21 | @BeforeEach 22 | public void setup() 23 | throws IOException 24 | { 25 | s3fsProvider = getS3fsProvider(); 26 | fileSystem = s3fsProvider.newFileSystem(S3_GLOBAL_URI_TEST, null); 27 | } 28 | 29 | @AfterEach 30 | public void tearDown() 31 | throws IOException 32 | { 33 | s3fsProvider.close((S3FileSystem) fileSystem); 34 | fileSystem.close(); 35 | } 36 | 37 | @Test 38 | void getRootReturnBucket() 39 | { 40 | assertEquals(getPath("/bucketA/"), getPath("/bucketA/dir/file").getRoot()); 41 | } 42 | 43 | @Test 44 | void getRootRelativeReturnNull() 45 | { 46 | assertNull(getPath("dir/file").getRoot()); 47 | } 48 | 49 | private S3Path getPath(String path) 50 | { 51 | return s3fsProvider.getFileSystem(S3_GLOBAL_URI_TEST).getPath(path); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/path/IteratorTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.path; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3Path; 4 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 5 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.Path; 9 | import java.util.Iterator; 10 | 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | import static org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant.S3_GLOBAL_URI_TEST; 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | import static org.junit.jupiter.api.Assertions.assertFalse; 16 | 17 | class IteratorTest 18 | extends S3UnitTestBase 19 | { 20 | 21 | 22 | @BeforeEach 23 | public void setup() 24 | throws IOException 25 | { 26 | s3fsProvider = getS3fsProvider(); 27 | fileSystem = s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 28 | } 29 | 30 | private S3Path getPath(String path) 31 | { 32 | return s3fsProvider.getFileSystem(S3_GLOBAL_URI_TEST).getPath(path); 33 | } 34 | 35 | @Test 36 | void iterator() 37 | { 38 | Iterator iterator = getPath("/bucket/path/to/file").iterator(); 39 | 40 | assertEquals(getPath("/bucket/"), iterator.next()); 41 | assertEquals(getPath("path/"), iterator.next()); 42 | assertEquals(getPath("to/"), iterator.next()); 43 | assertEquals(getPath("file"), iterator.next()); 44 | } 45 | 46 | @Test 47 | void iteratorEmtpy() 48 | { 49 | Iterator iterator = getPath("").iterator(); 50 | assertFalse(iterator.hasNext()); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/path/ResolveTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.path; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3FileSystem; 4 | import org.carlspring.cloud.storage.s3fs.S3Path; 5 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 6 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 7 | 8 | import java.io.IOException; 9 | 10 | import org.junit.jupiter.api.AfterEach; 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | import static org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant.S3_GLOBAL_URI_TEST; 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | 16 | class ResolveTest 17 | extends S3UnitTestBase 18 | { 19 | 20 | 21 | @BeforeEach 22 | public void setup() 23 | throws IOException 24 | { 25 | s3fsProvider = getS3fsProvider(); 26 | fileSystem = s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 27 | } 28 | 29 | @AfterEach 30 | public void tearDown() 31 | throws IOException 32 | { 33 | s3fsProvider.close((S3FileSystem) fileSystem); 34 | fileSystem.close(); 35 | } 36 | 37 | private S3Path getPath(String path) 38 | { 39 | return s3fsProvider.getFileSystem(S3_GLOBAL_URI_TEST).getPath(path); 40 | } 41 | 42 | @Test 43 | void resolve() 44 | { 45 | assertEquals(getPath("/bucket/path/to/dir/child/xyz"), 46 | getPath("/bucket/path/to/dir/").resolve(getPath("child/xyz"))); 47 | assertEquals(getPath("/bucket/path/to/dir/child/xyz"), getPath("/bucket/path/to/dir/").resolve("child/xyz")); 48 | 49 | assertEquals(getPath("/bucket/path/to/dir/child/xyz"), 50 | getPath("/bucket/path/to/dir").resolve(getPath("child/xyz"))); 51 | assertEquals(getPath("/bucket/path/to/dir/child/xyz"), getPath("/bucket/path/to/dir").resolve("child/xyz")); 52 | 53 | assertEquals(getPath("/bucket/path/to/file"), getPath("/bucket/path/to/file").resolve(getPath(""))); 54 | assertEquals(getPath("/bucket/path/to/file"), getPath("/bucket/path/to/file").resolve("")); 55 | 56 | assertEquals(getPath("path/to/file/child/xyz"), getPath("path/to/file").resolve(getPath("child/xyz"))); 57 | assertEquals(getPath("path/to/file/child/xyz"), getPath("path/to/file").resolve("child/xyz")); 58 | 59 | assertEquals(getPath("path/to/file"), getPath("path/to/file").resolve(getPath(""))); 60 | assertEquals(getPath("path/to/file"), getPath("path/to/file").resolve("")); 61 | 62 | assertEquals(getPath("/bucket2/other/child"), 63 | getPath("/bucket/path/to/file").resolve(getPath("/bucket2/other/child"))); 64 | assertEquals(getPath("/bucket2/other/child"), getPath("/bucket/path/to/file").resolve("/bucket2/other/child")); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/path/SubpathTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.path; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3FileSystem; 4 | import org.carlspring.cloud.storage.s3fs.S3Path; 5 | import org.carlspring.cloud.storage.s3fs.S3UnitTestBase; 6 | import org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant; 7 | 8 | import java.io.IOException; 9 | 10 | import org.junit.jupiter.api.AfterEach; 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | import static org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant.S3_GLOBAL_URI_TEST; 14 | import static org.junit.jupiter.api.Assertions.assertEquals; 15 | import static org.junit.jupiter.api.Assertions.assertNotNull; 16 | import static org.junit.jupiter.api.Assertions.assertThrows; 17 | 18 | class SubpathTest 19 | extends S3UnitTestBase 20 | { 21 | 22 | 23 | @BeforeEach 24 | public void setup() 25 | throws IOException 26 | { 27 | s3fsProvider = getS3fsProvider(); 28 | fileSystem = s3fsProvider.newFileSystem(S3EndpointConstant.S3_GLOBAL_URI_TEST, null); 29 | } 30 | 31 | @AfterEach 32 | public void tearDown() 33 | throws IOException 34 | { 35 | s3fsProvider.close((S3FileSystem) fileSystem); 36 | fileSystem.close(); 37 | } 38 | 39 | private S3Path getPath(String path) 40 | { 41 | return s3fsProvider.getFileSystem(S3_GLOBAL_URI_TEST).getPath(path); 42 | } 43 | 44 | @Test 45 | void subPath0() 46 | { 47 | assertEquals(getPath("/bucket/path/"), getPath("/bucket/path/to/file").subpath(0, 1)); 48 | } 49 | 50 | @Test 51 | void subPath() 52 | { 53 | assertEquals(getPath("/bucket/path/to/"), getPath("/bucket/path/to/file").subpath(0, 2)); 54 | assertEquals(getPath("/bucket/path/to/file"), getPath("/bucket/path/to/file").subpath(0, 3)); 55 | assertEquals(getPath("to/"), getPath("/bucket/path/to/file").subpath(1, 2)); 56 | assertEquals(getPath("to/file"), getPath("/bucket/path/to/file").subpath(1, 3)); 57 | assertEquals(getPath("file"), getPath("/bucket/path/to/file").subpath(2, 3)); 58 | } 59 | 60 | @Test 61 | void subPathOutOfRange() 62 | { 63 | S3Path path = getPath("/bucket/path/to/file"); 64 | 65 | // We're expecting an exception here to be thrown 66 | Exception exception = assertThrows(IllegalArgumentException.class, 67 | () -> path.subpath(0, 4)); 68 | 69 | assertNotNull(exception); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/spike/FileStoreTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.spike; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.FileStore; 5 | import java.nio.file.FileSystem; 6 | import java.nio.file.FileSystems; 7 | import java.nio.file.Path; 8 | 9 | import com.github.marschall.memoryfilesystem.MemoryFileSystemBuilder; 10 | import org.junit.jupiter.api.AfterEach; 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | 14 | class FileStoreTest 15 | { 16 | 17 | FileSystem fs; 18 | 19 | FileSystem fsWindows; 20 | 21 | FileSystem fsMac; 22 | 23 | 24 | @BeforeEach 25 | public void setup() 26 | throws IOException 27 | { 28 | fs = MemoryFileSystemBuilder.newLinux().build("linux"); 29 | fsWindows = MemoryFileSystemBuilder.newWindows().build("windows"); 30 | fsMac = MemoryFileSystemBuilder.newMacOs().build("mac"); 31 | } 32 | 33 | @AfterEach 34 | public void close() 35 | throws IOException 36 | { 37 | fs.close(); 38 | fsWindows.close(); 39 | fsMac.close(); 40 | } 41 | 42 | @Test 43 | void getFileStore() 44 | { 45 | System.out.println("Default:"); 46 | System.out.println("-------"); 47 | 48 | for (FileStore fileStore : FileSystems.getDefault().getFileStores()) 49 | { 50 | System.out.println("- " + fileStore.name()); 51 | } 52 | 53 | System.out.println("\nLinux:"); 54 | System.out.println("-----"); 55 | 56 | for (FileStore fileStore : fs.getFileStores()) 57 | { 58 | System.out.println("- " + fileStore.name()); 59 | } 60 | 61 | System.out.println("\nWindows:"); 62 | System.out.println("-------"); 63 | 64 | for (FileStore fileStore : fsWindows.getFileStores()) 65 | { 66 | System.out.println("- " + fileStore.name()); 67 | } 68 | 69 | System.out.println("\nMac:"); 70 | System.out.println("---"); 71 | 72 | for (FileStore fileStore : fsMac.getFileStores()) 73 | { 74 | System.out.println("- " + fileStore.name()); 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/spike/ProviderSpecTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.spike; 2 | 3 | import java.io.IOException; 4 | import java.nio.ByteBuffer; 5 | import java.nio.channels.SeekableByteChannel; 6 | import java.nio.file.FileSystem; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.nio.file.StandardOpenOption; 10 | import java.util.EnumSet; 11 | 12 | import com.github.marschall.memoryfilesystem.MemoryFileSystemBuilder; 13 | import org.junit.jupiter.api.AfterEach; 14 | import org.junit.jupiter.api.BeforeEach; 15 | import org.junit.jupiter.api.Test; 16 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 17 | import static org.junit.jupiter.api.Assertions.assertTrue; 18 | 19 | class ProviderSpecTest 20 | { 21 | 22 | FileSystem fs; 23 | 24 | 25 | @BeforeEach 26 | public void setup() 27 | throws IOException 28 | { 29 | fs = MemoryFileSystemBuilder.newLinux().build("linux"); 30 | } 31 | 32 | @AfterEach 33 | public void close() 34 | throws IOException 35 | { 36 | fs.close(); 37 | } 38 | 39 | @Test 40 | public void readNothing() 41 | throws IOException 42 | { 43 | //Path base = Files.createDirectories(fs.getPath("/dir")); 44 | Path base = Files.createTempDirectory("asdadadasd"); 45 | try (SeekableByteChannel seekable = Files.newByteChannel(Files.createFile(base.resolve("file1.html")), 46 | EnumSet.of(StandardOpenOption.DELETE_ON_CLOSE))) 47 | { 48 | // do nothing 49 | } 50 | 51 | assertTrue(Files.notExists(base.resolve("file1.html"))); 52 | } 53 | 54 | // FIXME @Test 55 | public void seekable() 56 | throws IOException 57 | { 58 | Path base = Files.createDirectories(fs.getPath("/dir")); 59 | 60 | // in windows throw exception 61 | try (SeekableByteChannel seekable = Files.newByteChannel(base.resolve("file1.html"), 62 | EnumSet.of(StandardOpenOption.CREATE, 63 | StandardOpenOption.WRITE, 64 | StandardOpenOption.READ))) 65 | { 66 | ByteBuffer buffer = ByteBuffer.wrap("content".getBytes()); 67 | 68 | seekable.position(7); 69 | seekable.write(buffer); 70 | 71 | ByteBuffer bufferRead = ByteBuffer.allocate(7); 72 | bufferRead.clear(); 73 | seekable.read(bufferRead); 74 | 75 | assertArrayEquals(bufferRead.array(), buffer.array()); 76 | } 77 | } 78 | 79 | @Test 80 | public void seekableRead() 81 | throws IOException 82 | { 83 | Path path = Files.write(Files.createTempFile("asdas", "asdsadad"), 84 | "content uyuhu".getBytes(), 85 | StandardOpenOption.APPEND); 86 | 87 | try (SeekableByteChannel channel = Files.newByteChannel(path)) 88 | { 89 | //channel = Paths.get("Path to file").newByteChannel(StandardOpenOption.READ); 90 | ByteBuffer buffer = ByteBuffer.allocate(4096); 91 | 92 | while (channel.read(buffer) > 0) 93 | { 94 | buffer.rewind(); 95 | buffer.flip(); 96 | } 97 | } 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/spike/SpecTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.spike; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3FileSystemProvider; 4 | 5 | import java.io.IOException; 6 | import java.nio.file.FileSystems; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.nio.file.spi.FileSystemProvider; 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | import org.junit.jupiter.api.Test; 14 | 15 | import static org.junit.jupiter.api.Assertions.assertNull; 16 | import static org.junit.jupiter.api.Assertions.assertTrue; 17 | 18 | class SpecTest 19 | { 20 | 21 | 22 | @Test 23 | void parentOfRelativeSinglePathIsNull() 24 | { 25 | Path path = FileSystems.getDefault().getPath("relative"); 26 | 27 | assertNull(path.getParent()); 28 | } 29 | 30 | @Test 31 | void readAttributes() 32 | throws IOException 33 | { 34 | Path path = Files.createTempFile("asdas", "sdasda"); 35 | 36 | Map attrs = Files.readAttributes(path, "*"); 37 | Map attrs2 = Files.readAttributes(path, "lastModifiedTime"); 38 | } 39 | 40 | @Test 41 | void installedFileSystemsLoadFromMetaInf() 42 | { 43 | List providers = FileSystemProvider.installedProviders(); 44 | boolean installed = false; 45 | 46 | for (FileSystemProvider prov : providers) 47 | { 48 | if (prov instanceof S3FileSystemProvider) 49 | { 50 | installed = true; 51 | 52 | return; 53 | } 54 | } 55 | 56 | assertTrue(installed); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/spike/URISpikeTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.spike; 2 | 3 | import java.net.URI; 4 | import java.nio.file.FileSystems; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | 8 | import com.google.common.base.Splitter; 9 | import org.junit.jupiter.api.Test; 10 | 11 | class URISpikeTest 12 | { 13 | 14 | private List s3Path_cases = Arrays.asList("/bucket//folder//folder2/file", 15 | "///bucket//folder//folder2/file", 16 | "//bucket//folder//folder2/file", 17 | "////bucket//folder//folder2/file", 18 | "folder//folder2/file", 19 | "folder//..//./folder2/file/.", 20 | "folder/folder2/folder3/"); 21 | 22 | @Test 23 | public void test() 24 | { 25 | for (String s3case : s3Path_cases) 26 | { 27 | System.out.println(URI.create(s3case).toString() + " ==> " + URI.create(s3case).normalize().toString()); 28 | 29 | System.out.println("Host:" + URI.create(s3case).getHost()); 30 | System.out.println("Raw query:" + URI.create(s3case).getRawQuery()); 31 | System.out.println("Authority:" + URI.create(s3case).getAuthority()); 32 | System.out.println("Query:" + URI.create(s3case).getQuery()); 33 | System.out.println("Raw Path:" + URI.create(s3case).getRawPath()); 34 | System.out.println("Path:" + URI.create(s3case).getPath()); 35 | 36 | // get bucket 37 | 38 | System.out.println("Bucket:" + Splitter.on("/") 39 | .omitEmptyStrings() 40 | .splitToList(URI.create(s3case).normalize().toString()) 41 | .get(0)); 42 | System.out.println("Parent:" + URI.create(s3case).resolve("..").toString()); 43 | } 44 | } 45 | 46 | @Test 47 | public void uriWithSpaces() 48 | { 49 | URI uri = FileSystems.getDefault().getPath("/file with spaces").toUri(); 50 | System.out.println(uri); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/util/BrokenS3Factory.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3Factory; 4 | 5 | import software.amazon.awssdk.services.s3.S3Client; 6 | import software.amazon.awssdk.services.s3.S3ClientBuilder; 7 | 8 | public class BrokenS3Factory 9 | extends S3Factory 10 | { 11 | 12 | 13 | /** 14 | * @param name to make the constructor non default 15 | */ 16 | public BrokenS3Factory(final String name) 17 | { 18 | // only non default constructor 19 | } 20 | 21 | @Override 22 | protected S3Client createS3Client(final S3ClientBuilder builder) 23 | { 24 | return null; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/util/CopyDirVisitor.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.FileVisitResult; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import java.nio.file.SimpleFileVisitor; 8 | import java.nio.file.StandardCopyOption; 9 | import java.nio.file.attribute.BasicFileAttributes; 10 | 11 | public class CopyDirVisitor 12 | extends SimpleFileVisitor 13 | { 14 | 15 | private final Path fromPath; 16 | 17 | private final Path toPath; 18 | 19 | private final StandardCopyOption copyOption; 20 | 21 | 22 | public CopyDirVisitor(final Path fromPath, final Path toPath, final StandardCopyOption copyOption) 23 | { 24 | this.fromPath = fromPath; 25 | this.toPath = toPath; 26 | this.copyOption = copyOption; 27 | } 28 | 29 | public CopyDirVisitor(final Path fromPath, final Path toPath) 30 | { 31 | this(fromPath, toPath, StandardCopyOption.REPLACE_EXISTING); 32 | } 33 | 34 | @Override 35 | public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) 36 | throws IOException 37 | { 38 | // we allow to work against different providers 39 | Path targetPath = appendPath(dir); 40 | 41 | if (Files.notExists(targetPath)) 42 | { 43 | if (!targetPath.toString().endsWith("/")) 44 | { 45 | targetPath = targetPath.getParent().resolve(targetPath.getFileName().toString() + "/"); 46 | } 47 | 48 | Files.createDirectory(targetPath); 49 | } 50 | 51 | return FileVisitResult.CONTINUE; 52 | } 53 | 54 | @Override 55 | public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) 56 | throws IOException 57 | { 58 | final Path targetPath = appendPath(file); 59 | 60 | Files.copy(file, targetPath, copyOption); 61 | 62 | return FileVisitResult.CONTINUE; 63 | } 64 | 65 | /** 66 | * Get the corresponding path in the parameter: {@link #fromPath}, and relativize it 67 | * to the Path to parameter. 68 | * 69 | * @param to Path 70 | * @return 71 | */ 72 | private Path appendPath(final Path to) 73 | { 74 | Path targetPath = toPath; 75 | 76 | // Get the relative path and traverse it to add it to the new path. 77 | for (Path path : fromPath.relativize(to)) 78 | { 79 | // If Path is used instead of String, an error is thrown because the paths are different. 80 | targetPath = targetPath.resolve(path.getFileName().toString()); 81 | } 82 | 83 | return targetPath; 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/util/ExposingS3Client.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | import software.amazon.awssdk.awscore.client.handler.AwsSyncClientHandler; 4 | import software.amazon.awssdk.core.client.config.SdkClientConfiguration; 5 | import software.amazon.awssdk.core.client.handler.SyncClientHandler; 6 | import software.amazon.awssdk.services.s3.S3Client; 7 | import software.amazon.awssdk.services.s3.S3ClientBuilder; 8 | 9 | public class ExposingS3Client 10 | implements S3Client 11 | { 12 | 13 | private final SyncClientHandler clientHandler; 14 | private final SdkClientConfiguration clientConfiguration; 15 | 16 | public ExposingS3Client(SdkClientConfiguration clientConfiguration) 17 | { 18 | this.clientHandler = new AwsSyncClientHandler(clientConfiguration); 19 | this.clientConfiguration = clientConfiguration; 20 | } 21 | 22 | public SdkClientConfiguration getClientConfiguration() 23 | { 24 | return clientConfiguration; 25 | } 26 | 27 | static S3ClientBuilder builder() { 28 | return new ExposingS3ClientBuilder(); 29 | } 30 | 31 | @Override 32 | public String serviceName() 33 | { 34 | return SERVICE_NAME; 35 | } 36 | 37 | @Override 38 | public void close() 39 | { 40 | this.clientHandler.close(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/util/ExposingS3ClientBuilder.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | import software.amazon.awssdk.core.client.config.SdkClientOption; 4 | import software.amazon.awssdk.services.s3.S3Client; 5 | import software.amazon.awssdk.services.s3.S3ClientBuilder; 6 | import software.amazon.awssdk.services.s3.endpoints.S3EndpointProvider; 7 | 8 | /** 9 | * This class follows the {@link software.amazon.awssdk.services.s3.DefaultS3ClientBuilder} implementation which is not public. 10 | */ 11 | public class ExposingS3ClientBuilder 12 | extends ExposingS3BaseClientBuilder 13 | implements S3ClientBuilder 14 | { 15 | 16 | ExposingS3ClientBuilder() 17 | { 18 | } 19 | 20 | protected final S3Client buildClient() 21 | { 22 | return new ExposingS3Client(super.syncClientConfiguration()); 23 | } 24 | 25 | 26 | public ExposingS3ClientBuilder endpointProvider(S3EndpointProvider endpointProvider) { 27 | this.clientConfiguration.option(SdkClientOption.ENDPOINT_PROVIDER, endpointProvider); 28 | return this; 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/util/ExposingS3ClientFactory.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3ClientFactory; 4 | 5 | import software.amazon.awssdk.services.s3.S3Client; 6 | import software.amazon.awssdk.services.s3.S3ClientBuilder; 7 | 8 | public class ExposingS3ClientFactory 9 | extends S3ClientFactory 10 | { 11 | 12 | @Override 13 | protected S3Client createS3Client(final S3ClientBuilder builder) 14 | { 15 | return builder.build(); 16 | } 17 | 18 | @Override 19 | protected S3ClientBuilder getS3ClientBuilder() 20 | { 21 | return ExposingS3Client.builder(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/util/FileAttributeBuilder.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | import java.nio.file.attribute.FileAttribute; 4 | 5 | public class FileAttributeBuilder 6 | { 7 | 8 | 9 | public static FileAttribute build(final String name, final T value) 10 | { 11 | return new FileAttribute() 12 | { 13 | 14 | @Override 15 | public String name() 16 | { 17 | return name; 18 | } 19 | 20 | @Override 21 | public T value() 22 | { 23 | return value; 24 | } 25 | 26 | }; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/util/MinioContainer.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | import java.time.Duration; 4 | 5 | import org.testcontainers.containers.GenericContainer; 6 | import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; 7 | import org.testcontainers.utility.Base58; 8 | 9 | public class MinioContainer 10 | extends GenericContainer 11 | { 12 | 13 | private static final int DEFAULT_PORT = 9000; 14 | private static final String DEFAULT_IMAGE = "minio/minio"; 15 | private static final String DEFAULT_TAG = "RELEASE.2021-01-30T00-20-58Z"; 16 | private static final String MINIO_ACCESS_KEY = "MINIO_ACCESS_KEY"; 17 | private static final String MINIO_SECRET_KEY = "MINIO_SECRET_KEY"; 18 | private static final String DEFAULT_STORAGE_DIRECTORY = "/data"; 19 | private static final String HEALTH_ENDPOINT = "/minio/health/ready"; 20 | 21 | public MinioContainer(String accessKey, 22 | String secretKey, 23 | String bucketName) 24 | { 25 | super(DEFAULT_IMAGE + ":" + DEFAULT_TAG); 26 | withNetworkAliases("minio-" + Base58.randomString(6)); 27 | addExposedPort(DEFAULT_PORT); 28 | withEnv(MINIO_ACCESS_KEY, accessKey); 29 | withEnv(MINIO_SECRET_KEY, secretKey); 30 | 31 | // Auto create bucket on start up because minio does not do this for you. 32 | // https://github.com/minio/minio/issues/4769#issuecomment-331033735 33 | withCreateContainerCmdModifier(cmd -> { 34 | cmd.withEntrypoint("/bin/sh"); 35 | cmd.withCmd("-c", "mkdir -p /data" + bucketName + " && minio server " + DEFAULT_STORAGE_DIRECTORY); 36 | }); 37 | 38 | setWaitStrategy(new HttpWaitStrategy().forPort(DEFAULT_PORT) 39 | .forPath(HEALTH_ENDPOINT) 40 | .withStartupTimeout(Duration.ofMinutes(2))); 41 | } 42 | 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/util/MockBucket.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Path; 5 | import java.nio.file.attribute.FileAttribute; 6 | 7 | public class MockBucket 8 | { 9 | 10 | private final S3ClientMock s3ClientMock; 11 | 12 | private final Path mockedPath; 13 | 14 | public MockBucket(final S3ClientMock s3ClientMock, 15 | final Path mockedPath) 16 | { 17 | this.s3ClientMock = s3ClientMock; 18 | this.mockedPath = mockedPath; 19 | } 20 | 21 | public MockBucket file(String... file) 22 | throws IOException 23 | { 24 | for (String string : file) 25 | { 26 | s3ClientMock.addFile(mockedPath, string, "sample-content".getBytes()); 27 | } 28 | 29 | return this; 30 | } 31 | 32 | public MockBucket file(String file, 33 | final byte[] content) 34 | throws IOException 35 | { 36 | s3ClientMock.addFile(mockedPath, file, content); 37 | 38 | return this; 39 | } 40 | 41 | public MockBucket file(String file, 42 | final byte[] content, 43 | final FileAttribute... attrs) 44 | throws IOException 45 | { 46 | s3ClientMock.addFile(mockedPath, file, content, attrs); 47 | 48 | return this; 49 | } 50 | 51 | public MockBucket dir(String... dir) 52 | throws IOException 53 | { 54 | for (String string : dir) 55 | { 56 | s3ClientMock.addDirectory(mockedPath, string); 57 | } 58 | 59 | return this; 60 | } 61 | 62 | public Path resolve(final String file) 63 | { 64 | final String encodedFile = file.replaceAll("/", "%2F"); 65 | return mockedPath.resolve(encodedFile); 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/util/S3EndpointConstant.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | import java.net.URI; 4 | 5 | public class S3EndpointConstant 6 | { 7 | 8 | public static final URI S3_GLOBAL_URI_TEST = URI.create("s3://s3.test.amazonaws.com/"); 9 | 10 | public static final String S3_REGION_URI_TEST = "s3://s3.test.%s.amazonaws.com/"; 11 | 12 | public static final URI S3_GLOBAL_URI_IT = URI.create("s3://s3.amazonaws.com/"); 13 | 14 | public static final URI MINIO_GLOBAL_URI_IT = URI.create("s3://localhost:9000"); 15 | 16 | public static final String S3_REGION_URI_IT = "s3://s3.%s.amazonaws.com/"; 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/util/S3MockFactory.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3Factory; 4 | 5 | import java.io.IOException; 6 | import java.net.URI; 7 | import java.nio.file.FileSystem; 8 | import java.nio.file.Path; 9 | import java.util.Properties; 10 | import java.util.UUID; 11 | 12 | import com.github.marschall.memoryfilesystem.MemoryFileSystemBuilder; 13 | import software.amazon.awssdk.services.s3.S3Client; 14 | import software.amazon.awssdk.services.s3.S3ClientBuilder; 15 | import static org.mockito.Mockito.spy; 16 | 17 | public class S3MockFactory 18 | extends S3Factory 19 | { 20 | 21 | private static FileSystem fsMem; 22 | 23 | private static S3ClientMock s3Client; 24 | 25 | 26 | @Override 27 | public S3Client getS3Client(final URI uri, final Properties props) 28 | { 29 | return getS3ClientMock(); 30 | } 31 | 32 | @Override 33 | protected S3Client createS3Client(final S3ClientBuilder builder) 34 | { 35 | return getS3ClientMock(); 36 | } 37 | 38 | public static S3ClientMock getS3ClientMock() 39 | { 40 | if (s3Client == null) 41 | { 42 | final Path path = getFsMem().getPath("/"); 43 | final S3ClientMock s3ClientMock = new S3ClientMock(path); 44 | s3Client = spy(s3ClientMock); 45 | } 46 | 47 | return s3Client; 48 | } 49 | 50 | private static FileSystem getFsMem() 51 | { 52 | if (fsMem == null) 53 | { 54 | try 55 | { 56 | fsMem = MemoryFileSystemBuilder.newLinux() 57 | .setCurrentWorkingDirectory("/") 58 | .build(UUID.randomUUID().toString()); 59 | } 60 | catch (IOException e) 61 | { 62 | throw new RuntimeException(e); 63 | } 64 | } 65 | 66 | return fsMem; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/org/carlspring/cloud/storage/s3fs/util/UnsupportedFileStoreAttributeView.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.util; 2 | 3 | import java.nio.file.attribute.FileStoreAttributeView; 4 | 5 | public class UnsupportedFileStoreAttributeView 6 | implements FileStoreAttributeView 7 | { 8 | 9 | @Override 10 | public String name() 11 | { 12 | return "unsupported"; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/test/resources/META-INF/services/java.nio.file.spi.FileSystemProvider: -------------------------------------------------------------------------------- 1 | org.carlspring.cloud.storage.s3fs.S3FileSystemProvider 2 | com.github.marschall.memoryfilesystem.MemoryFileSystemProvider 3 | -------------------------------------------------------------------------------- /src/test/resources/amazon-test-sample.properties: -------------------------------------------------------------------------------- 1 | s3fs.bucket.name=/your-bucket-name-for-test 2 | # http://docs.aws.amazon.com/general/latest/gr/rande.html 3 | s3fs.access.key=access-key-for-test 4 | s3fs.secret.key=secret-key-for-test 5 | # check software.amazon.awssdk.regions.Region for quick list 6 | s3fs.region=eu-central-1 7 | s3fs.protocol=https 8 | -------------------------------------------------------------------------------- /src/test/resources/config-test: -------------------------------------------------------------------------------- 1 | 2 | [default] 3 | region = us-west-2 4 | 5 | [profile test-no-region] 6 | test-no-region = test-no-region 7 | 8 | [profile test] 9 | region = eu-central-1 10 | 11 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | true 8 | 9 | 10 | 11 | 12 | %d{HH:mm:ss.SSS dd-MM-yyyy} | %-5.5p | %-20.20t | %-50.50logger{50} | %m%n 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/FileSystemsIT.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs; 2 | 3 | import org.carlspring.cloud.storage.s3fs.junit.annotations.S3IntegrationTest; 4 | import org.carlspring.cloud.storage.s3fs.util.EnvironmentBuilder; 5 | 6 | import java.io.IOException; 7 | import java.net.URI; 8 | import java.nio.file.FileSystem; 9 | import java.nio.file.FileSystemNotFoundException; 10 | import java.nio.file.FileSystems; 11 | 12 | import org.junit.jupiter.api.BeforeEach; 13 | import org.junit.jupiter.api.Test; 14 | import static org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant.S3_GLOBAL_URI_IT; 15 | import static org.junit.jupiter.api.Assertions.*; 16 | 17 | @S3IntegrationTest 18 | class FileSystemsIT extends BaseIntegrationTest 19 | { 20 | 21 | private static final URI uriEurope = URI.create("s3://s3-eu-west-1.amazonaws.com/"); 22 | 23 | private static final URI uriGlobal = EnvironmentBuilder.getS3URI(S3_GLOBAL_URI_IT); 24 | 25 | private FileSystem fileSystemAmazon; 26 | 27 | 28 | @BeforeEach 29 | public void setup() 30 | throws IOException 31 | { 32 | System.clearProperty(S3FileSystemProvider.S3_FACTORY_CLASS); 33 | 34 | fileSystemAmazon = build(); 35 | } 36 | 37 | private static FileSystem build() 38 | throws IOException 39 | { 40 | try 41 | { 42 | FileSystems.getFileSystem(uriGlobal).close(); 43 | 44 | return createNewFileSystem(); 45 | } 46 | catch (FileSystemNotFoundException e) 47 | { 48 | return createNewFileSystem(); 49 | } 50 | } 51 | 52 | private static FileSystem createNewFileSystem() 53 | throws IOException 54 | { 55 | return FileSystems.newFileSystem(uriGlobal, EnvironmentBuilder.getRealEnv()); 56 | } 57 | 58 | @Test 59 | void buildEnv() 60 | { 61 | FileSystem fileSystem = FileSystems.getFileSystem(uriGlobal); 62 | 63 | assertSame(fileSystemAmazon, fileSystem); 64 | } 65 | 66 | @Test 67 | void buildEnvAnotherURIReturnDifferent() 68 | throws IOException 69 | { 70 | FileSystem fileSystem = FileSystems.newFileSystem(uriEurope, EnvironmentBuilder.getRealEnv()); 71 | 72 | assertNotSame(fileSystemAmazon, fileSystem); 73 | } 74 | 75 | @Test 76 | void shouldReturnExistingFileSystem() 77 | throws IOException 78 | { 79 | FileSystem retrieveFileSystem = FileSystems.getFileSystem(uriGlobal); 80 | assertSame(fileSystemAmazon, retrieveFileSystem); 81 | } 82 | 83 | @Test 84 | void shouldReturnThrowExceptionOnMissingFileSystem() 85 | throws IOException 86 | { 87 | assertThrows(FileSystemNotFoundException.class,() -> FileSystems.getFileSystem(uriEurope)); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/fileSystemProvider/GetFileSystemIT.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.fileSystemProvider; 2 | 3 | import org.carlspring.cloud.storage.s3fs.S3FileSystemProvider; 4 | import org.carlspring.cloud.storage.s3fs.junit.annotations.S3IntegrationTest; 5 | import org.carlspring.cloud.storage.s3fs.BaseIntegrationTest; 6 | 7 | import java.io.IOException; 8 | import java.nio.file.FileSystem; 9 | import java.nio.file.FileSystemNotFoundException; 10 | import java.nio.file.FileSystems; 11 | import java.util.Map; 12 | import java.util.Properties; 13 | 14 | import com.google.common.collect.ImmutableMap; 15 | import org.junit.jupiter.api.BeforeEach; 16 | import org.junit.jupiter.api.Test; 17 | import static org.carlspring.cloud.storage.s3fs.S3Factory.ACCESS_KEY; 18 | import static org.carlspring.cloud.storage.s3fs.S3Factory.REGION; 19 | import static org.carlspring.cloud.storage.s3fs.S3Factory.SECRET_KEY; 20 | import static org.carlspring.cloud.storage.s3fs.util.S3EndpointConstant.S3_GLOBAL_URI_IT; 21 | import static org.junit.jupiter.api.Assertions.assertNotNull; 22 | import static org.junit.jupiter.api.Assertions.assertSame; 23 | import static org.mockito.ArgumentMatchers.any; 24 | import static org.mockito.ArgumentMatchers.anyString; 25 | import static org.mockito.Mockito.doReturn; 26 | import static org.mockito.Mockito.spy; 27 | 28 | @S3IntegrationTest 29 | class GetFileSystemIT extends BaseIntegrationTest 30 | { 31 | 32 | private S3FileSystemProvider provider; 33 | 34 | 35 | @BeforeEach 36 | public void setup() 37 | throws IOException 38 | { 39 | System.clearProperty(S3FileSystemProvider.S3_FACTORY_CLASS); 40 | System.clearProperty(ACCESS_KEY); 41 | System.clearProperty(SECRET_KEY); 42 | 43 | try 44 | { 45 | FileSystems.getFileSystem(S3_GLOBAL_URI_IT).close(); 46 | } 47 | catch (FileSystemNotFoundException e) 48 | { 49 | // ignore this 50 | } 51 | 52 | provider = spy(new S3FileSystemProvider()); 53 | 54 | // Don't override with system envs that we can have set, like Travis 55 | doReturn(false).when(provider).overloadPropertiesWithSystemEnv(any(Properties.class), anyString()); 56 | doReturn(false).when(provider).overloadPropertiesWithSystemProps(any(Properties.class), anyString()); 57 | } 58 | 59 | @Test 60 | void getFileSystemWithSameEnvReturnSameFileSystem() 61 | { 62 | Map env = ImmutableMap.of(ACCESS_KEY, "a", SECRET_KEY, "b", REGION, "c"); 63 | FileSystem fileSystem = provider.getFileSystem(S3_GLOBAL_URI_IT, env); 64 | 65 | assertNotNull(fileSystem); 66 | 67 | FileSystem sameFileSystem = provider.getFileSystem(S3_GLOBAL_URI_IT, env); 68 | 69 | assertSame(fileSystem, sameFileSystem); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/junit/annotations/BaseAnnotationTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.junit.annotations; 2 | 3 | public abstract class BaseAnnotationTest 4 | { 5 | 6 | /** 7 | * Check if there is an {@link S3IntegrationTest} annotation at a class level. 8 | * 9 | * @param clazz 10 | * @return 11 | */ 12 | protected static boolean hasS3Annotation(Class clazz) 13 | { 14 | return clazz.getAnnotation(S3IntegrationTest.class) != null; 15 | } 16 | 17 | /** 18 | * Check if there is an {@link S3IntegrationTest} annotation at a method level (and pick-up class level) 19 | * 20 | * @param clazz 21 | * @param methodName 22 | * @return 23 | * @throws NoSuchMethodException 24 | */ 25 | protected static boolean hasS3Annotation(Class clazz, 26 | String methodName) 27 | throws NoSuchMethodException 28 | { 29 | return hasS3Annotation(clazz) || clazz.getMethod(methodName).getAnnotation(S3IntegrationTest.class) != null; 30 | } 31 | 32 | /** 33 | * Check if there is an {@link MinioIntegrationTest} annotation at a class level. 34 | * 35 | * @param clazz 36 | * @return 37 | */ 38 | protected static boolean hasMinioAnnotation(Class clazz) 39 | { 40 | return clazz.getAnnotation(MinioIntegrationTest.class) != null; 41 | } 42 | 43 | /** 44 | * Check if there is an {@link MinioIntegrationTest} annotation at a method level (and pick-up class level) 45 | * 46 | * @param clazz 47 | * @param methodName 48 | * @return 49 | * @throws NoSuchMethodException 50 | */ 51 | protected static boolean hasMinioAnnotation(Class clazz, 52 | String methodName) 53 | throws NoSuchMethodException 54 | { 55 | return hasMinioAnnotation(clazz) || clazz.getMethod(methodName).getAnnotation(MinioIntegrationTest.class) != null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/junit/annotations/MinioIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.junit.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | import org.junit.jupiter.api.Tag; 9 | 10 | /** 11 | * This annotation should be used for integration tests which are specific to MinIO. 12 | * It is possible to combine it with {@link S3IntegrationTest} for test cases which are compatible with both (i.e. can 13 | * run on S3 and MinIO) 14 | */ 15 | @Target({ ElementType.TYPE, ElementType.METHOD }) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Tag("it-minio") 18 | public @interface MinioIntegrationTest 19 | { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/junit/annotations/MinioIntegrationTestAnnotationTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.junit.annotations; 2 | 3 | 4 | import org.carlspring.cloud.storage.s3fs.junit.examples.*; 5 | 6 | import org.junit.jupiter.api.Test; 7 | import static org.junit.jupiter.api.Assertions.assertFalse; 8 | import static org.junit.jupiter.api.Assertions.assertTrue; 9 | 10 | class MinioIntegrationTestAnnotationTest 11 | extends BaseAnnotationTest 12 | { 13 | 14 | @Test 15 | void testClassLevelAnnotation() 16 | { 17 | assertTrue(hasMinioAnnotation(MinioClassAnnotationIT.class)); 18 | assertTrue(hasMinioAnnotation(CombinedMinioS3IT.class)); 19 | 20 | assertFalse(hasMinioAnnotation(S3ClassAnnotationIT.class)); 21 | assertFalse(hasMinioAnnotation(CombinedS3MinioIT.class)); 22 | 23 | assertTrue(hasMinioAnnotation(CombinedIT.class)); 24 | } 25 | 26 | @Test 27 | void testMethodLevelAnnotation() 28 | throws NoSuchMethodException 29 | { 30 | assertTrue(hasMinioAnnotation(MinioMethodAnnotationIT.class, "testShouldExecuteBecauseOfMethodAnnotation")); 31 | 32 | assertTrue(hasMinioAnnotation(CombinedIT.class, "testMinioMethodShouldExecuteBecauseOfClassLevelAnnotation")); 33 | assertTrue(hasMinioAnnotation(CombinedIT.class, "testMinioMethodShouldExecuteBecauseOfClassLevelAnnotation")); 34 | 35 | assertTrue(hasMinioAnnotation(CombinedS3MinioIT.class, "testMinioMethodShouldExecuteBecauseOfMethodLevelAnnotation")); 36 | assertTrue(hasMinioAnnotation(CombinedMinioS3IT.class, "testMinioMethodShouldExecuteBecauseOfClassLevelAnnotation")); 37 | 38 | assertFalse(hasS3Annotation(MinioMethodAnnotationIT.class, "testShouldExecuteBecauseOfMethodAnnotation")); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/junit/annotations/S3IntegrationTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.junit.annotations; 2 | 3 | import org.junit.jupiter.api.Tag; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * This annotation should be used for integration tests which are specific to the official S3 API. 12 | * It is possible to combine it with {@link MinioIntegrationTest} for test cases which are compatible with both (i.e. can 13 | * run on S3 and MinIO) 14 | */ 15 | @Target({ ElementType.TYPE, ElementType.METHOD }) 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Tag("it-s3") 18 | public @interface S3IntegrationTest 19 | { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/junit/annotations/S3IntegrationTestAnnotationTest.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.junit.annotations; 2 | 3 | 4 | import org.carlspring.cloud.storage.s3fs.junit.examples.*; 5 | 6 | import org.junit.jupiter.api.Test; 7 | import static org.junit.jupiter.api.Assertions.*; 8 | 9 | class S3IntegrationTestAnnotationTest 10 | extends BaseAnnotationTest 11 | { 12 | 13 | @Test 14 | void testClassLevelAnnotation() 15 | { 16 | assertTrue(hasS3Annotation(S3ClassAnnotationIT.class)); 17 | assertTrue(hasS3Annotation(CombinedS3MinioIT.class)); 18 | 19 | assertFalse(hasS3Annotation(MinioClassAnnotationIT.class)); 20 | assertFalse(hasS3Annotation(CombinedMinioS3IT.class)); 21 | 22 | assertTrue(hasS3Annotation(CombinedIT.class)); 23 | } 24 | 25 | @Test 26 | void testMethodLevelAnnotation() 27 | throws NoSuchMethodException 28 | { 29 | assertTrue(hasS3Annotation(S3MethodAnnotationIT.class, "testShouldExecuteBecauseOfMethodAnnotation")); 30 | assertTrue(hasS3Annotation(CombinedIT.class, "testMinioMethodShouldExecuteBecauseOfClassLevelAnnotation")); 31 | assertTrue(hasS3Annotation(CombinedIT.class, "testS3MethodShouldExecuteBecauseOfClassLevelAnnotation")); 32 | assertTrue(hasS3Annotation(CombinedS3MinioIT.class, "testS3MethodShouldExecuteBecauseOfClassLevelAnnotation")); 33 | assertTrue(hasS3Annotation(CombinedMinioS3IT.class, "testS3MethodShouldExecuteBecauseOfMethodLevelAnnotation")); 34 | 35 | assertFalse(hasS3Annotation(MinioMethodAnnotationIT.class, "testShouldExecuteBecauseOfMethodAnnotation")); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/junit/examples/CombinedIT.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.junit.examples; 2 | 3 | import org.carlspring.cloud.storage.s3fs.junit.annotations.MinioIntegrationTest; 4 | import org.carlspring.cloud.storage.s3fs.junit.annotations.S3IntegrationTest; 5 | import org.carlspring.cloud.storage.s3fs.BaseIntegrationTest; 6 | 7 | import org.junit.jupiter.api.Test; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import static org.junit.jupiter.api.Assertions.assertTrue; 11 | 12 | /** 13 | * Class level {@link MinioIntegrationTest} annotation and class level {@link S3IntegrationTest} annotation. 14 | */ 15 | @MinioIntegrationTest 16 | @S3IntegrationTest 17 | public class CombinedIT extends BaseIntegrationTest 18 | { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(S3MethodAnnotationIT.class); 21 | 22 | @Test 23 | public void testS3MethodShouldExecuteBecauseOfClassLevelAnnotation() 24 | { 25 | logger.debug(new Exception().getStackTrace()[0].getMethodName()); 26 | assertTrue(true); 27 | } 28 | 29 | @Test 30 | public void testMinioMethodShouldExecuteBecauseOfClassLevelAnnotation() 31 | { 32 | logger.debug(new Exception().getStackTrace()[0].getMethodName()); 33 | assertTrue(true); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/junit/examples/CombinedMinioS3IT.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.junit.examples; 2 | 3 | import org.carlspring.cloud.storage.s3fs.junit.annotations.MinioIntegrationTest; 4 | import org.carlspring.cloud.storage.s3fs.junit.annotations.S3IntegrationTest; 5 | import org.carlspring.cloud.storage.s3fs.BaseIntegrationTest; 6 | 7 | import org.junit.jupiter.api.Test; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import static org.junit.jupiter.api.Assertions.assertTrue; 11 | 12 | /** 13 | * Class level {@link MinioIntegrationTest} annotation and method level {@link S3IntegrationTest} annotation. 14 | */ 15 | @MinioIntegrationTest 16 | public class CombinedMinioS3IT extends BaseIntegrationTest 17 | { 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(S3MethodAnnotationIT.class); 20 | 21 | @Test 22 | @S3IntegrationTest 23 | public void testS3MethodShouldExecuteBecauseOfMethodLevelAnnotation() 24 | { 25 | logger.debug(new Exception().getStackTrace()[0].getMethodName()); 26 | assertTrue(true); 27 | } 28 | 29 | @Test 30 | public void testMinioMethodShouldExecuteBecauseOfClassLevelAnnotation() 31 | { 32 | logger.debug(new Exception().getStackTrace()[0].getMethodName()); 33 | assertTrue(true); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/junit/examples/CombinedS3MinioIT.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.junit.examples; 2 | 3 | import org.carlspring.cloud.storage.s3fs.junit.annotations.MinioIntegrationTest; 4 | import org.carlspring.cloud.storage.s3fs.junit.annotations.S3IntegrationTest; 5 | import org.carlspring.cloud.storage.s3fs.BaseIntegrationTest; 6 | 7 | import org.junit.jupiter.api.Test; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import static org.junit.jupiter.api.Assertions.assertTrue; 11 | 12 | /** 13 | * Class level {@link S3IntegrationTest} annotation and method level {@link MinioIntegrationTest} annotation. 14 | */ 15 | @S3IntegrationTest 16 | public class CombinedS3MinioIT extends BaseIntegrationTest 17 | { 18 | 19 | private static final Logger logger = LoggerFactory.getLogger(S3MethodAnnotationIT.class); 20 | 21 | @Test 22 | public void testS3MethodShouldExecuteBecauseOfClassLevelAnnotation() 23 | { 24 | logger.debug(new Exception().getStackTrace()[0].getMethodName()); 25 | assertTrue(true); 26 | } 27 | 28 | @Test 29 | @MinioIntegrationTest 30 | public void testMinioMethodShouldExecuteBecauseOfMethodLevelAnnotation() 31 | { 32 | logger.debug(new Exception().getStackTrace()[0].getMethodName()); 33 | assertTrue(true); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/junit/examples/MinioClassAnnotationIT.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.junit.examples; 2 | 3 | import org.carlspring.cloud.storage.s3fs.junit.annotations.MinioIntegrationTest; 4 | import org.carlspring.cloud.storage.s3fs.BaseIntegrationTest; 5 | 6 | import org.junit.jupiter.api.Test; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | @MinioIntegrationTest 12 | public class MinioClassAnnotationIT extends BaseIntegrationTest 13 | { 14 | 15 | private static final Logger logger = LoggerFactory.getLogger(MinioClassAnnotationIT.class); 16 | 17 | @Test 18 | public void testShouldExecuteBecauseOfClassAnnotation() 19 | { 20 | logger.debug(new Exception().getStackTrace()[0].getMethodName()); 21 | assertTrue(true); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/junit/examples/MinioMethodAnnotationIT.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.junit.examples; 2 | 3 | import org.carlspring.cloud.storage.s3fs.junit.annotations.MinioIntegrationTest; 4 | import org.carlspring.cloud.storage.s3fs.BaseIntegrationTest; 5 | 6 | import org.junit.jupiter.api.Test; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | public class MinioMethodAnnotationIT extends BaseIntegrationTest 12 | { 13 | 14 | private static final Logger logger = LoggerFactory.getLogger(MinioMethodAnnotationIT.class); 15 | 16 | @Test 17 | @MinioIntegrationTest 18 | public void testShouldExecuteBecauseOfMethodAnnotation() 19 | { 20 | logger.debug(new Exception().getStackTrace()[0].getMethodName()); 21 | assertTrue(true); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/junit/examples/S3ClassAnnotationIT.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.junit.examples; 2 | 3 | import org.carlspring.cloud.storage.s3fs.junit.annotations.S3IntegrationTest; 4 | import org.carlspring.cloud.storage.s3fs.BaseIntegrationTest; 5 | 6 | import org.junit.jupiter.api.Test; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | @S3IntegrationTest 12 | public class S3ClassAnnotationIT extends BaseIntegrationTest 13 | { 14 | 15 | private static final Logger logger = LoggerFactory.getLogger(S3ClassAnnotationIT.class); 16 | 17 | @Test 18 | public void testShouldExecuteBecauseOfClassAnnotation() 19 | { 20 | logger.debug(new Exception().getStackTrace()[0].getMethodName()); 21 | assertTrue(true); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/junit/examples/S3MethodAnnotationIT.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.junit.examples; 2 | 3 | import org.carlspring.cloud.storage.s3fs.junit.annotations.S3IntegrationTest; 4 | import org.carlspring.cloud.storage.s3fs.BaseIntegrationTest; 5 | 6 | import org.junit.jupiter.api.Test; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | public class S3MethodAnnotationIT extends BaseIntegrationTest 12 | { 13 | 14 | private static final Logger logger = LoggerFactory.getLogger(S3MethodAnnotationIT.class); 15 | 16 | @Test 17 | @S3IntegrationTest 18 | public void testShouldExecuteBecauseOfMethodAnnotation() 19 | { 20 | logger.debug(new Exception().getStackTrace()[0].getMethodName()); 21 | assertTrue(true); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/testIntegration/java/org/carlspring/cloud/storage/s3fs/spike/EnvironmentIT.java: -------------------------------------------------------------------------------- 1 | package org.carlspring.cloud.storage.s3fs.spike; 2 | 3 | import org.carlspring.cloud.storage.s3fs.junit.annotations.MinioIntegrationTest; 4 | import org.carlspring.cloud.storage.s3fs.junit.annotations.S3IntegrationTest; 5 | import org.carlspring.cloud.storage.s3fs.BaseIntegrationTest; 6 | import org.carlspring.cloud.storage.s3fs.util.EnvironmentBuilder; 7 | 8 | import java.util.Map; 9 | 10 | import org.junit.jupiter.api.Test; 11 | import static org.carlspring.cloud.storage.s3fs.S3Factory.ACCESS_KEY; 12 | import static org.carlspring.cloud.storage.s3fs.S3Factory.SECRET_KEY; 13 | import static org.junit.jupiter.api.Assertions.assertNotNull; 14 | 15 | @S3IntegrationTest 16 | @MinioIntegrationTest 17 | class EnvironmentIT extends BaseIntegrationTest 18 | { 19 | @Test 20 | void couldCreateFileSystem() 21 | { 22 | Map res = EnvironmentBuilder.getRealEnv(); 23 | 24 | assertNotNull(res); 25 | assertNotNull(res.get(ACCESS_KEY)); 26 | assertNotNull(res.get(SECRET_KEY)); 27 | } 28 | 29 | } 30 | --------------------------------------------------------------------------------