Merge pull request 'upgrade-jdk17' (#3) from upgrade-jdk17 into develop

Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
2026-07-12 22:21:19 -05:00
86 changed files with 7990 additions and 5100 deletions

3
.gitignore vendored
View File

@@ -12,3 +12,6 @@
**/*.iml **/*.iml
**/git.properties **/git.properties
.metadata/ .metadata/
id_rsa*
named*
short*

4
Jenkinsfile vendored
View File

@@ -109,7 +109,7 @@ def getSha1() {
def mvn(args) { def mvn(args) {
withMaven( withMaven(
maven: 'maven-3.6.1', maven: 'maven-3.9.4',
globalMavenSettingsConfig: 'locusworks-settings' globalMavenSettingsConfig: 'locusworks-settings'
) { ) {
@@ -183,4 +183,4 @@ def SetVersion( v ) {
} }
} }
return this return this

129
pom.xml
View File

@@ -18,11 +18,11 @@
</scm> </scm>
<properties> <properties>
<log4j.version>2.14.1</log4j.version> <log4j.version>2.26.1</log4j.version>
<slf4j.version>1.7.32</slf4j.version> <slf4j.version>2.0.18</slf4j.version>
<jackson.version>2.12.5</jackson.version> <jackson.version>2.22.1</jackson.version>
<maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target> <maven.compiler.target>21</maven.compiler.target>
<nexus.repo>https://nexus.locusworks.net</nexus.repo> <nexus.repo>https://nexus.locusworks.net</nexus.repo>
</properties> </properties>
@@ -30,29 +30,38 @@
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version> <version>3.5.6</version>
<configuration> <configuration>
<forkMode>always</forkMode> <argLine>@{argLine} --add-exports java.base/sun.security.jca=ALL-UNNAMED</argLine>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version> <version>3.15.0</version>
<configuration> <configuration>
<source>${maven.compiler.source}</source> <source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target> <target>${maven.compiler.target}</target>
<failOnError>true</failOnError> <failOnError>true</failOnError>
<compilerArgs> <compilerArgs>
<arg>-Xlint:all</arg> <arg>-Xlint:all</arg>
</compilerArgs> <arg>--add-exports</arg>
<arg>java.base/sun.security.jca=ALL-UNNAMED</arg>
</compilerArgs>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.owasp</groupId> <groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId> <artifactId>dependency-check-maven</artifactId>
<version>6.3.1</version> <version>12.2.2</version>
<configuration>
<skipProvidedScope>true</skipProvidedScope>
<skipTestScope>true</skipTestScope>
<failOnError>true</failOnError>
<versionCheckEnabled>true</versionCheckEnabled>
<nvdApiKey>e2eb1036-9b5b-4df9-95e7-0888be947011</nvdApiKey>
</configuration>
<executions> <executions>
<execution> <execution>
<goals> <goals>
@@ -61,20 +70,85 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.15</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>jacoco-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>PACKAGE</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>1.00</minimum>
</limit>
<limit>
<counter>BRANCH</counter>
<value>COVEREDRATIO</value>
<minimum>1.00</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins> </plugins>
</build> </build>
<dependencies> <dependencies>
<!-- Source: https://mvnrepository.com/artifact/commons-codec/commons-codec -->
<dependency> <dependency>
<groupId>junit</groupId> <groupId>commons-codec</groupId>
<artifactId>junit</artifactId> <artifactId>commons-codec</artifactId>
<version>4.13.2</version> <version>1.22.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>6.1.1</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>6.1.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.23.0</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.flywaydb</groupId> <groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId> <artifactId>flyway-core</artifactId>
<version>7.15.0</version> <version>12.10.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
@@ -103,14 +177,9 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.httpcomponents</groupId> <groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient</artifactId> <artifactId>httpclient5</artifactId>
<version>4.5.13</version> <version>5.6.2</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version>
</dependency> </dependency>
<!-- Jackson --> <!-- Jackson -->
@@ -122,7 +191,7 @@
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId> <artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version> <version>2.22</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
@@ -133,7 +202,7 @@
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
<version>2.8.8</version> <version>2.14.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
@@ -176,4 +245,4 @@
</pluginRepository> </pluginRepository>
</pluginRepositories> </pluginRepositories>
</project> </project>

View File

@@ -24,7 +24,7 @@ import java.nio.charset.Charset;
* *
* <p>See the Guava User Guide article on <a * <p>See the Guava User Guide article on <a
* href="https://github.com/google/guava/wiki/StringsExplained#charsets">{@code Charsets}</a>. * href="https://github.com/google/guava/wiki/StringsExplained#charsets">{@code Charsets}</a>.
* * <p>
* Please do not add new Charset references to this class, unless those character encodings are * Please do not add new Charset references to this class, unless those character encodings are
* part of the set required to be supported by all Java platform implementations! Any Charsets * part of the set required to be supported by all Java platform implementations! Any Charsets
* initialized here may cause unexpected delays when this class is loaded. See the Charset * initialized here may cause unexpected delays when this class is loaded. See the Charset
@@ -32,37 +32,38 @@ import java.nio.charset.Charset;
* *
*/ */
public final class Charsets { public final class Charsets {
private Charsets() {} private Charsets() {
}
/** /**
* US-ASCII: seven-bit ASCII, the Basic Latin block of the Unicode character set (ISO646-US). * US-ASCII: seven-bit ASCII, the Basic Latin block of the Unicode character set (ISO646-US).
*/ */
public static final Charset US_ASCII = java.nio.charset.StandardCharsets.US_ASCII; public static final Charset US_ASCII = java.nio.charset.StandardCharsets.US_ASCII;
/** /**
* ISO-8859-1: ISO Latin Alphabet Number 1 (ISO-LATIN-1). * ISO-8859-1: ISO Latin Alphabet Number 1 (ISO-LATIN-1).
*/ */
public static final Charset ISO_8859_1 = java.nio.charset.StandardCharsets.ISO_8859_1; public static final Charset ISO_8859_1 = java.nio.charset.StandardCharsets.ISO_8859_1;
/** /**
* UTF-8: eight-bit UCS Transformation Format. * UTF-8: eight-bit UCS Transformation Format.
*/ */
public static final Charset UTF_8 = java.nio.charset.StandardCharsets.UTF_8; public static final Charset UTF_8 = java.nio.charset.StandardCharsets.UTF_8;
/** /**
* UTF-16BE: sixteen-bit UCS Transformation Format, big-endian byte order. * UTF-16BE: sixteen-bit UCS Transformation Format, big-endian byte order.
*/ */
public static final Charset UTF_16BE = java.nio.charset.StandardCharsets.UTF_16BE; public static final Charset UTF_16BE = java.nio.charset.StandardCharsets.UTF_16BE;
/** /**
* UTF-16LE: sixteen-bit UCS Transformation Format, little-endian byte order. * UTF-16LE: sixteen-bit UCS Transformation Format, little-endian byte order.
*/ */
public static final Charset UTF_16LE = java.nio.charset.StandardCharsets.UTF_16LE; public static final Charset UTF_16LE = java.nio.charset.StandardCharsets.UTF_16LE;
/** /**
* UTF-16: sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order * UTF-16: sixteen-bit UCS Transformation Format, byte order identified by an optional byte-order
* mark. * mark.
*/ */
public static final Charset UTF_16 = java.nio.charset.StandardCharsets.UTF_16; public static final Charset UTF_16 = java.nio.charset.StandardCharsets.UTF_16;
} }

View File

@@ -10,15 +10,16 @@ import java.lang.annotation.Target;
* This is at the field level and will map the * This is at the field level and will map the
* filed name as the key and the field value as the value * filed name as the key and the field value as the value
* in the map * in the map
*
* @author Isaac Parenteau * @author Isaac Parenteau
* *
*/ */
@Target({ElementType.FIELD, ElementType.TYPE}) @Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
public @interface MapValue { public @interface MapValue {
String value() default ""; String value() default "";
boolean ignore() default false; boolean ignore() default false;
} }

View File

@@ -2,5 +2,5 @@ package net.locusworks.common.configuration;
@FunctionalInterface @FunctionalInterface
public interface ConfigurationCallback { public interface ConfigurationCallback {
void results(String msg); void results(String msg);
} }

View File

@@ -19,132 +19,136 @@ import net.locusworks.common.utils.Utils;
public class ConfigurationManager { public class ConfigurationManager {
private Properties configuration; private Properties configuration;
private Properties defaults = null; private Properties defaults = null;
private Path conf = null; private Path conf = null;
protected AES aes; protected AES aes;
private ConfigurationCallback callback; private ConfigurationCallback callback;
protected void init(String baseDir, String propertiesFile, ConfigurationCallback callback) throws IOException { protected void init(String baseDir, String propertiesFile, ConfigurationCallback callback) throws IOException {
init(baseDir, propertiesFile, this.getClass().getName().getBytes(Charsets.UTF_8), callback); init(baseDir, propertiesFile, this.getClass().getName().getBytes(Charsets.UTF_8), callback);
}
protected void init(String baseDir, String propertiesFile, byte[] aesKey, ConfigurationCallback callback) throws IOException {
aes = aesKey.length > 0 ? AES.createInstance(aesKey) : AES.createInstance();
this.callback = callback;
try {
defaults = PropertiesManager.loadConfiguration(this.getClass(), propertiesFile);
} catch (IOException ex) {
throw ex;
}
// create patchrepoConf File object
conf = Paths.get(baseDir).resolve(propertiesFile);
loadConfiguration();
}
private void loadConfiguration() {
// load the active config file
// ignore read error, we can continue with an empty configuration map
// and all default items will be added below and the file created
callbackMessage("Loading config file: " + conf);
try {
configuration = PropertiesManager.loadConfiguration(conf);
} catch (Exception e) {
callbackMessage("Config file: " + conf + " will be created from template");
configuration = new Properties();
} }
Map<String, String> results = PropertiesManager.addConfiguration(configuration, defaults); protected void init(String baseDir, String propertiesFile, byte[] aesKey, ConfigurationCallback callback) throws IOException {
boolean changed = !results.isEmpty(); aes = aesKey.length > 0 ? AES.createInstance(aesKey) : AES.createInstance();
if (!results.isEmpty()) { this.callback = callback;
StringBuilder sb = new StringBuilder("Added new configuration items:\n"); try {
for (Entry<String, String> entry : results.entrySet()) { defaults = PropertiesManager.loadConfiguration(this.getClass(), propertiesFile);
sb.append(String.format("%s=%s\n", entry.getKey(), entry.getValue())); } catch (IOException ex) {
} throw ex;
callbackMessage(sb.toString()); }
} // create patchrepoConf File object
conf = Paths.get(baseDir).resolve(propertiesFile);
results = PropertiesManager.removeConfiguration(configuration, defaults);
changed |= !results.isEmpty();
if (!results.isEmpty()) {
StringBuilder sb = new StringBuilder("Added new configuration items:\n");
for (Entry<String, String> entry : results.entrySet()) {
sb.append(String.format("%s=%s\n", entry.getKey(), entry.getValue()));
}
callbackMessage(sb.toString());
}
if (changed) {
PropertiesManager.saveConfiguration(configuration, conf, "Patch Repository properties file");
}
}
/**
* Save the configuration values to file
* @param confs Configuration property values to save
* @throws Exception general exception
*/
public void saveToConf(Properties confs) throws Exception {
PropertiesManager.saveConfiguration(confs, conf, conf.getFileName().toString());
callbackMessage("Saved config file: " + conf + ", " + confs.size() + " entries");
loadConfiguration();
}
public String getPropertyValue(String key) {
return getPropertyValue(key, null);
}
public String getPropertyValue(String key, String defaultValue) {
return configuration.containsKey(key) ? configuration.getProperty(key) : defaultValue;
}
public Properties getConfiguration() {
return configuration;
}
public void saveConfiguration(PersistableRequest request, Set<String> fieldsToSave, Set<String> excryptedFields) throws Exception {
if (fieldsToSave == null || fieldsToSave.isEmpty()) {
throw ApplicationException.generic("No fields to save were defined");
}
if (excryptedFields == null) {
excryptedFields = new HashSet<>();
}
try {
Properties props = new Properties();
//copy what is current in the configuration settings into the new properties file
configuration.entrySet().forEach(item -> props.setProperty(String.valueOf(item.getKey()), String.valueOf(item.getValue())));
boolean changed = false;
for (Field f : request.getClass().getDeclaredFields()) {
f.setAccessible(true);
String fieldName = f.getName();
String fieldValue = String.valueOf(f.get(request));
//Ensures we are only saving values that are already configured loadConfiguration();
if (!fieldsToSave.contains(fieldName)) continue; }
//Check to see if the old value changed private void loadConfiguration() {
String oldValue = props.getProperty(fieldName); // load the active config file
if (Utils.isNotValid(oldValue, fieldValue) || oldValue.equals(fieldValue)) { continue; } // ignore read error, we can continue with an empty configuration map
// and all default items will be added below and the file created
changed = true;
callbackMessage("Loading config file: " + conf);
fieldValue = excryptedFields.contains(fieldName) ? aes.encrypt(fieldValue) : fieldValue; try {
configuration = PropertiesManager.loadConfiguration(conf);
props.setProperty(fieldName, fieldValue); } catch (Exception e) {
} callbackMessage("Config file: " + conf + " will be created from template");
if (changed) { configuration = new Properties();
saveToConf(props); }
}
} catch (Exception ex) { Map<String, String> results = PropertiesManager.addConfiguration(configuration, defaults);
throw ApplicationException.actionNotPermitted(ex.getMessage()); boolean changed = !results.isEmpty();
if (!results.isEmpty()) {
StringBuilder sb = new StringBuilder("Added new configuration items:\n");
for (Entry<String, String> entry : results.entrySet()) {
sb.append(String.format("%s=%s\n", entry.getKey(), entry.getValue()));
}
callbackMessage(sb.toString());
}
results = PropertiesManager.removeConfiguration(configuration, defaults);
changed |= !results.isEmpty();
if (!results.isEmpty()) {
StringBuilder sb = new StringBuilder("Added new configuration items:\n");
for (Entry<String, String> entry : results.entrySet()) {
sb.append(String.format("%s=%s\n", entry.getKey(), entry.getValue()));
}
callbackMessage(sb.toString());
}
if (changed) {
PropertiesManager.saveConfiguration(configuration, conf, "Patch Repository properties file");
}
}
/**
* Save the configuration values to file
*
* @param confs Configuration property values to save
* @throws Exception general exception
*/
public void saveToConf(Properties confs) throws Exception {
PropertiesManager.saveConfiguration(confs, conf, conf.getFileName().toString());
callbackMessage("Saved config file: " + conf + ", " + confs.size() + " entries");
loadConfiguration();
}
public String getPropertyValue(String key) {
return getPropertyValue(key, null);
}
public String getPropertyValue(String key, String defaultValue) {
return configuration.containsKey(key) ? configuration.getProperty(key) : defaultValue;
}
public Properties getConfiguration() {
return configuration;
}
public void saveConfiguration(PersistableRequest request, Set<String> fieldsToSave, Set<String> excryptedFields) throws Exception {
if (fieldsToSave == null || fieldsToSave.isEmpty()) {
throw ApplicationException.generic("No fields to save were defined");
}
if (excryptedFields == null) {
excryptedFields = new HashSet<>();
}
try {
Properties props = new Properties();
//copy what is current in the configuration settings into the new properties file
configuration.entrySet().forEach(item -> props.setProperty(String.valueOf(item.getKey()), String.valueOf(item.getValue())));
boolean changed = false;
for (Field f : request.getClass().getDeclaredFields()) {
f.setAccessible(true);
String fieldName = f.getName();
String fieldValue = String.valueOf(f.get(request));
//Ensures we are only saving values that are already configured
if (!fieldsToSave.contains(fieldName)) continue;
//Check to see if the old value changed
String oldValue = props.getProperty(fieldName);
if (Utils.isNotValid(oldValue, fieldValue) || oldValue.equals(fieldValue)) {
continue;
}
changed = true;
fieldValue = excryptedFields.contains(fieldName) ? aes.encrypt(fieldValue) : fieldValue;
props.setProperty(fieldName, fieldValue);
}
if (changed) {
saveToConf(props);
}
} catch (Exception ex) {
throw ApplicationException.actionNotPermitted(ex.getMessage());
}
}
private void callbackMessage(String msg) {
if (callback != null) callback.results(msg);
} }
}
private void callbackMessage(String msg) {
if (callback != null) callback.results(msg);
}
} }

View File

@@ -15,139 +15,147 @@ import java.util.Properties;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import net.locusworks.common.immutables.Pair; import net.locusworks.common.immutables.Pair;
import net.locusworks.common.immutables.Unit;
/** /**
* Properties manager class to help load and read properties * Properties manager class to help load and read properties
*
* @author Isaac Parenteau * @author Isaac Parenteau
* @version 1.0.0 * @version 2.0.0
* @date 02/15/2018 * @date 09/17/2023
*/ */
public class PropertiesManager { public class PropertiesManager {
/** /**
* Load a configuration from resource * Load a configuration from a resource
* @param clazz class loader *
* @param src source of the file * @param clazz class loader
* @return properties * @param src source of the file
* @throws IOException Exception thrown the file can't be read * @return properties
*/ * @throws IOException Exception thrown the file can't be read
public static Properties loadConfiguration(Class<?> clazz, String src) throws IOException { */
InputStream is = clazz.getResourceAsStream(src); public static Properties loadConfiguration(Class<?> clazz, String src) throws IOException {
if (is == null) { InputStream is = clazz.getResourceAsStream(src);
is = clazz.getClassLoader().getResourceAsStream(src); if (is == null) {
} is = clazz.getClassLoader().getResourceAsStream(src);
if (is == null) { }
return null; if (is == null) {
} return null;
BufferedReader br = new BufferedReader(new InputStreamReader(is, UTF_8)); }
return loadConfiguration(br); BufferedReader br = new BufferedReader(new InputStreamReader(is, UTF_8));
} return loadConfiguration(br);
/**
* Load configuration from a file. This method has been deprecated and may be removed in
* future released. Please use java.nio.Path
* @param file File to load
* @return properties
* @throws IOException Exception thrown the file can't be read
*/
@Deprecated
public static Properties loadConfiguration(File file) throws IOException {
return loadConfiguration(file.toPath());
}
/**
* Load configuration from a file.
* @param path the path to the file
* @return properties
* @throws IOException Exception thrown the file can't be read
*/
public static Properties loadConfiguration(Path path) throws IOException {
if (Files.notExists(path)) {
return new Properties();
} }
try(BufferedReader br = Files.newBufferedReader(path)) { /**
return loadConfiguration(br); * Load configuration from a file. This method has been deprecated and may be removed in
* future released. Please use java.nio.Path
*
* @param file File to load
* @return properties
* @throws IOException Exception thrown the file can't be read
*/
@Deprecated
public static Properties loadConfiguration(File file) throws IOException {
return loadConfiguration(file.toPath());
} }
}
/** /**
* Load configuration from a buffered reader * Load configuration from a file.
* @param reader Buffered reader to read the properties values from *
* @return properties * @param path the path to the file
* @throws IOException Exception thrown the file can't be read * @return properties
*/ * @throws IOException Exception thrown the file can't be read
public static Properties loadConfiguration(BufferedReader reader) throws IOException { */
Properties config = new Properties(); public static Properties loadConfiguration(Path path) throws IOException {
config.load(reader); if (Files.notExists(path)) {
return config; return new Properties();
} }
/** try (BufferedReader br = Files.newBufferedReader(path)) {
* Add configurations from one properties file to another return loadConfiguration(br);
* @param to Properties file to copy values to }
* @param from Properties file to copy values from }
* @return a map containing the results of the values added
*/ /**
public static Map<String, String> addConfiguration(Properties to, Properties from) { * Load configuration from a buffered reader
Map<String, String> results = from.entrySet() *
.stream() * @param reader Buffered reader to read the properties values from
.filter(entry -> !to.containsKey(entry.getKey())) * @return properties
.map(entry -> { * @throws IOException Exception thrown the file can't be read
String key = entry.getKey().toString(); */
String value = entry.getValue().toString(); public static Properties loadConfiguration(BufferedReader reader) throws IOException {
to.put(key, value); Properties config = new Properties();
return new Pair<String, String>(key, value); config.load(reader);
}) return config;
.collect(Collectors.toMap(key -> key.getValue1(), value -> value.getValue2())); }
return results; /**
} * Add configurations from one properties file to another
*
/** * @param to Properties file to copy values to
* Removes configuration values that are not present in the comparedTo * @param from Properties file to copy values from
* @param from Properties file to remove values from * @return a map containing the results of the values added
* @param comparedTo Properties file to compare to */
* @return a map containing the results of the values removed public static Map<String, String> addConfiguration(Properties to, Properties from) {
*/
public static Map<String, String> removeConfiguration(Properties from, Properties comparedTo) { return from.entrySet()
Map<String, String> results = from.keySet() .stream()
.stream() .filter(entry -> !to.containsKey(entry.getKey()))
.filter(key -> !comparedTo.containsKey(key)) //only get the items that are not in the comparedTo properties .map(entry -> {
.map(key -> new Pair<String, String>(String.valueOf(key), String.valueOf(from.get(key)))) String key = entry.getKey().toString();
.collect(Collectors.toList()) //Create a list of paired items (key value) of the items that were filtered String value = entry.getValue().toString();
.stream() to.put(key, value);
.map(pair -> { //remove those pairs from the from properties return new Pair<String, String>(key, value);
from.remove(pair.getValue1()); })
return pair; .collect(Collectors.toMap(Unit::getValue1, Pair::getValue2));
}) }
.collect(Collectors.toMap(key -> key.getValue1(), value -> value.getValue2())); //create a map of what was removed
/**
return results; * Removes configuration values that are not present in the comparedTo
} *
* @param from Properties file to remove values from
/** * @param comparedTo Properties file to compare to
* <p>Save the properties file to disk.</p> * @return a map containing the results of the values removed
* <p><b>This method has been depecreated and could be removed in future release.<br/>Please use java.nio.Path</b></p> */
* @param props Properties file to save public static Map<String, String> removeConfiguration(Properties from, Properties comparedTo) {
* @param fileToSave File to save to
* @param comment Any comments to add return from.keySet()
*/ .stream()
@Deprecated .filter(key -> !comparedTo.containsKey(key)) //only get the items that are not in the comparedTo properties
public static void saveConfiguration(Properties props, File fileToSave, String comment) { .map(key -> new Pair<String, String>(String.valueOf(key), String.valueOf(from.get(key))))
saveConfiguration(props, fileToSave.toPath(), comment); .toList() //Create a list of paired items (key value) of the items that were filtered
.stream()
} .peek(pair -> { //remove those pairs from the from properties
from.remove(pair.getValue1());
/** })
* <p>Save the properties file to disk.</p> .collect(Collectors.toMap(Unit::getValue1, Pair::getValue2));
* @param props Properties file to save }
* @param fileToSave File to save to
* @param comment Any comments to add /**
*/ * <p>Save the properties file to disk.</p>
public static void saveConfiguration(Properties props, Path fileToSave, String comment) { * <p><b>This method has been depecreated and could be removed in future release.<br/>Please use java.nio.Path</b></p>
try(OutputStream fos = Files.newOutputStream(fileToSave)) { *
props.store(fos, comment == null ? "" : comment); * @param props Properties file to save
} catch (IOException ex) { * @param fileToSave File to save to
throw new RuntimeException(ex.getMessage(), ex); * @param comment Any comments to add
*/
@Deprecated
public static void saveConfiguration(Properties props, File fileToSave, String comment) {
saveConfiguration(props, fileToSave.toPath(), comment);
}
/**
* <p>Save the properties file to disk.</p>
*
* @param props Properties file to save
* @param fileToSave File to save to
* @param comment Any comments to add
*/
public static void saveConfiguration(Properties props, Path fileToSave, String comment) {
try (OutputStream fos = Files.newOutputStream(fileToSave)) {
props.store(fos, comment == null ? "" : comment);
} catch (IOException ex) {
throw new RuntimeException(ex.getMessage(), ex);
}
} }
}
} }

View File

@@ -7,23 +7,22 @@ import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidAlgorithmParameterException; import java.security.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64; import java.util.Base64;
import net.locusworks.common.utils.RandomString; import net.locusworks.common.utils.RandomString;
import net.locusworks.common.utils.Utils; import net.locusworks.common.utils.Utils;
import static net.locusworks.common.Charsets.UTF_8; import static net.locusworks.common.Charsets.UTF_8;
/** /**
* AES encryption/decryption class * AES encryption/decryption class
* This class will encrypt/decrypt data. The encryption key is never known. * This class will encrypt/decrypt data. The encryption key is never known.
* Instead it is is generated by the provided seed. As long as the seed stays the same * Instead, it is generated by the provided seed. As long as the seed stays the same
* the key will remain the same and the encryption/decryption will work. This * the key will remain the same and the encryption/decryption will work. This
* provides and added security. * provides and added security.
*
* @author Isaac Parenteau * @author Isaac Parenteau
* @version 1.0.0 * @version 1.0.0
* @date 02/15/2018 * @date 02/15/2018
@@ -31,132 +30,133 @@ import static net.locusworks.common.Charsets.UTF_8;
*/ */
public class AES { public class AES {
private static final String ENCRYPTION_TYPE = "AES"; private static final String ENCRYPTION_TYPE = "AES";
private static final String ENCRYPTION_ALGORITH = "AES/CBC/PKCS5Padding"; private static final String ENCRYPTION_ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String PROVIDER = "SunJCE"; private static final String PROVIDER = "SunJCE";
private static final String ALGORITHM = "SHA1PRNG"; private static final String ALGORITHM = "SHA1PRNG";
private Cipher cipher; private Cipher cipher;
private SecretKeySpec secretKeySpec; private SecretKeySpec secretKeySpec;
private IvParameterSpec ivParamSpec; private IvParameterSpec ivParamSpec;
private String seed; private String seed;
private void initSecureKey(String seed) { private RandomString randomizer;
try {
SecureRandom sr = getSecureRandom(seed); private void initSecureKey(String seed) {
KeyGenerator generator = KeyGenerator.getInstance(ENCRYPTION_TYPE); try {
generator.init(128, sr); SecureRandom sr = getSecureRandom(seed);
init(generator.generateKey().getEncoded(), sr); KeyGenerator generator = KeyGenerator.getInstance(ENCRYPTION_TYPE);
} catch (Exception ex) { generator.init(128, sr);
System.err.println(ex);
throw new IllegalArgumentException("Unable to initalize encryption:", ex); randomizer = RandomString.newInstance(sr);
init(generator.generateKey().getEncoded());
} catch (Exception ex) {
throw new IllegalArgumentException("Unable to initialize encryption:", ex);
}
} }
}
/** /**
* Initializes the secure random object to be the same across all platforms * Initializes the secure random object to be the same across all platforms
* with regard to provider and algorithm used * with regard to provider and algorithm used
* @param seed Seed to initialize SecureRandom with *
* @return SecureRandom object * @param seed Seed to initialize SecureRandom with
* @throws NoSuchAlgorithmException thrown when algorithm can't be used * @return SecureRandom object
* @throws NoSuchProviderException thrown when the provider cant be found * @throws NoSuchAlgorithmException thrown when algorithm can't be used
*/ */
private static SecureRandom getSecureRandom(String seed) throws NoSuchAlgorithmException { private static SecureRandom getSecureRandom(String seed) throws NoSuchAlgorithmException {
SecureRandom sr = SecureRandom.getInstance(ALGORITHM); SecureRandom sr = SecureRandom.getInstance(ALGORITHM);
sr.setSeed(seed.getBytes(UTF_8)); sr.setSeed(seed.getBytes(UTF_8));
return sr; return sr;
} }
/** /**
* Initialize the aes engine * Initialize the aes engine
* @param key secret key to use *
* @param sr * @param key secret key to use
*/ */
private void init(final byte[] key, SecureRandom sr) { private void init(final byte[] key) {
try { try {
this.cipher = Cipher.getInstance(ENCRYPTION_ALGORITH, PROVIDER); this.cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM, PROVIDER);
this.secretKeySpec = new SecretKeySpec(key, ENCRYPTION_TYPE); this.secretKeySpec = new SecretKeySpec(key, ENCRYPTION_TYPE);
this.ivParamSpec = new IvParameterSpec(RandomString.getBytes(16, sr)); this.ivParamSpec = new IvParameterSpec(randomizer.getBytes(16));
} catch (Exception ex) { } catch (Exception ex) {
System.err.println(ex); throw new IllegalArgumentException("Unable to initialize encryption:", ex);
throw new IllegalArgumentException("Unable to initalize encryption:", ex); }
} }
}
/*** /***
* Encrypt a text string * Encrypt a text string
* @param plainText String to encrypt * @param plainText String to encrypt
* @return encrypted string * @return encrypted string
*/ */
public String encrypt(String plainText) { public String encrypt(String plainText) {
if (Utils.isEmptyString(plainText)) { if (Utils.isEmptyString(plainText)) {
plainText = ""; plainText = "";
}
try {
cipher.init(Cipher.ENCRYPT_MODE, this.secretKeySpec, this.ivParamSpec);
byte[] cypherText = cipher.doFinal(plainText.getBytes(UTF_8));
return new String(Base64.getEncoder().encode(cypherText), UTF_8);
} catch (Exception ex) {
throw new IllegalArgumentException(ex.getMessage(), ex);
}
} }
try {
cipher.init(Cipher.ENCRYPT_MODE, this.secretKeySpec, this.ivParamSpec);
byte[] cypherText = cipher.doFinal(plainText.getBytes(UTF_8));
return new String(Base64.getEncoder().encode(cypherText), UTF_8);
} catch (Exception ex) {
throw new IllegalArgumentException(ex.getMessage(), ex);
}
}
/*** /***
* Decrypt an encrypted string * Decrypt an encrypted string
* @param cipherString encrypted string to decrypt * @param cipherString encrypted string to decrypt
* @return unecrypted string * @return unecrypted string
*/ */
public String decrypt(String cipherString) { public String decrypt(String cipherString) {
if (Utils.isEmptyString(cipherString)) { if (Utils.isEmptyString(cipherString)) {
return ""; return "";
} }
byte[] cipherText = Base64.getDecoder().decode(cipherString.getBytes(UTF_8)); byte[] cipherText = Base64.getDecoder().decode(cipherString.getBytes(UTF_8));
try { try {
cipher.init(Cipher.DECRYPT_MODE, this.secretKeySpec, this.ivParamSpec); cipher.init(Cipher.DECRYPT_MODE, this.secretKeySpec, this.ivParamSpec);
return new String(cipher.doFinal(cipherText), UTF_8); return new String(cipher.doFinal(cipherText), UTF_8);
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) { } catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException |
throw new IllegalArgumentException(e.getMessage(), e); InvalidAlgorithmParameterException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
} }
}
public AES setSeed(String seed) {
if (this.seed == null || !this.seed.equals(seed)) {
initSecureKey(seed);
}
this.seed = seed;
return this;
}
public final String getSeed() {
return this.seed;
}
public static AES createInstance() { public AES withSeed(String seed) {
return createInstance(RandomString.getString(16)); if (this.seed == null || !this.seed.equals(seed)) {
} initSecureKey(seed);
}
public static AES createInstance(byte[] byteSeed) { this.seed = seed;
String seed = new String(byteSeed, UTF_8); return this;
return createInstance(seed);
}
public static AES createInstance(String seed) {
AES aes = new AES();
aes.setSeed(seed);
return aes;
}
public static void main(String[] args) throws NoSuchAlgorithmException {
if (args == null || !(args.length > 0)) {
throw new IllegalArgumentException("No args provided. Need password as argument");
} }
if (args.length % 2 == 0) {
System.out.println(AES.createInstance(String.valueOf(args[1])).decrypt(String.valueOf(args[0]))); public final String getSeed() {
} else { return this.seed;
System.out.println(AES.createInstance().encrypt(String.valueOf(args[0]))); }
public static AES createInstance() {
return createInstance(RandomString.getInstance().getString(16));
}
public static AES createInstance(byte[] byteSeed) {
String seed = new String(byteSeed, UTF_8);
return createInstance(seed);
}
public static AES createInstance(String seed) {
return new AES().withSeed(seed);
}
public static void main(String[] args) throws NoSuchAlgorithmException {
if (args == null || !(args.length > 0)) {
throw new IllegalArgumentException("No args provided. Need password as argument");
}
if (args.length % 2 == 0) {
System.out.println(AES.createInstance(String.valueOf(args[1])).decrypt(String.valueOf(args[0])));
} else {
System.out.println(AES.createInstance().encrypt(String.valueOf(args[0])));
}
} }
}
} }

View File

@@ -1,30 +1,32 @@
package net.locusworks.common.crypto; package net.locusworks.common.crypto;
import java.io.Serial;
import java.security.PrivateKey; import java.security.PrivateKey;
import net.locusworks.common.Charsets; import net.locusworks.common.Charsets;
public class AESKey implements PrivateKey { public class AESKey implements PrivateKey {
private static final long serialVersionUID = -8452357427706386362L;
private String seed;
public AESKey(String seed) {
this.seed = seed;
}
@Override @Serial
public String getAlgorithm() { private static final long serialVersionUID = -8452357427706386362L;
return "aes"; private final String seed;
}
@Override public AESKey(String seed) {
public String getFormat() { this.seed = seed;
return "aes-seed"; }
}
@Override @Override
public byte[] getEncoded() { public String getAlgorithm() {
return this.seed.getBytes(Charsets.UTF_8); return "aes";
} }
@Override
public String getFormat() {
return "aes-seed";
}
@Override
public byte[] getEncoded() {
return this.seed.getBytes(Charsets.UTF_8);
}
} }

View File

@@ -15,31 +15,31 @@ import static net.locusworks.common.utils.Utils.get;
import static net.locusworks.common.utils.Utils.size; import static net.locusworks.common.utils.Utils.size;
public class AESKeySpec extends EncodedKeySpec { public class AESKeySpec extends EncodedKeySpec {
private static final String AES_MARKER = "aes-seed";
public AESKeySpec(byte[] encodedKey) { private static final String AES_MARKER = "aes-seed";
super(encodedKey);
} public AESKeySpec(byte[] encodedKey) {
super(encodedKey);
public AESKey generateKey() throws InvalidKeySpecException {
try {
byte[] data = this.getEncoded();
InputStream stream = new ByteArrayInputStream(data);
Iterable<String> parts = Arrays.asList(IOUtils.toString(stream, Charsets.UTF_8).split(" "));
checkArguments(size(parts) == 2 && AES_MARKER.equals(get(parts, 0)), "Bad format, should be: aes-seed AAB3...");
stream = new ByteArrayInputStream(Base64.getDecoder().decode(String.valueOf(get(parts, 1))));
String marker = IOUtils.toString(stream, Charsets.UTF_8);
return new AESKey(marker);
} catch (Exception ex) {
throw new InvalidKeySpecException(ex);
} }
}
@Override public AESKey generateKey() throws InvalidKeySpecException {
public String getFormat() { try {
return "aes"; byte[] data = this.getEncoded();
} InputStream stream = new ByteArrayInputStream(data);
Iterable<String> parts = Arrays.asList(IOUtils.toString(stream, Charsets.UTF_8).split(" "));
checkArguments(size(parts) == 2 && AES_MARKER.equals(get(parts, 0)), "Bad format, should be: aes-seed AAB3...");
stream = new ByteArrayInputStream(Base64.getDecoder().decode(String.valueOf(get(parts, 1))));
String marker = IOUtils.toString(stream, Charsets.UTF_8);
return new AESKey(marker);
} catch (Exception ex) {
throw new InvalidKeySpecException(ex);
}
}
@Override
public String getFormat() {
return "aes";
}
} }

View File

@@ -14,25 +14,25 @@ import sun.security.jca.GetInstance.Instance;
public class EncryptionKeyFactory extends KeyFactory { public class EncryptionKeyFactory extends KeyFactory {
protected EncryptionKeyFactory(KeyFactorySpi keyFacSpi, Provider provider, String algorithm) { protected EncryptionKeyFactory(KeyFactorySpi keyFacSpi, Provider provider, String algorithm) {
super(keyFacSpi, provider, algorithm); super(keyFacSpi, provider, algorithm);
} }
public PrivateKey generatePrivateKey(KeySpec keySpec) throws InvalidKeySpecException { public PrivateKey generatePrivateKey(KeySpec keySpec) throws InvalidKeySpecException {
if (keySpec instanceof AESKeySpec) { if (keySpec instanceof AESKeySpec) {
return ((AESKeySpec)keySpec).generateKey(); return ((AESKeySpec) keySpec).generateKey();
}
return super.generatePrivate(keySpec);
}
public PublicKey generatePublicKey(KeySpec keySpec) throws InvalidKeySpecException {
keySpec = keySpec instanceof SSHEncodedKeySpec ? ((SSHEncodedKeySpec) keySpec).convertToRSAPubKeySpec() : keySpec;
return super.generatePublic(keySpec);
}
public static EncryptionKeyFactory getInstance(String algorithm) throws NoSuchAlgorithmException {
Instance instance = GetInstance.getInstance("KeyFactory", KeyFactorySpi.class, algorithm);
return new EncryptionKeyFactory((KeyFactorySpi) instance.impl, instance.provider, algorithm);
} }
return super.generatePrivate(keySpec);
}
public PublicKey generatePublicKey(KeySpec keySpec) throws InvalidKeySpecException {
keySpec = keySpec instanceof SSHEncodedKeySpec ? ((SSHEncodedKeySpec)keySpec).convertToRSAPubKeySpec() : keySpec;
return super.generatePublic(keySpec);
}
public static EncryptionKeyFactory getInstance(String algorithm) throws NoSuchAlgorithmException {
Instance instance = GetInstance.getInstance("KeyFactory", KeyFactorySpi.class, algorithm);
return new EncryptionKeyFactory((KeyFactorySpi)instance.impl, instance.provider, algorithm);
}
} }

View File

@@ -9,181 +9,178 @@ import javax.crypto.spec.PBEKeySpec;
/** /**
* The type Hash salt. * The type Hash salt.
*
* @author Isaac Parenteau * @author Isaac Parenteau
* @version 1.0.0 * @version 1.0.0
* @date 02/15/2018 * @date 02/15/2018
*/ */
public class HashSalt { public class HashSalt {
/** /**
* The constant PBKDF2_ALGORITHM. * The constant PBKDF2_ALGORITHM.
*/ */
private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256"; private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256";
/** /**
* The constant SALT_BYTE_SIZE. * The constant SALT_BYTE_SIZE.
*/ */
private static final int SALT_BYTE_SIZE = 24; private static final int SALT_BYTE_SIZE = 24;
/** /**
* The constant HASH_BYTE_SIZE. * The constant HASH_BYTE_SIZE.
*/ */
private static final int HASH_BYTE_SIZE = 24; private static final int HASH_BYTE_SIZE = 24;
/** /**
* The constant PBKDF2_ITERATIONS. * The constant PBKDF2_ITERATIONS.
*/ */
private static final int PBKDF2_ITERATIONS = 1000; private static final int PBKDF2_ITERATIONS = 1000;
/** /**
* The constant ITERATION_INDEX. * The constant ITERATION_INDEX.
*/ */
private static final int ITERATION_INDEX = 0; private static final int ITERATION_INDEX = 0;
/** /**
* The constant SALT_INDEX. * The constant SALT_INDEX.
*/ */
private static final int SALT_INDEX = 1; private static final int SALT_INDEX = 1;
/** /**
* The constant PBKDF2_INDEX. * The constant PBKDF2_INDEX.
*/ */
private static final int PBKDF2_INDEX = 2; private static final int PBKDF2_INDEX = 2;
/** /**
* Returns a salted PBKDF2 hash of the password. * Returns a salted PBKDF2 hash of the password.
* *
* @param password the password to hash * @param password the password to hash
* * @return a salted PBKDF2 hash of the password
* @return a salted PBKDF2 hash of the password * @throws NoSuchAlgorithmException the no such algorithm exception
* @throws NoSuchAlgorithmException the no such algorithm exception * @throws InvalidKeySpecException the invalid key spec exception
* @throws InvalidKeySpecException the invalid key spec exception */
*/ public static String createHash(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {
public static String createHash(String password) throws NoSuchAlgorithmException, InvalidKeySpecException { return createHash(password.toCharArray());
return createHash(password.toCharArray());
}
/**
* Returns a salted PBKDF2 hash of the password.
*
* @param password the password to hash
*
* @return a salted PBKDF2 hash of the password
* @throws NoSuchAlgorithmException the no such algorithm exception
* @throws InvalidKeySpecException the invalid key spec exception
*/
public static String createHash(char[] password) throws NoSuchAlgorithmException, InvalidKeySpecException {
// Generate a random salt
SecureRandom random = new SecureRandom();
byte[] salt = new byte[SALT_BYTE_SIZE];
random.nextBytes(salt);
// Hash the password
byte[] hash = pbkdf2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);
// format iterations:salt:hash
return PBKDF2_ITERATIONS + ":" + toHex(salt) + ":" + toHex(hash);
}
/**
* Validates a password using a hash.
*
* @param password the password to check
* @param correctHash the hash of the valid password
*
* @return true if the password is correct, false if not
* @throws NoSuchAlgorithmException the no such algorithm exception
* @throws InvalidKeySpecException the invalid key spec exception
*/
public static boolean validatePassword(String password, String correctHash) throws NoSuchAlgorithmException, InvalidKeySpecException {
return validatePassword(password.toCharArray(), correctHash);
}
/**
* Validates a password using a hash.
*
* @param password the password to check
* @param correctHash the hash of the valid password
*
* @return true if the password is correct, false if not
* @throws NoSuchAlgorithmException the no such algorithm exception
* @throws InvalidKeySpecException the invalid key spec exception
*/
public static boolean validatePassword(char[] password, String correctHash) throws NoSuchAlgorithmException, InvalidKeySpecException {
// Decode the hash into its parameters
String[] params = correctHash.split(":");
int iterations = Integer.parseInt(params[ITERATION_INDEX]);
byte[] salt = fromHex(params[SALT_INDEX]);
byte[] hash = fromHex(params[PBKDF2_INDEX]);
// Compute the hash of the provided password, using the same salt,
// iteration count, and hash length
byte[] testHash = pbkdf2(password, salt, iterations, hash.length);
// Compare the hashes in constant time. The password is correct if
// both hashes match.
return slowEquals(hash, testHash);
}
/**
* Compares two byte arrays in length-constant time. This comparison method
* is used so that password hashes cannot be extracted from an on-line
* system using a timing attack and then attacked off-line.
*
* @param a the first byte array
* @param b the second byte array
* @return true if both byte arrays are the same, false if not
*/
private static boolean slowEquals(byte[] a, byte[] b) {
int diff = a.length ^ b.length;
for(int i = 0; i < a.length && i < b.length; i++)
diff |= a[i] ^ b[i];
return diff == 0;
}
/**
* Computes the PBKDF2 hash of a password.
*
* @param password the password to hash.
* @param salt the salt
* @param iterations the iteration count (slowness factor)
* @param bytes the length of the hash to compute in bytes
* @return the PBDKF2 hash of the password
*/
private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8);
SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
return skf.generateSecret(spec).getEncoded();
}
/**
* Converts a string of hexadecimal characters into a byte array.
*
* @param hex the hex string
* @return the hex string decoded into a byte array
*/
private static byte[] fromHex(String hex) {
byte[] binary = new byte[hex.length() / 2];
for(int i = 0; i < binary.length; i++) {
binary[i] = (byte)Integer.parseInt(hex.substring(2*i, 2*i+2), 16);
} }
return binary;
}
/** /**
* Converts a byte array into a hexadecimal string. * Returns a salted PBKDF2 hash of the password.
* *
* @param array the byte array to convert * @param password the password to hash
* @return a length*2 character string encoding the byte array * @return a salted PBKDF2 hash of the password
*/ * @throws NoSuchAlgorithmException the no such algorithm exception
private static String toHex(byte[] array) { * @throws InvalidKeySpecException the invalid key spec exception
BigInteger bi = new BigInteger(1, array); */
String hex = bi.toString(16); public static String createHash(char[] password) throws NoSuchAlgorithmException, InvalidKeySpecException {
int paddingLength = (array.length * 2) - hex.length(); // Generate a random salt
if(paddingLength > 0) SecureRandom random = new SecureRandom();
return String.format("%0" + paddingLength + "d", 0) + hex; byte[] salt = new byte[SALT_BYTE_SIZE];
else random.nextBytes(salt);
return hex;
}
public static void main (String[] args) throws Exception { // Hash the password
if (args == null || !(args.length > 0)) { byte[] hash = pbkdf2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);
throw new IllegalArgumentException("No args provided. Need password as argument"); // format iterations:salt:hash
return PBKDF2_ITERATIONS + ":" + toHex(salt) + ":" + toHex(hash);
}
/**
* Validates a password using a hash.
*
* @param password the password to check
* @param correctHash the hash of the valid password
* @return true if the password is correct, false if not
* @throws NoSuchAlgorithmException the no such algorithm exception
* @throws InvalidKeySpecException the invalid key spec exception
*/
public static boolean validatePassword(String password, String correctHash) throws NoSuchAlgorithmException, InvalidKeySpecException {
return validatePassword(password.toCharArray(), correctHash);
}
/**
* Validates a password using a hash.
*
* @param password the password to check
* @param correctHash the hash of the valid password
* @return true if the password is correct, false if not
* @throws NoSuchAlgorithmException the no such algorithm exception
* @throws InvalidKeySpecException the invalid key spec exception
*/
public static boolean validatePassword(char[] password, String correctHash) throws NoSuchAlgorithmException, InvalidKeySpecException {
// Decode the hash into its parameters
String[] params = correctHash.split(":");
int iterations = Integer.parseInt(params[ITERATION_INDEX]);
byte[] salt = fromHex(params[SALT_INDEX]);
byte[] hash = fromHex(params[PBKDF2_INDEX]);
// Compute the hash of the provided password, using the same salt,
// iteration count, and hash length
byte[] testHash = pbkdf2(password, salt, iterations, hash.length);
// Compare the hashes in constant time. The password is correct if
// both hashes match.
return slowEquals(hash, testHash);
}
/**
* Compares two byte arrays in length-constant time. This comparison method
* is used so that password hashes cannot be extracted from an on-line
* system using a timing attack and then attacked off-line.
*
* @param a the first byte array
* @param b the second byte array
* @return true if both byte arrays are the same, false if not
*/
private static boolean slowEquals(byte[] a, byte[] b) {
int diff = a.length ^ b.length;
for (int i = 0; i < a.length && i < b.length; i++)
diff |= a[i] ^ b[i];
return diff == 0;
}
/**
* Computes the PBKDF2 hash of a password.
*
* @param password the password to hash.
* @param salt the salt
* @param iterations the iteration count (slowness factor)
* @param bytes the length of the hash to compute in bytes
* @return the PBDKF2 hash of the password
*/
private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int bytes) throws NoSuchAlgorithmException, InvalidKeySpecException {
PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8);
SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
return skf.generateSecret(spec).getEncoded();
}
/**
* Converts a string of hexadecimal characters into a byte array.
*
* @param hex the hex string
* @return the hex string decoded into a byte array
*/
private static byte[] fromHex(String hex) {
byte[] binary = new byte[hex.length() / 2];
for (int i = 0; i < binary.length; i++) {
binary[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
}
return binary;
}
/**
* Converts a byte array into a hexadecimal string.
*
* @param array the byte array to convert
* @return a length*2 character string encoding the byte array
*/
private static String toHex(byte[] array) {
BigInteger bi = new BigInteger(1, array);
String hex = bi.toString(16);
int paddingLength = (array.length * 2) - hex.length();
if (paddingLength > 0)
return String.format("%0" + paddingLength + "d", 0) + hex;
else
return hex;
}
public static void main(String[] args) throws Exception {
if (args == null || !(args.length > 0)) {
throw new IllegalArgumentException("No args provided. Need password as argument");
}
System.out.println(HashSalt.createHash(String.valueOf(args[0])));
} }
System.out.println(HashSalt.createHash(String.valueOf(args[0])));
}
} }

View File

@@ -27,187 +27,195 @@ import static net.locusworks.common.utils.Splitter.fixedLengthSplit;
import static java.lang.String.join; import static java.lang.String.join;
public class KeyFile implements AutoCloseable { public class KeyFile implements AutoCloseable {
public enum EncryptionType { public enum EncryptionType {
RSA, RSA,
AES, AES,
SSH SSH
}
private Key key;
private String description;
private Writer writer;
private EncryptionType encryptionType;
public KeyFile(Key key) {
this(key, null);
}
public KeyFile(Key key, String description) {
this(key, description, EncryptionType.valueOf(key.getAlgorithm().toUpperCase()));
}
public KeyFile(Key key, String description, EncryptionType encryptionType) {
this.key = key;
this.description = description;
this.encryptionType = encryptionType;
}
private KeyFile() {}
private void loadFromFile(String fileName) {
if (Utils.isEmptyString(fileName)) return;
this.key = null;
try {
File keyFile = new File(fileName);
if (!keyFile.exists()) {
throw new IllegalArgumentException(String.format("Unable to find file with name %s. Please check path", fileName));
}
String contentStr = IOUtils.toString(new FileInputStream(keyFile), Charsets.UTF_8);
boolean rsaFormat = !contentStr.startsWith("ssh-rsa") && !contentStr.startsWith("aes-seed");
if (rsaFormat) {
contentStr = contentStr.replace("-----.*", "");
}
contentStr = contentStr.replace("\\r?\\n", "");
byte[] content = rsaFormat ? Base64.getDecoder().decode(contentStr): contentStr.getBytes(Charsets.UTF_8);
EncryptionKeyFactory kf = EncryptionKeyFactory.getInstance("RSA");
List<KeySpecHelper> keySpecs = Utils.toList(
new KeySpecHelper(new AESKeySpec(content), true, EncryptionType.AES),
new KeySpecHelper(new SSHEncodedKeySpec(content), false, EncryptionType.SSH),
new KeySpecHelper(new PKCS8EncodedKeySpec(content), true, EncryptionType.RSA),
new KeySpecHelper(new X509EncodedKeySpec(content), false, EncryptionType.RSA)
);
for (KeySpecHelper ksh : keySpecs) {
try {
this.key = ksh.isPrivate() ? kf.generatePrivateKey(ksh.getKeySpec()) : kf.generatePublicKey(ksh.getKeySpec());
this.encryptionType = ksh.getEncryptionType();
break;
} catch (NullPointerException | InvalidKeySpecException ikse) { continue; }
}
throw new InvalidKeySpecException(String.format("Unable to determine if file %s is a private or public key. Not type of PKCS8, X509, SSH or AES spec", fileName));
} catch (Exception ex) {
throw new RuntimeException(ex);
} }
}
private Key key;
public void write(String fileName) { private String description;
try { private Writer writer;
String data;
switch(this.encryptionType) {
case AES:
data = String.format("%s %s", this.key.getFormat(), Base64.getEncoder().encodeToString(this.key.getEncoded()));
IOUtils.writeStringToFile(fileName, data);
break;
case SSH:
try (DataOutputStreamHelper dosh = new DataOutputStreamHelper()) {
getSSHPubKeyBytes(this.key)
.forEach(handleExceptionWrapper(item ->{
dosh.writeInt(item.length);
dosh.write(item);
}));
data = String.format("ssh-rsa", dosh.base64Encoded(), this.description);
IOUtils.writeStringToFile(fileName, data);
}
break;
default:
writePem(fileName);
}
} catch (Exception ex) {
throw new IllegalArgumentException(ex);
}
}
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
if (Utils.isEmptyString(this.description)) {
return this.key instanceof PrivateKey ? "PRIVATE KEY" : "PUBLIC KEY";
}
return this.description;
}
public Key getKey() {
return this.key;
}
@Override
public void close() {
try {
if (this.writer != null) {
this.writer.flush();
this.writer.close();
this.writer = null;
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static KeyFile read(String fileName) {
try (KeyFile kf = new KeyFile()) {
kf.loadFromFile(fileName);
return kf;
}
}
private void writePem(String fileName) {
try {
String desc = getDescription();
this.writer = new OutputStreamWriter(new FileOutputStream(fileName), Charsets.UTF_8);
this.writer.write(String.format("-----BEGIN RSA %s-----", desc));
String encoded = Base64.getEncoder().encodeToString(this.key.getEncoded());
String out = join("\n", fixedLengthSplit(60).split(encoded));
this.writer.write(out);
this.writer.write(String.format("-----END RSA %s-----", desc));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
private List<byte[]> getSSHPubKeyBytes(Key key) {
RSAPublicKey rpk = (RSAPublicKey)key;
return Arrays.asList("ssh-rsa".getBytes(Charsets.UTF_8),
rpk.getPublicExponent().toByteArray(),
rpk.getModulus().toByteArray()
);
}
private static class KeySpecHelper {
private KeySpec keySpec;
private boolean isPrivate;
private EncryptionType encryptionType; private EncryptionType encryptionType;
public KeySpecHelper(KeySpec keySpec, boolean isPrivate, EncryptionType encryptionType) { public KeyFile(Key key) {
super(); this(key, null);
this.keySpec = keySpec;
this.isPrivate = isPrivate;
this.encryptionType = encryptionType;
} }
public synchronized final KeySpec getKeySpec() { public KeyFile(Key key, String description) {
return keySpec; this(key, description, EncryptionType.valueOf(key.getAlgorithm().toUpperCase()));
} }
public synchronized final boolean isPrivate() { public KeyFile(Key key, String description, EncryptionType encryptionType) {
return isPrivate; this.key = key;
this.description = description;
this.encryptionType = encryptionType;
} }
public synchronized final EncryptionType getEncryptionType() { private KeyFile() {
return encryptionType; }
private void loadFromFile(String fileName) {
if (Utils.isEmptyString(fileName)) return;
this.key = null;
try {
File keyFile = new File(fileName);
if (!keyFile.exists()) {
throw new IllegalArgumentException(String.format("Unable to find file with name %s. Please check path", fileName));
}
String contentStr;
try (FileInputStream input = new FileInputStream(keyFile)) {
contentStr = IOUtils.toString(input, Charsets.UTF_8);
}
boolean rsaFormat = !contentStr.startsWith("ssh-rsa") && !contentStr.startsWith("aes-seed");
if (rsaFormat) {
contentStr = contentStr.replaceAll("-----[^-]+-----", "");
contentStr = contentStr.replaceAll("\\s", "");
} else {
contentStr = contentStr.trim();
}
byte[] content = rsaFormat ? Base64.getDecoder().decode(contentStr) : contentStr.getBytes(Charsets.UTF_8);
EncryptionKeyFactory kf = EncryptionKeyFactory.getInstance("RSA");
List<KeySpecHelper> keySpecs = Utils.toList(
new KeySpecHelper(new AESKeySpec(content), true, EncryptionType.AES),
new KeySpecHelper(new SSHEncodedKeySpec(content), false, EncryptionType.SSH),
new KeySpecHelper(new PKCS8EncodedKeySpec(content), true, EncryptionType.RSA),
new KeySpecHelper(new X509EncodedKeySpec(content), false, EncryptionType.RSA)
);
for (KeySpecHelper ksh : keySpecs) {
try {
this.key = ksh.isPrivate() ? kf.generatePrivateKey(ksh.getKeySpec()) : kf.generatePublicKey(ksh.getKeySpec());
this.encryptionType = ksh.getEncryptionType();
break;
} catch (NullPointerException | InvalidKeySpecException ikse) {
continue;
}
}
if (this.key == null) {
throw new InvalidKeySpecException(String.format("Unable to determine if file %s is a private or public key. Not type of PKCS8, X509, SSH or AES spec", fileName));
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public void write(String fileName) {
try {
String data;
switch (this.encryptionType) {
case AES:
data = String.format("%s %s", this.key.getFormat(), Base64.getEncoder().encodeToString(this.key.getEncoded()));
IOUtils.writeStringToFile(fileName, data);
break;
case SSH:
try (DataOutputStreamHelper dosh = new DataOutputStreamHelper()) {
getSSHPubKeyBytes(this.key)
.forEach(handleExceptionWrapper(item -> {
dosh.writeInt(item.length);
dosh.write(item);
}));
data = String.format("ssh-rsa %s %s", dosh.base64Encoded(), this.description);
IOUtils.writeStringToFile(fileName, data);
}
break;
default:
writePem(fileName);
}
} catch (Exception ex) {
throw new IllegalArgumentException(ex);
}
}
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
if (Utils.isEmptyString(this.description)) {
return this.key instanceof PrivateKey ? "PRIVATE KEY" : "PUBLIC KEY";
}
return this.description;
}
public Key getKey() {
return this.key;
}
@Override
public void close() {
try {
if (this.writer != null) {
this.writer.flush();
this.writer.close();
this.writer = null;
}
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static KeyFile read(String fileName) {
try (KeyFile kf = new KeyFile()) {
kf.loadFromFile(fileName);
return kf;
}
}
private void writePem(String fileName) {
try {
String desc = getDescription();
this.writer = new OutputStreamWriter(new FileOutputStream(fileName), Charsets.UTF_8);
this.writer.write(String.format("-----BEGIN RSA %s-----", desc));
String encoded = Base64.getEncoder().encodeToString(this.key.getEncoded());
String out = join("\n", fixedLengthSplit(60).split(encoded));
this.writer.write(out);
this.writer.write(String.format("-----END RSA %s-----", desc));
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
private List<byte[]> getSSHPubKeyBytes(Key key) {
RSAPublicKey rpk = (RSAPublicKey) key;
return Arrays.asList("ssh-rsa".getBytes(Charsets.UTF_8),
rpk.getPublicExponent().toByteArray(),
rpk.getModulus().toByteArray()
);
}
private static class KeySpecHelper {
private final KeySpec keySpec;
private final boolean isPrivate;
private final EncryptionType encryptionType;
public KeySpecHelper(KeySpec keySpec, boolean isPrivate, EncryptionType encryptionType) {
super();
this.keySpec = keySpec;
this.isPrivate = isPrivate;
this.encryptionType = encryptionType;
}
public synchronized final KeySpec getKeySpec() {
return keySpec;
}
public synchronized final boolean isPrivate() {
return isPrivate;
}
public synchronized final EncryptionType getEncryptionType() {
return encryptionType;
}
} }
}
} }

View File

@@ -21,136 +21,136 @@ import net.locusworks.common.crypto.KeyFile.EncryptionType;
import net.locusworks.common.io.IOUtils; import net.locusworks.common.io.IOUtils;
public class RSA { public class RSA {
private static final String ENCRYPTION_TYPE = "RSA";
private static final String ENCRYPTION_ALGORITHM = "RSA/ECB/PKCS10PADDING";
private static final String PROVIDER = "SunJCE";
private static final String RANDOM_ALGORITHM = "SHA1PRNG";
private static final int PADDING_LENGTH = 11;
private static final int DEFAULT_KEY_LENGTH = 2048;
public static KeyPair generateKeyPair() {
return generateKeyPair(DEFAULT_KEY_LENGTH);
}
public static KeyPair generateKeyPair(int keyLength) {
try {
KeyPairGenerator kpg = KeyPairGenerator.getInstance(ENCRYPTION_TYPE);
SecureRandom sr = SecureRandom.getInstance(RANDOM_ALGORITHM);
kpg.initialize(keyLength, sr);
return kpg.genKeyPair();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static KeyPair loadPrivateKey(String privateKeyFileName) {
return loadKeyPair(null, privateKeyFileName);
}
public static KeyPair loadPublicKey(String publicKeyFileName) {
return loadKeyPair(publicKeyFileName, null);
}
public static KeyPair loadKeyPair(String publicKey, String privateKey) {
KeyFile pubKey = KeyFile.read(publicKey);
KeyFile prvKey = KeyFile.read(privateKey);
return new KeyPair((PublicKey)pubKey.getKey(), (PrivateKey)prvKey.getKey());
}
public static boolean generateAndWriteSSHKeys() {
KeyPair kp = generateKeyPair();
return writePrivateKey(kp) && writePublicKey(kp, true);
}
public static boolean generateAndWriteKeyPair() {
return generateAndWriteKeyPair(DEFAULT_KEY_LENGTH);
}
public static boolean generateAndWriteKeyPair(String keyPairName) {
return generateAndWriteKeyPair(keyPairName, DEFAULT_KEY_LENGTH);
}
public static boolean generateAndWriteKeyPair(int keyLength) {
return generateAndWriteKeyPair("id_rsa", keyLength);
}
public static boolean generateAndWriteKeyPair(String keyPairName, int keyLength) {
KeyPair kp = generateKeyPair(keyLength);
return writePrivateKey(kp, keyPairName, "PRIVATE KEY") && writePublicKey(kp, keyPairName + ".pub", "PUBLIC KEY");
}
public static boolean writePrivateKey(KeyPair kp) {
return writePrivateKey(kp, "id_rsa", "PRIVATE KEY");
}
public static boolean writePrivateKey(KeyPair kp, String fileName, String description) {
return writePemFile(kp.getPrivate(), fileName, description);
}
public static boolean writePublicKey(KeyPair kp) {
return writePublicKey(kp, false);
}
public static boolean writePublicKey(KeyPair kp, boolean sshFormat) {
return writePublicKey(kp, "id_rsa.pub", "PUBLIC KEY", sshFormat);
}
public static boolean writePublicKey(KeyPair kp, String fileName, String description) {
return writePemFile(kp.getPublic(), fileName, description, false);
}
public static boolean writePublicKey(KeyPair kp, String fileName, String description, boolean sshFormat) {
return writePemFile(kp.getPublic(), fileName, description, sshFormat);
}
public static int calculateRequiredKeyLength(String message) {
return (message.getBytes(Charsets.UTF_8).length + PADDING_LENGTH) * 8;
}
public static String encrypt(Key key, String message) {
try {
calculateKeyLength(key, message);
Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, key);
CipherInputStream cis = new CipherInputStream(new ByteArrayInputStream(message.getBytes(Charsets.UTF_8)), cipher);
byte[] encrypted = IOUtils.toByteArray(cis);
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static String decrypt(Key key, String message) {
try {
Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM, PROVIDER);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decoded = Base64.getDecoder().decode(message);
byte[] plainTextArray = cipher.doFinal(decoded);
return new String(plainTextArray, Charsets.UTF_8);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static boolean writePemFile(Key key, String fileName, String description) { private static final String ENCRYPTION_TYPE = "RSA";
return writePemFile(key, fileName, description, false); private static final String ENCRYPTION_ALGORITHM = "RSA/ECB/PKCS1PADDING";
} private static final String PROVIDER = "SunJCE";
private static final String RANDOM_ALGORITHM = "SHA1PRNG";
private static boolean writePemFile(Key key, String fileName, String description, boolean sshFormat) {
try(KeyFile kf = sshFormat ? new KeyFile(key, description, EncryptionType.SSH) : new KeyFile(key, description)) { private static final int PADDING_LENGTH = 11;
kf.write(fileName); private static final int DEFAULT_KEY_LENGTH = 2048;
public static KeyPair generateKeyPair() {
return generateKeyPair(DEFAULT_KEY_LENGTH);
} }
return Files.exists(Paths.get(fileName));
} public static KeyPair generateKeyPair(int keyLength) {
try {
private static void calculateKeyLength(Key key, String message) throws IllegalBlockSizeException { KeyPairGenerator kpg = KeyPairGenerator.getInstance(ENCRYPTION_TYPE);
int keyLength = ((RSAKey)key).getModulus().bitLength(); SecureRandom sr = SecureRandom.getInstance(RANDOM_ALGORITHM);
int requiredKeyLength = calculateRequiredKeyLength(message); kpg.initialize(keyLength, sr);
if (keyLength < requiredKeyLength) { return kpg.genKeyPair();
throw new IllegalBlockSizeException(String.format("RSA key size of %d is not large enough to encrypt message of length %d. " } catch (Exception ex) {
+ "Increase key size to a minimum of %d and re-encrypt with new key", keyLength, message.length(), requiredKeyLength)); throw new RuntimeException(ex);
}
}
public static KeyPair loadPrivateKey(String privateKeyFileName) {
return loadKeyPair(null, privateKeyFileName);
}
public static KeyPair loadPublicKey(String publicKeyFileName) {
return loadKeyPair(publicKeyFileName, null);
}
public static KeyPair loadKeyPair(String publicKey, String privateKey) {
KeyFile pubKey = KeyFile.read(publicKey);
KeyFile prvKey = KeyFile.read(privateKey);
return new KeyPair((PublicKey) pubKey.getKey(), (PrivateKey) prvKey.getKey());
}
public static boolean generateAndWriteSSHKeys() {
KeyPair kp = generateKeyPair();
return writePrivateKey(kp) && writePublicKey(kp, true);
}
public static boolean generateAndWriteKeyPair() {
return generateAndWriteKeyPair(DEFAULT_KEY_LENGTH);
}
public static boolean generateAndWriteKeyPair(String keyPairName) {
return generateAndWriteKeyPair(keyPairName, DEFAULT_KEY_LENGTH);
}
public static boolean generateAndWriteKeyPair(int keyLength) {
return generateAndWriteKeyPair("id_rsa", keyLength);
}
public static boolean generateAndWriteKeyPair(String keyPairName, int keyLength) {
KeyPair kp = generateKeyPair(keyLength);
return writePrivateKey(kp, keyPairName, "PRIVATE KEY") && writePublicKey(kp, keyPairName + ".pub", "PUBLIC KEY");
}
public static boolean writePrivateKey(KeyPair kp) {
return writePrivateKey(kp, "id_rsa", "PRIVATE KEY");
}
public static boolean writePrivateKey(KeyPair kp, String fileName, String description) {
return writePemFile(kp.getPrivate(), fileName, description);
}
public static boolean writePublicKey(KeyPair kp) {
return writePublicKey(kp, false);
}
public static boolean writePublicKey(KeyPair kp, boolean sshFormat) {
return writePublicKey(kp, "id_rsa.pub", "PUBLIC KEY", sshFormat);
}
public static boolean writePublicKey(KeyPair kp, String fileName, String description) {
return writePemFile(kp.getPublic(), fileName, description, false);
}
public static boolean writePublicKey(KeyPair kp, String fileName, String description, boolean sshFormat) {
return writePemFile(kp.getPublic(), fileName, description, sshFormat);
}
public static int calculateRequiredKeyLength(String message) {
return (message.getBytes(Charsets.UTF_8).length + PADDING_LENGTH) * 8;
}
public static String encrypt(Key key, String message) {
try {
calculateKeyLength(key, message);
Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, key);
CipherInputStream cis = new CipherInputStream(new ByteArrayInputStream(message.getBytes(Charsets.UTF_8)), cipher);
byte[] encrypted = IOUtils.toByteArray(cis);
return Base64.getEncoder().encodeToString(encrypted);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static String decrypt(Key key, String message) {
try {
Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM, PROVIDER);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decoded = Base64.getDecoder().decode(message);
byte[] plainTextArray = cipher.doFinal(decoded);
return new String(plainTextArray, Charsets.UTF_8);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private static boolean writePemFile(Key key, String fileName, String description) {
return writePemFile(key, fileName, description, false);
}
private static boolean writePemFile(Key key, String fileName, String description, boolean sshFormat) {
try (KeyFile kf = sshFormat ? new KeyFile(key, description, EncryptionType.SSH) : new KeyFile(key, description)) {
kf.write(fileName);
}
return Files.exists(Paths.get(fileName));
}
private static void calculateKeyLength(Key key, String message) throws IllegalBlockSizeException {
int keyLength = ((RSAKey) key).getModulus().bitLength();
int requiredKeyLength = calculateRequiredKeyLength(message);
if (keyLength < requiredKeyLength) {
throw new IllegalBlockSizeException(String.format("RSA key size of %d is not large enough to encrypt message of length %d. "
+ "Increase key size to a minimum of %d and re-encrypt with new key", keyLength, message.length(), requiredKeyLength));
}
} }
}
} }

View File

@@ -18,48 +18,47 @@ import static net.locusworks.common.utils.Utils.get;
import static net.locusworks.common.utils.Utils.size; import static net.locusworks.common.utils.Utils.size;
public class SSHEncodedKeySpec extends EncodedKeySpec { public class SSHEncodedKeySpec extends EncodedKeySpec {
private static final String SSH_MARKER = "ssh-rsa";
public SSHEncodedKeySpec(byte[] encodedKey) { private static final String SSH_MARKER = "ssh-rsa";
super(encodedKey);
}
public RSAPublicKeySpec convertToRSAPubKeySpec() throws InvalidKeySpecException {
try {
byte[] data = this.getEncoded();
InputStream stream = new ByteArrayInputStream(data);
Iterable<String> parts = Arrays.asList(IOUtils.toString(stream, Charsets.UTF_8).split(" "));
checkArguments(size(parts) >= 2 && SSH_MARKER.equals(get(parts, 0)), "Bad format, should be: ssh-rsa AAB3...");
stream = new ByteArrayInputStream(Base64.getDecoder().decode(String.valueOf(get(parts, 1))));
String marker = new String(readLengthFirst(stream));
checkArguments(SSH_MARKER.equals(marker), "Looking for marker %s but received %s", SSH_MARKER, marker);
BigInteger publicExponent = new BigInteger(readLengthFirst(stream));
BigInteger modulus = new BigInteger(readLengthFirst(stream));
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus, publicExponent);
return keySpec;
} catch (Exception ex) {
throw new InvalidKeySpecException(ex);
}
}
@Override public SSHEncodedKeySpec(byte[] encodedKey) {
public String getFormat() { super(encodedKey);
return null; }
}
public RSAPublicKeySpec convertToRSAPubKeySpec() throws InvalidKeySpecException {
private static byte[] readLengthFirst(InputStream in) throws IOException { try {
int[] bytes = new int[] {in.read(), in.read(), in.read(), in.read()}; byte[] data = this.getEncoded();
int length = 0; InputStream stream = new ByteArrayInputStream(data);
int shift = 24; Iterable<String> parts = Arrays.asList(IOUtils.toString(stream, Charsets.UTF_8).split(" "));
for (int i = 0; i < bytes.length; i++) {
length += bytes[i] << shift; checkArguments(size(parts) >= 2 && SSH_MARKER.equals(get(parts, 0)), "Bad format, should be: ssh-rsa AAB3...");
shift -= 8; stream = new ByteArrayInputStream(Base64.getDecoder().decode(String.valueOf(get(parts, 1))));
String marker = new String(readLengthFirst(stream));
checkArguments(SSH_MARKER.equals(marker), "Looking for marker %s but received %s", SSH_MARKER, marker);
BigInteger publicExponent = new BigInteger(readLengthFirst(stream));
BigInteger modulus = new BigInteger(readLengthFirst(stream));
return new RSAPublicKeySpec(modulus, publicExponent);
} catch (Exception ex) {
throw new InvalidKeySpecException(ex);
}
}
@Override
public String getFormat() {
return null;
}
private static byte[] readLengthFirst(InputStream in) throws IOException {
int[] bytes = new int[]{in.read(), in.read(), in.read(), in.read()};
int length = 0;
int shift = 24;
for (int aByte : bytes) {
length += aByte << shift;
shift -= 8;
}
byte[] val = new byte[length];
in.read(val);
return val;
} }
byte[] val = new byte[length];
in.read(val);
return val;
}
} }

View File

@@ -1,108 +1,111 @@
package net.locusworks.common.exceptions; package net.locusworks.common.exceptions;
import java.io.Serial;
/*** /***
* Custom exception class for the patch repository * Custom exception class for the patch repository
* @author Isaac Parenteau * @author Isaac Parenteau
* *
*/ */
public class ApplicationException extends Exception { public class ApplicationException extends Exception {
private final Integer code; private final Integer code;
boolean success = false; boolean success = false;
private static final long serialVersionUID = 1L; @Serial
private static final long serialVersionUID = 1L;
public static ApplicationException egregiousServer() {
return new ApplicationException(9001, "Something went wrong. Please see logs for details");
}
public static ApplicationException invalidCreds() { public static ApplicationException egregiousServer() {
return new ApplicationException(9001, "Invalid credentials provided"); return new ApplicationException(9001, "Something went wrong. Please see logs for details");
} }
public static ApplicationException notLoggedIn() { public static ApplicationException invalidCreds() {
return new ApplicationException(9002, "Not logged in"); return new ApplicationException(9001, "Invalid credentials provided");
} }
public static ApplicationException invalidEmailAddress() { public static ApplicationException notLoggedIn() {
return new ApplicationException(9005, "Invalid email address"); return new ApplicationException(9002, "Not logged in");
} }
public static ApplicationException actionNotPermitted() { public static ApplicationException invalidEmailAddress() {
return new ApplicationException(9007, "Action not permitted"); return new ApplicationException(9005, "Invalid email address");
} }
public static ApplicationException actionNotPermitted(String message) {
return new ApplicationException(9007, "Action not permitted: " + message);
}
public static ApplicationException passwordsNotEqual() { public static ApplicationException actionNotPermitted() {
return new ApplicationException(9008, "Passwords do not match"); return new ApplicationException(9007, "Action not permitted");
} }
public static ApplicationException unAuthorized() {
return new ApplicationException(9009, "unauthorized");
}
public static ApplicationException duplicateEntry(String message) {
return new ApplicationException(9100, message);
}
public static ApplicationException duplicateEntry(String messageFmt, Object... args) {
return new ApplicationException(9100, String.format(messageFmt, args));
}
public static ApplicationException noEntryExists(String message) {
return new ApplicationException(9101, message);
}
public static ApplicationException noEntryExists(String messageFmt, Object... items) {
return new ApplicationException(9101, String.format(messageFmt, items));
}
public static ApplicationException constraintViolation(String message) {
return new ApplicationException(9102, message);
}
public static ApplicationException constraintViolation(String messageFmt, Object... items) {
return new ApplicationException(9102, String.format(messageFmt, items));
}
public static ApplicationException illegalArgument(String message) {
return new ApplicationException(9103, message);
}
public static ApplicationException generic(String message) {
return new ApplicationException(9999, message);
}
public static ApplicationException fromException(Throwable e) {
return new ApplicationException(9999, e);
}
public ApplicationException(int code, Throwable e) {
this(code, e.getMessage(), e);
}
public ApplicationException(int code, String message) { public static ApplicationException actionNotPermitted(String message) {
super(message); return new ApplicationException(9007, "Action not permitted: " + message);
this.code = code; }
}
public ApplicationException(int code, String message, Throwable e) {
super(message, e);
this.code = code;
}
public boolean getSuccess() { public static ApplicationException passwordsNotEqual() {
return success; return new ApplicationException(9008, "Passwords do not match");
} }
public Integer getCode() { public static ApplicationException unAuthorized() {
return code; return new ApplicationException(9009, "unauthorized");
} }
@Override public static ApplicationException duplicateEntry(String message) {
public String getMessage() { return new ApplicationException(9100, message);
return super.getMessage(); }
}
public static ApplicationException duplicateEntry(String messageFmt, Object... args) {
return new ApplicationException(9100, String.format(messageFmt, args));
}
public static ApplicationException noEntryExists(String message) {
return new ApplicationException(9101, message);
}
public static ApplicationException noEntryExists(String messageFmt, Object... items) {
return new ApplicationException(9101, String.format(messageFmt, items));
}
public static ApplicationException constraintViolation(String message) {
return new ApplicationException(9102, message);
}
public static ApplicationException constraintViolation(String messageFmt, Object... items) {
return new ApplicationException(9102, String.format(messageFmt, items));
}
public static ApplicationException illegalArgument(String message) {
return new ApplicationException(9103, message);
}
public static ApplicationException generic(String message) {
return new ApplicationException(9999, message);
}
public static ApplicationException fromException(Throwable e) {
return new ApplicationException(9999, e);
}
public ApplicationException(int code, Throwable e) {
this(code, e.getMessage(), e);
}
public ApplicationException(int code, String message) {
super(message);
this.code = code;
}
public ApplicationException(int code, String message, Throwable e) {
super(message, e);
this.code = code;
}
public boolean getSuccess() {
return success;
}
public Integer getCode() {
return code;
}
@Override
public String getMessage() {
return super.getMessage();
}
} }

View File

@@ -1,55 +1,64 @@
package net.locusworks.common.immutables; package net.locusworks.common.immutables;
import java.util.Objects;
/** /**
* Class that holds two immutable objects as a pair * Class that holds two immutable objects as a pair
* @author Isaac Parenteau *
* @version 1.0.0
* @param <V1> class type of object 1 * @param <V1> class type of object 1
* @param <V2> class type of object 2 * @param <V2> class type of object 2
* @author Isaac Parenteau
* @version 1.0.0
*/ */
public class Pair<V1, V2> extends Unit<V1> { public class Pair<V1, V2> extends Unit<V1> {
private V2 value2; private V2 value2;
/** /**
* Constructor with no values * Constructor with no values
*/ */
public Pair() { public Pair() {
super(); super();
} }
/** /**
* Constructor * Constructor
* @param value1 Object 1 *
* @param value2 Object 2 * @param value1 Object 1
*/ * @param value2 Object 2
public Pair(V1 value1, V2 value2) { */
super(value1); public Pair(V1 value1, V2 value2) {
this.value2 = value2; super(value1);
} this.value2 = value2;
}
/**
* Set value2 /**
* @param value2 value to set it * Set value2
*/ *
public void setValue2(V2 value2) { * @param value2 value to set it
this.value2 = value2; */
} public void setValue2(V2 value2) {
if (Objects.nonNull(this.value2)) {
/** throw new IllegalArgumentException("Value2 already set. Cannot change");
* Get value2 }
* @return value2 this.value2 = value2;
*/ }
public V2 getValue2() {
return this.value2; /**
} * Get value2
*
@Override * @return value2
public boolean equals(Object other) { */
if (!(other instanceof Pair)) return false; public V2 getValue2() {
return this.value2;
Pair<?, ?> otherPair = (Pair<?, ?>)other; }
return super.equals(otherPair) && this.getValue2().equals(otherPair.getValue2()); @Override
} public boolean equals(Object other) {
} if (other == null) return false;
if (!(other instanceof Pair<?, ?> pair)) return false;
return this.getValue2().equals(pair.getValue2()) &&
this.getValue1().equals(pair.getValue1());
}
}

View File

@@ -1,57 +1,67 @@
package net.locusworks.common.immutables; package net.locusworks.common.immutables;
import java.util.Objects;
/** /**
* Class that holds three immutable objects as triplets * Class that holds three immutable objects as triplets
* @author Isaac Parenteau *
* @version 1.0.0
* @param <V1> class type of object 1 * @param <V1> class type of object 1
* @param <V2> class type of object 2 * @param <V2> class type of object 2
* @param <V3> class type of object 3 * @param <V3> class type of object 3
* @author Isaac Parenteau
* @version 1.0.0
*/ */
public class Triplet<V1, V2, V3> extends Pair<V1, V2> { public class Triplet<V1, V2, V3> extends Pair<V1, V2> {
private V3 value3; private V3 value3;
/** /**
* default constructor with no values * default constructor with no values
*/ */
public Triplet() { public Triplet() {
super(); super();
} }
/** /**
* Constructor * Constructor
* @param value1 Object 1 *
* @param value2 Object 2 * @param value1 Object 1
* @param value3 Object 3 * @param value2 Object 2
*/ * @param value3 Object 3
public Triplet(V1 value1, V2 value2, V3 value3) { */
super(value1, value2); public Triplet(V1 value1, V2 value2, V3 value3) {
this.value3 = value3; super(value1, value2);
} this.value3 = value3;
}
/**
* Set value 3 /**
* @param value3 value 3 * Set value 3
*/ *
public void setValue3(V3 value3) { * @param value3 value 3
this.value3 = value3; */
} public void setValue3(V3 value3) {
if (Objects.nonNull(this.value3)) {
/** throw new IllegalArgumentException("Value3 already set. Cannot change");
* Get value 3 }
* @return value 3 this.value3 = value3;
*/ }
public V3 getValue3() {
return value3; /**
} * Get value 3
*
@Override * @return value 3
public boolean equals(Object other) { */
if (!(other instanceof Triplet)) return false; public V3 getValue3() {
return value3;
Triplet<?, ?, ?> otherTriplet = (Triplet<?, ?, ?>)other; }
return super.equals(otherTriplet) && this.getValue3().equals(otherTriplet.getValue3()); @Override
} public boolean equals(Object other) {
} if (other == null) return false;
if (!(other instanceof Triplet<?, ?, ?> triplet)) return false;
return this.getValue3().equals(triplet.getValue3()) &&
this.getValue2().equals(triplet.getValue2()) &&
this.getValue1().equals(triplet.getValue1());
}
}

View File

@@ -1,50 +1,58 @@
package net.locusworks.common.immutables; package net.locusworks.common.immutables;
import java.util.Objects;
/** /**
* Class that holds three immutable objects as triplets * Class that holds three immutable objects as triplets
* @author Isaac Parenteau *
* @version 1.0.0
* @param <V1> class type of object 1 * @param <V1> class type of object 1
* @author Isaac Parenteau
* @version 1.0.0
*/ */
public class Unit<V1> { public class Unit<V1> {
private V1 value1; private V1 value1;
/** /**
* Default constructor with no values * Default constructor with no values
*/ */
public Unit() {} public Unit() {
}
/**
* Constuctor /**
* @param value1 value 1 * Constructor
*/ *
public Unit(V1 value1) { * @param value1 value 1
this.value1 = value1; */
} public Unit(V1 value1) {
this.value1 = value1;
/** }
* Set value 1
* @param value1 value 1 /**
*/ * Set value 1
public void setValue1(V1 value1) { *
this.value1 = value1; * @param value1 value 1
} */
public void setValue1(V1 value1) {
/** if (Objects.nonNull(this.value1)) {
* Get value 1 throw new IllegalArgumentException("Value1 already set. Cannot change");
* @return value1 }
*/ this.value1 = value1;
public V1 getValue1() { }
return value1;
} /**
* Get value 1
@Override *
public boolean equals(Object other) { * @return value1
if (!(other instanceof Unit)) return false; */
public V1 getValue1() {
Unit<?> otherUnit = (Unit<?>)other; return value1;
}
return this.getValue1().equals(otherUnit.getValue1());
} @Override
} public boolean equals(Object other) {
if (other == null) return false;
if (!(other instanceof Unit<?>)) return false;
return this.getValue1().equals(((Unit<?>) other).getValue1());
}
}

View File

@@ -3,8 +3,8 @@ package net.locusworks.common.interfaces;
import java.util.Iterator; import java.util.Iterator;
public interface AutoCloseableIterator<T> extends Iterator<T>, AutoCloseable { public interface AutoCloseableIterator<T> extends Iterator<T>, AutoCloseable {
@Override @Override
public void close(); void close();
} }

View File

@@ -2,5 +2,5 @@ package net.locusworks.common.interfaces;
@FunctionalInterface @FunctionalInterface
public interface ThrowingConsumer<T, E extends Exception> { public interface ThrowingConsumer<T, E extends Exception> {
void accept(T t) throws E; void accept(T t) throws E;
} }

View File

@@ -38,321 +38,322 @@ import net.locusworks.common.Charsets;
public class IOUtils { public class IOUtils {
private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
public static final int EOF = -1; public static final int EOF = -1;
/** /**
* Gets the contents of an <code>InputStream</code> as a list of Strings, * Gets the contents of an <code>InputStream</code> as a list of Strings,
* one entry per line, using the specified character encoding. * one entry per line, using the specified character encoding.
* <p> * <p>
* This method buffers the input internally, so there is no need to use a * This method buffers the input internally, so there is no need to use a
* <code>BufferedInputStream</code>. * <code>BufferedInputStream</code>.
* *
* @param input the <code>InputStream</code> to read from, not null * @param stream the <code>InputStream</code> to read from, not null
* @param encoding the encoding to use, null means platform default * @param charset the encoding to use, null means platform default
* @return the list of Strings, never null * @return the list of Strings, never null
* @throws NullPointerException if the input is null * @throws NullPointerException if the input is null
* @throws IOException if an I/O error occurs * @throws IOException if an I/O error occurs
*/ */
public static List<String> readLines(final InputStream stream, final Charset charset) throws IOException { public static List<String> readLines(final InputStream stream, final Charset charset) throws IOException {
final InputStreamReader reader = new InputStreamReader(stream, charset); final InputStreamReader reader = new InputStreamReader(stream, charset);
return readLines(reader); return readLines(reader);
}
/**
* Gets the contents of a <code>Reader</code> as a list of Strings,
* one entry per line.
* <p>
* This method buffers the input internally, so there is no need to use a
* <code>BufferedReader</code>.
*
* @param input the <code>Reader</code> to read from, not null
* @return the list of Strings, never null
* @throws NullPointerException if the input is null
* @throws IOException if an I/O error occurs
*/
public static List<String> readLines(final Reader input) throws IOException {
final BufferedReader reader = toBufferedReader(input);
final List<String> list = new ArrayList<>();
for(String line = reader.readLine(); line != null; line = reader.readLine()) {
list.add(line);
} }
return list;
}
/** /**
* Returns the given reader if it is a {@link BufferedReader}, otherwise creates a BufferedReader from the given * Gets the contents of a <code>Reader</code> as a list of Strings,
* reader. * one entry per line.
* * <p>
* @param reader the reader to wrap or return (not null) * This method buffers the input internally, so there is no need to use a
* @return the given reader or a new {@link BufferedReader} for the given reader * <code>BufferedReader</code>.
* @throws NullPointerException if the input parameter is null *
* @see #buffer(Reader) * @param input the <code>Reader</code> to read from, not null
*/ * @return the list of Strings, never null
public static BufferedReader toBufferedReader(final Reader reader) { * @throws NullPointerException if the input is null
return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); * @throws IOException if an I/O error occurs
} */
public static List<String> readLines(final Reader input) throws IOException {
/** final BufferedReader reader = toBufferedReader(input);
* Gets the contents of an <code>InputStream</code> as a <code>byte[]</code>. final List<String> list = new ArrayList<>();
* <p> for (String line = reader.readLine(); line != null; line = reader.readLine()) {
* This method buffers the input internally, so there is no need to use a list.add(line);
* <code>BufferedInputStream</code>. }
* return list;
* @param input the <code>InputStream</code> to read from
* @return the requested byte array
* @throws NullPointerException if the input is null
* @throws IOException if an I/O error occurs
*/
public static byte[] toByteArray(final InputStream input) throws IOException {
try (final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
copy(input, output);
return output.toByteArray();
} }
}
/** /**
* Copies bytes from an <code>InputStream</code> to an * Returns the given reader if it is a {@link BufferedReader}, otherwise creates a BufferedReader from the given
* <code>OutputStream</code>. * reader.
* <p> *
* This method buffers the input internally, so there is no need to use a * @param reader the reader to wrap or return (not null)
* <code>BufferedInputStream</code>. * @return the given reader or a new {@link BufferedReader} for the given reader
* <p> * @throws NullPointerException if the input parameter is null
* Large streams (over 2GB) will return a bytes copied value of * @see #buffer(Reader)
* <code>-1</code> after the copy has completed since the correct */
* number of bytes cannot be returned as an int. For large streams public static BufferedReader toBufferedReader(final Reader reader) {
* use the <code>copyLarge(InputStream, OutputStream)</code> method. return reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader);
*
* @param input the <code>InputStream</code> to read from
* @param output the <code>OutputStream</code> to write to
* @return the number of bytes copied, or -1 if &gt; Integer.MAX_VALUE
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
*/
public static int copy(final InputStream input, final OutputStream output) throws IOException {
final long count = copyLarge(input, output);
if (count > Integer.MAX_VALUE) {
return -1;
} }
return (int) count;
}
/**
* Copies bytes from an <code>InputStream</code> to an <code>OutputStream</code> using an internal buffer of the
* given size.
* <p>
* This method buffers the input internally, so there is no need to use a <code>BufferedInputStream</code>.
* <p>
*
* @param input the <code>InputStream</code> to read from
* @param output the <code>OutputStream</code> to write to
* @param bufferSize the bufferSize used to copy from the input to the output
* @return the number of bytes copied
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
*/
public static long copy(final InputStream input, final OutputStream output, final int bufferSize)
throws IOException {
return copyLarge(input, output, new byte[bufferSize]);
}
/** /**
* Copies bytes from an <code>InputStream</code> to chars on a * Gets the contents of an <code>InputStream</code> as a <code>byte[]</code>.
* <code>Writer</code> using the specified character encoding. * <p>
* <p> * This method buffers the input internally, so there is no need to use a
* This method buffers the input internally, so there is no need to use a * <code>BufferedInputStream</code>.
* <code>BufferedInputStream</code>. *
* <p> * @param input the <code>InputStream</code> to read from
* This method uses {@link InputStreamReader}. * @return the requested byte array
* * @throws NullPointerException if the input is null
* @param input the <code>InputStream</code> to read from * @throws IOException if an I/O error occurs
* @param output the <code>Writer</code> to write to */
* @param inputEncoding the encoding to use for the input stream, null means platform default public static byte[] toByteArray(final InputStream input) throws IOException {
* @throws NullPointerException if the input or output is null try (final ByteArrayOutputStream output = new ByteArrayOutputStream()) {
* @throws IOException if an I/O error occurs copy(input, output);
*/ return output.toByteArray();
public static void copy(final InputStream input, final Writer output, final Charset inputEncoding) }
throws IOException {
final InputStreamReader in = new InputStreamReader(input, inputEncoding.toString());
copy(in, output);
}
/**
* Copies chars from a <code>Reader</code> to a <code>Writer</code>.
* <p>
* This method buffers the input internally, so there is no need to use a
* <code>BufferedReader</code>.
* <p>
* Large streams (over 2GB) will return a chars copied value of
* <code>-1</code> after the copy has completed since the correct
* number of chars cannot be returned as an int. For large streams
* use the <code>copyLarge(Reader, Writer)</code> method.
*
* @param input the <code>Reader</code> to read from
* @param output the <code>Writer</code> to write to
* @return the number of characters copied, or -1 if &gt; Integer.MAX_VALUE
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
*/
public static int copy(final Reader input, final Writer output) throws IOException {
final long count = copyLarge(input, output);
if (count > Integer.MAX_VALUE) {
return -1;
} }
return (int) count;
}
/** /**
* Copies chars from a large (over 2GB) <code>Reader</code> to a <code>Writer</code>. * Copies bytes from an <code>InputStream</code> to an
* <p> * <code>OutputStream</code>.
* This method buffers the input internally, so there is no need to use a * <p>
* <code>BufferedReader</code>. * This method buffers the input internally, so there is no need to use a
* <p> * <code>BufferedInputStream</code>.
* The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}. * <p>
* * Large streams (over 2GB) will return a bytes copied value of
* @param input the <code>Reader</code> to read from * <code>-1</code> after the copy has completed since the correct
* @param output the <code>Writer</code> to write to * number of bytes cannot be returned as an int. For large streams
* @return the number of characters copied * use the <code>copyLarge(InputStream, OutputStream)</code> method.
* @throws NullPointerException if the input or output is null *
* @throws IOException if an I/O error occurs * @param input the <code>InputStream</code> to read from
*/ * @param output the <code>OutputStream</code> to write to
public static long copyLarge(final Reader input, final Writer output) throws IOException { * @return the number of bytes copied, or -1 if &gt; Integer.MAX_VALUE
return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]); * @throws NullPointerException if the input or output is null
} * @throws IOException if an I/O error occurs
*/
/** public static int copy(final InputStream input, final OutputStream output) throws IOException {
* Copies bytes from a large (over 2GB) <code>InputStream</code> to an final long count = copyLarge(input, output);
* <code>OutputStream</code>. if (count > Integer.MAX_VALUE) {
* <p> return -1;
* This method buffers the input internally, so there is no need to use a }
* <code>BufferedInputStream</code>. return (int) count;
* <p>
* The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}.
*
* @param input the <code>InputStream</code> to read from
* @param output the <code>OutputStream</code> to write to
* @return the number of bytes copied
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
*/
public static long copyLarge(final InputStream input, final OutputStream output)
throws IOException {
return copy(input, output, DEFAULT_BUFFER_SIZE);
}
/**
* Copies bytes from a large (over 2GB) <code>InputStream</code> to an
* <code>OutputStream</code>.
* <p>
* This method uses the provided buffer, so there is no need to use a
* <code>BufferedInputStream</code>.
* <p>
*
* @param input the <code>InputStream</code> to read from
* @param output the <code>OutputStream</code> to write to
* @param buffer the buffer to use for the copy
* @return the number of bytes copied
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
*/
public static long copyLarge(final InputStream input, final OutputStream output, final byte[] buffer)
throws IOException {
long count = 0;
int n;
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
} }
return count;
}
/** /**
* Copies chars from a large (over 2GB) <code>Reader</code> to a <code>Writer</code>. * Copies bytes from an <code>InputStream</code> to an <code>OutputStream</code> using an internal buffer of the
* <p> * given size.
* This method uses the provided buffer, so there is no need to use a * <p>
* <code>BufferedReader</code>. * This method buffers the input internally, so there is no need to use a <code>BufferedInputStream</code>.
* <p> * <p>
* *
* @param input the <code>Reader</code> to read from * @param input the <code>InputStream</code> to read from
* @param output the <code>Writer</code> to write to * @param output the <code>OutputStream</code> to write to
* @param buffer the buffer to be used for the copy * @param bufferSize the bufferSize used to copy from the input to the output
* @return the number of characters copied * @return the number of bytes copied
* @throws NullPointerException if the input or output is null * @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs * @throws IOException if an I/O error occurs
*/ */
public static long copyLarge(final Reader input, final Writer output, final char[] buffer) throws IOException { public static long copy(final InputStream input, final OutputStream output, final int bufferSize)
long count = 0; throws IOException {
int n; return copyLarge(input, output, new byte[bufferSize]);
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
} }
return count;
}
/** /**
* Gets the contents of an <code>InputStream</code> as a String * Copies bytes from an <code>InputStream</code> to chars on a
* using the specified character encoding. * <code>Writer</code> using the specified character encoding.
* <p> * <p>
* This method buffers the input internally, so there is no need to use a * This method buffers the input internally, so there is no need to use a
* <code>BufferedInputStream</code>. * <code>BufferedInputStream</code>.
* </p> * <p>
* * This method uses {@link InputStreamReader}.
* @param input the <code>InputStream</code> to read from *
* @param encoding the encoding to use, null means platform default * @param input the <code>InputStream</code> to read from
* @return the requested String * @param output the <code>Writer</code> to write to
* @throws NullPointerException if the input is null * @param inputEncoding the encoding to use for the input stream, null means platform default
* @throws IOException if an I/O error occurs * @throws NullPointerException if the input or output is null
*/ * @throws IOException if an I/O error occurs
public static String toString(final InputStream input, final Charset encoding) throws IOException { */
try (final StringWriter sw = new StringWriter()) { public static void copy(final InputStream input, final Writer output, final Charset inputEncoding)
copy(input, sw, encoding); throws IOException {
return sw.toString(); final InputStreamReader in = new InputStreamReader(input, inputEncoding);
copy(in, output);
} }
}
public static void writeStringToFile(String fileName, String data) throws IOException { /**
writeStringToFile(Paths.get(fileName), data, Charsets.UTF_8); * Copies chars from a <code>Reader</code> to a <code>Writer</code>.
} * <p>
* This method buffers the input internally, so there is no need to use a
* <code>BufferedReader</code>.
public static void writeStringToFile(String fileName, String data, Charset charset) throws IOException { * <p>
writeStringToFile(Paths.get(fileName), data, charset); * Large streams (over 2GB) will return a chars copied value of
} * <code>-1</code> after the copy has completed since the correct
* number of chars cannot be returned as an int. For large streams
public static void writeStringToFile(Path file, String data) throws IOException { * use the <code>copyLarge(Reader, Writer)</code> method.
writeStringToFile(file, data, Charsets.UTF_8); *
} * @param input the <code>Reader</code> to read from
* @param output the <code>Writer</code> to write to
@Deprecated * @return the number of characters copied, or -1 if &gt; Integer.MAX_VALUE
public static void writeStringToFile(File file, String data, Charset charset) throws IOException { * @throws NullPointerException if the input or output is null
writeStringToFile(file.toPath(), data, charset); * @throws IOException if an I/O error occurs
} */
public static int copy(final Reader input, final Writer output) throws IOException {
public static void writeStringToFile(Path file, String data, Charset charset) throws IOException { final long count = copyLarge(input, output);
try(Writer writer = Files.newBufferedWriter(file, charset)) { if (count > Integer.MAX_VALUE) {
writer.write(data); return -1;
writer.flush(); }
return (int) count;
} }
}
public static void deleteFile(String fileName) { /**
deleteFile(Paths.get(fileName)); * Copies chars from a large (over 2GB) <code>Reader</code> to a <code>Writer</code>.
} * <p>
* This method buffers the input internally, so there is no need to use a
@Deprecated * <code>BufferedReader</code>.
public static void deleteFile(File file) { * <p>
deleteFile(file.toPath()); * The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}.
} *
* @param input the <code>Reader</code> to read from
public static void deleteFile(Path file) { * @param output the <code>Writer</code> to write to
try { * @return the number of characters copied
Files.deleteIfExists(file); * @throws NullPointerException if the input or output is null
} catch (IOException ex) { * @throws IOException if an I/O error occurs
throw new IllegalArgumentException(ex); */
public static long copyLarge(final Reader input, final Writer output) throws IOException {
return copyLarge(input, output, new char[DEFAULT_BUFFER_SIZE]);
} }
}
public static void deleteFiles(String... fileNames) { /**
Arrays.asList(fileNames).forEach(file -> deleteFile(file)); * Copies bytes from a large (over 2GB) <code>InputStream</code> to an
} * <code>OutputStream</code>.
* <p>
* This method buffers the input internally, so there is no need to use a
* <code>BufferedInputStream</code>.
* <p>
* The buffer size is given by {@link #DEFAULT_BUFFER_SIZE}.
*
* @param input the <code>InputStream</code> to read from
* @param output the <code>OutputStream</code> to write to
* @return the number of bytes copied
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
*/
public static long copyLarge(final InputStream input, final OutputStream output)
throws IOException {
return copy(input, output, DEFAULT_BUFFER_SIZE);
}
/**
* Copies bytes from a large (over 2GB) <code>InputStream</code> to an
* <code>OutputStream</code>.
* <p>
* This method uses the provided buffer, so there is no need to use a
* <code>BufferedInputStream</code>.
* <p>
*
* @param input the <code>InputStream</code> to read from
* @param output the <code>OutputStream</code> to write to
* @param buffer the buffer to use for the copy
* @return the number of bytes copied
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
*/
public static long copyLarge(final InputStream input, final OutputStream output, final byte[] buffer)
throws IOException {
long count = 0;
int n;
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
/**
* Copies chars from a large (over 2GB) <code>Reader</code> to a <code>Writer</code>.
* <p>
* This method uses the provided buffer, so there is no need to use a
* <code>BufferedReader</code>.
* <p>
*
* @param input the <code>Reader</code> to read from
* @param output the <code>Writer</code> to write to
* @param buffer the buffer to be used for the copy
* @return the number of characters copied
* @throws NullPointerException if the input or output is null
* @throws IOException if an I/O error occurs
*/
public static long copyLarge(final Reader input, final Writer output, final char[] buffer) throws IOException {
long count = 0;
int n;
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
/**
* Gets the contents of an <code>InputStream</code> as a String
* using the specified character encoding.
* <p>
* This method buffers the input internally, so there is no need to use a
* <code>BufferedInputStream</code>.
* </p>
*
* @param input the <code>InputStream</code> to read from
* @param encoding the encoding to use, null means platform default
* @return the requested String
* @throws NullPointerException if the input is null
* @throws IOException if an I/O error occurs
*/
public static String toString(final InputStream input, final Charset encoding) throws IOException {
try (final StringWriter sw = new StringWriter()) {
copy(input, sw, encoding);
return sw.toString();
}
}
public static void writeStringToFile(String fileName, String data) throws IOException {
writeStringToFile(Paths.get(fileName), data, Charsets.UTF_8);
}
public static void writeStringToFile(String fileName, String data, Charset charset) throws IOException {
writeStringToFile(Paths.get(fileName), data, charset);
}
public static void writeStringToFile(Path file, String data) throws IOException {
writeStringToFile(file, data, Charsets.UTF_8);
}
@Deprecated
public static void writeStringToFile(File file, String data, Charset charset) throws IOException {
writeStringToFile(file.toPath(), data, charset);
}
public static void writeStringToFile(Path file, String data, Charset charset) throws IOException {
try (Writer writer = Files.newBufferedWriter(file, charset)) {
writer.write(data);
writer.flush();
}
}
public static void deleteFile(String fileName) {
deleteFile(Paths.get(fileName));
}
@Deprecated
public static void deleteFile(File file) {
deleteFile(file.toPath());
}
public static void deleteFile(Path file) {
try {
Files.deleteIfExists(file);
} catch (IOException ex) {
throw new IllegalArgumentException(ex);
}
}
public static void deleteFiles(String... fileNames) {
Arrays.asList(fileNames).forEach(file -> deleteFile(file));
}
} }

View File

@@ -4,9 +4,9 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public abstract class BaseMigrationManager { public abstract class BaseMigrationManager {
protected List<MigrationItem> migrations = new ArrayList<>(); protected List<MigrationItem> migrations = new ArrayList<>();
public abstract void migrate() throws Exception; public abstract void migrate() throws Exception;
} }

View File

@@ -2,5 +2,5 @@ package net.locusworks.common.migration;
@FunctionalInterface @FunctionalInterface
public interface MigrationCallback { public interface MigrationCallback {
void results(String msg); void results(String msg);
} }

View File

@@ -7,68 +7,68 @@ import org.flywaydb.core.api.MigrationInfo;
import org.flywaydb.core.internal.info.MigrationInfoDumper; import org.flywaydb.core.internal.info.MigrationInfoDumper;
public class MigrationItem { public class MigrationItem {
protected Flyway flyway = null;
protected MigrationInfo[] pendingMigrations;
protected MigrationInfo[] allMigrations;
private MigrationCallback callback;
public MigrationItem(Flyway flyway, MigrationCallback callback) {
this.allMigrations = flyway.info().all();
this.pendingMigrations = flyway.info().pending();
this.flyway=flyway;
this.callback = callback;
}
public MigrationItem(Flyway flyway) { protected Flyway flyway = null;
this(flyway, null); protected MigrationInfo[] pendingMigrations;
} protected MigrationInfo[] allMigrations;
private final MigrationCallback callback;
public String[] getSchemas() { public MigrationItem(Flyway flyway, MigrationCallback callback) {
return flyway.getConfiguration().getSchemas(); this.allMigrations = flyway.info().all();
} this.pendingMigrations = flyway.info().pending();
this.flyway = flyway;
public int qtyPending() { this.callback = callback;
return pendingMigrations.length;
}
public void repair() {
// no harm in calling migrate even if none pending, log will contain
// assurance that the migrations were verified
try {
String schemas = Arrays.toString(getSchemas()).replace("[", "").replace("]", "");
String status = String.format("Repair status for %s:%n%s", schemas, getAllMigrationsLog());
callback(status);
flyway.repair();
} catch (Exception e) {
String message = String.format("%nDatabase migration error:%n %s %nPortal webapp cannot continue.", e.getMessage());
throw new RuntimeException(message, e);
} }
}
public void migrate() { public MigrationItem(Flyway flyway) {
// no harm in calling migrate even if none pending, log will contain this(flyway, null);
// assurance that the migrations were verified }
try {
String schemas = Arrays.toString(getSchemas()).replace("[", "").replace("]", ""); public String[] getSchemas() {
String status = String.format("Repair status for %s:%n%s", schemas, getAllMigrationsLog()); return flyway.getConfiguration().getSchemas();
callback(status); }
flyway.migrate();
} catch (Exception e) { public int qtyPending() {
String message = String.format("%nDatabase migration error:%n %s %nPortal webapp cannot continue.", e.getMessage()); return pendingMigrations.length;
throw new RuntimeException(message, e); }
public void repair() {
// no harm in calling migrate even if none pending, log will contain
// assurance that the migrations were verified
try {
String schemas = Arrays.toString(getSchemas()).replace("[", "").replace("]", "");
String status = String.format("Repair status for %s:%n%s", schemas, getAllMigrationsLog());
callback(status);
flyway.repair();
} catch (Exception e) {
String message = String.format("%nDatabase migration error:%n %s %nPortal webapp cannot continue.", e.getMessage());
throw new RuntimeException(message, e);
}
}
public void migrate() {
// no harm in calling migrate even if none pending, log will contain
// assurance that the migrations were verified
try {
String schemas = Arrays.toString(getSchemas()).replace("[", "").replace("]", "");
String status = String.format("Repair status for %s:%n%s", schemas, getAllMigrationsLog());
callback(status);
flyway.migrate();
} catch (Exception e) {
String message = String.format("%nDatabase migration error:%n %s %nPortal webapp cannot continue.", e.getMessage());
throw new RuntimeException(message, e);
}
}
public String getAllMigrationsLog() {
return MigrationInfoDumper.dumpToAsciiTable(allMigrations);
}
private void callback(String msg) {
if (this.callback != null) this.callback.results(msg);
} }
}
public String getAllMigrationsLog() {
return MigrationInfoDumper.dumpToAsciiTable(allMigrations);
}
private void callback(String msg) {
if (this.callback != null) this.callback.results(msg);
}
} }

View File

@@ -1,95 +1,79 @@
package net.locusworks.common.net; package net.locusworks.common.net;
import java.security.SecureRandom;
import javax.net.ssl.SSLContext; import net.locusworks.common.net.ssl.SSLManager;
import javax.net.ssl.TrustManager; import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.http.HttpEntity; import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.http.HttpResponse; import org.apache.hc.core5.http.HttpEntity;
import org.apache.http.client.HttpClient; import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import net.locusworks.common.net.certmanagers.TrustAllCertsManager;
import net.locusworks.common.net.hostverifiers.AllHostValidVerifyer;
public class HttpClientHelper { public class HttpClientHelper {
public enum HttpSchema { public enum HttpSchema {
HTTP, HTTP,
HTTPS; HTTPS;
public static HttpSchema findEnum(String value) { public static HttpSchema findEnum(String value) {
for (HttpSchema schema : values()) { for (HttpSchema schema : values()) {
if (value.equalsIgnoreCase(schema.toString())) { if (value.equalsIgnoreCase(schema.toString())) {
return schema; return schema;
}
}
return null;
} }
}
return null;
} }
}
private static final String[] TLS = new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"};
private HttpClient client; private static final String[] TLS = new String[]{"TLSv1", "TLSv1.1", "TLSv1.2"};
private String baseUrl;
private final HttpClient client;
/** private final String baseUrl;
* Constructor to handle http connection
* @param protocol protocol to use (http or https) /**
* @param host the host url * Constructor to handle http connection
* @param port the host port *
* @throws Exception exception * @param protocol protocol to use (http or https)
*/ * @param host the host url
public HttpClientHelper(String protocol, String host, String port) throws Exception { * @param port the host port
HttpSchema schema = HttpSchema.findEnum(protocol); * @throws Exception exception
if (schema == null) { */
throw new Exception("Unable to find http schema of " + protocol); public HttpClientHelper(String protocol, String host, String port) throws Exception {
HttpSchema schema = HttpSchema.findEnum(protocol);
if (schema == null) {
throw new Exception("Unable to find http schema of " + protocol);
}
this.baseUrl = String.format("%s://%s:%s", schema.toString().toLowerCase(), host, port);
this.client = createClient(schema);
} }
this.baseUrl = String.format("%s://%s:%s", schema.toString().toLowerCase(), host, port);
this.client = createClient(schema); private HttpClient createClient(HttpSchema schema) throws Exception {
} HttpClientBuilder builder = HttpClientBuilder.create();
private HttpClient createClient(HttpSchema schema) throws Exception { if (schema == HttpSchema.HTTP) {
HttpClientBuilder builder = HttpClientBuilder.create(); return builder.build();
}
if (schema == HttpSchema.HTTP) {
return builder.build(); return SSLManager.getTrustAllTLSClient();
} }
TrustManager[] trustAllCerts = new TrustManager[] { new TrustAllCertsManager() }; /**
//Setup the ssl instance using tls * Get the http GET response code
SSLContext sslContext = SSLContext.getInstance("TLS"); *
sslContext.init(null, trustAllCerts, new SecureRandom()); * @param endpoint endpoint to get the response from
* @return responseCode
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, TLS, null, new AllHostValidVerifyer()); * @throws Exception general exception
*/
builder = builder.setSSLSocketFactory(sslsf); public Integer getResponseCode(String endpoint) throws Exception {
String url = this.baseUrl + endpoint;
return builder.build();
} return this.client.execute(new HttpGet(url), response -> {
HttpEntity entity = response.getEntity();
/** int responseCode = response.getCode();
* Get the http GET response code EntityUtils.consume(entity);
* @param endpoint endpoint to get the response from return responseCode;
* @return responseCode });
* @throws Exception general exception }
*/
public Integer getGetResponseCode(String endpoint) throws Exception { ;
String url = this.baseUrl + endpoint;
HttpResponse response = this.client.execute(new HttpGet(url));
HttpEntity entity = response.getEntity();
Integer responseCode = response.getStatusLine().getStatusCode();
EntityUtils.consume(entity);
return responseCode;
};
} }

View File

@@ -8,17 +8,21 @@ import javax.net.ssl.X509TrustManager;
public class TrustAllCertsManager implements X509TrustManager { public class TrustAllCertsManager implements X509TrustManager {
@Override @Override
public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } public void checkClientTrusted(X509Certificate[] arg0, String arg1) {
}
@Override @Override
public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } public void checkServerTrusted(X509Certificate[] arg0, String arg1) {
}
@Override @Override
public X509Certificate[] getAcceptedIssuers() { return null; } public X509Certificate[] getAcceptedIssuers() {
return null;
public static TrustManager[] trustAllCerts() { }
return new TrustManager[] { new TrustAllCertsManager() };
} public static TrustManager[] trustAllCerts() {
return new TrustManager[]{new TrustAllCertsManager()};
}
} }

View File

@@ -0,0 +1,13 @@
package net.locusworks.common.net.hostverifiers;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
public class AllHostValidVerifier implements HostnameVerifier {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
}

View File

@@ -1,11 +0,0 @@
package net.locusworks.common.net.hostverifiers;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;
public class AllHostValidVerifyer implements HostnameVerifier {
@Override
public boolean verify(String arg0, SSLSession arg1) { return true; }
}

View File

@@ -7,24 +7,35 @@ import java.security.SecureRandom;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManager;
import org.apache.http.client.HttpClient;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import net.locusworks.common.net.certmanagers.TrustAllCertsManager; import net.locusworks.common.net.certmanagers.TrustAllCertsManager;
import net.locusworks.common.net.hostverifiers.AllHostValidVerifyer; import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.client5.http.ssl.TlsSocketStrategy;
public class SSLManager { public class SSLManager {
public static final String[] TLS = new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"}; public static final String[] TLS = new String[] {"TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"};
public static HttpClient getTrustAllTLSClient() throws NoSuchAlgorithmException, KeyManagementException { public static HttpClient getTrustAllTLSClient() throws NoSuchAlgorithmException, KeyManagementException {
SSLContext context = SSLContext.getInstance("TLS"); TrustManager[] trustAllCerts = new TrustManager[]{new TrustAllCertsManager()};
context.init(null, new TrustManager[] { new TrustAllCertsManager() }, new SecureRandom()); //Setup the ssl instance using tls
SSLContext sslContext = SSLContext.getInstance("TLS");
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(context, TLS, null, new AllHostValidVerifyer()); sslContext.init(null, trustAllCerts, new SecureRandom());
TlsSocketStrategy tlsStrategy = new DefaultClientTlsStrategy(sslContext);
return HttpClientBuilder.create().setSSLSocketFactory(sslsf).build();
HttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setTlsSocketStrategy(tlsStrategy)
.build();
return HttpClients.custom()
.setConnectionManager(connectionManager)
.build();
} }
private SSLManager() { }
} }

View File

@@ -2,11 +2,12 @@ package net.locusworks.common.objectmapper;
/** /**
* Error handler for the object mapper class * Error handler for the object mapper class
*
* @author Isaac Parenteau * @author Isaac Parenteau
* @version 1.0.0 * @version 1.0.0
* @date 02/15/2018 * @date 02/15/2018
* *
*/ */
public interface ObjectMapperError { public interface ObjectMapperError {
void getError(Throwable e); void getError(Throwable e);
} }

View File

@@ -12,137 +12,146 @@ import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
/** /**
* Object mapper to map to convert objects to json string or * Object mapper to map to convert objects to json string or
* json string back to object * json string back to object
*
* @author Isaac Parenteau * @author Isaac Parenteau
* @version 1.0.0 * @version 1.0.0
* @date 02/15/2018 * @date 02/15/2018
*/ */
public class ObjectMapperHelper { public class ObjectMapperHelper {
private static ObjectMapper mapper; private static final ObjectMapper mapper;
static {
mapper = new ObjectMapper(); static {
mapper.setSerializationInclusion(Include.NON_NULL); mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY); mapper.setDefaultPropertyInclusion(Include.NON_NULL);
} mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
}
/**
* Write an object out to json /**
* @param object Object to convert to json * Write an object out to json
* @return return a string representation of the object converted to json *
*/ * @param object Object to convert to json
public static ObjectMapperResults<String> writeValue(Object object) { * @return return a string representation of the object converted to json
String results = ""; */
try { public static ObjectMapperResults<String> writeValue(Object object) {
results = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(object); String results = "";
} catch (Exception ex) { try {
try { results = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
GsonBuilder gsonBuilder = new GsonBuilder(); } catch (Exception ex) {
gsonBuilder.setPrettyPrinting(); try {
Gson gson = gsonBuilder.create(); GsonBuilder gsonBuilder = new GsonBuilder();
results = gson.toJson(object); gsonBuilder.setPrettyPrinting();
} catch (Exception e) { Gson gson = gsonBuilder.create();
new ObjectMapperResults<>(e); results = gson.toJson(object);
} } catch (Throwable e) {
} return new ObjectMapperResults<>(e);
return new ObjectMapperResults<>(results); }
} }
return new ObjectMapperResults<>(results);
/** }
* Convert binary data to an object
* @param src Binary data to convert /**
* @param clazz Class to convert the data to * Convert binary data to an object
* @param <T> The expected class of the value *
* @return the object populated with the data in the json string * @param src Binary data to convert
*/ * @param clazz Class to convert the data to
public static <T> ObjectMapperResults<T> readValue(byte[] src, Class<T> clazz) { * @param <T> The expected class of the value
try { * @return the object populated with the data in the json string
return new ObjectMapperResults<T>(mapper.readValue(src, clazz)); */
} catch (Exception ex) { public static <T> ObjectMapperResults<T> readValue(byte[] src, Class<T> clazz) {
return new ObjectMapperResults<T>(ex); try {
} return new ObjectMapperResults<T>(mapper.readValue(src, clazz));
} } catch (Exception ex) {
return new ObjectMapperResults<T>(ex);
/** }
* Convert a json string to an object }
* @param src Json String
* @param clazz Class to convert the json string to /**
* @param <T> The expected class of the value * Convert a json string to an object
* @return the object populated with the data in the json string *
*/ * @param src Json String
public static <T> ObjectMapperResults<T> readValue(String src, Class<T> clazz) { * @param clazz Class to convert the json string to
try { * @param <T> The expected class of the value
return new ObjectMapperResults<T>(mapper.readValue(src, clazz)); * @return the object populated with the data in the json string
} catch (Exception ex) { */
return new ObjectMapperResults<T>(ex); public static <T> ObjectMapperResults<T> readValue(String src, Class<T> clazz) {
} try {
} return new ObjectMapperResults<T>(mapper.readValue(src, clazz));
} catch (Exception ex) {
/** return new ObjectMapperResults<T>(ex);
* Convert an java object to a class }
* @param src Object to convert }
* @param clazz Class to convert the object to
* @param <T> The expected class of the value /**
* @return the object populated with the data in the json string * Convert an java object to a class
*/ *
public static <T> ObjectMapperResults<T> readValue(Object src, Class<T> clazz) { * @param src Object to convert
try { * @param clazz Class to convert the object to
if (src instanceof String) { * @param <T> The expected class of the value
return readValue((String)src, clazz); * @return the object populated with the data in the json string
} */
return readValue(mapper.writeValueAsString(src), clazz); public static <T> ObjectMapperResults<T> readValue(Object src, Class<T> clazz) {
} catch (Exception ex) { try {
return new ObjectMapperResults<T>(ex); if (src instanceof String) {
} return readValue((String) src, clazz);
} }
return readValue(mapper.writeValueAsString(src), clazz);
/** } catch (Exception ex) {
* Converts an object to a list return new ObjectMapperResults<T>(ex);
* @param object Object to convert }
* @param objectClass Class to convert the object to }
* @param <T> The expected class of the object
* @return the object list populated with the data in the json string /**
*/ * Converts an object to a list
public static <T, L extends Collection<?>> ObjectMapperListResults<List<T>> readListValue(Object object, Class<T> objectClass) { *
return readListValue(object, objectClass, ArrayList.class); * @param object Object to convert
} * @param objectClass Class to convert the object to
* @param <T> The expected class of the object
/** * @return the object list populated with the data in the json string
* Converts an object to a list */
* @param object Object to convert public static <T, L extends Collection<?>> ObjectMapperListResults<List<T>> readListValue(Object object, Class<T> objectClass) {
* @param objectClass Class to convert the object to return readListValue(object, objectClass, ArrayList.class);
* @param listClass List type to make }
* @param <T> The expected class of the object
* @param <L> The expect class of the list /**
* @return the object list populated with the data in the json string * Converts an object to a list
*/ *
public static <T, L extends Collection<?>> ObjectMapperListResults<List<T>> readListValue(Object object, Class<T> objectClass, Class<L> listClass) { * @param object Object to convert
try { * @param objectClass Class to convert the object to
if (object instanceof String) { * @param listClass List type to make
return readListValue((String)object, objectClass, listClass); * @param <T> The expected class of the object
} * @param <L> The expect class of the list
return readListValue(mapper.writeValueAsString(object), objectClass, listClass); * @return the object list populated with the data in the json string
} catch (Exception ex) { */
return new ObjectMapperListResults<>(ex); public static <T, L extends Collection<?>> ObjectMapperListResults<List<T>> readListValue(Object object, Class<T> objectClass, Class<L> listClass) {
} try {
} if (object instanceof String) {
return readListValue((String) object, objectClass, listClass);
/** }
* Converts an object to a list return readListValue(mapper.writeValueAsString(object), objectClass, listClass);
* @param src Source to convert } catch (Exception ex) {
* @param objectClass Class to convert the object to return new ObjectMapperListResults<>(ex);
* @param listClass List type to make }
* @param <T> The expected class of the object }
* @param <L> The expect class of the list
* @return the object list populated with the data in the json string /**
*/ * Converts an object to a list
public static <T, L extends Collection<?>> ObjectMapperListResults<List<T>> readListValue(String src, Class<T> objectClass, Class<L> listClass) { *
try { * @param src Source to convert
List<T> item = mapper.readValue(src, mapper.getTypeFactory().constructCollectionType(listClass, objectClass)); * @param objectClass Class to convert the object to
return new ObjectMapperListResults<>(item); * @param listClass List type to make
} catch (Exception ex) { * @param <T> The expected class of the object
return new ObjectMapperListResults<>(ex); * @param <L> The expect class of the list
} * @return the object list populated with the data in the json string
} */
} public static <T, L extends Collection<?>> ObjectMapperListResults<List<T>> readListValue(String src, Class<T> objectClass, Class<L> listClass) {
try {
List<T> item = mapper.readValue(src, mapper.getTypeFactory().constructCollectionType(listClass, objectClass));
return new ObjectMapperListResults<>(item);
} catch (Exception ex) {
return new ObjectMapperListResults<>(ex);
}
}
}

View File

@@ -4,48 +4,53 @@ import java.util.Collection;
/** /**
* Holds the results from the object mapper list conversion * Holds the results from the object mapper list conversion
*
* @param <T> class type of the object mapper
* @author Isaac Parenteau * @author Isaac Parenteau
* @version 1.0.0 * @version 1.0.0
* @date 02/15/2018 * @date 02/15/2018
* @param <T> class type of the object mapper
*/ */
public class ObjectMapperListResults<T extends Collection<?>> extends ObjectMapperResults<T> { public class ObjectMapperListResults<T extends Collection<?>> extends ObjectMapperResults<T> {
/**
* Constructor
* @param results results from the conversion
*/
public ObjectMapperListResults(T results) {
this(results, null);
}
/**
* Constructor
* @param exception exception that was thrown during conversion
*/
public ObjectMapperListResults(Throwable exception) {
this(null, exception);
}
/**
* Constructor
* @param results results from the conversion
* @param exception exception that was thrown during conversion
*/
public ObjectMapperListResults(T results, Throwable exception) {
super(results, exception);
}
/** /**
* Add the error handler to the results to retrieve the error that caused * Constructor
* the exception *
* @param error the error handler to use * @param results results from the conversion
* @return this */
*/ public ObjectMapperListResults(T results) {
public ObjectMapperListResults<T> withErrorHandler(ObjectMapperError error) { this(results, null);
if (this.hasError() && error != null) { }
error.getError(this.getException());
/**
* Constructor
*
* @param exception exception that was thrown during conversion
*/
public ObjectMapperListResults(Throwable exception) {
this(null, exception);
}
/**
* Constructor
*
* @param results results from the conversion
* @param exception exception that was thrown during conversion
*/
public ObjectMapperListResults(T results, Throwable exception) {
super(results, exception);
}
/**
* Add the error handler to the results to retrieve the error that caused
* the exception
*
* @param error the error handler to use
* @return this
*/
public ObjectMapperListResults<T> withErrorHandler(ObjectMapperError error) {
if (this.hasError() && error != null) {
error.getError(this.getException());
}
return this;
} }
return this;
}
} }

View File

@@ -2,92 +2,102 @@ package net.locusworks.common.objectmapper;
/** /**
* Holds the results from the object mapper list conversion * Holds the results from the object mapper list conversion
*
* @param <T> class type of the object being converted from json to object
* @author Isaac Parenteau * @author Isaac Parenteau
* @version 1.0.0 * @version 1.0.0
* @date 02/15/2018 * @date 02/15/2018
* @param <T> class type of the object being converted from json to object
*/ */
public class ObjectMapperResults<T> { public class ObjectMapperResults<T> {
private Throwable exception;
private T results;
/**
* Constructor
* @param results results from the conversion
*/
public ObjectMapperResults(T results) {
this(results, null);
}
/**
* Constructor
* @param exception exception that was thrown during conversion
*/
public ObjectMapperResults(Throwable exception) {
this(null, exception);
}
/**
* Constructor
* @param results results from the conversion
* @param exception exception that was thrown during conversion
*/
public ObjectMapperResults(T results, Throwable exception) {
this.results = results;
this.exception = exception;
}
/** private Throwable exception;
* get the exception that happened during conversion private T results;
* @return exception
*/
public Throwable getException() {
return exception;
}
/** /**
* Set the exception * Constructor
* @param exception *
*/ * @param results results from the conversion
public void setException(Throwable exception) { */
this.exception = exception; public ObjectMapperResults(T results) {
} this(results, null);
}
/**
* Get the result /**
* @return the converted results * Constructor
*/ *
public T getResults() { * @param exception exception that was thrown during conversion
return results; */
} public ObjectMapperResults(Throwable exception) {
this(null, exception);
/** }
* set the results
* @param results results to set /**
*/ * Constructor
public void setResults(T results) { *
this.results = results; * @param results results from the conversion
} * @param exception exception that was thrown during conversion
*/
/** public ObjectMapperResults(T results, Throwable exception) {
* Check to see if the conversion caused an error this.results = results;
* @return true if there is an error, false otherwise this.exception = exception;
*/ }
public boolean hasError() {
return this.exception != null; /**
} * get the exception that happened during conversion
*
/** * @return exception
* Add the error handler to the results to retrieve the error that caused */
* the exception public Throwable getException() {
* @param error the error handler to use return exception;
* @return this }
*/
public ObjectMapperResults<T> withErrorHandler(ObjectMapperError error) { /**
if (this.hasError() && error != null) { * Set the exception
error.getError(this.getException()); *
* @param exception
*/
public void setException(Throwable exception) {
this.exception = exception;
}
/**
* Get the result
*
* @return the converted results
*/
public T getResults() {
return results;
}
/**
* set the results
*
* @param results results to set
*/
public void setResults(T results) {
this.results = results;
}
/**
* Check to see if the conversion caused an error
*
* @return true if there is an error, false otherwise
*/
public boolean hasError() {
return this.exception != null;
}
/**
* Add the error handler to the results to retrieve the error that caused
* the exception
*
* @param error the error handler to use
* @return this
*/
public ObjectMapperResults<T> withErrorHandler(ObjectMapperError error) {
if (this.hasError() && error != null) {
error.getError(this.getException());
}
return this;
} }
return this;
}
} }

View File

@@ -1,31 +1,33 @@
package net.locusworks.common.properties; package net.locusworks.common.properties;
import java.io.Serial;
import java.util.Properties; import java.util.Properties;
public class ImmutableProperties extends Properties { public class ImmutableProperties extends Properties {
private static final long serialVersionUID = 65942088008978137L; @Serial
private static final long serialVersionUID = 65942088008978137L;
public ImmutableProperties() {
super(); public ImmutableProperties() {
} super();
}
public ImmutableProperties(Properties props) {
super(); public ImmutableProperties(Properties props) {
if (props == null || props.isEmpty()) return; super();
if (props == null || props.isEmpty()) return;
props.entrySet().forEach(item -> this.put(item.getKey(), item.getValue()));
} this.putAll(props);
}
@Override
public synchronized Object setProperty(String key, String value) { @Override
return put(key, value); public synchronized Object setProperty(String key, String value) {
} return put(key, value);
}
public synchronized Object put(Object key, Object value) {
if (containsKey(key)) public synchronized Object put(Object key, Object value) {
throw new RuntimeException("Cannot change key value once its set: " + key); if (containsKey(key))
return super.put(key, value); throw new RuntimeException("Cannot change key value once its set: " + key);
} return super.put(key, value);
}
} }

View File

@@ -1,25 +1,27 @@
package net.locusworks.common.utils; package net.locusworks.common.utils;
import java.util.Objects;
public class Checks { public class Checks {
public static void checkArguments(boolean expression, String error) { public static void checkArguments(boolean expression, String error) {
checkArguments(expression, "%s", error); checkArguments(expression, "%s", error);
} }
public static void checkArguments(boolean expression, String errorFmt, Object... args) { public static void checkArguments(boolean expression, String errorFmt, Object... args) {
if (!expression) throw new IllegalArgumentException(String.format(errorFmt, args)); if (!expression) throw new IllegalArgumentException(String.format(errorFmt, args));
} }
public static void checkState(boolean expression, String error) { public static void checkState(boolean expression, String error) {
checkState(expression, "%s", error); checkState(expression, "%s", error);
} }
public static void checkState(boolean expression, String errorFmt, Object... args) { public static void checkState(boolean expression, String errorFmt, Object... args) {
if (!expression) throw new IllegalStateException(String.format(errorFmt, args)); if (!expression) throw new IllegalStateException(String.format(errorFmt, args));
} }
public static void checkNotNull(Object item, String error) { public static void checkNotNull(Object item, String error) {
if (item == null) throw new IllegalAccessError("Provided item is null"); Objects.requireNonNull(item, error);
} }
} }

View File

@@ -2,18 +2,19 @@ package net.locusworks.common.utils;
/** /**
* Class to hold final static constant values used across the system * Class to hold final static constant values used across the system
*
* @author Isaac Parenteau * @author Isaac Parenteau
* @version 1.0.0 * @version 1.0.0
* @date 02/15/2018 * @date 02/15/2018
*/ */
public class Constants { public class Constants {
public static final short TRUE = (short)1; public static final short TRUE = (short) 1;
public static final short FALSE = (short)0; public static final short FALSE = (short) 0;
public static final short EXIT_SUCCESS = (short)0; public static final short EXIT_SUCCESS = (short) 0;
public static final short EXIT_FAIL = (short)1; public static final short EXIT_FAIL = (short) 1;
public static final String LOG4J_CONFIG_PROPERTY = "log4j.configurationFile"; public static final String LOG4J_CONFIG_PROPERTY = "log4j.configurationFile";
public static final String JUNIT_TEST_CHECK = "junit.test"; public static final String JUNIT_TEST_CHECK = "junit.test";
} }

View File

@@ -8,43 +8,44 @@ import java.util.Base64;
import net.locusworks.common.Charsets; import net.locusworks.common.Charsets;
public class DataOutputStreamHelper extends DataOutputStream implements AutoCloseable{ public class DataOutputStreamHelper extends DataOutputStream implements AutoCloseable {
public DataOutputStreamHelper() { public DataOutputStreamHelper() {
this(new ByteArrayOutputStream()); this(new ByteArrayOutputStream());
}
public DataOutputStreamHelper(OutputStream out) {
super(out);
}
public byte[] toByteArray() {
if (super.out == null) {
return new byte[0];
} }
if (super.out instanceof ByteArrayOutputStream) {
return ((ByteArrayOutputStream)super.out).toByteArray(); public DataOutputStreamHelper(OutputStream out) {
super(out);
}
public byte[] toByteArray() {
if (super.out == null) {
return new byte[0];
}
if (super.out instanceof ByteArrayOutputStream) {
return ((ByteArrayOutputStream) super.out).toByteArray();
}
return super.out.toString().getBytes(Charsets.UTF_8);
}
public String base64Encoded() {
return Base64.getEncoder().encodeToString(this.toByteArray());
}
@Override
public String toString() {
return new String(this.toByteArray(), Charsets.UTF_8);
}
@Override
public void close() throws IOException {
if (super.out != null) {
try {
super.out.close();
super.out = null;
} catch (Exception ignored) {
}
}
} }
return super.out.toString().getBytes(Charsets.UTF_8);
}
public String base64Encoded() {
return Base64.getEncoder().encodeToString(this.toByteArray());
}
@Override
public String toString() {
return new String(this.toByteArray(), Charsets.UTF_8);
}
@Override
public void close() throws IOException {
if (super.out != null) {
try {
super.out.close();
super.out = null;
} catch (Exception ex) {}
}
}
} }

View File

@@ -18,90 +18,94 @@ import com.fasterxml.jackson.databind.JsonDeserializer;
* </pre> * </pre>
* Will specify to use this deserializer class when a json field {@code purgeEndDate} is encountered in the json * Will specify to use this deserializer class when a json field {@code purgeEndDate} is encountered in the json
* string and will try to convert the string into a date object and inject the value back into the class * string and will try to convert the string into a date object and inject the value back into the class
*
* @author Isaac Parenteau * @author Isaac Parenteau
* @version 1.0 * @version 1.0
* @date 02/15/2018 * @date 02/15/2018
* @see com.fasterxml.jackson.databind.annotation.JsonDeserialize * @see com.fasterxml.jackson.databind.annotation.JsonDeserialize
*/ */
public class DateTimeStampDeserializer extends JsonDeserializer<Date> { public class DateTimeStampDeserializer extends JsonDeserializer<Date> {
private static final String DEFAULT = "MM/dd/yyyy";
private static final String EXPANDED = "MM/dd/yyyy HH:mm:ss z";
private static final String EXPANDED_WITH_TIMEZONE = "MMM d, yyyy HH:mm:ss z";
private static final String EXPANDED_WITH_AM_PM = "MMM d, yyyy h:mm:ss a";
private static final String[] formats = new String[] {
DEFAULT,
EXPANDED,
EXPANDED_WITH_TIMEZONE,
EXPANDED_WITH_AM_PM,
};
private static final Integer[] styles = new Integer[] {
SimpleDateFormat.LONG,
SimpleDateFormat.FULL,
SimpleDateFormat.MEDIUM,
SimpleDateFormat.SHORT
};
@Override private static final String DEFAULT = "MM/dd/yyyy";
public Date deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException { private static final String EXPANDED = "MM/dd/yyyy HH:mm:ss z";
String value = p.getText(); private static final String EXPANDED_WITH_TIMEZONE = "MMM d, yyyy HH:mm:ss z";
private static final String EXPANDED_WITH_AM_PM = "MMM d, yyyy h:mm:ss a";
Date date = null;
//First try to see if the value can be parsed into a long private static final String[] formats = new String[]{
try { DEFAULT,
date = new Date(Long.parseLong(value)); EXPANDED,
return date; EXPANDED_WITH_TIMEZONE,
} catch (Exception ex) { } EXPANDED_WITH_AM_PM,
};
//Next iterate over the built in styles to see if it can be converted
for (Integer style: styles) { private static final Integer[] styles = new Integer[]{
date = formatDate(style, value); SimpleDateFormat.LONG,
if (date != null) { SimpleDateFormat.FULL,
return date; SimpleDateFormat.MEDIUM,
} SimpleDateFormat.SHORT
};
@Override
public Date deserialize(JsonParser p, DeserializationContext context) throws IOException {
String value = p.getText();
Date date;
//First, try to see if the value can be parsed into along
try {
date = new Date(Long.parseLong(value));
return date;
} catch (Exception ignored) {
}
//Next, iterate over the built-in styles to see if it can be converted
for (Integer style : styles) {
date = formatDate(style, value);
if (date != null) {
return date;
}
}
//Lastly, iterate over the custom styles specified in format to see if it can be converted
for (String fmt : formats) {
date = formatDate(fmt, value);
if (date != null) {
return date;
}
}
//Return null if date format can't be converted
return null;
} }
//Lastly iterate over the custom styles specified in format to see if it can be converted /**
for (String fmt : formats) { * Convert a string value to a date object
date = formatDate(fmt, value); *
if (date != null) { * @param format The format to use in reference to the source
return date; * @param source the source to convert
} * @return Date object if the conversion was success; null otherwise
*/
private static Date formatDate(String format, String source) {
try {
return new SimpleDateFormat(format).parse(source);
} catch (Exception ex) {
return null;
}
} }
//Return null if date format can't be converted /**
return null; * Convert a string value to a date object using SimpleDateFormats
} * built in styles
*
/** * @param style The style to use
* Convert a string value to a date object * @param source the source to convert
* @param format The format to use in reference to the source * @return Date object if the conversion was success; null otherwise
* @param source the source to convert */
* @return Date object if the conversion was success; null otherwise private static Date formatDate(Integer style, String source) {
*/ try {
private static Date formatDate(String format, String source) { return SimpleDateFormat.getDateInstance(style).parse(source);
try { } catch (Exception ex) {
return new SimpleDateFormat(format).parse(source); return null;
} catch (Exception ex) { }
return null;
} }
}
/**
* Convert a string value to a date object using SimpleDateFormats
* built in styles
* @param style The style to use
* @param source the source to convert
* @return Date object if the conversion was success; null otherwise
*/
private static Date formatDate(Integer style, String source) {
try {
return SimpleDateFormat.getDateInstance(style).parse(source);
} catch (Exception ex) {
return null;
}
}
} }

View File

@@ -1,6 +1,7 @@
package net.locusworks.common.utils; package net.locusworks.common.utils;
import java.io.IOException; import java.io.IOException;
import java.io.Serial;
import java.util.Date; import java.util.Date;
import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonGenerator;
@@ -18,29 +19,31 @@ import com.fasterxml.jackson.databind.ser.std.StdSerializer;
* </pre> * </pre>
* Will specify to use this serializer class when the {@code purgeEndDate} is encountered when converting to json * Will specify to use this serializer class when the {@code purgeEndDate} is encountered when converting to json
* and will try to convert the date object to its timestamp equivalent * and will try to convert the date object to its timestamp equivalent
*
* @author Isaac Parenteau * @author Isaac Parenteau
* @date 02/15/2018
* @version 1.0 * @version 1.0
* @date 02/15/2018
* @see com.fasterxml.jackson.databind.annotation.JsonSerialize * @see com.fasterxml.jackson.databind.annotation.JsonSerialize
* @see com.fasterxml.jackson.databind.ser.std.StdSerializer * @see com.fasterxml.jackson.databind.ser.std.StdSerializer
*/ */
public class DateTimeStampSerializer extends StdSerializer<Date> { public class DateTimeStampSerializer extends StdSerializer<Date> {
/** /**
* *
*/ */
private static final long serialVersionUID = -4753139740916300831L; @Serial
private static final long serialVersionUID = -4753139740916300831L;
public DateTimeStampSerializer() { public DateTimeStampSerializer() {
this(null); this(null);
} }
public DateTimeStampSerializer(Class<Date> t) {
super(t);
}
@Override public DateTimeStampSerializer(Class<Date> t) {
public void serialize(Date date, JsonGenerator generator, SerializerProvider provider) throws IOException { super(t);
generator.writeNumber(date.getTime()); }
}
@Override
public void serialize(Date date, JsonGenerator generator, SerializerProvider provider) throws IOException {
generator.writeNumber(date.getTime());
}
} }

View File

@@ -1,6 +1,7 @@
package net.locusworks.common.utils; package net.locusworks.common.utils;
import static net.locusworks.common.Charsets.UTF_8; import static net.locusworks.common.Charsets.UTF_8;
import static net.locusworks.common.utils.ObjectUtils.getResourceStream;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
@@ -11,204 +12,212 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Iterator; import java.util.Iterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Objects;
import net.locusworks.common.interfaces.AutoCloseableIterator; import net.locusworks.common.interfaces.AutoCloseableIterator;
/** /**
* Class to read in a file that can be used in the try-with-resource block * Class to read in a file that can be used in the try-with-resource block
*
* @author Isaac Parenteau * @author Isaac Parenteau
* @version 1.0.0 * @version 1.0.0
* @date 02/15/2018 * @date 02/15/2018
*/ */
public class FileReader implements AutoCloseableIterator<FileReader.LineInfo>, Iterable<FileReader.LineInfo>, AutoCloseable { public class FileReader implements AutoCloseableIterator<FileReader.LineInfo>, Iterable<FileReader.LineInfo>, AutoCloseable {
private BufferedReader reader; private BufferedReader reader;
private LineInfo info; private LineInfo info;
private Integer lineNumber;
/**
* Constructor
* @param fileName Name of the file to read
*/
public FileReader(String fileName) {
init(fileName);
}
/**
* Constructor
* @param file File to read
*/
@Deprecated
public FileReader(File file) {
init(file.toPath());
}
/**
* Constructor
* @param file File to read
*/
public FileReader(Path file) {
init(file);
}
/**
* Constructor
* @param reader Buffered reader to read data from
*/
public FileReader(BufferedReader reader) {
init(reader);
}
/**
* Initialization helper
* @param fileName Name of the file to load
* This will look into the resources directory if it cannot
* find the file directly.
*/
private void init(String fileName) {
//check to see if the file exists
Path f = Paths.get(fileName);
if (Files.exists(f)) {
init(f); //If it does. load through the file initializer
return;
}
//Check to see if the file is in the resources directory
InputStream is = this.getClass().getResourceAsStream(fileName);
if (is == null) {
is = this.getClass().getClassLoader().getResourceAsStream(fileName);
}
//If it cant be found, throw a runtime exception
if (is == null) {
throw new IllegalArgumentException("Unable to find resource with name of" + fileName);
}
BufferedReader br = new BufferedReader(new InputStreamReader(is, UTF_8));
init(br);
}
/**
* Initializer helper to load file
* @param file File to load
*/
private void init(Path file) {
if (file == null) throw new IllegalArgumentException("File cannot be null");
if (Files.notExists(file)) throw new IllegalArgumentException("File " + file + " does not exist");
if (!Files.isRegularFile(file)) throw new IllegalArgumentException("File " + file + " is not a file");
try {
BufferedReader br = Files.newBufferedReader(file);
init(br);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
/**
* Initializer helper for buffered reader
* This is ultimately where all initializers end as a buffered reader
* @param reader buffered reader to load
*/
private void init(BufferedReader reader) {
this.reader = reader;
this.lineNumber = 0;
}
@Override
public boolean hasNext() {
try {
String line = this.reader.readLine();
if (line == null) {
this.close();
this.info = null;
return false;
}
this.lineNumber++;
this.info = new LineInfo(this.lineNumber, line);
return true;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
@Override
public LineInfo next() {
if (this.info == null) {
throw new NoSuchElementException("Call to next was initiated but there are no more elements to read");
}
return this.info;
}
@Override
public Iterator<LineInfo> iterator() {
return this;
}
@Override
public void close() {
if (this.reader != null) {
try {
this.reader.close();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
public static class LineInfo {
private Integer lineNumber; private Integer lineNumber;
private Integer lineLength;
private String line;
/** /**
* @param lineNumber the current line number in the file * Constructor
* @param line the line information from the file *
* @param fileName Name of the file to read
*/ */
public LineInfo(Integer lineNumber, String line) { public FileReader(String fileName) {
this.lineNumber = lineNumber; init(fileName);
this.line = line;
this.lineLength = line.length();
} }
/** /**
* @return the lineNumber * Constructor
*
* @param file File to read
*/ */
public Integer getLineNumber() { @Deprecated
return lineNumber; public FileReader(File file) {
init(file.toPath());
} }
/** /**
* @param lineNumber the lineNumber to set * Constructor
*
* @param file File to read
*/ */
public void setLineNumber(Integer lineNumber) { public FileReader(Path file) {
this.lineNumber = lineNumber; init(file);
} }
/** /**
* @return the lineLength * Constructor
*
* @param reader Buffered reader to read data from
*/ */
public Integer getLineLength() { public FileReader(BufferedReader reader) {
return lineLength; init(reader);
} }
/** /**
* @param lineLength the lineLength to set * Initialization helper
*
* @param fileName Name of the file to load
* This will look into the resources directory if it cannot
* find the file directly.
*/ */
public void setLineLength(Integer lineLength) { private void init(String fileName) {
this.lineLength = lineLength; if (fileName == null) {
throw new IllegalArgumentException("File name cannot be null");
}
//check to see if the file exists
Path f = Paths.get(fileName);
if (Files.exists(f)) {
init(f); //If it does. load through the file initializer
return;
}
//Check to see if the file is in the resources directory
InputStream is = getResourceStream(fileName);
BufferedReader br = new BufferedReader(new InputStreamReader(is, UTF_8));
init(br);
} }
/** /**
* @return the line * Initializer helper to load file
*
* @param file File to load
*/ */
public String getLine() { private void init(Path file) {
return line; if (file == null) throw new IllegalArgumentException("File cannot be null");
if (Files.notExists(file)) throw new IllegalArgumentException("File " + file + " does not exist");
if (!Files.isRegularFile(file)) throw new IllegalArgumentException("File " + file + " is not a file");
try {
BufferedReader br = Files.newBufferedReader(file);
init(br);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
} }
/** /**
* @param line the line to set * Initializer helper for buffered reader
* This is ultimately where all initializers end as a buffered reader
*
* @param reader buffered reader to load
*/ */
public void setLine(String line) { private void init(BufferedReader reader) {
this.line = line; if (reader == null) {
throw new IllegalArgumentException("Buffered Reader cannot be null");
}
this.reader = reader;
this.lineNumber = 0;
}
@Override
public boolean hasNext() {
try {
String line = this.reader.readLine();
if (line == null) {
this.close();
this.info = null;
return false;
}
this.lineNumber++;
this.info = new LineInfo(this.lineNumber, line);
return true;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
@Override
public LineInfo next() {
if (this.info == null) {
throw new NoSuchElementException("Call to next was initiated but there are no more elements to read");
}
return this.info;
}
@Override
public Iterator<LineInfo> iterator() {
return this;
}
@Override
public void close() {
if (this.reader != null) {
try {
this.reader.close();
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
public static class LineInfo {
private Integer lineNumber;
private Integer lineLength;
private String line;
/**
* @param lineNumber the current line number in the file
* @param line the line information from the file
*/
public LineInfo(Integer lineNumber, String line) {
this.lineNumber = lineNumber;
this.line = line;
this.lineLength = line.length();
}
/**
* @return the lineNumber
*/
public Integer getLineNumber() {
return lineNumber;
}
/**
* @param lineNumber the lineNumber to set
*/
public void setLineNumber(Integer lineNumber) {
this.lineNumber = lineNumber;
}
/**
* @return the lineLength
*/
public Integer getLineLength() {
return lineLength;
}
/**
* @param lineLength the lineLength to set
*/
public void setLineLength(Integer lineLength) {
this.lineLength = lineLength;
}
/**
* @return the line
*/
public String getLine() {
return line;
}
/**
* @param line the line to set
*/
public void setLine(String line) {
this.line = line;
}
} }
}
} }

View File

@@ -13,176 +13,188 @@ import java.security.MessageDigest;
/** /**
* Wrapper class that leverages java's MessageDigest to hash files * Wrapper class that leverages java's MessageDigest to hash files
*
* @author Isaac Parenteau * @author Isaac Parenteau
* *
*/ */
public class HashUtils { public class HashUtils {
private static final Charset UTF_8 = StandardCharsets.UTF_8;
/** private static final Charset UTF_8 = StandardCharsets.UTF_8;
* Used to build output as Hex
*/
private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/** /**
* Used to build output as Hex * Used to build output as Hex
*/ */
private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* Size of the streaming buffer
*/
public static final Integer STREAM_BUFFER_LENGTH = 1024;
/** /**
* Hash a string literal * Used to build output as Hex
* @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) */
* @param data String to hash private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
* @return hash value of the string literal
*/
public static String hash(String hashType, String data) {
return hash(hashType, data, true);
}
/**
* Hash a string literal
* @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512)
* @param data String to hash
* @param toLower True to output the hash in lower case. False to output in upper case
* @return hash value of the string literal
*/
public static String hash(String hashType, String data, boolean toLower) {
byte[] stringData = data.getBytes(UTF_8);
return hash(hashType, stringData, toLower);
}
@Deprecated
public static String hash(String hashType, File data) {
return hash(hashType, data.toPath());
}
/** /**
* Hash a file * Size of the streaming buffer
* @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) */
* @param data File to hash public static final Integer STREAM_BUFFER_LENGTH = 1024;
* @return hash value of the file
*/
public static String hash(String hashType, Path data) {
return hash(hashType, data, true);
}
@Deprecated
public static String hash(String hashType, File data, boolean toLower) {
return hash(hashType, data.toPath(), toLower);
}
/** /**
* Hash a file * Hash a string literal
* @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512) *
* @param data File to hash * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512)
* @param toLower True to output the hash in lower case. False to output in upper case * @param data String to hash
* @return hash value of the file * @return hash value of the string literal
*/ */
public static String hash(String hashType, Path data, boolean toLower) { public static String hash(String hashType, String data) {
try (InputStream stream = Files.newInputStream(data)) { return hash(hashType, data, true);
return hash(stream, hashType, toLower);
} catch (IOException ex) {
throw new IllegalArgumentException(ex.getMessage());
} }
}
/**
* Hash a byte array
* @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512)
* @param data data to hash
* @return hash value of the data
*/
public static String hash(String hashType, byte[] data) {
return hash(hashType, data, true);
}
/**
* Hash a byte array
* @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512)
* @param data data to hash
* @param toLower True to output the hash in lower case. False to output in upper case
* @return hash value of the data
*/
public static String hash(String hashType, byte[] data, boolean toLower) {
return hash(new BufferedInputStream(new ByteArrayInputStream(data)), hashType, toLower);
}
/**
* Hash an input stream
* @param stream Stream with the data to hash
* @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512)
* @return Hash value of the input stream
*/
public static String hash(InputStream stream, String hashType) {
return hash(stream, hashType, true);
}
/**
* Hash an input stream
* @param stream Stream with the data to hash
* @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512)
* @param toLower True to output the hash in lower case. False to output in upper case
* @return Hash value of the input stream
*/
public static String hash(InputStream stream, String hashType, boolean toLower) {
MessageDigest digest = null;
try(InputStream is = stream) {
digest = MessageDigest.getInstance(hashType);
byte[] buffer = new byte[STREAM_BUFFER_LENGTH];
int read = is.read(buffer, 0, STREAM_BUFFER_LENGTH);
while (read > -1) { /**
digest.update(buffer, 0, read); * Hash a string literal
read = is.read(buffer, 0, STREAM_BUFFER_LENGTH); *
} * @param hashType Hash types supported by MessageDigest (i.e., MD5, SHA-1, SHA-512)
* @param data String to hash
return encodeHexString(digest.digest(), toLower); * @param toLower True to output the hash in lower case. False to output in the upper case
} catch (Exception ex) { * @return hash value of the string literal
throw new IllegalArgumentException(ex.getMessage()); */
} public static String hash(String hashType, String data, boolean toLower) {
} byte[] stringData = data.getBytes(UTF_8);
return hash(hashType, stringData, toLower);
/** }
* Encode the hash data back to a string
* @param data Data to encode @Deprecated
* @param toLower output to lower case public static String hash(String hashType, File data) {
* @return return hash(hashType, data.toPath());
*/ }
private static String encodeHexString(byte[] data, boolean toLower) {
return new String(encodeHex(data, toLower)); /**
} * Hash a file
*
/** * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512)
* Encode the hash data to a character array * @param data File to hash
* @param data Data to encode * @return hash value of the file
* @param toLower output to lower case */
* @return public static String hash(String hashType, Path data) {
*/ return hash(hashType, data, true);
private static char[] encodeHex(byte[] data, boolean toLower) { }
return encodeHex(data, toLower ? DIGITS_LOWER : DIGITS_UPPER);
} @Deprecated
public static String hash(String hashType, File data, boolean toLower) {
/** return hash(hashType, data.toPath(), toLower);
* Encode the hex to a character array }
* @param data Data to encode
* @param toDigits digits to use /**
* @return * Hash a file
*/ *
private static char[] encodeHex(byte[] data, char[] toDigits) { * @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512)
int l = data.length; * @param data File to hash
char[] out = new char[l << 1]; * @param toLower True to output the hash in lower case. False to output in upper case
// two characters form the hex value. * @return hash value of the file
for (int i = 0, j = 0; i < l; i++) { */
out[j++] = toDigits[(0xF0 & data[i]) >>> 4]; public static String hash(String hashType, Path data, boolean toLower) {
out[j++] = toDigits[0x0F & data[i]]; try (InputStream stream = Files.newInputStream(data)) {
return hash(stream, hashType, toLower);
} catch (IOException ex) {
throw new IllegalArgumentException(ex.getMessage());
}
}
/**
* Hash a byte array
*
* @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512)
* @param data data to hash
* @return hash value of the data
*/
public static String hash(String hashType, byte[] data) {
return hash(hashType, data, true);
}
/**
* Hash a byte array
*
* @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512)
* @param data data to hash
* @param toLower True to output the hash in lower case. False to output in upper case
* @return hash value of the data
*/
public static String hash(String hashType, byte[] data, boolean toLower) {
return hash(new BufferedInputStream(new ByteArrayInputStream(data)), hashType, toLower);
}
/**
* Hash an input stream
*
* @param stream Stream with the data to hash
* @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512)
* @return Hash value of the input stream
*/
public static String hash(InputStream stream, String hashType) {
return hash(stream, hashType, true);
}
/**
* Hash an input stream
*
* @param stream Stream with the data to hash
* @param hashType Hash types supported by MessageDigest (i.e MD5, SHA-1, SHA-512)
* @param toLower True to output the hash in lower case. False to output in upper case
* @return Hash value of the input stream
*/
public static String hash(InputStream stream, String hashType, boolean toLower) {
MessageDigest digest = null;
try (InputStream is = stream) {
digest = MessageDigest.getInstance(hashType);
byte[] buffer = new byte[STREAM_BUFFER_LENGTH];
int read = is.read(buffer, 0, STREAM_BUFFER_LENGTH);
while (read > -1) {
digest.update(buffer, 0, read);
read = is.read(buffer, 0, STREAM_BUFFER_LENGTH);
}
return encodeHexString(digest.digest(), toLower);
} catch (Exception ex) {
throw new IllegalArgumentException(ex.getMessage());
}
}
/**
* Encode the hash data back to a string
*
* @param data Data to encode
* @param toLower output to lower case
* @return encoded string
*/
private static String encodeHexString(byte[] data, boolean toLower) {
return new String(encodeHex(data, toLower));
}
/**
* Encode the hash data to a character array
*
* @param data Data to encode
* @param toLower output to lower case
* @return encoded character array
*/
private static char[] encodeHex(byte[] data, boolean toLower) {
return encodeHex(data, toLower ? DIGITS_LOWER : DIGITS_UPPER);
}
/**
* Encode the hex to a character array
*
* @param data Data to encode
* @param toDigits digits to use
* @return encoded character array
*/
private static char[] encodeHex(byte[] data, char[] toDigits) {
int l = data.length;
char[] out = new char[l << 1];
// two characters form the hex value.
for (int i = 0, j = 0; i < l; i++) {
out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
out[j++] = toDigits[0x0F & data[i]];
}
return out;
} }
return out;
}
} }

View File

@@ -0,0 +1,38 @@
package net.locusworks.common.utils;
import java.io.InputStream;
import java.util.Objects;
import java.util.function.Consumer;
/**
* Object Utils. Functions to perform on objects
*/
public class ObjectUtils {
/**
* Perform an action if an object is not null otherwise do nothing.
*
* @param object the object to test
* @param consumer the consumer to perform.
* @param <T> the type
*/
public static <T> void performIfNotNull(T object, Consumer<T> consumer) {
if (Objects.isNull(object)) {
return;
}
consumer.accept(object);
}
public static InputStream getResourceStream(String resourceName) {
InputStream is = ObjectUtils.class.getResourceAsStream(resourceName);
if (is == null) {
is = ObjectUtils.class.getClassLoader().getResourceAsStream(resourceName);
}
if (is == null) {
throw new NullPointerException("Unable to find resource with name of " + resourceName);
}
return is;
}
}

View File

@@ -1,5 +1,6 @@
package net.locusworks.common.utils; package net.locusworks.common.utils;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.Objects; import java.util.Objects;
import java.util.Random; import java.util.Random;
@@ -8,72 +9,68 @@ import static net.locusworks.common.Charsets.UTF_8;
public class RandomString { public class RandomString {
public static final String LOWER = "abcdefghijklmnopqrstuvwxyz"; public static final String LOWER = "abcdefghijklmnopqrstuvwxyz";
public static final String UPPER = LOWER.toUpperCase(); public static final String UPPER = LOWER.toUpperCase();
public static final String DIGITS = "0123456789"; public static final String DIGITS = "0123456789";
public static final String ALPHA_NUMERIC = LOWER + UPPER + DIGITS;
private Random random;
private char[] symbols;
private int length;
private static RandomString instance;
private RandomString(Integer length) {
this(length, new SecureRandom());
}
private RandomString(Integer length, Random random) {
this(length, random, ALPHA_NUMERIC);
}
private RandomString(Integer length, Random random, String symbols) {
if (length < 1) throw new IllegalArgumentException("Length has to be greater than 1");
if (symbols.length() < 2) throw new IllegalArgumentException("Symbols need to be greater than 2");
this.random = Objects.requireNonNull(random);
this.symbols = symbols.toCharArray();
}
private synchronized final void setRandom(Random random) {
this.random = random;
}
private synchronized final void setLength(int length) { public static final String ALPHA_NUMERIC = LOWER + UPPER + DIGITS;
this.length = length;
}
public String nextString() { private Random random;
char[] buffer = new char[length];
for (int index = 0; index < buffer.length; index++) { private char[] symbols;
buffer[index] = symbols[random.nextInt(symbols.length)];
private static RandomString instance;
private RandomString() {
Random random;
try {
random = SecureRandom.getInstance("SHA1PRNG");
} catch (NoSuchAlgorithmException e) {
random = new SecureRandom();
}
init(random);
} }
return new String(buffer);
} private RandomString(Random random) {
init(random);
public static String getString(Integer length) {
if (instance == null) {
instance = new RandomString(length);
} }
instance.setLength(length);
return instance.nextString(); private void init(Random random) {
} this.random = Objects.requireNonNull(random, "Random generator cannot be null");
this.symbols = ALPHA_NUMERIC.toCharArray();
public static String getString(Integer length, Random random) { }
if (instance == null) {
instance = new RandomString(length); private String nextString(int length) {
if (length < 1) throw new IllegalArgumentException("String Length has to be greater than 0");
char[] buffer = new char[length];
for (int index = 0; index < buffer.length; index++) {
buffer[index] = symbols[random.nextInt(symbols.length)];
}
return new String(buffer);
}
public String getString(Integer length) {
return this.nextString(length);
}
public byte[] getBytes(Integer length) {
return getString(length).getBytes(UTF_8);
}
public static RandomString getInstance() {
if (instance == null) {
instance = new RandomString();
}
return instance;
}
public static RandomString newInstance() {
instance = new RandomString();
return instance;
}
public static RandomString newInstance(Random random) {
instance = new RandomString(random);
return instance;
} }
instance.setLength(length);
instance.setRandom(random);
return instance.nextString();
}
public static byte[] getBytes(Integer length) {
return getString(length).getBytes(UTF_8);
}
public static byte[] getBytes(Integer length, Random random) {
return getString(length, random).getBytes(UTF_8);
}
} }

View File

@@ -8,123 +8,188 @@ import java.util.Map;
import static net.locusworks.common.utils.Checks.checkArguments; import static net.locusworks.common.utils.Checks.checkArguments;
import static net.locusworks.common.utils.Checks.checkNotNull; import static net.locusworks.common.utils.Checks.checkNotNull;
/**
* Class to help split a string in various ways.
* By partition (fixed length split) or by sequence (look for a specific string sequence).
* it can also split on new line or not.
*/
public class Splitter { public class Splitter {
private enum SplitterType {
private String splitSeq; PARTITION,
private boolean omitEmptyStrings = false; SEQUENCE
private int partition; }
private int limit;
private static Splitter splitter;
private Splitter(String seq) {
this.splitSeq = seq;
}
private Splitter(int partition) {
this.partition = partition;
}
public Splitter omitEmptyStrings() {
this.omitEmptyStrings = true;
return this;
}
public Splitter withLimit(int limit) {
this.limit = limit;
return this;
}
public MapSplitter withKeyValueSeparator(String separator) {
checkArguments(!Utils.isEmptyString(separator), "Key value separator cannot be empty or null");
return new MapSplitter(this, separator);
}
public String[] splitToArray(String sentence) {
List<String> list = split(sentence);
return list.toArray(new String[list.size()]);
}
public List<String> split(String sentence) {
checkArguments(!Utils.isEmptyString(sentence), "provided value is null or empty");
List<String> list = new ArrayList<>();
if (!Utils.isEmptyString(splitSeq))
populateForTrimmer(sentence, list);
else
populateForFixedWidth(sentence, list);
return limit > 0 ? list.subList(0, limit) : list;
}
private void populateForFixedWidth(String sentence, List<String> list) { private String splitSeq;
int strLength = sentence.length(); private boolean omitEmptyStrings = false;
for (int i = 0; i < strLength; i += partition) { private int partition;
list.add(sentence.substring(i, Math.min(strLength, i + partition))); private int limit;
} private final SplitterType splitterType;
} private static Splitter splitter;
private void populateForTrimmer(String sentence, List<String> list) { private Splitter(String seq) {
for (String s : sentence.split(splitSeq)) { this.splitSeq = seq;
if (s == null || (omitEmptyStrings && s.trim().isEmpty())) continue; this.splitterType = SplitterType.SEQUENCE;
list.add(s.trim());
} }
}
private Splitter(int partition) {
public static Splitter fixedLengthSplit(int partition) { this.partition = partition;
checkArguments(partition > 0, "Partition has to be greater than 0"); this.splitterType = SplitterType.PARTITION;
splitter = new Splitter(partition);
return splitter;
}
public static Splitter on(String split) {
checkNotNull(split, "Split value provided was null");
splitter = new Splitter(split);
return splitter;
}
public static Splitter onNewLine() {
return on("\\r?\\n");
}
public static Splitter onSpace() {
return on(" ");
}
public static class MapSplitter {
private Splitter splitter;
private String separator;
private boolean skipInvalid = false;
private MapSplitter(Splitter splitter, String separator) {
checkNotNull(splitter, "Splitter cannot be null");
checkArguments(!Utils.isEmptyString(separator), "Key value separator cannot be empty or null");
this.splitter = splitter;
this.separator = separator;
} }
public MapSplitter skipInvalidKeyValues() { /**
this.skipInvalid = true; * Remove empty string from the resulting lists
return this; *
* @return this
*/
public Splitter omitEmptyStrings() {
this.omitEmptyStrings = true;
return this;
} }
public Map<String, String> split(String sentence) { /**
checkArguments(!Utils.isEmptyString(sentence), "provided value is null or empty"); * Return a subset of the resulting list
Map<String, String> map = new LinkedHashMap<>(); *
* @param limit how many items to retrieve
for (String s : splitter.split(sentence)) { * @return this
String[] keyValue = s.split(separator); */
try { public Splitter withLimit(int limit) {
checkArguments(keyValue.length == 2, "invalid length found for key value mapping"); this.limit = limit;
} catch (IllegalArgumentException ex) { return this;
if (!skipInvalid) throw ex; }
continue;
/**
* Return an array instead of a list
*
* @param sentence the string sentence to split
* @return this
*/
public String[] splitToArray(String sentence) {
List<String> list = split(sentence);
return list.toArray(new String[0]);
}
/**
* Split the string
*
* @param sentence the string to split
* @return the resulting list.
*/
public List<String> split(String sentence) {
checkArguments(!Utils.isEmptyString(sentence), "provided value is null or empty");
List<String> list = new ArrayList<>();
if (splitterType == SplitterType.PARTITION) {
populateForFixedWidth(sentence, list);
} else {
populateForTrimmer(sentence, list);
}
return limit > 0 ? list.subList(0, limit) : list;
}
private void populateForFixedWidth(String sentence, List<String> list) {
checkArguments(partition > 0, "Partition should be greater than 0");
int strLength = sentence.length();
for (int i = 0; i < strLength; i += partition) {
list.add(sentence.substring(i, Math.min(strLength, i + partition)));
}
}
private void populateForTrimmer(String sentence, List<String> list) {
checkNotNull(splitSeq, "Split value provided was null");
for (String s : sentence.split(splitSeq)) {
if (omitEmptyStrings && s.trim().isEmpty())
continue;
list.add(s.trim());
}
}
/**
* Split the string on fixed length partitions
*
* @param partition the length to split the string on
* @return this
*/
public static Splitter fixedLengthSplit(int partition) {
splitter = new Splitter(partition);
return splitter;
}
/**
* Split the length on a specified string sequence
*
* @param split the sequence to split
* @return this
*/
public static Splitter on(String split) {
splitter = new Splitter(split);
return splitter;
}
/**
* Split on new line sequence
*
* @return this
*/
public static Splitter onNewLine() {
return on("\\r?\\n");
}
/**
* Split on spaces
*
* @return this
*/
public static Splitter onSpace() {
return on(" ");
}
/**
* Separator on what the key value is. return map
*
* @param separator the separator value
* @return this
*/
public MapSplitter withKeyValueSeparator(String separator) {
return new MapSplitter(this, separator);
}
public static class MapSplitter {
private final Splitter splitter;
private final String separator;
private boolean skipInvalid = false;
private MapSplitter(Splitter splitter, String separator) {
checkNotNull(splitter, "Splitter cannot be null");
checkArguments(!Utils.isEmptyString(separator),
"Key value separator cannot be empty or null");
this.splitter = splitter;
this.separator = separator;
}
public MapSplitter skipInvalidKeyValues() {
this.skipInvalid = true;
return this;
}
public Map<String, String> split(String sentence) {
checkArguments(!Utils.isEmptyString(sentence), "provided value is null or empty");
Map<String, String> map = new LinkedHashMap<>();
for (String s : splitter.split(sentence)) {
String[] keyValue = s.split(separator);
try {
checkArguments(keyValue.length == 2, "invalid length found for key value mapping");
} catch (IllegalArgumentException ex) {
if (!skipInvalid)
throw ex;
continue;
}
map.put(keyValue[0], keyValue[1]);
}
return map;
} }
map.put(keyValue[0], keyValue[1]);
}
return map;
} }
}
} }

View File

@@ -7,60 +7,65 @@ import java.util.stream.StreamSupport;
/** /**
* Utility class to make iterators streamable * Utility class to make iterators streamable
*
* @author Isaac Parenteau * @author Isaac Parenteau
* @version 1.0.0 * @version 1.0.0
* @date 02/15/2018 * @date 02/15/2018
*/ */
public class StreamUtils { public class StreamUtils {
/** /**
* Convert a iterator to a stream * Convert an iterator to a stream
* @param iterator the iterator to convert *
* @param <T> the class type * @param iterator the iterator to convert
* @return stream of the iterator * @param <T> the class type
*/ * @return stream of the iterator
public static <T> Stream<T> asStream(Iterator<T> iterator) { */
return asStream(iterator, false); public static <T> Stream<T> asStream(Iterator<T> iterator) {
} return asStream(iterator, false);
}
public static <T> Stream<T> asStream(Iterable<T> iterable) {
return asStream(iterable, false); public static <T> Stream<T> asStream(Iterable<T> iterable) {
} return asStream(iterable, false);
}
/**
* Converts an array to a stream /**
* @param items the items to convert * Converts an array to a stream
* @param <T> the class type *
* @return stream of the array * @param items the items to convert
*/ * @param <T> the class type
public static <T> Stream<T> asStream(T[] items) { * @return stream of the array
return asStream(Arrays.asList(items).iterator(), false); */
} public static <T> Stream<T> asStream(T[] items) {
return asStream(Arrays.asList(items).iterator(), false);
/** }
* Converts an array to a stream
* @param items the items to convert /**
* @param parallel make the stream parallel if set to true * Converts an array to a stream
* @param <T> the class type *
* @return stream of the array * @param items the items to convert
*/ * @param parallel make the stream parallel if set to true
public static <T> Stream<T> asStream(T[] items, boolean parallel) { * @param <T> the class type
return asStream(Arrays.asList(items).iterator(), parallel); * @return stream of the array
} */
public static <T> Stream<T> asStream(T[] items, boolean parallel) {
public static <T> Stream<T> asStream(Iterable<T> iterable, boolean parallel) { return asStream(Arrays.asList(items).iterator(), parallel);
return StreamSupport.stream(iterable.spliterator(), parallel); }
}
public static <T> Stream<T> asStream(Iterable<T> iterable, boolean parallel) {
/** return StreamSupport.stream(iterable.spliterator(), parallel);
* Convert an iterator to a stream }
* @param iterator iterator to convert
* @param parallel make the stream parallel if set to true. /**
* @param <T> the class type * Convert an iterator to a stream
* @return stream of the iterator *
*/ * @param iterator iterator to convert
public static <T> Stream<T> asStream(Iterator<T> iterator, boolean parallel) { * @param parallel make the stream parallel if set to true.
Iterable<T> iterable = () -> iterator; * @param <T> the class type
return StreamSupport.stream(iterable.spliterator(), parallel); * @return stream of the iterator
} */
public static <T> Stream<T> asStream(Iterator<T> iterator, boolean parallel) {
Iterable<T> iterable = () -> iterator;
return StreamSupport.stream(iterable.spliterator(), parallel);
}
} }

View File

@@ -7,35 +7,35 @@ package net.locusworks.common.utils;
* @date 02/15/2018 * @date 02/15/2018
*/ */
public class Success { public class Success {
private boolean success = true; private boolean success = true;
private Object body; private final Object body;
protected Success() { protected Success() {
this(true, true); this(true, true);
} }
protected Success(boolean success) { protected Success(boolean success) {
this(success, success); this(success, success);
} }
public Success(boolean success, Object body) {
this.success = success;
this.body = body;
}
public Object getBody() { public Success(boolean success, Object body) {
return body; this.success = success;
} this.body = body;
}
public boolean getSuccess() { public Object getBody() {
return success; return body;
} }
public static Success success() { public boolean getSuccess() {
return new Success(); return success;
} }
public static Success fail() { public static Success success() {
return new Success(false); return new Success();
} }
public static Success fail() {
return new Success(false);
}
} }

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,165 @@
package net.locusworks.common.configuration;
import net.locusworks.common.exceptions.ApplicationException;
import net.locusworks.common.interfaces.PersistableRequest;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.BufferedReader;
import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.mockStatic;
import org.mockito.MockedStatic;
class ConfigurationCoverageTest {
@TempDir
Path tempDir;
static class Manager extends ConfigurationManager {
void initialize(Path base, byte[] key, ConfigurationCallback callback) throws Exception {
init(base.toString(), "test.properties", key, callback);
}
void initialize(Path base, ConfigurationCallback callback) throws Exception {
init(base.toString(), "test.properties", callback);
}
}
static class Request implements PersistableRequest {
private String dbHost;
private String dbPort;
private String ignored;
Request(String dbHost, String dbPort, String ignored) {
this.dbHost = dbHost;
this.dbPort = dbPort;
this.ignored = ignored;
}
}
@Test
void managerCreatesLoadsReconcilesSavesAndReportsConfiguration() throws Exception {
List<String> messages = new ArrayList<>();
Manager manager = new Manager();
manager.initialize(tempDir, "seed".getBytes(), messages::add);
assertEquals("localhost", manager.getPropertyValue("dbHost"));
assertEquals("fallback", manager.getPropertyValue("missing", "fallback"));
assertNull(manager.getPropertyValue("missing"));
assertEquals(4, manager.getConfiguration().size());
assertTrue(Files.exists(tempDir.resolve("test.properties")));
assertTrue(messages.stream().anyMatch(m -> m.contains("Added new configuration")));
Properties changed = new Properties();
changed.putAll(manager.getConfiguration());
changed.setProperty("dbHost", "remote");
changed.setProperty("obsolete", "remove me");
manager.saveToConf(changed);
assertEquals("remote", manager.getPropertyValue("dbHost"));
assertFalse(manager.getConfiguration().containsKey("obsolete"));
assertTrue(messages.stream().anyMatch(m -> m.contains("Saved config file")));
}
@Test
void managerPersistsSelectedPlainAndEncryptedFieldsAndSkipsUnchangedOnes() throws Exception {
Manager manager = new Manager();
manager.initialize(tempDir, new byte[0], null);
assertThrows(ApplicationException.class,
() -> manager.saveConfiguration(new Request("x", "1", "x"), null, null));
assertThrows(ApplicationException.class,
() -> manager.saveConfiguration(new Request("x", "1", "x"), Set.of(), null));
manager.saveConfiguration(new Request("new-host", "3306", "ignored"),
Set.of("dbHost", "dbPort"), null);
assertEquals("new-host", manager.getPropertyValue("dbHost"));
assertEquals("3306", manager.getPropertyValue("dbPort"));
manager.saveConfiguration(new Request("encrypted-host", "3306", "ignored"),
Set.of("dbHost", "dbPort", "ignored"), Set.of("dbHost"));
assertNotEquals("encrypted-host", manager.getPropertyValue("dbHost"));
assertThrows(ApplicationException.class,
() -> manager.saveConfiguration(null, Set.of("dbHost"), Set.of()));
assertDoesNotThrow(() -> manager.saveConfiguration(
new Request("ignored", "3306", "ignored"), Set.of("dbPort"), Set.of()));
}
@Test
void managerConvenienceInitializationAndBothLoadFailuresAreCovered() throws Exception {
Path convenience = Files.createDirectory(tempDir.resolve("convenience"));
Manager normal = new Manager();
normal.initialize(convenience, null);
assertNotNull(normal.getConfiguration());
try (MockedStatic<PropertiesManager> properties = mockStatic(PropertiesManager.class, CALLS_REAL_METHODS)) {
properties.when(() -> PropertiesManager.loadConfiguration(Manager.class, "test.properties"))
.thenThrow(new java.io.IOException("template failed"));
assertThrows(java.io.IOException.class,
() -> new Manager().initialize(tempDir, "seed".getBytes(), null));
}
Path activeFailure = Files.createDirectory(tempDir.resolve("active-failure"));
try (MockedStatic<PropertiesManager> properties = mockStatic(PropertiesManager.class, CALLS_REAL_METHODS)) {
Path activeFile = activeFailure.resolve("test.properties");
properties.when(() -> PropertiesManager.loadConfiguration(activeFile))
.thenThrow(new java.io.IOException("active failed"));
Manager recovered = new Manager();
recovered.initialize(activeFailure, "seed".getBytes(), null);
assertEquals(4, recovered.getConfiguration().size());
}
}
@SuppressWarnings("deprecation")
@Test
void propertiesManagerCoversResourcesFilesReadersMergeRemovalAndSaveFailures() throws Exception {
assertNotNull(new PropertiesManager());
assertNull(PropertiesManager.loadConfiguration(getClass(), "missing.properties"));
Properties resource = PropertiesManager.loadConfiguration(getClass(), "/test.properties");
assertEquals(4, resource.size());
Path missing = tempDir.resolve("missing.properties");
assertTrue(PropertiesManager.loadConfiguration(missing).isEmpty());
Properties parsed = PropertiesManager.loadConfiguration(
new BufferedReader(new StringReader("one=1\ntwo=2")));
assertEquals("1", parsed.getProperty("one"));
Properties destination = new Properties();
destination.setProperty("one", "existing");
Properties additions = new Properties();
additions.setProperty("one", "replacement");
additions.setProperty("two", "2");
assertEquals(1, PropertiesManager.addConfiguration(destination, additions).size());
assertEquals("existing", destination.getProperty("one"));
destination.setProperty("obsolete", "old");
Properties expected = new Properties();
expected.setProperty("one", "existing");
assertEquals(2, PropertiesManager.removeConfiguration(destination, expected).size());
assertEquals(Set.of("one"), destination.keySet());
Path saved = tempDir.resolve("saved.properties");
PropertiesManager.saveConfiguration(expected, saved.toFile(), null);
assertEquals("existing", PropertiesManager.loadConfiguration(saved.toFile()).getProperty("one"));
assertThrows(RuntimeException.class,
() -> PropertiesManager.saveConfiguration(expected, tempDir, "failure"));
}
}

View File

@@ -0,0 +1,80 @@
package net.locusworks.common.crypto;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class AESAndHashSaltCoverageTest {
@Test
void aesFactoriesSeedChangesEmptyValuesAndFailures() throws Exception {
AES bytes = AES.createInstance("byte seed".getBytes());
assertEquals("byte seed", bytes.getSeed());
assertEquals("", bytes.decrypt(bytes.encrypt(null)));
assertEquals("", bytes.decrypt(null));
String encrypted = bytes.encrypt("value");
assertSame(bytes, bytes.withSeed("byte seed"));
assertEquals("value", bytes.decrypt(encrypted));
assertSame(bytes, bytes.withSeed("different seed"));
assertEquals("different seed", bytes.getSeed());
assertThrows(IllegalArgumentException.class, () -> bytes.decrypt(encrypted));
assertThrows(IllegalArgumentException.class, () -> bytes.decrypt("AA=="));
assertNotNull(AES.createInstance());
assertThrows(IllegalArgumentException.class, () -> AES.createInstance((String) null));
AES uninitialized = new AES();
assertThrows(IllegalArgumentException.class, () -> uninitialized.encrypt("value"));
Method init = AES.class.getDeclaredMethod("init", byte[].class);
init.setAccessible(true);
InvocationTargetException initFailure = assertThrows(InvocationTargetException.class,
() -> init.invoke(uninitialized, (Object) new byte[16]));
assertInstanceOf(IllegalArgumentException.class, initFailure.getCause());
}
@Test
void aesMainValidatesArgumentsAndSupportsEncryptAndDecryptModes() throws Exception {
assertThrows(IllegalArgumentException.class, () -> AES.main(null));
assertThrows(IllegalArgumentException.class, () -> AES.main(new String[0]));
assertDoesNotThrow(() -> AES.main(new String[]{"plain"}));
AES aes = AES.createInstance("seed");
assertDoesNotThrow(() -> AES.main(new String[]{aes.encrypt("plain"), "seed"}));
}
@Test
void hashSaltSupportsStringAndCharacterPasswordsAndInvalidInputs() throws Exception {
assertNotNull(new HashSalt());
String hash = HashSalt.createHash("password".toCharArray());
assertTrue(HashSalt.validatePassword("password".toCharArray(), hash));
assertFalse(HashSalt.validatePassword("wrong", hash));
assertFalse(HashSalt.validatePassword("wrong".toCharArray(), hash));
assertThrows(RuntimeException.class, () -> HashSalt.validatePassword("password", "bad"));
Method toHex = HashSalt.class.getDeclaredMethod("toHex", byte[].class);
toHex.setAccessible(true);
assertEquals("0001", toHex.invoke(null, (Object) new byte[]{0, 1}));
Method slowEquals = HashSalt.class.getDeclaredMethod("slowEquals", byte[].class, byte[].class);
slowEquals.setAccessible(true);
assertEquals(false, slowEquals.invoke(null, new byte[]{1}, new byte[]{1, 2}));
assertEquals(false, slowEquals.invoke(null, new byte[]{1, 2}, new byte[]{1}));
}
@Test
void hashSaltMainValidatesArgumentsAndPrintsHash() {
assertThrows(IllegalArgumentException.class, () -> HashSalt.main(null));
assertThrows(IllegalArgumentException.class, () -> HashSalt.main(new String[0]));
assertDoesNotThrow(() -> HashSalt.main(new String[]{"password"}));
}
}

View File

@@ -0,0 +1,227 @@
package net.locusworks.common.crypto;
import net.locusworks.common.crypto.KeyFile.EncryptionType;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.KeyPair;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Field;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.mockStatic;
import org.mockito.MockedStatic;
class CryptoCoverageTest {
@TempDir
Path tempDir;
@Test
void aesKeyAndSpecRoundTripAndRejectMalformedData() throws Exception {
AESKey key = new AESKey("repeatable seed");
assertEquals("aes", key.getAlgorithm());
assertEquals("aes-seed", key.getFormat());
assertArrayEquals("repeatable seed".getBytes(StandardCharsets.UTF_8), key.getEncoded());
String encoded = "aes-seed " + Base64.getEncoder().encodeToString(key.getEncoded());
AESKeySpec spec = new AESKeySpec(encoded.getBytes(StandardCharsets.UTF_8));
assertEquals("aes", spec.getFormat());
assertArrayEquals(key.getEncoded(), spec.generateKey().getEncoded());
assertThrows(InvalidKeySpecException.class,
() -> new AESKeySpec("wrong value".getBytes(StandardCharsets.UTF_8)).generateKey());
assertThrows(InvalidKeySpecException.class,
() -> new AESKeySpec("aes-seed !!!".getBytes(StandardCharsets.UTF_8)).generateKey());
}
@Test
void encryptionKeyFactoryHandlesAesRsaAndSshSpecs() throws Exception {
EncryptionKeyFactory factory = EncryptionKeyFactory.getInstance("RSA");
String aes = "aes-seed " + Base64.getEncoder().encodeToString("seed".getBytes(StandardCharsets.UTF_8));
assertInstanceOf(AESKey.class, factory.generatePrivateKey(new AESKeySpec(aes.getBytes(StandardCharsets.UTF_8))));
KeyPair pair = RSA.generateKeyPair(1024);
assertEquals(pair.getPrivate(), factory.generatePrivateKey(
new java.security.spec.PKCS8EncodedKeySpec(pair.getPrivate().getEncoded())));
assertEquals(pair.getPublic(), factory.generatePublicKey(
new java.security.spec.X509EncodedKeySpec(pair.getPublic().getEncoded())));
Path ssh = tempDir.resolve("public.ssh");
try (KeyFile file = new KeyFile(pair.getPublic(), "test", EncryptionType.SSH)) {
file.write(ssh.toString());
}
SSHEncodedKeySpec sshSpec = new SSHEncodedKeySpec(Files.readAllBytes(ssh));
assertNull(sshSpec.getFormat());
assertEquals(pair.getPublic(), factory.generatePublicKey(sshSpec));
assertEquals(pair.getPublic(), KeyFile.read(ssh.toString()).getKey());
assertThrows(InvalidKeySpecException.class,
() -> new SSHEncodedKeySpec("ssh-ed25519 bad".getBytes(StandardCharsets.UTF_8)).convertToRSAPubKeySpec());
assertThrows(InvalidKeySpecException.class,
() -> new SSHEncodedKeySpec("ssh-rsa !!!".getBytes(StandardCharsets.UTF_8)).convertToRSAPubKeySpec());
}
@Test
void rsaGeneratesCalculatesEncryptsDecryptsAndRejectsOversizedMessages() {
assertNotNull(new RSA());
KeyPair defaultPair = RSA.generateKeyPair();
assertNotNull(defaultPair.getPrivate());
KeyPair pair = RSA.generateKeyPair(1024);
assertEquals(("hello".getBytes(StandardCharsets.UTF_8).length + 11) * 8,
RSA.calculateRequiredKeyLength("hello"));
String encrypted = RSA.encrypt(pair.getPublic(), "hello RSA");
assertEquals("hello RSA", RSA.decrypt(pair.getPrivate(), encrypted));
assertThrows(RuntimeException.class, () -> RSA.decrypt(pair.getPrivate(), "not base64"));
assertThrows(RuntimeException.class, () -> RSA.encrypt(pair.getPublic(), "x".repeat(200)));
assertThrows(RuntimeException.class, () -> RSA.generateKeyPair(1));
}
@Test
void keyFileWritesAesPemAndSshFormatsAndProvidesDescriptions() throws Exception {
AESKey aesKey = new AESKey("seed");
Path aesPath = tempDir.resolve("aes.key");
try (KeyFile file = new KeyFile(aesKey)) {
assertEquals("PRIVATE KEY", file.getDescription());
file.setDescription("AES KEY");
assertEquals("AES KEY", file.getDescription());
assertSame(aesKey, file.getKey());
file.write(aesPath.toString());
}
assertEquals("aes-seed c2VlZA==", Files.readString(aesPath));
KeyPair pair = RSA.generateKeyPair(1024);
Path privatePath = tempDir.resolve("private.pem");
try (KeyFile file = new KeyFile(pair.getPrivate())) {
file.write(privatePath.toString());
}
assertTrue(Files.readString(privatePath).contains("BEGIN RSA PRIVATE KEY"));
Path publicPath = tempDir.resolve("public.pem");
try (KeyFile file = new KeyFile(pair.getPublic(), "CUSTOM")) {
file.write(publicPath.toString());
}
assertTrue(Files.readString(publicPath).contains("BEGIN RSA CUSTOM"));
assertEquals("PUBLIC KEY", new KeyFile(pair.getPublic()).getDescription());
Path sshPath = tempDir.resolve("id.pub");
try (KeyFile file = new KeyFile(pair.getPublic(), "comment", EncryptionType.SSH)) {
file.write(sshPath.toString());
}
assertTrue(Files.readString(sshPath).startsWith("ssh-rsa "));
assertTrue(Files.readString(sshPath).endsWith(" comment"));
}
@Test
void keyFileReadHandlesEmptyMissingRecognizedAndInvalidFiles() throws Exception {
assertNull(KeyFile.read(null).getKey());
RuntimeException missing = assertThrows(RuntimeException.class,
() -> KeyFile.read(tempDir.resolve("missing").toString()));
assertInstanceOf(IllegalArgumentException.class, missing.getCause());
Path aesPath = tempDir.resolve("recognized-aes.key");
new KeyFile(new AESKey("seed")).write(aesPath.toString());
assertArrayEquals("seed".getBytes(StandardCharsets.UTF_8), KeyFile.read(aesPath.toString()).getKey().getEncoded());
Path invalid = tempDir.resolve("invalid.key");
Files.writeString(invalid, "not a key");
assertThrows(RuntimeException.class, () -> KeyFile.read(invalid.toString()));
}
@Test
void rsaWriteConvenienceOverloadsUseRequestedPaths() {
KeyPair pair = RSA.generateKeyPair(1024);
Path privatePath = tempDir.resolve("private.key");
Path publicPath = tempDir.resolve("public.key");
assertTrue(RSA.writePrivateKey(pair, privatePath.toString(), "PRIVATE KEY"));
assertTrue(RSA.writePublicKey(pair, publicPath.toString(), "PUBLIC KEY"));
assertTrue(Files.exists(privatePath));
assertTrue(Files.exists(publicPath));
Path sshPath = tempDir.resolve("rsa-ssh.pub");
assertTrue(RSA.writePublicKey(pair, sshPath.toString(), "comment", true));
assertTrue(RSA.generateAndWriteKeyPair(tempDir.resolve("generated").toString(), 1024));
assertNotNull(RSA.loadKeyPair(publicPath.toString(), privatePath.toString()));
assertNotNull(RSA.loadPublicKey(publicPath.toString()).getPublic());
assertNotNull(RSA.loadPrivateKey(privatePath.toString()).getPrivate());
}
@Test
void keyFileWrapsWritePemAndCloseFailures() throws Exception {
KeyPair pair = RSA.generateKeyPair(1024);
KeyFile invalidWrite = new KeyFile(pair.getPrivate());
assertThrows(IllegalArgumentException.class, () -> invalidWrite.write(tempDir.toString()));
KeyFile closeFailure = new KeyFile(pair.getPrivate());
Field writerField = KeyFile.class.getDeclaredField("writer");
writerField.setAccessible(true);
writerField.set(closeFailure, new Writer() {
@Override
public void write(char[] cbuf, int off, int len) {
}
@Override
public void flush() throws IOException {
throw new IOException("flush failed");
}
@Override
public void close() {
}
});
assertThrows(RuntimeException.class, closeFailure::close);
}
@Test
void rsaConvenienceDelegatesAndShortCircuitPaths() {
KeyPair pair = RSA.generateKeyPair(1024);
try (MockedStatic<RSA> rsa = mockStatic(RSA.class, CALLS_REAL_METHODS)) {
rsa.when(RSA::generateKeyPair).thenReturn(pair);
rsa.when(() -> RSA.writePrivateKey(pair)).thenReturn(true);
rsa.when(() -> RSA.writePublicKey(pair, true)).thenReturn(true);
assertTrue(RSA.generateAndWriteSSHKeys());
rsa.when(() -> RSA.writePrivateKey(pair)).thenReturn(false);
assertFalse(RSA.generateAndWriteSSHKeys());
rsa.when(() -> RSA.writePrivateKey(pair)).thenReturn(true);
rsa.when(() -> RSA.writePublicKey(pair, true)).thenReturn(false);
assertFalse(RSA.generateAndWriteSSHKeys());
rsa.when(() -> RSA.generateAndWriteKeyPair(2048)).thenReturn(true);
assertTrue(RSA.generateAndWriteKeyPair());
rsa.when(() -> RSA.generateAndWriteKeyPair("named", 2048)).thenReturn(true);
assertTrue(RSA.generateAndWriteKeyPair("named"));
rsa.when(() -> RSA.generateAndWriteKeyPair("id_rsa", 1024)).thenReturn(true);
assertTrue(RSA.generateAndWriteKeyPair(1024));
rsa.when(() -> RSA.generateKeyPair(512)).thenReturn(pair);
rsa.when(() -> RSA.writePrivateKey(pair, "short", "PRIVATE KEY")).thenReturn(false);
assertFalse(RSA.generateAndWriteKeyPair("short", 512));
rsa.when(() -> RSA.writePrivateKey(pair, "short", "PRIVATE KEY")).thenReturn(true);
rsa.when(() -> RSA.writePublicKey(pair, "short.pub", "PUBLIC KEY")).thenReturn(false);
assertFalse(RSA.generateAndWriteKeyPair("short", 512));
rsa.when(() -> RSA.writePrivateKey(pair, "id_rsa", "PRIVATE KEY")).thenReturn(true);
assertTrue(RSA.writePrivateKey(pair));
rsa.when(() -> RSA.writePublicKey(pair, false)).thenReturn(true);
assertTrue(RSA.writePublicKey(pair));
rsa.when(() -> RSA.writePublicKey(pair, "id_rsa.pub", "PUBLIC KEY", true)).thenReturn(true);
rsa.when(() -> RSA.writePublicKey(pair, true)).thenCallRealMethod();
assertTrue(RSA.writePublicKey(pair, true));
}
}
}

View File

@@ -0,0 +1,43 @@
package net.locusworks.common.exceptions;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import java.util.List;
class ApplicationExceptionTest {
@Test
void factoryMethodsExposeExpectedCodesAndMessages() {
List<ApplicationException> exceptions = List.of(
ApplicationException.egregiousServer(), ApplicationException.invalidCreds(),
ApplicationException.notLoggedIn(), ApplicationException.invalidEmailAddress(),
ApplicationException.actionNotPermitted(), ApplicationException.actionNotPermitted("delete"),
ApplicationException.passwordsNotEqual(), ApplicationException.unAuthorized(),
ApplicationException.duplicateEntry("duplicate"), ApplicationException.duplicateEntry("duplicate %s", "item"),
ApplicationException.noEntryExists("missing"), ApplicationException.noEntryExists("missing %s", "item"),
ApplicationException.constraintViolation("constraint"), ApplicationException.constraintViolation("constraint %s", "x"),
ApplicationException.illegalArgument("illegal"), ApplicationException.generic("generic"));
assertTrue(exceptions.stream().noneMatch(ApplicationException::getSuccess));
assertTrue(exceptions.stream().allMatch(e -> e.getCode() != null && e.getMessage() != null));
}
@Test
void constructorsAndThrowableFactoryPreserveCause() {
IllegalStateException cause = new IllegalStateException("failure");
ApplicationException fromCause = ApplicationException.fromException(cause);
assertEquals(9999, fromCause.getCode());
assertSame(cause, fromCause.getCause());
ApplicationException direct = new ApplicationException(42, cause);
assertEquals("failure", direct.getMessage());
assertSame(cause, direct.getCause());
ApplicationException explicit = new ApplicationException(43, "message", cause);
assertEquals(43, explicit.getCode());
assertSame(cause, explicit.getCause());
}
}

View File

@@ -0,0 +1,69 @@
package net.locusworks.common.immutables;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
class ImmutablesTest {
@Test
void testUnit() {
Unit<String> unit1 = new Unit<>();
unit1.setValue1("123");
Unit<String> unit2 = new Unit<>("123");
Unit<String> unit3 = new Unit<>("1234");
assertEquals(unit1, unit2);
assertNotEquals(null, unit1);
assertNotEquals(new Object(), unit1);
assertNotEquals(unit3, unit1);
assertThrows(IllegalArgumentException.class, () -> unit1.setValue1("value"));
}
@Test
void testPair() {
Pair<String, String> unit1 = new Pair<>();
unit1.setValue2("value2");
unit1.setValue1("123");
Pair<String, String> unit2 = new Pair<>("123", "value2");
Pair<String, String> unit3 = new Pair<>("1234", "value2");
assertEquals(unit1, unit2);
assertNotEquals(null, unit1);
assertNotEquals(new Object(), unit1);
assertNotEquals(unit3, unit1);
assertThrows(IllegalArgumentException.class, () -> unit1.setValue1("value"));
assertThrows(IllegalArgumentException.class, () -> unit1.setValue2("value"));
}
@Test
void testTriplet() {
Triplet<String, String, String> unit1 = new Triplet<>();
unit1.setValue3("value3");
unit1.setValue2("value2");
unit1.setValue1("123");
Triplet<String, String, String> unit2 = new Triplet<>("123", "value2", "value3");
Triplet<String, String, String> unit3 = new Triplet<>("1234", "value2", "value3");
assertEquals(unit1, unit2);
assertNotEquals(null, unit1);
assertNotEquals(new Object(), unit1);
assertNotEquals(unit3, unit1);
assertThrows(IllegalArgumentException.class, () -> unit1.setValue1("value"));
assertThrows(IllegalArgumentException.class, () -> unit1.setValue2("value"));
assertThrows(IllegalArgumentException.class, () -> unit1.setValue3("value"));
}
}

View File

@@ -0,0 +1,158 @@
package net.locusworks.common.io;
import net.locusworks.common.Charsets;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.MockedStatic;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.mockStatic;
class IOUtilsTest {
@TempDir
Path tempDir;
@Test
void readsLinesFromStreamsAndBothReaderKinds() throws Exception {
byte[] text = "first\nsecond\nthird".getBytes(StandardCharsets.UTF_8);
assertEquals(List.of("first", "second", "third"),
IOUtils.readLines(new ByteArrayInputStream(text), StandardCharsets.UTF_8));
assertEquals(List.of("first", "second", "third"), IOUtils.readLines(new StringReader(new String(text))));
BufferedReader buffered = new BufferedReader(new StringReader("line"));
assertSame(buffered, IOUtils.toBufferedReader(buffered));
assertNotNull(IOUtils.toBufferedReader(new StringReader("line")));
assertThrows(NullPointerException.class, () -> IOUtils.readLines((Reader) null));
}
@Test
void convertsStreamsAndCopiesBytesThroughEveryOverload() throws Exception {
byte[] source = "some binary data".getBytes(StandardCharsets.UTF_8);
assertArrayEquals(source, IOUtils.toByteArray(new ByteArrayInputStream(source)));
ByteArrayOutputStream defaultCopy = new ByteArrayOutputStream();
assertEquals(source.length, IOUtils.copy(new ByteArrayInputStream(source), defaultCopy));
assertArrayEquals(source, defaultCopy.toByteArray());
ByteArrayOutputStream sizedCopy = new ByteArrayOutputStream();
assertEquals(source.length, IOUtils.copy(new ByteArrayInputStream(source), sizedCopy, 2));
assertArrayEquals(source, sizedCopy.toByteArray());
ByteArrayOutputStream bufferedCopy = new ByteArrayOutputStream();
assertEquals(source.length,
IOUtils.copyLarge(new ByteArrayInputStream(source), bufferedCopy, new byte[3]));
assertArrayEquals(source, bufferedCopy.toByteArray());
ByteArrayOutputStream largeCopy = new ByteArrayOutputStream();
assertEquals(source.length, IOUtils.copyLarge(new ByteArrayInputStream(source), largeCopy));
assertArrayEquals(source, largeCopy.toByteArray());
assertEquals(0, IOUtils.copyLarge(new ByteArrayInputStream(new byte[0]), new ByteArrayOutputStream()));
}
@Test
void copiesCharactersThroughEveryOverload() throws Exception {
String source = "characters-å";
StringWriter encoded = new StringWriter();
IOUtils.copy(new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8)), encoded,
StandardCharsets.UTF_8);
assertEquals(source, encoded.toString());
StringWriter regular = new StringWriter();
assertEquals(source.length(), IOUtils.copy(new StringReader(source), regular));
assertEquals(source, regular.toString());
StringWriter buffered = new StringWriter();
assertEquals(source.length(), IOUtils.copyLarge(new StringReader(source), buffered, new char[2]));
assertEquals(source, buffered.toString());
StringWriter large = new StringWriter();
assertEquals(source.length(), IOUtils.copyLarge(new StringReader(source), large));
assertEquals(source, large.toString());
assertEquals(0, IOUtils.copyLarge(new StringReader(""), new StringWriter()));
assertEquals(source, IOUtils.toString(
new ByteArrayInputStream(source.getBytes(StandardCharsets.UTF_8)), StandardCharsets.UTF_8));
}
@Test
void integerCopyMethodsReturnEofMarkerForCountsOverIntegerMaximum() throws Exception {
try (MockedStatic<IOUtils> mocked = mockStatic(IOUtils.class, CALLS_REAL_METHODS)) {
mocked.when(() -> IOUtils.copyLarge(any(InputStream.class), any(OutputStream.class)))
.thenReturn((long) Integer.MAX_VALUE + 1);
assertEquals(IOUtils.EOF,
IOUtils.copy(new ByteArrayInputStream(new byte[0]), new ByteArrayOutputStream()));
mocked.when(() -> IOUtils.copyLarge(any(Reader.class), any(Writer.class)))
.thenReturn((long) Integer.MAX_VALUE + 1);
assertEquals(IOUtils.EOF, IOUtils.copy(new StringReader(""), new StringWriter()));
}
}
@SuppressWarnings("deprecation")
@Test
void writesFilesUsingEveryOverload() throws Exception {
Path byName = tempDir.resolve("name.txt");
IOUtils.writeStringToFile(byName.toString(), "default");
assertEquals("default", Files.readString(byName));
Path namedCharset = tempDir.resolve("name-charset.txt");
IOUtils.writeStringToFile(namedCharset.toString(), "named", StandardCharsets.UTF_16);
assertEquals("named", Files.readString(namedCharset, StandardCharsets.UTF_16));
Path byPath = tempDir.resolve("path.txt");
IOUtils.writeStringToFile(byPath, "path");
assertEquals("path", Files.readString(byPath, Charsets.UTF_8));
Path byFile = tempDir.resolve("file.txt");
IOUtils.writeStringToFile(byFile.toFile(), "file", StandardCharsets.UTF_8);
assertEquals("file", Files.readString(byFile));
}
@SuppressWarnings("deprecation")
@Test
void deletesFilesUsingEveryOverloadAndWrapsIoFailures() throws Exception {
Path byName = Files.createFile(tempDir.resolve("name-delete.txt"));
IOUtils.deleteFile(byName.toString());
assertFalse(Files.exists(byName));
Path byFile = Files.createFile(tempDir.resolve("file-delete.txt"));
IOUtils.deleteFile(byFile.toFile());
assertFalse(Files.exists(byFile));
Path first = Files.createFile(tempDir.resolve("first.txt"));
Path second = Files.createFile(tempDir.resolve("second.txt"));
IOUtils.deleteFiles(first.toString(), second.toString());
assertFalse(Files.exists(first));
assertFalse(Files.exists(second));
Path nonEmptyDirectory = Files.createDirectory(tempDir.resolve("non-empty"));
Files.createFile(nonEmptyDirectory.resolve("child"));
IllegalArgumentException error = assertThrows(IllegalArgumentException.class,
() -> IOUtils.deleteFile(nonEmptyDirectory));
assertInstanceOf(IOException.class, error.getCause());
}
@Test
void constructorAndInvalidInputsAreCovered() {
assertNotNull(new IOUtils());
assertThrows(NullPointerException.class, () -> IOUtils.toByteArray(null));
assertThrows(NegativeArraySizeException.class,
() -> IOUtils.copy(new ByteArrayInputStream(new byte[0]), new ByteArrayOutputStream(), -1));
}
}

View File

@@ -0,0 +1,83 @@
package net.locusworks.common.migration;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.flywaydb.core.Flyway;
import org.flywaydb.core.api.MigrationInfo;
import org.flywaydb.core.api.MigrationInfoService;
import org.flywaydb.core.api.configuration.Configuration;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
class MigrationItemTest {
private static Flyway flyway(MigrationInfo[] all, MigrationInfo[] pending) {
Flyway flyway = mock(Flyway.class);
MigrationInfoService info = mock(MigrationInfoService.class);
Configuration configuration = mock(Configuration.class);
when(flyway.info()).thenReturn(info);
when(info.all()).thenReturn(all);
when(info.pending()).thenReturn(pending);
when(flyway.getConfiguration()).thenReturn(configuration);
when(configuration.getSchemas()).thenReturn(new String[]{"first", "second"});
return flyway;
}
@Test
void exposesSchemasPendingCountAndMigrationLog() {
Flyway flyway = flyway(new MigrationInfo[0], new MigrationInfo[]{mock(MigrationInfo.class)});
MigrationItem item = new MigrationItem(flyway);
assertArrayEquals(new String[]{"first", "second"}, item.getSchemas());
assertEquals(1, item.qtyPending());
assertNotNull(item.getAllMigrationsLog());
}
@Test
void repairAndMigrateInvokeCallbackAndFlyway() {
Flyway flyway = flyway(new MigrationInfo[0], new MigrationInfo[0]);
List<String> messages = new ArrayList<>();
MigrationItem item = new MigrationItem(flyway, messages::add);
item.repair();
item.migrate();
verify(flyway).repair();
verify(flyway).migrate();
assertEquals(2, messages.size());
assertTrue(messages.stream().allMatch(s -> s.contains("first, second")));
}
@Test
void repairAndMigrateWrapFlywayFailures() {
Flyway repairFlyway = flyway(new MigrationInfo[0], new MigrationInfo[0]);
doThrow(new IllegalStateException("repair failed")).when(repairFlyway).repair();
RuntimeException repair = assertThrows(RuntimeException.class, () -> new MigrationItem(repairFlyway).repair());
assertTrue(repair.getMessage().contains("repair failed"));
Flyway migrateFlyway = flyway(new MigrationInfo[0], new MigrationInfo[0]);
doThrow(new IllegalStateException("migrate failed")).when(migrateFlyway).migrate();
RuntimeException migrate = assertThrows(RuntimeException.class, () -> new MigrationItem(migrateFlyway).migrate());
assertTrue(migrate.getMessage().contains("migrate failed"));
}
@Test
void baseManagerInitializesMutableMigrationList() {
class Manager extends BaseMigrationManager {
@Override
public void migrate() {
}
}
Manager manager = new Manager();
assertNotNull(manager.migrations);
assertDoesNotThrow(manager::migrate);
}
}

View File

@@ -0,0 +1,45 @@
package net.locusworks.common.net;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.sun.net.httpserver.HttpServer;
import org.junit.jupiter.api.Test;
import java.net.InetSocketAddress;
class HttpClientHelperTest {
@Test
void schemaLookupHandlesBothProtocolsAndUnknownValue() {
assertEquals(HttpClientHelper.HttpSchema.HTTP, HttpClientHelper.HttpSchema.findEnum("http"));
assertEquals(HttpClientHelper.HttpSchema.HTTPS, HttpClientHelper.HttpSchema.findEnum("HtTpS"));
assertNull(HttpClientHelper.HttpSchema.findEnum("ftp"));
}
@Test
void constructorRejectsUnknownProtocol() {
Exception error = assertThrows(Exception.class, () -> new HttpClientHelper("ftp", "localhost", "21"));
assertEquals("Unable to find http schema of ftp", error.getMessage());
}
@Test
void executesHttpGetAndConsumesResponse() throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress("localhost", 0), 0);
server.createContext("/status", exchange -> {
byte[] body = "ok".getBytes();
exchange.sendResponseHeaders(202, body.length);
exchange.getResponseBody().write(body);
exchange.close();
});
server.start();
try {
HttpClientHelper helper = new HttpClientHelper("http", "localhost", Integer.toString(server.getAddress().getPort()));
assertEquals(202, helper.getResponseCode("/status"));
assertNotNull(new HttpClientHelper("https", "localhost", "443"));
} finally {
server.stop(0);
}
}
}

View File

@@ -0,0 +1,25 @@
package net.locusworks.common.net.certmanagers;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.junit.jupiter.api.Test;
import javax.net.ssl.TrustManager;
class TrustAllCertsManagerTest {
@Test
void trustsClientsAndServersAndBuildsTrustManagerArray() throws Exception {
TrustAllCertsManager manager = new TrustAllCertsManager();
assertDoesNotThrow(() -> manager.checkClientTrusted(null, null));
assertDoesNotThrow(() -> manager.checkServerTrusted(null, null));
assertNull(manager.getAcceptedIssuers());
TrustManager[] managers = TrustAllCertsManager.trustAllCerts();
assertEquals(1, managers.length);
assertInstanceOf(TrustAllCertsManager.class, managers[0]);
}
}

View File

@@ -0,0 +1,17 @@
package net.locusworks.common.net.hostverifiers;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
class AllHostValidVerifierTest {
@Test
public void testVerify() {
AllHostValidVerifier verifier = new AllHostValidVerifier();
assertTrue(verifier.verify("localhost", null));
assertTrue(verifier.verify("127.0.0.1", null));
assertTrue(verifier.verify("::1", null));
}
}

View File

@@ -0,0 +1,25 @@
package net.locusworks.common.net.ssl;
import org.junit.jupiter.api.Test;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class SSLManagerTest {
@Test
public void testCreateSSLContext() throws NoSuchAlgorithmException, KeyManagementException {
assertNotNull(SSLManager.getTrustAllTLSClient());
}
@Test
public void testTLS() {
assertNotNull(SSLManager.TLS);
assertEquals(4, SSLManager.TLS.length);
}
}

View File

@@ -0,0 +1,119 @@
package net.locusworks.common.objectmapper;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
class ObjectMapperCoverageTest {
static class Value {
public String name;
Value() {
}
Value(String name) {
this.name = name;
}
}
static class SelfReference {
SelfReference self = this;
}
static class CyclicReference {
Object next;
}
static class ThrowingGetter {
String name = "field value";
public String getName() {
throw new IllegalStateException("Jackson getter failure");
}
}
@Test
void resultContainersSupportEveryConstructorMutationAndHandlerPath() {
RuntimeException error = new RuntimeException("bad json");
ObjectMapperResults<String> result = new ObjectMapperResults<>("value", null);
assertEquals("value", result.getResults());
assertFalse(result.hasError());
assertSame(result, result.withErrorHandler(e -> fail("handler must not run")));
result.setResults("changed");
result.setException(error);
AtomicReference<Throwable> handled = new AtomicReference<>();
assertSame(result, result.withErrorHandler(handled::set));
assertSame(error, handled.get());
assertSame(result, result.withErrorHandler(null));
assertEquals("simple", new ObjectMapperResults<>("simple").getResults());
assertSame(error, new ObjectMapperResults<String>(error).getException());
ObjectMapperListResults<List<String>> list = new ObjectMapperListResults<>(List.of("a"), null);
assertEquals(List.of("a"), list.getResults());
assertFalse(list.hasError());
assertSame(list, list.withErrorHandler(e -> fail("handler must not run")));
ObjectMapperListResults<List<String>> failed = new ObjectMapperListResults<>(error);
handled.set(null);
assertSame(failed, failed.withErrorHandler(handled::set));
assertSame(error, handled.get());
assertSame(failed, failed.withErrorHandler(null));
assertEquals(List.of("b"), new ObjectMapperListResults<>(List.of("b")).getResults());
}
@Test
void readsBytesStringsObjectsAndCollectionImplementations() {
Value fromBytes = ObjectMapperHelper.readValue("{\"name\":\"bytes\"}".getBytes(), Value.class).getResults();
assertEquals("bytes", fromBytes.name);
assertEquals("string", ObjectMapperHelper.readValue("{\"name\":\"string\"}", Value.class).getResults().name);
assertEquals("object", ObjectMapperHelper.readValue(new Value("object"), Value.class).getResults().name);
Object delegatedJson = "{\"name\":\"delegated\"}";
assertEquals("delegated", ObjectMapperHelper.readValue(delegatedJson, Value.class).getResults().name);
List<Value> values = List.of(new Value("one"), new Value("two"));
assertEquals(2, ObjectMapperHelper.readListValue(values, Value.class).getResults().size());
assertEquals(2, ObjectMapperHelper.readListValue("[{\"name\":\"one\"},{\"name\":\"two\"}]", Value.class, LinkedList.class).getResults().size());
assertInstanceOf(LinkedList.class,
ObjectMapperHelper.readListValue(values, Value.class, LinkedList.class).getResults());
}
@Test
void malformedInputReturnsErrorsForEveryReadOverload() {
assertTrue(ObjectMapperHelper.readValue(new byte[]{1, 2}, Value.class).hasError());
assertTrue(ObjectMapperHelper.readValue("not-json", Value.class).hasError());
assertTrue(ObjectMapperHelper.readValue(new Object(), Thread.class).hasError());
assertTrue(ObjectMapperHelper.readListValue("not-json", Value.class, ArrayList.class).hasError());
assertTrue(ObjectMapperHelper.readListValue(new Object(), Value.class, List.class).hasError());
}
@Test
void writeFallsBackWhenJacksonAndGsonRejectCircularObject() {
CyclicReference first = new CyclicReference();
CyclicReference second = new CyclicReference();
first.next = second;
second.next = first;
ObjectMapperResults<String> result = ObjectMapperHelper.writeValue(first);
assertTrue(result.hasError());
ObjectMapperResults<String> fallback = ObjectMapperHelper.writeValue(new ThrowingGetter());
assertFalse(fallback.hasError());
assertTrue(fallback.getResults().contains("field value"));
assertNotNull(new ObjectMapperHelper());
assertTrue(ObjectMapperHelper.readValue(new SelfReference(), Value.class).hasError());
}
}

View File

@@ -0,0 +1,41 @@
package net.locusworks.common.properties;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
import java.util.Properties;
class ImmutablePropertiesTest {
@Test
public void testImmutableProperties() {
assertNotNull(new ImmutableProperties());
}
@Test
public void testImmutablePropertiesWithEmptyOrNullProperties() {
assertNotNull(new ImmutableProperties(new Properties()));
assertNotNull(new ImmutableProperties(null));
}
@Test
public void testImmutablePropertiesWthProperties() {
final Properties properties = new Properties();
properties.put("Hello", "World");
assertNotNull(new ImmutableProperties(properties));
}
@Test
public void testSetProperties() {
final ImmutableProperties properties = new ImmutableProperties(new Properties());
properties.setProperty("Hello", "World");
assertEquals("World", properties.getProperty("Hello"));
assertThrows(RuntimeException.class, () -> properties.setProperty("Hello", "World"));
}
}

View File

@@ -0,0 +1,196 @@
package net.locusworks.common.properties;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
class OrderedPropertiesCoverageTest {
@Test
void loadsAllPropertySyntaxFromReaderAndInputStream() throws Exception {
String source = "# comment\n! another\n" +
"simple=value\ncolon:value2\nspace value3\n" +
"spaced = spaced-value\n" +
"tabbed\t:\tvalue5\nform\f=\fvalue6\n" +
"slashes\\\\key:value4\n" +
"escaped\\:colon=value7\n" +
"escaped\\ key=escaped\\ value\n" +
"unicode=\\u0041\\tB\\nC\\rD\\fE\n" +
"continued=first\\\n second\n" +
"emptyKey\n=empty-key-value\n";
OrderedProperties readerProperties = new OrderedProperties();
readerProperties.load(new StringReader(source));
assertEquals("value", readerProperties.getProperty("simple"));
assertEquals("value2", readerProperties.getProperty("colon"));
assertEquals("value3", readerProperties.getProperty("space"));
assertEquals("spaced-value", readerProperties.getProperty("spaced"));
assertEquals("value4", readerProperties.getProperty("slashes\\key"));
assertEquals("value5", readerProperties.getProperty("tabbed"));
assertEquals("value6", readerProperties.getProperty("form"));
assertEquals("value7", readerProperties.getProperty("escaped:colon"));
assertEquals("escaped value", readerProperties.getProperty("escaped key"));
assertEquals("A\tB\nC\rD\fE", readerProperties.getProperty("unicode"));
assertEquals("firstsecond", readerProperties.getProperty("continued"));
assertEquals("", readerProperties.getProperty("emptyKey"));
assertEquals("empty-key-value", readerProperties.getProperty(""));
OrderedProperties streamProperties = new OrderedProperties();
streamProperties.load(new ByteArrayInputStream(source.getBytes(StandardCharsets.ISO_8859_1)));
assertEquals(readerProperties, streamProperties);
assertThrows(IllegalArgumentException.class,
() -> streamProperties.load(new StringReader("bad=\\u12G4")));
}
@Test
void storesToWriterAndStreamWithCommentsUnicodeAndEscapes() throws Exception {
OrderedProperties properties = new OrderedProperties();
properties.setProperty(" leading:=#!", " value\t\n\r\f\\é");
StringWriter writer = new StringWriter();
properties.store(writer, "first\nsecond\rthird\r\nfourth\u0100");
String writerText = writer.toString();
assertTrue(writerText.contains("#first"));
assertTrue(writerText.contains("#second"));
assertTrue(writerText.contains("\\ leading\\:\\=\\#\\!"));
assertTrue(writerText.contains("é"));
StringWriter bufferedTarget = new StringWriter();
properties.store(new BufferedWriter(bufferedTarget), "\u0100first\rsolo\n#already\n!also\nlast\r\nend\r");
assertTrue(bufferedTarget.toString().contains("#already"));
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
properties.store(bytes, null);
String streamText = bytes.toString(StandardCharsets.ISO_8859_1);
assertTrue(streamText.contains("\\u00E9"));
assertDoesNotThrow(() -> properties.save(new OutputStream() {
@Override
public void write(int b) throws IOException {
throw new IOException("expected");
}
}, "ignored error"));
}
@Test
void defaultsEnumerationListingAndNonStringValuesAreHandled() {
OrderedProperties defaults = new OrderedProperties();
defaults.setProperty("default", "value");
defaults.put("nonStringDefault", 1);
OrderedProperties properties = new OrderedProperties(defaults);
properties.setProperty("local", "x".repeat(50));
properties.put("nonString", 2);
assertEquals("value", properties.getProperty("default"));
assertEquals("fallback", properties.getProperty("missing", "fallback"));
assertEquals("value", properties.getProperty("default", "fallback"));
Enumeration<?> names = properties.propertyNames();
assertTrue(java.util.Collections.list(names).containsAll(List.of("default", "local")));
properties.put(3, "nonStringKey");
assertTrue(properties.stringPropertyNames().containsAll(java.util.Set.of("default", "local")));
properties.remove("nonString");
properties.remove(3);
defaults.remove("nonStringDefault");
ByteArrayOutputStream printBytes = new ByteArrayOutputStream();
properties.list(new PrintStream(printBytes));
assertTrue(printBytes.toString().contains("..."));
StringWriter output = new StringWriter();
properties.list(new PrintWriter(output));
assertTrue(output.toString().contains("-- listing properties --"));
}
@Test
void lineReaderHandlesCommentsWhitespaceContinuationLongLinesAndEndings() throws Exception {
String longValue = "x".repeat(9000);
String input = " #comment\r\n\t!comment\n key=value\\\r\n continued\r" + longValue;
OrderedProperties.LineReader reader = new OrderedProperties.LineReader(new StringReader(input));
int length = reader.readLine();
assertEquals("key=valuecontinued", new String(reader.lineBuf, 0, length));
assertEquals(9000, reader.readLine());
OrderedProperties.LineReader stream = new OrderedProperties.LineReader(
new ByteArrayInputStream(("a=b\n" + longValue).getBytes(StandardCharsets.ISO_8859_1)));
assertEquals(3, stream.readLine());
assertEquals(9000, stream.readLine());
assertEquals(-1, stream.readLine());
OrderedProperties.LineReader trailingReader = new OrderedProperties.LineReader(new StringReader("key=value\\"));
assertEquals("key=value", new String(trailingReader.lineBuf, 0, trailingReader.readLine()));
OrderedProperties.LineReader trailingStream = new OrderedProperties.LineReader(
new ByteArrayInputStream("key=value\\\n".getBytes(StandardCharsets.ISO_8859_1)));
assertEquals("key=value", new String(trailingStream.lineBuf, 0, trailingStream.readLine()));
OrderedProperties.LineReader whiteContinuation = new OrderedProperties.LineReader(
new StringReader("key=value\\\n \t\fcontinued\n"));
int continuedLength = whiteContinuation.readLine();
assertEquals("key=valuecontinued", new String(whiteContinuation.lineBuf, 0, continuedLength));
assertEquals(-1, new OrderedProperties.LineReader(new StringReader("#comment without newline")).readLine());
OrderedProperties.LineReader crWithoutLf = new OrderedProperties.LineReader(new StringReader("a\\\rb\n"));
int crLength = crWithoutLf.readLine();
assertEquals("ab", new String(crWithoutLf.lineBuf, 0, crLength));
OrderedProperties.LineReader blankEndings = new OrderedProperties.LineReader(new StringReader("\n\r\nvalue\n"));
assertEquals("value", new String(blankEndings.lineBuf, 0, blankEndings.readLine()));
String boundaryInput = "z".repeat(8191) + "\nnext\n";
OrderedProperties.LineReader boundary = new OrderedProperties.LineReader(new StringReader(boundaryInput));
assertEquals(8191, boundary.readLine());
assertEquals(4, boundary.readLine());
}
@Test
void privateConversionHelpersCoverEveryEscapeAndHexPath() throws Exception {
OrderedProperties properties = new OrderedProperties();
Method load = OrderedProperties.class.getDeclaredMethod("loadConvert", char[].class, int.class, int.class, char[].class);
Method save = OrderedProperties.class.getDeclaredMethod("saveConvert", String.class, boolean.class, boolean.class);
Method hex = OrderedProperties.class.getDeclaredMethod("toHex", int.class);
load.setAccessible(true);
save.setAccessible(true);
hex.setAccessible(true);
String escaped = "a\\tb\\nc\\rd\\fe\\\\f\\ g\\:h\\=i\\#j\\!k\\u0041";
assertEquals("a\tb\nc\rd\fe\\f g:h=i#j!kA",
load.invoke(properties, escaped.toCharArray(), 0, escaped.length(), new char[1]));
String hexCases = "\\u00af\\u00AF";
assertEquals("¯¯", load.invoke(properties, hexCases.toCharArray(), 0, hexCases.length(), new char[1]));
assertEquals("\\ a\\:\\=\\#\\!\\t\\n\\r\\f\\\\\\u0100",
save.invoke(properties, " a:=#!\t\n\r\f\\\u0100", true, true));
assertEquals("\\ a\\:\\=\\#\\!\\t\\n\\r\\f\\\\\u0100",
save.invoke(properties, " a:=#!\t\n\r\f\\\u0100", false, false));
assertEquals("a\\ b", save.invoke(properties, "a b", true, false));
assertEquals("a b", save.invoke(properties, "a b", false, false));
assertEquals("\\u0001", save.invoke(properties, "\u0001", false, true));
assertEquals("\u0001", save.invoke(properties, "\u0001", false, false));
assertEquals("0", save.invoke(properties, "0", false, true));
assertEquals('0', hex.invoke(null, 0));
assertEquals('F', hex.invoke(null, 15));
}
@Test
void immutablePropertiesCopiesValuesAndRejectsReplacement() {
Properties source = new Properties();
source.setProperty("key", "value");
ImmutableProperties immutable = new ImmutableProperties(source);
assertEquals("value", immutable.getProperty("key"));
assertThrows(RuntimeException.class, () -> immutable.setProperty("key", "changed"));
}
}

View File

@@ -0,0 +1,75 @@
package net.locusworks.common.properties;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
class OrderedPropertiesTest {
@Test
void testSetProperty() {
OrderedProperties properties = new OrderedProperties();
properties.setProperty("Hello", "World");
assertEquals("World", properties.getProperty("Hello"));
}
@Test
void testLoad() throws IOException {
OrderedProperties properties = new OrderedProperties();
properties.load(new ByteArrayInputStream("Hello=World".getBytes()));
assertEquals("World", properties.getProperty("Hello"));
}
@Test
void testSave() {
OrderedProperties properties = new OrderedProperties();
properties.setProperty("Hello", "World");
properties.save(new ByteArrayOutputStream(), "");
}
@Test
void testStore() {
OrderedProperties properties = new OrderedProperties();
properties.setProperty("Hello", "World");
try {
properties.store(new ByteArrayOutputStream(), "");
} catch (IOException e) {
fail("IOException should not be thrown");
}
}
@Test
void testGetProperty() {
OrderedProperties properties = new OrderedProperties();
properties.setProperty("Hello", "World");
assertEquals("World", properties.getProperty("Hello"));
}
@Test
void testPropertyNames() {
OrderedProperties properties = new OrderedProperties();
properties.setProperty("Hello", "World");
var propertyNames = properties.stringPropertyNames();
assertFalse(propertyNames.isEmpty());
assertTrue(propertyNames.contains("Hello"));
assertEquals(1, propertyNames.size());
}
@Test
void testList() {
PrintStream ps = new PrintStream(new ByteArrayOutputStream());
OrderedProperties properties = new OrderedProperties();
properties.setProperty("Hello", "World");
properties.list(ps);
ps.flush();
}
}

View File

@@ -0,0 +1,28 @@
package net.locusworks.common.utils;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
class ChecksTest {
@Test
void testCheckArgument() {
Throwable ex = assertThrows(IllegalArgumentException.class, () -> Checks.checkArguments(false, "One does not equal 2"));
assertEquals("One does not equal 2", ex.getMessage());
assertDoesNotThrow(() -> Checks.checkArguments(true, "One does not equal 2"));
}
@Test
void testCheckState() {
Throwable ex = assertThrows(IllegalStateException.class, () -> Checks.checkState(false, "One does not equal 2"));
assertEquals("One does not equal 2", ex.getMessage());
assertDoesNotThrow(() -> Checks.checkState(true, "One does not equal 2"));
}
}

View File

@@ -0,0 +1,286 @@
package net.locusworks.common.utils;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.fasterxml.jackson.core.Base64Variant;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.core.SerializableString;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.core.Version;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import com.fasterxml.jackson.databind.ser.impl.WritableObjectId;
import org.junit.jupiter.api.Test;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import java.util.concurrent.atomic.AtomicLong;
class DateTimeStampSerializerTest {
static AtomicLong atomicLong = new AtomicLong(0);
@Test
void testSerializer() {
DateTimeStampSerializer serializer = new DateTimeStampSerializer();
assertDoesNotThrow(() -> serializer.serialize(new Date(), new MyJsonGenerator(), new MySerializerProvider()));
assertTrue(atomicLong.get() > 0);
}
private static class MyJsonGenerator extends JsonGenerator {
@Override
public JsonGenerator setCodec(ObjectCodec objectCodec) {
return null;
}
@Override
public ObjectCodec getCodec() {
return null;
}
@Override
public Version version() {
return null;
}
@Override
public JsonStreamContext getOutputContext() {
return null;
}
@Override
public JsonGenerator enable(Feature feature) {
return null;
}
@Override
public JsonGenerator disable(Feature feature) {
return null;
}
@Override
public boolean isEnabled(Feature feature) {
return false;
}
@Override
public int getFeatureMask() {
return 0;
}
@Override
public JsonGenerator setFeatureMask(int i) {
return null;
}
@Override
public JsonGenerator useDefaultPrettyPrinter() {
return null;
}
@Override
public void writeStartArray() {
fail();
}
@Override
public void writeEndArray() {
fail();
}
@Override
public void writeStartObject() {
fail();
}
@Override
public void writeEndObject() {
fail();
}
@Override
public void writeFieldName(String s) {
fail();
}
@Override
public void writeFieldName(SerializableString serializableString) {
fail();
}
@Override
public void writeString(String s) {
fail();
}
@Override
public void writeString(char[] chars, int i, int i1) {
fail();
}
@Override
public void writeString(SerializableString serializableString) {
fail();
}
@Override
public void writeRawUTF8String(byte[] bytes, int i, int i1) {
fail();
}
@Override
public void writeUTF8String(byte[] bytes, int i, int i1) {
fail();
}
@Override
public void writeRaw(String s) {
fail();
}
@Override
public void writeRaw(String s, int i, int i1) {
fail();
}
@Override
public void writeRaw(char[] chars, int i, int i1) {
fail();
}
@Override
public void writeRaw(char c) {
fail();
}
@Override
public void writeRawValue(String s) {
fail();
}
@Override
public void writeRawValue(String s, int i, int i1) {
fail();
}
@Override
public void writeRawValue(char[] chars, int i, int i1) {
fail();
}
@Override
public void writeBinary(Base64Variant base64Variant, byte[] bytes, int i, int i1) {
fail();
}
@Override
public int writeBinary(Base64Variant base64Variant, InputStream inputStream, int i) {
fail();
return 0;
}
@Override
public void writeNumber(int i) {
fail();
}
@Override
public void writeNumber(long l) {
atomicLong.set(l);
}
@Override
public void writeNumber(BigInteger bigInteger) {
fail();
}
@Override
public void writeNumber(double v) {
fail();
}
@Override
public void writeNumber(float v) {
fail();
}
@Override
public void writeNumber(BigDecimal bigDecimal) {
fail();
}
@Override
public void writeNumber(String s) {
fail();
}
@Override
public void writeBoolean(boolean b) {
fail();
}
@Override
public void writeNull() {
fail();
}
@Override
public void writeObject(Object o) {
fail();
}
@Override
public void writeTree(TreeNode treeNode) {
fail();
}
@Override
public void flush() {
fail();
}
@Override
public boolean isClosed() {
return false;
}
@Override
public void close() {
fail();
}
}
private static class MySerializerProvider extends SerializerProvider {
@Override
public WritableObjectId findObjectId(Object o, ObjectIdGenerator<?> objectIdGenerator) {
return null;
}
@Override
public JsonSerializer<Object> serializerInstance(Annotated annotated, Object o) {
return null;
}
@Override
public Object includeFilterInstance(BeanPropertyDefinition beanPropertyDefinition,
Class<?> aClass) {
return null;
}
@Override
public boolean includeFilterSuppressNulls(Object o) {
return false;
}
}
}

View File

@@ -0,0 +1,275 @@
package net.locusworks.common.utils;
import net.locusworks.common.interfaces.AutoCloseableIterator;
import net.locusworks.common.utils.FileReader.LineInfo;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.NoSuchElementException;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mockStatic;
public class FileReaderTest {
private static final String TEST_FILE = "test_file.txt";
private static final Map<Integer, Integer> numLines = new LinkedHashMap<>();
private BufferedReader testBufferedReader;
private static final String[] TEST_CONTENT = new String[]{
"Line 1",
"Line 2",
"Line 3"
};
@BeforeAll
public static void setUpBeforeClass() throws Exception {
File testFile = new File(TEST_FILE);
FileOutputStream fos = new FileOutputStream(testFile);
for (int i = 1; i <= TEST_CONTENT.length; i++) {
String randomString = TEST_CONTENT[i - 1] + "\n";
numLines.put(i, randomString.length());
fos.write(randomString.getBytes());
}
fos.close();
}
@BeforeEach
void setUp() throws FileNotFoundException {
testBufferedReader = new BufferedReader(new java.io.FileReader(TEST_FILE));
}
@AfterEach
void tearDown() {
try {
if (testBufferedReader != null) {
testBufferedReader.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@AfterAll
public static void tearDownAfterClass() {
File file = new File(TEST_FILE);
file.delete();
}
@Test
public void testForLoop() {
int lineCount = 0;
try (FileReader fr = new FileReader(Paths.get(TEST_FILE))) {
for (LineInfo s : fr) {
lineCount++;
Integer lineNumber = s.getLineNumber();
int lineLength = s.getLine().length();
Integer mapLineLength = numLines.get(lineNumber);
assertEquals(lineLength, (mapLineLength - 1));
}
}
assertEquals(lineCount, numLines.size());
}
@Test
void testDeprecatedConstructor() {
try (FileReader fr = new FileReader(new File(TEST_FILE))) {
assertNotNull(fr);
}
}
@Test
void testInit() {
}
@Test
public void testIterator() {
int lineCount = 0;
try (AutoCloseableIterator<LineInfo> iter = new FileReader(Paths.get(TEST_FILE))) {
while (iter.hasNext()) {
lineCount++;
LineInfo s = iter.next();
Integer lineNumber = s.getLineNumber();
int lineLength = s.getLine().length();
Integer mapLineLength = numLines.get(lineNumber);
assertEquals(lineLength, (mapLineLength - 1));
}
}
assertEquals(lineCount, numLines.size());
}
@Test
public void testForIterator() {
int lineCount = 0;
try (FileReader fr = new FileReader(Paths.get(TEST_FILE))) {
while (((Iterator<LineInfo>) fr).hasNext()) {
lineCount++;
LineInfo s = ((Iterator<LineInfo>) fr).next();
Integer lineNumber = s.getLineNumber();
int lineLength = s.getLine().length();
Integer mapLineLength = numLines.get(lineNumber);
assertEquals(lineLength, (mapLineLength - 1));
}
assertEquals(lineCount, numLines.size());
}
}
@Test
public void testNoAlgorithmException() {
try (MockedStatic<SecureRandom> mocked = mockStatic(SecureRandom.class)) {
mocked.when(() -> SecureRandom.getInstance(anyString())).thenThrow(NoSuchAlgorithmException.class);
assertDoesNotThrow(() -> RandomString.newInstance());
}
}
@Test
void testFileReaderWithBufferedReader() {
try (FileReader fileReader = new FileReader(testBufferedReader)) {
Iterator<FileReader.LineInfo> iterator = fileReader.iterator();
assertTrue(iterator.hasNext());
FileReader.LineInfo lineInfo = iterator.next();
assertEquals(1, lineInfo.getLineNumber());
assertEquals("Line 1", lineInfo.getLine());
assertTrue(iterator.hasNext());
lineInfo = iterator.next();
assertEquals(2, lineInfo.getLineNumber());
assertEquals("Line 2", lineInfo.getLine());
assertTrue(iterator.hasNext());
lineInfo = iterator.next();
assertEquals(3, lineInfo.getLineNumber());
assertEquals("Line 3", lineInfo.getLine());
assertFalse(iterator.hasNext());
}
}
@Test
void testFileReaderWithFile() {
try (FileReader fileReader = new FileReader(TEST_FILE)) {
// Add assertions based on the content of the file
Iterator<FileReader.LineInfo> iterator = fileReader.iterator();
assertTrue(iterator.hasNext());
FileReader.LineInfo lineInfo = iterator.next();
assertNotNull(lineInfo);
}
}
@Test
void testFileReaderWithNonexistentFile() {
// Provide the path to a nonexistent file for testing
String filePath = "path/to/nonexistent/file.txt";
// Ensure that the constructor throws an IllegalArgumentException
assertThrows(NullPointerException.class, () -> new FileReader(filePath));
}
@Test
void testFileReaderInitWithNullFile() {
// Ensure that the constructor throws an IllegalArgumentException with a null file
assertThrows(IllegalArgumentException.class, () -> new FileReader((String) null));
}
@Test
void testFileReaderInitWithNullBufferedReader() {
// Ensure that the constructor throws an IllegalArgumentException with a null BufferedReader
assertThrows(IllegalArgumentException.class, () -> new FileReader((BufferedReader) null));
}
@Test
void testFileReaderInitWithNonexistentResource() {
// Provide the name of a nonexistent resource for testing
String resourceName = "nonexistent_resource.txt";
// Ensure that the constructor throws an IllegalArgumentException
assertThrows(NullPointerException.class, () -> new FileReader(resourceName));
}
@Test
void testFileReaderInitWithInvalidResource() {
// Provide the name of an invalid resource for testing
String resourceName = "invalid_resource.txt";
// Ensure that the constructor throws a RuntimeException
assertThrows(RuntimeException.class, () -> new FileReader(resourceName));
}
@Test
void testFileReaderInitWithNullPath() {
// Ensure that the constructor throws an IllegalArgumentException with a null path
assertThrows(IllegalArgumentException.class, () -> new FileReader((Path) null));
}
@Test
void testFileReaderInitWithNonexistentPath() {
// Provide a nonexistent path for testing
Path path = Paths.get("nonexistent_path.txt");
// Ensure that the constructor throws a RuntimeException
assertThrows(RuntimeException.class, () -> new FileReader(path));
}
@Test
void testFileReaderInitWithNonRegularFile() {
// Provide the path to a non-regular file for testing
Path path = Paths.get("path/to/non_regular_file");
// Ensure that the constructor throws an IllegalArgumentException
assertThrows(IllegalArgumentException.class, () -> new FileReader(path));
}
@Test
void testFileReaderHasNextIOException() {
// Provide a BufferedReader that throws an IOException when reading
BufferedReader bufferedReader = new BufferedReader(new StringReader(TEST_FILE)) {
@Override
public String readLine() throws IOException {
throw new IOException("Simulated IOException");
}
};
try (FileReader fileReader = new FileReader(bufferedReader)) {
Iterator<FileReader.LineInfo> iterator = fileReader.iterator();
assertThrows(RuntimeException.class, iterator::hasNext);
}
}
@Test
void testFileReaderNextNoMoreElements() {
// Provide a BufferedReader with no more elements for testing
BufferedReader bufferedReader = new BufferedReader(new StringReader(""));
try (FileReader fileReader = new FileReader(bufferedReader)) {
Iterator<FileReader.LineInfo> iterator = fileReader.iterator();
assertThrows(NoSuchElementException.class, iterator::next);
}
}
}

View File

@@ -0,0 +1,36 @@
package net.locusworks.common.utils;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class HashUtilsTest {
private static final String TEST_STRING = "Hello World";
@Test
public void testMD5() {
String digestUtilsMD5 = DigestUtils.md5Hex(TEST_STRING.getBytes());
String hashUtilsMD5 = HashUtils.hash("MD5", TEST_STRING);
assertEquals(digestUtilsMD5, hashUtilsMD5);
}
@Test
public void testSHA1() {
String digestUtilsMD5 = DigestUtils.sha1Hex(TEST_STRING.getBytes());
String hashUtilsMD5 = HashUtils.hash("SHA-1", TEST_STRING);
assertEquals(digestUtilsMD5, hashUtilsMD5);
}
@Test
public void testSHA512() {
String digestUtilsMD5 = DigestUtils.sha512Hex(TEST_STRING.getBytes());
String hashUtilsMD5 = HashUtils.hash("SHA-512", TEST_STRING);
assertEquals(digestUtilsMD5, hashUtilsMD5);
}
}

View File

@@ -0,0 +1,81 @@
package net.locusworks.common.utils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Objects;
import static net.locusworks.common.utils.ObjectUtils.performIfNotNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
class ObjectUtilsTest {
private static final String TEST_FILE = "object-utils-test.txt";
private static Path testFile;
@BeforeAll
public static void setUpBeforeClass() throws Exception {
Path classpathRoot = Paths.get(Objects.requireNonNull(ObjectUtilsTest.class.getResource("/")).toURI());
testFile = classpathRoot.resolve(TEST_FILE);
Files.writeString(testFile, "test resource");
}
@AfterAll
public static void tearDownAfterClass() throws Exception {
Files.deleteIfExists(testFile);
}
@Test
void testNullObject() {
performIfNotNull(null, str -> fail());
}
@Test
void testNotNullObject() {
String str = "hello world";
performIfNotNull(str, obj -> assertEquals(str, obj));
}
@Test
void getResourceStream_whenResourceExists_ReturnsInputStream() throws Exception {
try (InputStream result = ObjectUtils.getResourceStream("/" + TEST_FILE)) {
assertNotNull(result);
}
}
@Test
void getResourceStream_whenResourceDoesNotExist_ThrowsException() {
// Arrange
String resourceName = "nonexistent.txt";
// Act & Assert
assertThrows(NullPointerException.class,
() -> ObjectUtils.getResourceStream(resourceName));
}
@Test
void getResourceStream_whenResourceExistsInClassLoader_ReturnsInputStream() throws Exception {
try (InputStream result = ObjectUtils.getResourceStream(TEST_FILE)) {
assertNotNull(result);
}
}
@Test
void getResourceStream_whenResourceDoesNotExistInClassLoader_ThrowsException() {
// Arrange
String resourceName = "nonexistent.txt";
// Act & Assert
assertThrows(NullPointerException.class,
() -> ObjectUtils.getResourceStream(resourceName));
}
}

View File

@@ -0,0 +1,30 @@
package net.locusworks.common.utils;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
public class RandomStringTest {
@Test
public void testStaticBytes() {
for (int length = 3; length < 50; length++) {
assertEquals(RandomString.getInstance().getBytes(length).length, length);
}
}
@Test
public void testStaticString() {
for (int length = 3; length < 50; length++) {
String random = RandomString.getInstance().getString(length);
assertEquals(random.length(), length);
}
}
@Test
public void testExceptions() {
assertThrows(IllegalArgumentException.class, () -> RandomString.newInstance().getString(0));
}
}

View File

@@ -0,0 +1,83 @@
package net.locusworks.common.utils;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
class SplitterTest {
@Test
void testBasicSplitter() {
List<String> split = Splitter.onSpace().split("Hello World");
assertNotNull(split);
assertEquals(2, split.size());
assertEquals("Hello", split.get(0));
assertEquals("World", split.get(1));
split = Splitter.fixedLengthSplit(5).split("HelloWorld");
assertNotNull(split);
assertEquals(2, split.size());
assertEquals("Hello", split.get(0));
assertEquals("World", split.get(1));
split = Splitter.onNewLine().split("Hello\nWorld");
assertNotNull(split);
assertEquals(2, split.size());
assertEquals("Hello", split.get(0));
assertEquals("World", split.get(1));
split = Splitter.onNewLine().split("Hello\r\nWorld");
assertNotNull(split);
assertEquals(2, split.size());
assertEquals("Hello", split.get(0));
assertEquals("World", split.get(1));
String[] array = Splitter.onSpace().splitToArray("Hello World");
assertNotNull(array);
assertEquals(2, array.length);
assertEquals("Hello", array[0]);
assertEquals("World", array[1]);
}
@Test
void testOmitEmptyStringWithLimits() {
List<String> split = Splitter.onSpace().split("Hello World");
assertEquals(3, split.size());
split = Splitter.onSpace().omitEmptyStrings().withLimit(1).split("Hello World");
assertNotNull(split);
assertEquals(1, split.size());
}
@Test
void testExceptions() {
assertThrows(IllegalArgumentException.class, () -> Splitter.onSpace().split(""));
assertThrows(IllegalArgumentException.class, () -> Splitter.fixedLengthSplit(0).split("Hello World"));
assertThrows(NullPointerException.class, () -> Splitter.on(null).split("Hello World"));
}
@Test
void testWithKeyValueSeparator() {
Map<String, String> map = Splitter.on(";").withKeyValueSeparator("=").split("Hello=World;bubba=hotep");
assertEquals(2, map.size());
assertEquals("World", map.get("Hello"));
assertEquals("hotep", map.get("bubba"));
}
@Test
void testWithMapSplitterErrors() {
assertThrows(IllegalArgumentException.class, () -> Splitter.on(";").withKeyValueSeparator("").split("Hello=;bubba=hotep"));
assertThrows(IllegalArgumentException.class, () -> Splitter.on(";").withKeyValueSeparator("=").split("Hello=;bubba=hotep"));
assertThrows(IllegalArgumentException.class, () -> Splitter.on(";").withKeyValueSeparator("=").split(null));
Map<String, String> map = Splitter.on(";").withKeyValueSeparator("=").skipInvalidKeyValues().split("Hello=;bubba=hotep");
assertEquals(1, map.size());
assertEquals("hotep", map.get("bubba"));
}
}

View File

@@ -0,0 +1,19 @@
package net.locusworks.common.utils;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
class StreamUtilsTest {
@Test
void testAsStream() {
List<String> list = List.of("Hello", "World");
assertEquals(2, StreamUtils.asStream(list).count());
assertEquals(2, StreamUtils.asStream(list.iterator()).count());
assertEquals(2, StreamUtils.asStream(list.toArray(), false).count());
}
}

View File

@@ -0,0 +1,65 @@
package net.locusworks.common.utils;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class UtilityWrappersTest {
@Test
void successFactoriesAndConstructorExposeBodyAndStatus() {
assertTrue(Success.success().getSuccess());
assertEquals(true, Success.success().getBody());
assertFalse(Success.fail().getSuccess());
assertEquals(false, Success.fail().getBody());
Success value = new Success(true, "body");
assertEquals("body", value.getBody());
}
@Test
void dataOutputStreamConvertsAndClosesByteArrayOutput() throws Exception {
DataOutputStreamHelper stream = new DataOutputStreamHelper();
stream.write("hello".getBytes(StandardCharsets.UTF_8));
assertArrayEquals("hello".getBytes(StandardCharsets.UTF_8), stream.toByteArray());
assertEquals("hello", stream.toString());
assertEquals("aGVsbG8=", stream.base64Encoded());
stream.close();
assertArrayEquals(new byte[0], stream.toByteArray());
assertDoesNotThrow(stream::close);
}
@Test
void dataOutputStreamSupportsOtherOutputsAndSwallowsCloseFailure() throws Exception {
OutputStream custom = new OutputStream() {
@Override
public void write(int b) {
}
@Override
public String toString() {
return "custom";
}
@Override
public void close() {
throw new IllegalStateException("expected");
}
};
DataOutputStreamHelper stream = new DataOutputStreamHelper(custom);
assertEquals("custom", stream.toString());
assertDoesNotThrow(stream::close);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
try (DataOutputStreamHelper explicit = new DataOutputStreamHelper(bytes)) {
explicit.writeByte(1);
assertArrayEquals(new byte[]{1}, explicit.toByteArray());
}
}
}

View File

@@ -0,0 +1,161 @@
package net.locusworks.common.utils;
import com.fasterxml.jackson.core.JsonParser;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.when;
import org.mockito.MockedStatic;
class UtilsPackageCoverageTest {
@TempDir
Path tempDir;
private static java.util.Date deserialize(String value) throws Exception {
JsonParser parser = mock(JsonParser.class);
when(parser.getText()).thenReturn(value);
return new DateTimeStampDeserializer().deserialize(parser, null);
}
@Test
void dateDeserializerHandlesEpochBuiltInCustomAndInvalidFormats() throws Exception {
assertEquals(1234L, deserialize("1234").getTime());
Locale original = Locale.getDefault();
Locale.setDefault(Locale.US);
try {
assertNotNull(deserialize("January 1, 2020"));
assertNotNull(deserialize("12/31/2020"));
assertNotNull(deserialize("Dec 31, 2020 1:02:03 PM"));
assertNull(deserialize("definitely-not-a-date"));
Field stylesField = DateTimeStampDeserializer.class.getDeclaredField("styles");
stylesField.setAccessible(true);
Integer[] styles = (Integer[]) stylesField.get(null);
Integer[] originalStyles = styles.clone();
java.util.Arrays.fill(styles, -1);
try {
assertNotNull(deserialize("12/31/2020"));
} finally {
System.arraycopy(originalStyles, 0, styles, 0, styles.length);
}
} finally {
Locale.setDefault(original);
}
}
@SuppressWarnings("deprecation")
@Test
void hashUtilsCoversEveryInputOverloadCaseAndFailure() throws Exception {
String expected = HashUtils.hash("SHA-256", "content");
byte[] bytes = "content".getBytes(StandardCharsets.UTF_8);
Path path = tempDir.resolve("hash.txt");
Files.write(path, bytes);
File file = path.toFile();
assertEquals(expected, HashUtils.hash("SHA-256", bytes));
assertEquals(expected, HashUtils.hash("SHA-256", path));
assertEquals(expected, HashUtils.hash("SHA-256", file));
assertEquals(expected.toUpperCase(Locale.ROOT), HashUtils.hash("SHA-256", file, false));
assertEquals(expected.toUpperCase(Locale.ROOT), HashUtils.hash("SHA-256", path, false));
assertEquals(expected, HashUtils.hash(new ByteArrayInputStream(bytes), "SHA-256"));
assertEquals(expected.toUpperCase(Locale.ROOT),
HashUtils.hash(new ByteArrayInputStream(bytes), "SHA-256", false));
assertThrows(IllegalArgumentException.class,
() -> HashUtils.hash("SHA-256", tempDir.resolve("missing")));
assertThrows(IllegalArgumentException.class,
() -> HashUtils.hash(new ByteArrayInputStream(bytes), "not-an-algorithm"));
assertThrows(IllegalArgumentException.class,
() -> HashUtils.hash(new java.io.InputStream() {
@Override public int read() throws IOException { throw new IOException("read failed"); }
}, "SHA-256"));
}
@Test
void fileReaderCoversResourceValidationIterationMutationAndFailures() throws Exception {
try (FileReader resource = new FileReader("test.properties")) {
assertTrue(resource.hasNext());
assertSame(resource, resource.iterator());
assertNotNull(resource.next());
}
assertThrows(IllegalArgumentException.class, () -> new FileReader((String) null));
assertThrows(IllegalArgumentException.class, () -> new FileReader((Path) null));
assertThrows(IllegalArgumentException.class, () -> new FileReader(tempDir.resolve("missing")));
assertThrows(IllegalArgumentException.class, () -> new FileReader(tempDir));
assertThrows(IllegalArgumentException.class, () -> new FileReader((BufferedReader) null));
BufferedReader readFailure = new BufferedReader(new StringReader("")) {
@Override public String readLine() throws IOException { throw new IOException("read failed"); }
};
assertThrows(RuntimeException.class, () -> new FileReader(readFailure).hasNext());
BufferedReader closeFailure = new BufferedReader(new StringReader("")) {
@Override public void close() throws IOException { throw new IOException("close failed"); }
};
assertThrows(RuntimeException.class, () -> new FileReader(closeFailure).close());
FileReader nullReader = new FileReader(new BufferedReader(new StringReader("")));
Field readerField = FileReader.class.getDeclaredField("reader");
readerField.setAccessible(true);
readerField.set(nullReader, null);
assertDoesNotThrow(nullReader::close);
Path simulated = tempDir.resolve("simulated.txt");
try (MockedStatic<Files> files = mockStatic(Files.class, CALLS_REAL_METHODS)) {
files.when(() -> Files.notExists(simulated)).thenReturn(false);
files.when(() -> Files.isRegularFile(simulated)).thenReturn(true);
files.when(() -> Files.newBufferedReader(simulated)).thenThrow(new IOException("open failed"));
assertThrows(RuntimeException.class, () -> new FileReader(simulated));
}
FileReader.LineInfo info = new FileReader.LineInfo(1, "line");
info.setLineNumber(2);
info.setLineLength(10);
info.setLine("changed");
assertEquals(2, info.getLineNumber());
assertEquals(10, info.getLineLength());
assertEquals("changed", info.getLine());
}
@Test
void singletonAndUtilityClassConstructorsAreCovered() throws Exception {
Field instance = RandomString.class.getDeclaredField("instance");
instance.setAccessible(true);
instance.set(null, null);
assertNotNull(RandomString.getInstance());
assertNotNull(new HashUtils());
assertNotNull(new Checks());
assertNotNull(new Utils());
assertNotNull(new Constants());
assertNotNull(new ObjectUtils());
assertNotNull(new StreamUtils());
assertNotNull(ObjectUtils.getResourceStream("/test.properties"));
assertEquals(List.of("a", "b"), StreamUtils.asStream(List.of("a", "b"), true).toList());
assertTrue(StreamUtils.asStream(new String[]{"a"}, true).isParallel());
}
}

View File

@@ -0,0 +1,520 @@
package net.locusworks.common.utils;
import net.locusworks.common.annotations.MapValue;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.junit.jupiter.params.ParameterizedTest;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
/**
* Test cases for the Utils class
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
*
*/
public class UtilsTest {
@Test
public void testSafeString() {
assertNotNull(Utils.safeString(null));
assertTrue(Utils.safeString(null).isEmpty());
assertFalse(Utils.safeString("hello world").isEmpty());
}
@Test
public void testAreEqual() {
String val1 = "H";
String val2 = "H";
assertTrue(Utils.areEqual(val1, val2));
assertThrows(IllegalArgumentException.class, () -> Utils.areEqual("1"));
}
@ParameterizedTest
@MethodSource("areEqualParams")
public void testAreEqualWithParams(List<Object> objectList, boolean or, boolean equal) {
assertEquals(equal, Utils.areEqual(or, objectList.toArray()));
}
static Stream<Arguments> areEqualParams() {
return Stream.of(
Arguments.of(List.of("Hello", "Hello"), false, true),
Arguments.of(List.of("Hello", "World"), false, false),
Arguments.of(List.of("Hello", "World", "Hello"), true, true),
Arguments.of(List.of("Hello", "World", "Fair"), true, false)
);
}
@Test
public void testEmptyString() {
assertTrue(Utils.isEmptyString(null));
assertTrue(Utils.isEmptyString(""));
assertTrue(Utils.isEmptyString(" "));
assertFalse(Utils.isEmptyString("foo"));
assertFalse(Utils.isEmptyString(" bar "));
}
@Test
public void testToInteger() {
assertEquals(2, (int) Utils.toInteger("Hello word", 2));
assertEquals(23, (int) Utils.toInteger("23", 5023));
}
@ParameterizedTest
@MethodSource("validateValueParams")
public <V> void testValidateValue(V value, boolean valid) {
assertEquals(valid, Utils.validateValue(value));
}
static Stream<Arguments> validateValueParams() {
return Stream.of(
Arguments.of(null, false),
Arguments.of(Collections.emptyList(), false),
Arguments.of(Map.of(), false),
Arguments.of(false, false),
Arguments.of("", false),
Arguments.of("Hello", true),
Arguments.of(List.of("Hello"), true),
Arguments.of(Map.of("Hello", "World"), true),
Arguments.of(true, true)
);
}
@ParameterizedTest
@MethodSource("areValidParams")
public void testValidateValues(List<Object> objects, boolean or, boolean valid) {
assertEquals(valid, Utils.areValid(objects.toArray()));
assertEquals(!valid, Utils.areNotValid(objects.toArray()));
}
static Stream<Arguments> areValidParams() {
return Stream.of(
Arguments.of(List.of("Hello", List.of("Hello"), Map.of("Hello", "World"), true), false, true),
Arguments.of(List.of(Collections.emptyList(), Map.of(), false), false, false)
);
}
@Test
public void testBuildMap() {
Map<String, String> mymap = Utils.buildMap(TreeMap.class, String.class, String.class, "Hello", "World");
assertNotNull(mymap);
assertEquals(1, mymap.size());
assertEquals("World", mymap.get("Hello"));
}
@Test
public void testCloneList() {
String[] values = new String[]{
"Hello",
"world"
};
List<String> cloned = Utils.cloneList(new ArrayList<>(Arrays.stream(values).toList()));
assertEquals(2, cloned.size());
assertThrows(UnsupportedOperationException.class, () -> cloned.add("asdf"));
}
@Test
public void testCloneObject()
throws InvocationTargetException, InstantiationException, IllegalAccessException,
NoSuchMethodException {
TestClass clazz = new TestClass();
clazz.field1 = "hi";
clazz.field2 = "low";
clazz.field3 = "world";
TestClass clazz3 = new TestClass();
clazz3.field1 = "hi";
clazz3.field2 = "smash";
clazz3.field3 = "world";
clazz.testClass = clazz3;
TestClass clazz2 = Utils.cloneObject(clazz);
assertNotNull(clazz2);
assertEquals(clazz.field1, clazz2.field1);
assertEquals(clazz.field2, clazz2.field2);
assertEquals(clazz.field3, clazz2.field3);
assertEquals(clazz.field5, clazz2.field5);
assertEquals(clazz.number, clazz2.number);
assertEquals(clazz.aBoolean, clazz2.aBoolean);
assertNotNull(clazz2.field4);
assertNull(clazz2.nullField);
}
@Test
public void testCovertToMap() throws Exception {
TestClass clazz = new TestClass();
clazz.field1 = "hi";
clazz.field2 = "low";
clazz.field3 = "world";
TestClass clazz3 = new TestClass();
clazz.field1 = "hi";
clazz.field2 = "low";
clazz.field3 = "world";
clazz.testClass = clazz3;
Map<String, Object> converted = Utils.convertToMap(clazz);
assertEquals(7, converted.size());
assertEquals("hi", converted.get("other_field"));
}
@Test
public void testConvertToStringMap() throws Exception {
TestClass clazz = new TestClass();
clazz.field1 = "hi";
clazz.field2 = "low";
clazz.field3 = "world";
Map<String, String> converted = Utils.convertToStringMap(clazz);
assertEquals(6, converted.size());
assertEquals("hi", converted.get("other_field"));
converted = Utils.convertToStringMap(converted);
assertEquals(6, converted.size());
assertEquals("hi", converted.get("other_field"));
}
@Test
public void testBuildStringMap() {
Map<String, String> myMap = Utils.buildStringHashMap("hello", "world");
assertNotNull(myMap);
assertEquals(1, myMap.size());
}
@Test
public void testExtractFieldToList() {
List<TestClass> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
TestClass clazz = new TestClass();
clazz.field5 = i;
list.add(clazz);
}
List<Integer> extractedList = Utils.extractFieldToList("field5", list);
java.util.Set<Object> extractedSet = Utils.extractFieldToSet("field5", list);
assertEquals(10, extractedList.size());
assertEquals(10, extractedSet.size());
for (int i = 0; i < 10; i++) {
assertEquals(i, extractedList.get(i));
assertTrue(extractedSet.contains(i));
}
assertThrows(IllegalArgumentException.class, () -> Utils.extractFieldToList("fieldName", list));
assertThrows(IllegalArgumentException.class, () -> Utils.extractFieldToSet("fieldName", list));
}
@Test
public void testFilterList() {
List<TestClass> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
TestClass clazz = new TestClass();
clazz.field5 = i;
list.add(clazz);
}
list.get(0).testClass = list.get(1);
List<TestClass> filteredList = Utils.filterList("field5", 5, list);
assertEquals(1, filteredList.size());
assertEquals(5, filteredList.get(0).field5);
assertThrows(IllegalArgumentException.class, () -> Utils.filterList("fieldName", 5, list));
int value = Utils.findValue(5, "field5", list);
assertEquals(5, value);
TestClass tc = Utils.findValue(list.get(1), "testClass", list);
assertNotNull(tc);
assertEquals(1, tc.field5);
}
@Test
public void testFindValues() {
List<TestClass> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
TestClass clazz = new TestClass();
clazz.field5 = i % 2;
list.add(clazz);
}
List<Integer> values = Utils.findValues(0, "field5", list);
assertEquals(5, values.size());
}
@Test
public void testBuildMapExceptions() {
Throwable ex = assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(String.class, String.class, String.class, "" ));
assertNotNull(ex);
ex = assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, String.class, String.class, "Hello"));
assertEquals("Odd number of arguments provided", ex.getMessage());
assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, String.class, String.class, new Object(), "Hello"));
assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, String.class, String.class, "Hello", new Object()));
assertThrows(IllegalArgumentException.class, () -> Utils.buildMap(TreeMap.class, Object.class, String.class, "Hello", new Object()));
}
@Test
public void testBuildSet() {
java.util.Set<String> myset = Utils.buildSet(HashSet.class, String.class, "Hello", "World");
assertNotNull(myset);
assertEquals(2, myset.size());
}
@Test
public void testFormatString() {
String formatted = Utils.formatString("{} {}", "Hi", 1);
assertEquals("Hi 1", formatted);
}
@Test
public void testGetClassName() {
TestClass tc = new TestClass();
assertEquals(tc.getClass().getSimpleName(), Utils.getClassName(tc));
assertEquals(tc.getClass().getName(), Utils.getClassName(tc, true));
assertEquals("Unknown", Utils.getClassName(null));
}
@Test
public void testGetMapValue() {
Integer value = Utils.getMapValue(Map.of("hi", 1), "hi");
assertEquals(1, value);
value = Utils.getMapValue(Map.of("hi", 1), "low", 0);
assertEquals(0, value);
}
@Test
public void testIsNotValid() {
assertTrue(Utils.isNotValid(null, null, null));
assertFalse(Utils.isNotValid("null", "null", "null"));
}
@Test
public void testIsValid() {
assertFalse(Utils.isValid(null, null, null));
assertTrue(Utils.isValid("null", "null", "null"));
}
@Test
public void testListToMap() {
List<TestClass> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
TestClass clazz = new TestClass();
clazz.field5 = i;
list.add(clazz);
}
Map<Integer, TestClass> values = Utils.listToMap("field5", list);
assertEquals(10, values.size());
assertEquals(1, values.get(1).field5);
assertThrows(Throwable.class, () -> Utils.listToMap("fieldasdf5", list));
}
@Test
public void testMakeArray() {
List<TestClass> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
TestClass clazz = new TestClass();
clazz.field5 = i;
list.add(clazz);
}
assertNull(Utils.makeArray(null, String.class));
TestClass[] array = Utils.makeArray(list, TestClass.class);
assertEquals(10, array.length);
for(int i = 0; i < 10; i++) {
assertEquals(list.get(i), array[i]);
}
assertNull(Utils.makeList((Collection<Object>) null));
assertNull(Utils.makeList((Iterable<?>) null));
assertNull(Utils.makeList((String[]) null));
List<TestClass> testList = Utils.makeList(list);
for(int i = 0; i < 10; i++) {
assertEquals(list.get(i), testList.get(i));
}
TestClassList tcl = new TestClassList();
tcl.testClassList = testList;
List<TestClass> testClassList = Utils.makeList(tcl);
for(int i = 0; i < 10; i++) {
assertEquals(list.get(i), testClassList.get(i));
}
List<TestClass> testArrayList = Utils.makeList(array);
for(int i = 0; i < 10; i++) {
assertEquals(list.get(i), testArrayList.get(i));
}
Set<TestClass> testSet = Utils.makeSet(list);
assertEquals(10, testSet.size());
}
@Test
public void testMapToList() {
List<Integer> values = Utils.mapToList(Map.of("Hello", 1));
assertEquals(1, values.size());
assertEquals(1, values.get(0));
Set<Integer> setValues = Utils.mapToSet(Map.of("Hello", 1));
assertEquals(1, setValues.size());
assertTrue(setValues.contains(1));
}
@Test
public void testReverseMap() {
Map<Integer, String> map = Utils.reverseMap(Map.of("Hello", 1));
assertEquals(1, map.size());
assertEquals("Hello", map.get(1));
}
@Test
public void testSafeList() {
assertEquals(0, Utils.safeArray(null).length);
assertTrue(Utils.safeList(null).isEmpty());
assertFalse(Utils.safeIterable(null).iterator().hasNext());
assertTrue(Utils.safeSet(null).isEmpty());
List<String> list = List.of("Hello", "World");
String[] stringArray = list.toArray(new String[0]);
assertEquals(list, Utils.safeList(list));
assertEquals(stringArray, Utils.safeArray(stringArray));
assertEquals(list, Utils.safeIterable(list));
Set<String> testSet = Set.of("Hello", "World");
assertEquals(testSet, Utils.safeSet(testSet));
}
@Test
public void testSetToList() {
List<String> list = Utils.setToList(Set.of("Hello", "World"));
assertEquals(2, list.size());
list = Utils.toList(Set.of("Hello", "World"));
assertEquals(2, list.size());
Set<String> set = Utils.toSet("Hello", "World");
assertEquals(2, set.size());
assertTrue(set.contains("Hello"));
assertTrue(set.contains("World"));
}
@Test
public void testToByteList() {
byte[] bytes = new byte[10];
for (int i = 0; i < bytes.length; i++) {
bytes[i] = (byte)i;
}
List<Byte> byteList = Utils.toByteList(bytes);
assertEquals(bytes.length, byteList.size());
for(int i = 0; i < bytes.length; i++) {
assertEquals(bytes[i], byteList.get(i));
}
}
@Test
public void testIterableSize() {
List<String> stringList = List.of("Hello", "World");
assertEquals(stringList.size(), (int)Utils.size(stringList));
assertEquals(stringList.get(1), Utils.get(stringList, 1));
}
@Test
public void testExceptionWrapper() {
assertThrows(RuntimeException.class, () -> List.of("Hi", "Low").forEach(Utils.handleExceptionWrapper(o -> {
throw new Exception("bleh");
})));
AtomicInteger atomicInteger = new AtomicInteger(0);
List.of("Hi", "Low").forEach(Utils.handleExceptionWrapper(o -> {
atomicInteger.incrementAndGet();
}));
assertEquals(2, atomicInteger.get());
}
@Test
public void testValidateValues() {
assertTrue(Utils.validateValues("Hello", "World"));
assertTrue(Utils.validateValuesOr("Hello", null));
assertTrue(Utils.validateValuesOr(null, "Hello"));
assertFalse(Utils.validateValuesOr(null, null));
}
@Test
public void testIsJunitRunning() {
assertTrue(Utils.isJUnitRunning());
}
static class TestClassList implements Iterable<TestClass> {
private List<TestClass> testClassList;
@Override public Iterator<TestClass> iterator() {
return testClassList.iterator();
}
}
public static class TestClass {
@MapValue("other_field")
private String field1;
@MapValue(ignore = true)
private String field2;
private String field3;
private final String field4 = "final";
@MapValue
private int field5 = 5;
private Boolean aBoolean = Boolean.valueOf("true");
private Double number = 1.0d;
private String nullField = null;
private TestClass testClass;
}
}

View File

@@ -1,41 +1,44 @@
package net.locusworks.test; package net.locusworks.test;
import org.junit.Assert; import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.Test; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.fail;
import net.locusworks.common.crypto.AES; import net.locusworks.common.crypto.AES;
import net.locusworks.common.utils.Utils; import net.locusworks.common.utils.Utils;
import org.junit.jupiter.api.Test;
public class AESEncryptionTest { public class AESEncryptionTest {
@Test @Test
public void testEncryption() { public void testEncryption() {
try { try {
String encrypted = AES.createInstance().encrypt("hello world"); String encrypted = AES.createInstance().encrypt("hello world");
Assert.assertTrue(String.format("Encrypted String is not blank? :%s", encrypted), !Utils.isEmptyString(encrypted)); assertFalse(Utils.isEmptyString(encrypted), String.format("Encrypted String is not blank? :%s", encrypted));
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(System.err); ex.printStackTrace(System.err);
Assert.fail(); fail();
}
} }
}
@Test @Test
public void testDecryption() { public void testDecryption() {
String testString ="hello world"; String testString = "hello world";
try { try {
AES aes = AES.createInstance(); AES aes = AES.createInstance();
String encrypted = aes.encrypt(testString); String encrypted = aes.encrypt(testString);
Assert.assertTrue(String.format("Encrypted String is not blank? :%s", encrypted), !Utils.isEmptyString(encrypted)); assertFalse(Utils.isEmptyString(encrypted), String.format("Encrypted String is not blank? :%s", encrypted));
String decrypted = aes.decrypt(encrypted); String decrypted = aes.decrypt(encrypted);
Assert.assertTrue(String.format("Decrypted String is not blank? :%s", decrypted), !Utils.isEmptyString(encrypted)); assertFalse(Utils.isEmptyString(encrypted), String.format("Decrypted String is not blank? :%s", decrypted));
Assert.assertTrue("Test String and Original String the same? :%s", testString.equals(decrypted)); assertEquals(testString, decrypted, "Test String and Original String the same? :%s");
} catch (Exception ex) { } catch (Exception ex) {
ex.printStackTrace(System.err); ex.printStackTrace(System.err);
Assert.fail(); fail();
}
} }
}
} }

View File

@@ -1,11 +0,0 @@
package net.locusworks.test;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({ AESEncryptionTest.class, FileReaderTest.class, HashSaltTest.class, RandomStringTest.class })
public class AllTests {
}

View File

@@ -1,95 +0,0 @@
package net.locusworks.test;
import static org.junit.Assert.*;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import net.locusworks.common.interfaces.AutoCloseableIterator;
import net.locusworks.common.utils.FileReader;
import net.locusworks.common.utils.FileReader.LineInfo;
import net.locusworks.common.utils.RandomString;
public class FileReaderTest {
private static final String TEST_FILE = "test_file.txt";
private static Map<Integer, Integer> numLines = new LinkedHashMap<>();
@BeforeClass
public static void setUpBeforeClass() throws Exception {
File testFile = new File(TEST_FILE);
FileOutputStream fos = new FileOutputStream(testFile);
Integer count = ThreadLocalRandom.current().nextInt(100);
for (int i = 1; i <= count; i++) {
String randomString = RandomString.getString(ThreadLocalRandom.current().nextInt(5, 100)) + "\n";
numLines.put(i, randomString.length());
fos.write(randomString.getBytes());
}
fos.close();
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
File file = new File(TEST_FILE);
file.delete();
}
@Test
public void testForLoop() {
Integer lineCount = 0;
try (FileReader fr = new FileReader(Paths.get(TEST_FILE))) {
for (LineInfo s : fr) {
lineCount++;
Integer lineNumber = s.getLineNumber();
Integer lineLength = s.getLine().length();
Integer mapLineLength = numLines.get(lineNumber);
assertTrue(lineLength == (mapLineLength-1));
}
}
assertTrue(lineCount == numLines.size());
}
@Test
public void testIterator() {
Integer lineCount = 0;
try(AutoCloseableIterator<LineInfo> iter = new FileReader(Paths.get(TEST_FILE))) {
while(iter.hasNext()) {
lineCount++;
LineInfo s = iter.next();
Integer lineNumber = s.getLineNumber();
Integer lineLength = s.getLine().length();
Integer mapLineLength = numLines.get(lineNumber);
assertTrue(lineLength == (mapLineLength-1));
}
}
assertTrue(lineCount == numLines.size());
}
@Test
public void testForIterator() {
Integer lineCount = 0;
for(Iterator<LineInfo> iter = new FileReader(Paths.get(TEST_FILE)); iter.hasNext();) {
lineCount++;
LineInfo s = iter.next();
Integer lineNumber = s.getLineNumber();
Integer lineLength = s.getLine().length();
Integer mapLineLength = numLines.get(lineNumber);
assertTrue(lineLength == (mapLineLength-1));
}
assertTrue(lineCount == numLines.size());
}
}

View File

@@ -1,34 +1,36 @@
package net.locusworks.test; package net.locusworks.test;
import org.junit.Assert; import static org.junit.jupiter.api.Assertions.assertFalse;
import org.junit.Test; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import net.locusworks.common.crypto.HashSalt; import net.locusworks.common.crypto.HashSalt;
import net.locusworks.common.utils.Utils; import net.locusworks.common.utils.Utils;
import org.junit.jupiter.api.Test;
public class HashSaltTest { public class HashSaltTest {
private static String samplePassword="Hello World";
@Test private static final String samplePassword = "Hello World";
public void testEncryption() {
try { @Test
String hashSalt = HashSalt.createHash(samplePassword); public void testEncryption() {
Assert.assertTrue(String.format("Encrypted String is not blank? :%s", hashSalt), !Utils.isEmptyString(hashSalt)); try {
} catch(Exception ex) { String hashSalt = HashSalt.createHash(samplePassword);
Assert.fail(); assertFalse(Utils.isEmptyString(hashSalt), String.format("Encrypted String is not blank? :%s", hashSalt));
} catch (Exception ex) {
fail();
}
} }
}
@Test
@Test public void testDecryption() {
public void testDecryption() { try {
try { String hashSalt = HashSalt.createHash(samplePassword);
String hashSalt = HashSalt.createHash(samplePassword); boolean decrypted = HashSalt.validatePassword(samplePassword, hashSalt);
boolean decrypted = HashSalt.validatePassword(samplePassword, hashSalt); assertTrue(decrypted, "Test String and Original String the same? :%s");
Assert.assertTrue("Test String and Original String the same? :%s", decrypted); } catch (Exception ex) {
} catch(Exception ex) { fail();
Assert.fail(); }
} }
}
} }

View File

@@ -1,38 +0,0 @@
package net.locusworks.test;
import static org.junit.Assert.*;
import org.apache.commons.codec.digest.DigestUtils;
import org.junit.Test;
import net.locusworks.common.utils.HashUtils;
public class HashUtilsTest {
private static final String TEST_STRING = "Hello World";
@Test
public void testMD5() throws Exception {
String digestUtilsMD5 = DigestUtils.md5Hex(TEST_STRING.getBytes());
String hashUtilsMD5 = HashUtils.hash("MD5", TEST_STRING);
assertTrue(digestUtilsMD5.equals(hashUtilsMD5));
}
@Test
public void testSHA1() throws Exception {
String digestUtilsMD5 = DigestUtils.sha1Hex(TEST_STRING.getBytes());
String hashUtilsMD5 = HashUtils.hash("SHA-1", TEST_STRING);
assertTrue(digestUtilsMD5.equals(hashUtilsMD5));
}
@Test
public void testSHA512() throws Exception {
String digestUtilsMD5 = DigestUtils.sha512Hex(TEST_STRING.getBytes());
String hashUtilsMD5 = HashUtils.hash("SHA-512", TEST_STRING);
assertTrue(digestUtilsMD5.equals(hashUtilsMD5));
}
}

View File

@@ -1,45 +1,63 @@
package net.locusworks.test; package net.locusworks.test;
import static org.junit.Assert.*; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import org.junit.Test;
import net.locusworks.common.immutables.Pair; import net.locusworks.common.immutables.Pair;
import net.locusworks.common.immutables.Triplet; import net.locusworks.common.immutables.Triplet;
import net.locusworks.common.immutables.Unit; import net.locusworks.common.immutables.Unit;
import org.junit.jupiter.api.Test;
public class ImmutablesTest { public class ImmutablesTest {
@Test @Test
public void testUnit() { public void testUnit() {
Unit<String> unit = new Unit<>("Hello World"); Unit<String> unit1 = new Unit<>("Hello World");
assertTrue(unit.getValue1().equals("Hello World")); Unit<String> unit3 = new Unit<>("Hello World");
Unit<Integer> unit2 = new Unit<>(2); assertEquals("Hello World", unit1.getValue1());
assertTrue(unit2.getValue1().equals(2));
} Unit<Integer> unit2 = new Unit<>(2);
assertEquals(2, (int) unit2.getValue1());
@Test
public void testPair() { assertEquals(unit1, unit3);
Pair<String, String> pair1 = new Pair<>("Hello", "World"); assertNotEquals(unit1, unit2);
assertTrue(pair1.getValue1().equals("Hello")); assertNotEquals(unit1, new Object());
assertTrue(pair1.getValue2().equals("World")); assertNotEquals(unit1, null);
Pair<String, Integer> pair2 = new Pair<>("Foo", 25); }
assertTrue(pair2.getValue1().equals("Foo"));
assertTrue(pair2.getValue2().equals(25)); @Test
public void testPair() {
Pair<Integer, Integer> pair3 = new Pair<>(1, 23); Pair<String, String> pair1 = new Pair<>("Hello", "World");
assertTrue(pair3.getValue1().equals(1)); Pair<String, String> pair2 = new Pair<>("Hello", "World");
assertTrue(pair3.getValue2().equals(23)); Pair<String, String> pair3 = new Pair<>("Hello", "World2");
} assertEquals("Hello", pair1.getValue1());
assertEquals("World", pair1.getValue2());
@Test assertEquals(pair1, pair2);
public void testTriplet() {
Triplet<String, Integer, String> triplet1 = new Triplet<>("Hello", 24, "World"); assertNotEquals(pair1, pair3);
assertTrue(triplet1.getValue1().equals("Hello")); assertNotEquals(pair1, new Object());
assertTrue(triplet1.getValue2().equals(24)); assertNotEquals(pair1, null);
assertTrue(triplet1.getValue3().equals("World"));
} }
@Test
public void testTriplet() {
Triplet<String, Integer, String> triplet1 = new Triplet<>("Hello", 24, "World");
Triplet<String, Integer, String> triplet2 = new Triplet<>("Hello", 25, "World");
Triplet<String, Integer, String> triplet3 = new Triplet<>("Hello", 24, "World2");
Triplet<String, Integer, String> triplet4 = new Triplet<>("Hello", 24, "World");
assertEquals("Hello", triplet1.getValue1());
assertEquals(24, (int) triplet1.getValue2());
assertEquals("World", triplet1.getValue3());
assertEquals(triplet1, triplet4);
assertNotEquals(triplet1, triplet2);
assertNotEquals(triplet1, triplet3);
assertNotEquals(triplet1, new Object());
assertNotEquals(triplet1, null);
}
} }

View File

@@ -1,22 +1,22 @@
/** /**
* *
*/ */
package net.locusworks.test; package net.locusworks.test;
import static org.junit.Assert.*;
import java.util.List; import java.util.List;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import net.locusworks.common.immutables.Triplet; import net.locusworks.common.immutables.Triplet;
import net.locusworks.common.objectmapper.ObjectMapperHelper; import net.locusworks.common.objectmapper.ObjectMapperHelper;
import net.locusworks.common.utils.Utils; import net.locusworks.common.utils.Utils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static net.locusworks.common.utils.Constants.JUNIT_TEST_CHECK; import static net.locusworks.common.utils.Constants.JUNIT_TEST_CHECK;
import static net.locusworks.common.utils.Constants.LOG4J_CONFIG_PROPERTY; import static net.locusworks.common.utils.Constants.LOG4J_CONFIG_PROPERTY;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
/** /**
* Test cases to test ObjectMapperHelper.class * Test cases to test ObjectMapperHelper.class
@@ -25,49 +25,49 @@ import static net.locusworks.common.utils.Constants.LOG4J_CONFIG_PROPERTY;
* *
*/ */
public class ObjectMapperHelperTest { public class ObjectMapperHelperTest {
private static Triplet<String, Integer, String> test;
/**
* @throws java.lang.Exception exception
*/
@BeforeClass
public static void setUpBeforeClass() throws Exception {
System.setProperty(LOG4J_CONFIG_PROPERTY, "log4j2-test.xml");
System.setProperty(JUNIT_TEST_CHECK, "true");
test = new Triplet<String, Integer, String>("Hello", 24, "World");
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
System.clearProperty(LOG4J_CONFIG_PROPERTY);
System.clearProperty(JUNIT_TEST_CHECK);
}
@Test private static Triplet<String, Integer, String> test;
public void testWrite() {
String value = ObjectMapperHelper.writeValue(test).getResults(); /**
assertTrue(value != null && !value.trim().isEmpty()); * @throws java.lang.Exception exception
} */
@BeforeAll
@Test public static void setUpBeforeClass() throws Exception {
public void testRead() { System.setProperty(LOG4J_CONFIG_PROPERTY, "log4j2-test.xml");
String value = ObjectMapperHelper.writeValue(test).getResults(); System.setProperty(JUNIT_TEST_CHECK, "true");
assertTrue(value != null && !value.trim().isEmpty()); test = new Triplet<String, Integer, String>("Hello", 24, "World");
}
Triplet<?, ?, ?> tmp = ObjectMapperHelper.readValue(value, Triplet.class).getResults();
assertTrue(tmp != null); @AfterAll
assertTrue(tmp.equals(test)); public static void tearDownAfterClass() throws Exception {
} System.clearProperty(LOG4J_CONFIG_PROPERTY);
System.clearProperty(JUNIT_TEST_CHECK);
@SuppressWarnings("rawtypes") }
@Test
public void testListWriteRead() { @Test
List<Triplet<String, Integer, String>> htrList = Utils.toList(test); public void testWrite() {
String value = ObjectMapperHelper.writeValue(htrList).getResults(); String value = ObjectMapperHelper.writeValue(test).getResults();
assertTrue(value != null && !value.trim().isEmpty()); assertTrue(value != null && !value.trim().isEmpty());
List<Triplet> tmpList = ObjectMapperHelper.readListValue(value, Triplet.class).getResults(); }
assertTrue(tmpList != null && tmpList.size() > 0);
} @Test
public void testRead() {
String value = ObjectMapperHelper.writeValue(test).getResults();
assertTrue(value != null && !value.trim().isEmpty());
Triplet<?, ?, ?> tmp = ObjectMapperHelper.readValue(value, Triplet.class).getResults();
assertNotNull(tmp);
assertEquals(tmp, test);
}
@SuppressWarnings("rawtypes")
@Test
public void testListWriteRead() {
List<Triplet<String, Integer, String>> htrList = Utils.toList(test);
String value = ObjectMapperHelper.writeValue(htrList).getResults();
assertTrue(value != null && !value.trim().isEmpty());
List<Triplet> tmpList = ObjectMapperHelper.readListValue(value, Triplet.class).getResults();
assertTrue(tmpList != null && !tmpList.isEmpty());
}
} }

View File

@@ -1,6 +1,9 @@
package net.locusworks.test; package net.locusworks.test;
import static org.junit.Assert.*; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -8,117 +11,119 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Properties; import java.util.Properties;
import org.junit.AfterClass;
import org.junit.Test;
import net.locusworks.common.configuration.PropertiesManager; import net.locusworks.common.configuration.PropertiesManager;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Test;
/** /**
* Test cases for the properties manager class * Test cases for the properties manager class
*
* @author Isaac Parenteau * @author Isaac Parenteau
* @since 1.0.0-RELEASE * @since 1.0.0-RELEASE
* *
*/ */
public class PropertiesManagerTest { public class PropertiesManagerTest {
private static final String PROPERTIES_FILE = "test.properties"; private static final String PROPERTIES_FILE = "test.properties";
private static final String TMP_PROPS = "temp.properties"; private static final String TMP_PROPS = "temp.properties";
private static final int ENTRY_SIZE = 4; private static final int ENTRY_SIZE = 4;
public static enum Configuration {
DB_HOST("dbHost"),
DB_PORT("dbPort"),
USER_EXPIRATION_DAYS("userExpirationDays"),
LOG_LEVEL("logLevel");
private String value; public enum Configuration {
DB_HOST("dbHost"),
DB_PORT("dbPort"),
USER_EXPIRATION_DAYS("userExpirationDays"),
LOG_LEVEL("logLevel");
private Configuration(String value) { private final String value;
this.value = value;
private Configuration(String value) {
this.value = value;
}
/**
* Get the current value of the enumeration
*
* @return value
*/
public String getValue() {
return this.value;
}
@Override
public String toString() {
return this.value;
}
} }
/** @AfterAll
* Get the current value of the enumeration public static void removeSavedProps() {
* @return value File tmp = new File(TMP_PROPS);
*/ if (tmp.exists()) {
public String getValue() { tmp.delete();
return this.value; }
} }
@Override @Test
public String toString() { public void testPropertiesLoad() {
return this.value; try {
Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE);
assertNotNull(props);
assertTrue(props.containsKey(Configuration.USER_EXPIRATION_DAYS.toString()));
assertTrue(props.containsKey(Configuration.DB_HOST.toString()));
assertTrue(props.containsKey(Configuration.DB_PORT.toString()));
assertTrue(props.containsKey(Configuration.LOG_LEVEL.toString()));
} catch (IOException e) {
fail(e.getMessage());
}
} }
}
@AfterClass @Test
public static void removeSavedProps() { public void testAddConfiguration() {
File tmp = new File(TMP_PROPS); try {
if (tmp.exists()) { Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE);
tmp.delete(); Properties tmp = new Properties();
assertEquals(0, tmp.size());
PropertiesManager.addConfiguration(tmp, props);
assertEquals(ENTRY_SIZE, tmp.size());
assertTrue(tmp.containsKey(Configuration.USER_EXPIRATION_DAYS.toString()));
assertTrue(tmp.containsKey(Configuration.DB_HOST.toString()));
assertTrue(tmp.containsKey(Configuration.DB_PORT.toString()));
assertTrue(tmp.containsKey(Configuration.LOG_LEVEL.toString()));
} catch (IOException e) {
fail(e.getMessage());
}
} }
}
@Test @Test
public void testPropertiesLoad() { public void testRemoveConfiguration() {
try { try {
Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE); Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE);
assertTrue(props != null); Properties tmp = new Properties();
assertTrue(props.containsKey(Configuration.USER_EXPIRATION_DAYS.toString())); assert props != null;
assertTrue(props.containsKey(Configuration.DB_HOST.toString())); assertEquals(ENTRY_SIZE, props.size());
assertTrue(props.containsKey(Configuration.DB_PORT.toString())); assertEquals(0, tmp.size());
assertTrue(props.containsKey(Configuration.LOG_LEVEL.toString())); PropertiesManager.removeConfiguration(props, tmp);
} catch (IOException e) { assertEquals(0, props.size());
fail(e.getMessage()); assertEquals(0, tmp.size());
} catch (IOException e) {
fail(e.getMessage());
}
} }
}
@Test @Test
public void testAddConfiguration() { public void testSaveConfiguration() {
try { try {
Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE); Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE);
Properties tmp = new Properties(); Path tmpFile = Paths.get(TMP_PROPS);
assertTrue(tmp.keySet().size() == 0); PropertiesManager.saveConfiguration(props, tmpFile, "test propertis");
PropertiesManager.addConfiguration(tmp, props); Properties tmp = PropertiesManager.loadConfiguration(tmpFile);
assertTrue(tmp.keySet().size() == ENTRY_SIZE); assertEquals(ENTRY_SIZE, tmp.size());
assertTrue(tmp.containsKey(Configuration.USER_EXPIRATION_DAYS.toString())); assertTrue(tmp.containsKey(Configuration.USER_EXPIRATION_DAYS.toString()));
assertTrue(tmp.containsKey(Configuration.DB_HOST.toString())); assertTrue(tmp.containsKey(Configuration.DB_HOST.toString()));
assertTrue(tmp.containsKey(Configuration.DB_PORT.toString())); assertTrue(tmp.containsKey(Configuration.DB_PORT.toString()));
assertTrue(tmp.containsKey(Configuration.LOG_LEVEL.toString())); assertTrue(tmp.containsKey(Configuration.LOG_LEVEL.toString()));
} catch (IOException e) { } catch (IOException e) {
fail(e.getMessage()); fail(e.getMessage());
}
} }
}
@Test
public void testRemoveConfiguration() {
try {
Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE);
Properties tmp = new Properties();
assertTrue(props.keySet().size() == ENTRY_SIZE);
assertTrue(tmp.keySet().size() == 0);
PropertiesManager.removeConfiguration(props, tmp);
assertTrue(props.keySet().size() == 0);
assertTrue(tmp.keySet().size() == 0);
} catch (IOException e) {
fail(e.getMessage());
}
}
@Test
public void testSaveConfiguration() {
try {
Properties props = PropertiesManager.loadConfiguration(this.getClass(), PROPERTIES_FILE);
Path tmpFile = Paths.get(TMP_PROPS);
PropertiesManager.saveConfiguration(props, tmpFile, "test propertis");
Properties tmp = PropertiesManager.loadConfiguration(tmpFile);
assertTrue(tmp.keySet().size() == ENTRY_SIZE);
assertTrue(tmp.containsKey(Configuration.USER_EXPIRATION_DAYS.toString()));
assertTrue(tmp.containsKey(Configuration.DB_HOST.toString()));
assertTrue(tmp.containsKey(Configuration.DB_PORT.toString()));
assertTrue(tmp.containsKey(Configuration.LOG_LEVEL.toString()));
} catch (IOException e) {
fail(e.getMessage());
}
}
} }

View File

@@ -1,26 +0,0 @@
package net.locusworks.test;
import static org.junit.Assert.*;
import org.junit.Test;
import net.locusworks.common.utils.RandomString;
public class RandomStringTest {
@Test
public void testStaticBytes() {
for (Integer length = 3; length < 50; length++) {
assertTrue(RandomString.getBytes(length).length == length);
}
}
@Test
public void testStaticString() {
for (Integer length = 3; length < 50; length++) {
String random = RandomString.getString(length);
assertTrue(random.length() == length);
}
}
}

View File

@@ -1,39 +0,0 @@
package net.locusworks.test;
import static org.junit.Assert.*;
import org.junit.Test;
import net.locusworks.common.utils.Utils;
/**
* Test cases for the Utils class
* @author Isaac Parenteau
* @since 1.0.0-RELEASE
*
*/
public class UtilsTest {
@Test
public void testSafeString() {
assertNotNull(Utils.safeString(null));
assertTrue(Utils.safeString(null).isEmpty());
assertFalse(Utils.safeString("hello world").isEmpty());
}
@Test
public void testEmptyString() {
assertTrue(Utils.isEmptyString(null));
assertTrue(Utils.isEmptyString(""));
assertTrue(Utils.isEmptyString(" "));
assertFalse(Utils.isEmptyString("foo"));
assertFalse(Utils.isEmptyString(" bar "));
}
@Test
public void testToInteger() {
assertTrue(Utils.toInteger("Hello word", 2) == 2);
assertTrue(Utils.toInteger("23", 5023) == 23);
}
}