commit 83df2cc6cc1520bf5039e7bf5eaf4a3cfd93582f Author: Paul Campbell Date: Sat Mar 28 20:55:16 2020 +0000 Initial Import 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 0000000..f5c71e0 Binary files /dev/null and b/.github/codesigning.asc.gpg differ 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