Initial import
This commit is contained in:
commit
5ae4bbf231
11 changed files with 457 additions and 0 deletions
41
.github/GitHub-Actions.org
vendored
Normal file
41
.github/GitHub-Actions.org
vendored
Normal 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
53
.github/NOTES
vendored
Normal 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
BIN
.github/codesigning.asc.gpg
vendored
Normal file
Binary file not shown.
28
.github/settings.xml
vendored
Normal file
28
.github/settings.xml
vendored
Normal 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
25
.github/workflows/maven-build.yml
vendored
Normal 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
38
.github/workflows/sonatype-deploy.yml
vendored
Normal 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
44
README.md
Normal 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
66
pom.xml
Normal 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>
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package net.kemitix.properties.typed;
|
||||
|
||||
public interface TypedProperty<T> {
|
||||
}
|
|
@ -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> {
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue