From 83df2cc6cc1520bf5039e7bf5eaf4a3cfd93582f Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sat, 28 Mar 2020 20:55:16 +0000 Subject: [PATCH] Initial Import --- .github/GitHub-Actions.org | 41 ++++++++++ .github/NOTES | 53 +++++++++++++ .github/codesigning.asc.gpg | Bin 0 -> 9875 bytes .github/settings.xml | 28 +++++++ .github/workflows/maven-build.yml | 25 +++++++ .github/workflows/sonatype-deploy.yml | 38 ++++++++++ .gitignore | 16 ++++ README.md | 52 +++++++++++++ pom.xml | 57 ++++++++++++++ .../java/net/kemitix/files/FileReader.java | 17 +++++ .../net/kemitix/files/FileReaderWriter.java | 36 +++++++++ .../java/net/kemitix/files/FileWriter.java | 21 ++++++ .../kemitix/files/FileReaderWriterTests.java | 70 ++++++++++++++++++ 13 files changed, 454 insertions(+) create mode 100644 .github/GitHub-Actions.org create mode 100644 .github/NOTES create mode 100644 .github/codesigning.asc.gpg create mode 100644 .github/settings.xml create mode 100644 .github/workflows/maven-build.yml create mode 100644 .github/workflows/sonatype-deploy.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 pom.xml create mode 100644 src/main/java/net/kemitix/files/FileReader.java create mode 100644 src/main/java/net/kemitix/files/FileReaderWriter.java create mode 100644 src/main/java/net/kemitix/files/FileWriter.java create mode 100644 src/test/java/net/kemitix/files/FileReaderWriterTests.java diff --git a/.github/GitHub-Actions.org b/.github/GitHub-Actions.org new file mode 100644 index 0000000..1c882b8 --- /dev/null +++ b/.github/GitHub-Actions.org @@ -0,0 +1,41 @@ +* Deploying using Github Actions + +** Actions definition: workflow/sonatype-deploy.yml + +When a GitHub Release is created, usually from a tag, this action will trigger. + +Using JDK8 the software will be packaged, including running any tests. + +Then the Deploy script will sign the created artifacts then deploy them according to the distributionManagement configuration in the `pom.xml`. + +** Deploy Script + +Uses a signing key provided from the GitHub Actions Secrets as an environment variable to sign the artifact(s) before they are then deployed. + +*** Inputs + +**** DEPLOY_PROJECTS (optional) + +An optional list of modules in a multi-module project to be deployed. If this value is not specified, then all projects will be deployed. + +** Maven Configuration + +Picks up the credentials from Environment variables for authenticating both with GPG and with the target deployment server (e.g. sonatype-nexus). + +*** Inputs + +**** NEXUS_USERNAME + +The username for your account on the deployment server. + +**** NEXUS_PASSWORD + +The password for your account on the deployement server. + +**** GPG_KEYNAME + +The key to use when signing. + +**** GPG_PASSPHRASE + +The passphrase to unlock the key to use when signing. diff --git a/.github/NOTES b/.github/NOTES new file mode 100644 index 0000000..64253c9 --- /dev/null +++ b/.github/NOTES @@ -0,0 +1,53 @@ +Add subkeys: + +???? + +Publish: + +gpg --send-keys --keyserver keyserver.ubuntu.com $KEYID +gpg --send-keys --keyserver pgp.mit.edu $KEYID +gpg --send-keys --keyserver pool.sks-keyservers.net $KEYID + +Backup: + +gpg --export --armor pcampbell@kemitix.net > gpg-key-backup.asc +gpg --export-secret-keys --armor pcampbell@kemitix.net >> gpg-key-backup.asc + +Export sub-keys: + +gpg --export-secret-subkeys pcampbell@kemitix.net > subkeys + +Remove master keys: + +gpg --delete-secret-key pcampbell@kemitix.net + +Import sub-keys and clean up: + +gpg --import subkeys + +shred --remove subkeys + +Delete any encryption subkeys: + +gpg --edit-key pcampbell@kemitix.net + +2 +delkey +save + +Change passphrase: + +gpg --edit-key pcampbell@kemitix.net +passwd +save + +Export keys: + +gpg --export --armor pcampbell@kemitix.net > codesigning.asc +gpg --export-secret-keys --armor pcampbell@kemitix.net >> codesigning.asc + +Encrypt keys: + +gpg --symmetric --cipher-algo AES256 codesigning.asc + +shred codesigning.asc diff --git a/.github/codesigning.asc.gpg b/.github/codesigning.asc.gpg new file mode 100644 index 0000000000000000000000000000000000000000..f5c71e0bcd94084c026a3726820e37bb7e6ff004 GIT binary patch literal 9875 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 FO%j@IVfX+5 literal 0 HcmV?d00001 diff --git a/.github/settings.xml b/.github/settings.xml new file mode 100644 index 0000000..8791e47 --- /dev/null +++ b/.github/settings.xml @@ -0,0 +1,28 @@ + + + + + sonatype-nexus-snapshots + ${env.NEXUS_USERNAME} + ${env.NEXUS_PASSWORD} + + + sonatype-nexus-staging + ${env.NEXUS_USERNAME} + ${env.NEXUS_PASSWORD} + + + + + gpg-sign + + true + + + gpg + ${env.GPG_KEYNAME} + ${env.GPG_PASSPHRASE} + + + + diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml new file mode 100644 index 0000000..470baeb --- /dev/null +++ b/.github/workflows/maven-build.yml @@ -0,0 +1,25 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: '*' + pull_request: + branches: '*' + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + java: [ 8, 11, 13 ] + steps: + - uses: actions/checkout@v2 + - name: Set up JDK ${{ matrix.java }} + uses: actions/setup-java@v1 + with: + java-version: ${{ matrix.java }} + - name: Build with Maven + run: mvn -B install diff --git a/.github/workflows/sonatype-deploy.yml b/.github/workflows/sonatype-deploy.yml new file mode 100644 index 0000000..63b14fe --- /dev/null +++ b/.github/workflows/sonatype-deploy.yml @@ -0,0 +1,38 @@ +name: Deploy to Sonatype Nexus + +on: + release: + types: [created] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Build with Maven + run: mvn -B install + - name: Nexus Repo Publish + run: | + gpg --quiet \ + --batch \ + --yes \ + --decrypt \ + --passphrase="${{ secrets.GPG_PASSPHRASE }}" \ + --output codesigning.asc \ + .github/codesigning.asc.gpg + gpg --batch \ + --fast-import codesigning.asc + mvn --settings .github/settings.xml \ + -Dskip-Tests=true \ + -P release \ + -B \ + deploy + env: + NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }} + NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} + GPG_KEYNAME: ${{ secrets.GPG_KEYNAME }} + GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8bcd005 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +*.jqwik-database* + +*.iml +.idea/ +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) +!/.mvn/wrapper/maven-wrapper.jar diff --git a/README.md b/README.md new file mode 100644 index 0000000..b736493 --- /dev/null +++ b/README.md @@ -0,0 +1,52 @@ +# File Reader/Writer + +![Sonatype Nexus (Release)](https://img.shields.io/nexus/r/https/oss.sonatype.org/net.kemitix/file-reader-writer.svg?style=for-the-badge) +![Maven Central](https://img.shields.io/maven-central/v/net.kemitix/file-reader-writer.svg?style=for-the-badge) + +Simple wrapper for the static methods `Files.readAllLines` and `Files.write`, +bringing into simple classes that can be injected into code using Dependency +Injection allowing them to be mocked during testing without touching the real +filesystem. + +## Assumptions + +* All files will be read and written in `ÙTF-8`. +* When reading a file lines breaks will be replaced by the newline character. +* When writing a file it will be truncated first. + +## Usage + +### Jakarta EE + +```java +@Produces +FileReaderWriter fileReaderWriter() { + return new FileReaderWriter(); +} +@Produces +FileReader fileReader() { + return new FileReaderWriter(); +} +@Produces +FileWriter fileWriter() { + return new FileReaderWriter(); +} +``` + +### Spring + +```java +@Bean +FileReaderWriter fileReaderWriter() { + return new FileReaderWriter(); +} +@Bean +FileReader fileReader() { + return new FileReaderWriter(); +} +@Bean +FileWriter fileWriter() { + return new FileReaderWriter(); +} +``` + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f030b02 --- /dev/null +++ b/pom.xml @@ -0,0 +1,57 @@ + + + 4.0.0 + + + net.kemitix + kemitix-parent + 5.3.0 + + + + file-reader-writer + DEV-SNAPSHOT + + + 2.16 + 2.5.0 + 5.4.0 + + true + + + + + org.junit.jupiter + junit-jupiter + 5.6.1 + test + + + org.assertj + assertj-core + 3.15.0 + test + + + + + + + io.repaint.maven + tiles-maven-plugin + ${tiles-maven-plugin.version} + true + + + net.kemitix.tiles:all-jdk-11:${kemitix-maven-tiles.version} + net.kemitix.checkstyle:tile:${kemitix-checkstyle-ruleset.version} + + + + + + + \ No newline at end of file diff --git a/src/main/java/net/kemitix/files/FileReader.java b/src/main/java/net/kemitix/files/FileReader.java new file mode 100644 index 0000000..8719434 --- /dev/null +++ b/src/main/java/net/kemitix/files/FileReader.java @@ -0,0 +1,17 @@ +package net.kemitix.files; + +import java.io.File; +import java.io.IOException; + +public interface FileReader { + /** + * Reads the content from the file path. + * + *

File content will be read using UTF-8 encoding and line endings will + * be replaced with a newline (\n).

+ * + * @param file the file to read + * @throws IOException if there is an error. + */ + String read(File file) throws IOException; +} diff --git a/src/main/java/net/kemitix/files/FileReaderWriter.java b/src/main/java/net/kemitix/files/FileReaderWriter.java new file mode 100644 index 0000000..2087388 --- /dev/null +++ b/src/main/java/net/kemitix/files/FileReaderWriter.java @@ -0,0 +1,36 @@ +package net.kemitix.files; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.StandardOpenOption; + +public class FileReaderWriter implements FileReader, FileWriter { + + private static final String NEWLINE = "\n"; + + @Override + public String read(final File file) throws IOException { + return String.join(NEWLINE, + Files.readAllLines(file.toPath(), StandardCharsets.UTF_8)); + } + + @Override + public void write( + final File file, + final String content + ) throws IOException { + if (!file.exists()) { + try { + file.createNewFile(); + } catch (final IOException e) { + throw new IOException("Could not create file", e); + } + } + Files.write(file.toPath(), + content.getBytes(StandardCharsets.UTF_8), + StandardOpenOption.TRUNCATE_EXISTING + ); + } +} diff --git a/src/main/java/net/kemitix/files/FileWriter.java b/src/main/java/net/kemitix/files/FileWriter.java new file mode 100644 index 0000000..4512f82 --- /dev/null +++ b/src/main/java/net/kemitix/files/FileWriter.java @@ -0,0 +1,21 @@ +package net.kemitix.files; + + +import java.io.File; +import java.io.IOException; + +public interface FileWriter { + + /** + * Writes the content to the file path, replacing any existing file. + * + *

File content will be written using UTF-8 encoding.

+ * + * @param file the file to write + * @param content the content to write + */ + void write( + File file, + String content + ) throws IOException; +} diff --git a/src/test/java/net/kemitix/files/FileReaderWriterTests.java b/src/test/java/net/kemitix/files/FileReaderWriterTests.java new file mode 100644 index 0000000..07578a4 --- /dev/null +++ b/src/test/java/net/kemitix/files/FileReaderWriterTests.java @@ -0,0 +1,70 @@ +package net.kemitix.files; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +public class FileReaderWriterTests { + + private final String line1 = UUID.randomUUID().toString(); + private final String line2 = UUID.randomUUID().toString(); + private final String body = line1 + "\n" + line2; + private final FileWriter writer = new FileReaderWriter(); + private final FileReader reader = new FileReaderWriter(); + @TempDir + Path directory; + private File file; + + @BeforeEach + void setUp() { + file = directory.resolve(UUID.randomUUID().toString()) + .toFile(); + } + + @Test + @DisplayName("Create a new file then read it again") + public void readTheNewlyCreatedFile() throws IOException { + //when + writer.write(file, body); + final String read = reader.read(file); + //then + assertThat(read).isEqualTo(body); + } + + @Test + @DisplayName("Replace an existing file then read it again") + public void readTheReplacedFile() throws IOException { + //given + writer.write(file, "Original file content"); + //when + writer.write(file, body); + final String read = reader.read(file); + //then + assertThat(read).isEqualTo(body); + } + + @Test + @DisplayName("When write to directory does not exist then throw exception") + public void writeToMissingDirThrows() throws IOException { + //given + directory.toFile().setReadOnly(); + //then + try { + assertThatExceptionOfType(IOException.class) + .isThrownBy(() -> + writer.write(file, body)) + .withMessage("Could not create file"); + } finally { + directory.toFile().setWritable(true); + } + } +} \ No newline at end of file