Initial import

This commit is contained in:
Paul Campbell 2020-04-06 16:04:06 +01:00
commit 5ae4bbf231
11 changed files with 457 additions and 0 deletions

41
.github/GitHub-Actions.org vendored Normal file
View file

@ -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.

53
.github/NOTES vendored Normal file
View file

@ -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

BIN
.github/codesigning.asc.gpg vendored Normal file

Binary file not shown.

28
.github/settings.xml vendored Normal file
View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0">
<servers>
<server>
<id>sonatype-nexus-snapshots</id>
<username>${env.NEXUS_USERNAME}</username>
<password>${env.NEXUS_PASSWORD}</password>
</server>
<server>
<id>sonatype-nexus-staging</id>
<username>${env.NEXUS_USERNAME}</username>
<password>${env.NEXUS_PASSWORD}</password>
</server>
</servers>
<profiles>
<profile>
<id>gpg-sign</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<gpg.executable>gpg</gpg.executable>
<gpg.keyname>${env.GPG_KEYNAME}</gpg.keyname>
<gpg.passphrase>${env.GPG_PASSPHRASE}</gpg.passphrase>
</properties>
</profile>
</profiles>
</settings>

25
.github/workflows/maven-build.yml vendored Normal file
View file

@ -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

38
.github/workflows/sonatype-deploy.yml vendored Normal file
View file

@ -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 }}

44
README.md Normal file
View file

@ -0,0 +1,44 @@
# TypedProperties
![Sonatype Nexus (Release)](https://img.shields.io/nexus/r/https/oss.sonatype.org/net.kemitix/typed-properties.svg?style=for-the-badge)
![Maven Central](https://img.shields.io/maven-central/v/net.kemitix/typed-properties.svg?style=for-the-badge)
![Java 8](https://img.shields.io/badge/Java-8-success "Compatible with Java 8")
![Java 11](https://img.shields.io/badge/Java-11-success "Compatible with Java 11")
![Java 13](https://img.shields.io/badge/Java-13-success "Compatible with Java 13")
![Java 14](https://img.shields.io/badge/Java-14-important "Unresolved Issues with Java 14")
A strongly-typed, immutable, simple alternative to `Properties` or, sanity-save-you, `Map<String, Object>`.
## Usage
```java
import net.kemitix.properties.typed.TypedProperties;
import net.kemitix.properties.typed.TypedProperty;
class Usage {
public static void main(String[] args){
// Create a new instance
TypedProperties properties = TypedProperties.create();
// Set a value - creates a new instance - immutability
// parameters: key, value
// - key is a class/interface, that extends TypedProperty<T>,
// where T is the type of the value.
// - value is of type T
TypedProperties updated = properties.with(Name.class, "Name");
// Retrieve a value
// parameters: key, value-type
// - key is same as above
// - value-type is the type of the value - must match key
Optional<String> result = updated.find(Name.class, String.class);
}
}
public interface Name
extends TypedProperty<String> {}
```
```java
```

66
pom.xml Normal file
View file

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.kemitix</groupId>
<artifactId>kemitix-parent</artifactId>
<version>5.3.0</version>
<relativePath/>
</parent>
<artifactId>typed-properties</artifactId>
<version>DEV-SNAPSHOT</version>
<inceptionYear>2020</inceptionYear>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<tiles-maven-plugin.version>2.16</tiles-maven-plugin.version>
<kemitix-maven-tiles.version>2.5.0</kemitix-maven-tiles.version>
<kemitix-checkstyle.version>5.4.0</kemitix-checkstyle.version>
<junit.version>5.6.0</junit.version>
<assertj.version>3.15.0</assertj.version>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>io.repaint.maven</groupId>
<artifactId>tiles-maven-plugin</artifactId>
<version>${tiles-maven-plugin.version}</version>
<extensions>true</extensions>
<configuration>
<tiles>
<tile>
net.kemitix.tiles:all:${kemitix-maven-tiles.version}
</tile>
<tile>
net.kemitix.checkstyle:tile:${kemitix-checkstyle.version}
</tile>
</tiles>
</configuration>
</plugin>
</plugins>
</build>
</project>

View file

@ -0,0 +1,42 @@
package net.kemitix.properties.typed;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public class TypedProperties {
private static final TypedProperties EMPTY_PROPERTIES =
new TypedProperties(Collections.emptyMap());
private final Map<Class<? extends TypedProperty<?>>, Object> valueMap;
private TypedProperties(
final Map<Class<? extends TypedProperty<?>>, Object> valueMap
) {
this.valueMap = valueMap;
}
public static TypedProperties create() {
return EMPTY_PROPERTIES;
}
public <T> TypedProperties with(
final Class<? extends TypedProperty<T>> key,
final T value
) {
final Map<Class<? extends TypedProperty<?>>, Object> updated =
new HashMap<>(valueMap);
updated.put(key, value);
return new TypedProperties(updated);
}
public <T> Optional<T> find(
final Class<? extends TypedProperty<T>> key,
final Class<T> tClass
) {
return Optional.ofNullable(valueMap.get(key))
.map(tClass::cast);
}
}

View file

@ -0,0 +1,4 @@
package net.kemitix.properties.typed;
public interface TypedProperty<T> {
}

View file

@ -0,0 +1,116 @@
package net.kemitix.properties.typed;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
public class TypedPropertiesTest {
@Test
@DisplayName("Can set and then find a value")
public void canSetAndFindValue() {
//given
final TypedProperties typedProperties =
TypedProperties.create()
.with(Name.class, "Name");
//when
final Optional<String> result =
typedProperties.find(Name.class, String.class);
//then
assertThat(result).contains("Name");
}
@Test
@DisplayName("Find a value not set is empty")
public void findMissingIsEmpty() {
//given
final TypedProperties typedProperties = TypedProperties.create();
//when
final Optional<String> result =
typedProperties.find(Name.class, String.class);
//then
assertThat(result).isEmpty();
}
@Test
@DisplayName("After replacing a value, find returns the new value")
public void replaceValueFindIsNewValue() {
//given
final TypedProperties typedProperties =
TypedProperties.create()
.with(Name.class, "Original Name")
.with(Name.class, "Updated Name");
//when
final Optional<String> result =
typedProperties.find(Name.class, String.class);
//then
assertThat(result).contains("Updated Name");
}
@Test
@DisplayName("Immutable - setting value creates new instance")
public void immutableCreatesNewInstanceOnSet() {
//given
final TypedProperties original = TypedProperties.create();
//when
final TypedProperties updated = original.with(Name.class, "Name");
//then
assertThat(updated).isNotSameAs(original);
}
@Test
@DisplayName("Immutable - original instance lacks new value")
public void immutableOriginalInstanceUnmodified() {
//given
final TypedProperties original = TypedProperties.create();
final TypedProperties updated = original.with(Name.class, "Name");
//when
final Optional<String> result = original.find(Name.class, String.class);
//then
assertThat(result).isEmpty();
}
@Test
@DisplayName("Multiple values of same underlying type are unique")
public void multipleValuesOfSameBaseTypeAreSafe() {
//given
final TypedProperties typedProperties =
TypedProperties.create()
.with(Name.class, "Name")
.with(Email.class, "name@example.org");
//when
final Optional<String> name =
typedProperties.find(Name.class, String.class);
final Optional<String> email =
typedProperties.find(Email.class, String.class);
//then
assertThat(name).contains("Name");
assertThat(email).contains("name@example.org");
}
// @Test
// @DisplayName("Attempt to find a value by wrong type is a compilation error")
// public void findWithWrongType() {
// TypedProperties.create()
// .set(Name.class, "Original Name")
// .find(Name.class, Integer.class);
// }
// @Test
// @DisplayName("Attempt to set a value by wrong type is a compilation error")
// public void setWithWrongType() {
// TypedProperties.create()
// .set(Name.class, 42);
// }
private interface Name
extends TypedProperty<String> {
}
private interface Email
extends TypedProperty<String> {
}
}