Skip to content

Commit

Permalink
Parser: env variable resolving expanded with default values and lenie…
Browse files Browse the repository at this point in the history
…nt type manipulations when resolving values ; Vault: enabling lenient type handling for named injections
  • Loading branch information
eledhwen authored May 1, 2021
1 parent c49ddce commit a9272c8
Show file tree
Hide file tree
Showing 29 changed files with 889 additions and 96 deletions.
23 changes: 16 additions & 7 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven

name: Java CI with Maven
name: Maven Build

on:
push:
Expand All @@ -11,14 +8,26 @@ on:

jobs:
build:
strategy:
matrix:
os: [ ubuntu-latest, macOS-latest ]
java: [ 11, 15 ]
fail-fast: false

runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
timeout-minutes: 120

steps:
- uses: actions/checkout@v2
- name: Set up JDK 11
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v1
with:
java-version: 11
java-version: ${{ matrix.java }}
- name: Build with Maven
run: mvn -B package --file pom.xml
- name: Upload surefire artifact
uses: actions/upload-artifact@v2-preview
if: failure()
with:
name: ${{ matrix.os }}-surefire
path: target/surefire-reports/*
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Add the following in your `pom.xml`:
<dependency>
<groupId>com.noleme</groupId>
<artifactId>noleme-vault</artifactId>
<version>0.11</version>
<version>0.12</version>
</dependency>
```

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.noleme</groupId>
<artifactId>noleme-vault</artifactId>
<version>0.11</version>
<version>0.12</version>
<packaging>jar</packaging>

<name>Noleme Vault</name>
Expand Down
41 changes: 28 additions & 13 deletions src/main/java/com/noleme/vault/Vault.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
import com.noleme.vault.container.definition.Definitions;
import com.noleme.vault.exception.RuntimeVaultException;
import com.noleme.vault.exception.VaultException;
import com.noleme.vault.legacy.InjectableField;
import com.noleme.vault.legacy.Key;
import com.noleme.vault.legacy.VaultLegacyCompiler;
import com.noleme.vault.parser.adjuster.VaultAdjuster;
import com.noleme.vault.reflect.LenientClassUtils;

import javax.inject.Provider;
import javax.inject.Singleton;
Expand All @@ -26,12 +28,13 @@
* Created on 23/05/2020.
* Adapted from org.codejargon.feather
*/
@SuppressWarnings("rawtypes")
public final class Vault implements AutoCloseable
{
private final Map<Key, Provider<?>> providers = new ConcurrentHashMap<>();
private final Map<Key, Object> singletons = new ConcurrentHashMap<>();
private final Map<String, Key> namedProviders = new ConcurrentHashMap<>();
private final Map<Class, Object[][]> injectFields = new ConcurrentHashMap<>(0);
private final Map<Class, InjectableField[]> injectFields = new ConcurrentHashMap<>(0);
private final List<AutoCloseable> enclosedCloseables = new ArrayList<>();

/**
Expand Down Expand Up @@ -219,13 +222,13 @@ public <T> T inject(T target) throws VaultException
if (!this.injectFields.containsKey(target.getClass()))
this.injectFields.put(target.getClass(), VaultLegacyCompiler.injectFields(target.getClass()));

for (Object[] f : this.injectFields.get(target.getClass()))
for (InjectableField injectable : this.injectFields.get(target.getClass()))
{
Field field = (Field) f[0];
Key<?> key = (Key<?>) f[2];
Field field = injectable.getField();
Key<?> key = (Key<?>) injectable.getKey();

try {
field.set(target, (boolean) f[1] ? this.provider(key) : this.instance(key));
field.set(target, injectable.isProvider() ? this.provider(key) : this.instance(key));
}
catch (IllegalAccessException e) {
throw new VaultException(String.format("Can't inject field %s in %s", field.getName(), target.getClass().getName()), e);
Expand All @@ -247,14 +250,26 @@ public <T> Provider<T> provider(final Key<T> key, Set<Key> chain) throws Runtime
{
if (!this.providers.containsKey(key))
{
if (key.name != null && this.namedProviders.containsKey(key.name))
if (key.name != null)
{
Key potentialParentKey = this.namedProviders.get(key.name);
if (key.type.isAssignableFrom(potentialParentKey.type))
return (Provider<T>) this.providers.get(potentialParentKey);
if (this.namedProviders.containsKey(key.name))
{
Key potentialParentKey = this.namedProviders.get(key.name);
if (key.type.isAssignableFrom(potentialParentKey.type))
return (Provider<T>) this.providers.get(potentialParentKey);
else if (potentialParentKey.type == String.class && LenientClassUtils.isConversionTarget(key.type))
{
Provider<String> originalProvider = (Provider<String>) this.providers.get(potentialParentKey);

return () -> {
String value = originalProvider.get();
return LenientClassUtils.attemptTypeConversion(value, key.type);
};
}
}
else
throw new RuntimeVaultException("No service could be found for name "+key.name+" and type "+key.type.getName()+".");
}
if (key.name != null)
throw new RuntimeVaultException("No service could be found for name "+key.name+" and type "+key.type.getName()+".");

final Constructor constructor = VaultLegacyCompiler.constructor(key);
final Provider<?>[] paramProviders = VaultLegacyCompiler.paramProviders(
Expand All @@ -266,13 +281,13 @@ public <T> Provider<T> provider(final Key<T> key, Set<Key> chain) throws Runtime
chain
);

this.register(key, (Provider<?>) () -> {
this.register(key, () -> {
try {
var instance = constructor.newInstance(VaultLegacyCompiler.params(paramProviders));
return this.inject(instance);
}
catch (IllegalAccessException | InstantiationException | InvocationTargetException | VaultException e) {
throw new RuntimeVaultException(String.format("Can't instantiate %s", key.toString()), e);
throw new RuntimeVaultException(String.format("Can't instantiate %s", key), e);
}
});
}
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/noleme/vault/builder/CellarStage.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* @author Pierre Lecerf (plecerf@lumiomedical.com)
* Created on 2020/05/24
*/
@SuppressWarnings("rawtypes")
public class CellarStage implements BuildStage
{
private final Cellar cellar;
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/noleme/vault/builder/ModuleStage.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public void build(Vault vault) throws VaultException
logger.debug("Populating vault using module class {}", module.getClass().getName());

if (module instanceof Class)
throw new VaultException(String.format("%s provided as class instead of an instance.", ((Class) module).getName()));
throw new VaultException(String.format("%s provided as class instead of an instance.", ((Class<?>) module).getName()));
for (Method providerMethod : VaultLegacyCompiler.providers(module.getClass()))
VaultLegacyCompiler.providerMethod(vault, module, providerMethod);
}
Expand Down
67 changes: 56 additions & 11 deletions src/main/java/com/noleme/vault/container/Cellar.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.noleme.vault.container;

import com.noleme.vault.exception.RuntimeVaultException;
import com.noleme.vault.exception.VaultInvalidTypeException;
import com.noleme.vault.exception.VaultNotFoundException;
import com.noleme.vault.reflect.LenientClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -83,8 +85,9 @@ public boolean isEmpty()

/**
*
* @param name
* @return
* @param name The name of a service expected to be registered in the Cellar
* @return The corresponding service if found
* @throws VaultNotFoundException If no service could be found for the provided name
*/
public Object getService(String name)
{
Expand All @@ -95,17 +98,19 @@ public Object getService(String name)

/**
*
* @param name
* @param type
* @param <T>
* @return
* @param name The name of a service expected to be registered in the Cellar
* @param type The class of the expected type for the service
* @param <T> The expected type of the service
* @return The corresponding service if found
* @throws VaultNotFoundException If no service could be found for the provided name
* @throws VaultInvalidTypeException If the service is not of the expected type
*/
public <T> T getService(String name, Class<T> type)
{
Object service = this.getService(name);

if (!type.isInstance(service))
throw new VaultNotFoundException("The cellar has a \""+name+"\" service but its type does not match the required "+type.getName());
throw new VaultInvalidTypeException("The cellar has a \""+name+"\" service but its type does not match the required "+type.getName());

return type.cast(service);
}
Expand Down Expand Up @@ -139,18 +144,58 @@ public Map<String, Object> getServices()
** Variables
*/

/**
*
* @param name The name of a variable expected to be registered in the Cellar
* @return The corresponding variable if found
* @throws VaultNotFoundException If no variable could be found for the provided name
*/
public Object getVariable(String name)
{
if (!this.variables.containsKey(name))
throw new VaultNotFoundException("The cellar has no declared \""+name+"\" variable.");
return this.variables.get(name);
}

public boolean hasVariable(String name) { return this.variables.containsKey(name); }
/**
*
* @param name The name of a variable expected to be registered in the Cellar
* @param type The class of the expected type for the variable
* @param <T> The expected type of the variable
* @return The corresponding variable if found
* @throws VaultNotFoundException If no variable could be found for the provided name
* @throws VaultInvalidTypeException If the variable is not of the expected type
*/
public <T> T getVariable(String name, Class<T> type)
{
Object variable = this.getVariable(name);

if (!type.isInstance(variable))
{
if (variable instanceof String)
return LenientClassUtils.attemptTypeConversion((String)variable, type);

throw new VaultInvalidTypeException("The cellar has a \""+name+"\" variable but its type does not match the required "+type.getName());
}

//noinspection unchecked
return (T) this.variables.get(name);
}

public boolean hasVariable(String name)
{
return this.variables.containsKey(name);
}

public void putVariable(String name, Object o) { this.variables.put(name, o); }
public void putVariable(String name, Object o)
{
this.variables.put(name, o);
}

public Map<String, Object> getVariables() { return this.variables; }
public Map<String, Object> getVariables()
{
return this.variables;
}

@Override
public void close()
Expand All @@ -165,7 +210,7 @@ public void close()
((AutoCloseable)service).close();
}
catch (Exception e) {
System.err.println("The \""+closeable+"\" closeable service could not be successfully closed ("+e.getMessage()+").");
logger.error("The \""+closeable+"\" closeable service could not be successfully closed ("+e.getMessage()+").", e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.noleme.vault.exception;

/**
* @author Pierre Lecerf (pierre@noleme.com) on 25/01/15.
*/
public class VaultInvalidTypeException extends RuntimeVaultException
{
public VaultInvalidTypeException(String message) { super(message); }
public VaultInvalidTypeException(String message, Throwable cause)
{
super(message, cause);
}
}
29 changes: 20 additions & 9 deletions src/main/java/com/noleme/vault/factory/VaultFactory.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.noleme.vault.factory;

import com.noleme.commons.container.Pair;
import com.noleme.vault.container.Cellar;
import com.noleme.vault.container.Invocation;
import com.noleme.vault.container.definition.*;
import com.noleme.vault.exception.*;
import com.noleme.vault.parser.VaultCompositeParser;
import com.noleme.vault.parser.VaultParser;
import com.noleme.vault.reflect.ClassUtils;
import com.noleme.vault.reflect.LenientClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -289,13 +290,13 @@ else if (def instanceof ServiceProvider)
private Object makeInstantiation(ServiceInstantiation definition, Cellar cellar) throws VaultInstantiationException
{
try {
Class c = this.classLoader != null
Class<?> c = this.classLoader != null
? Class.forName(definition.getType(), true, this.classLoader)
: Class.forName(definition.getType())
;

Object[] params = definition.getCtorParams();
Class[] paramTypes = new Class[params.length];
Class<?>[] paramTypes = new Class[params.length];
for (int i = 0; i < params.length; ++i)
{
Object o = params[i];
Expand All @@ -307,7 +308,11 @@ private Object makeInstantiation(ServiceInstantiation definition, Cellar cellar)
}
paramTypes[i] = o != null ? o.getClass() : null;
}
Constructor ctor = ClassUtils.getConstructor(c, paramTypes);

Pair<Constructor<?>, Object[]> resolvedCtor = LenientClassUtils.getLenientConstructor(c, paramTypes, params);
Constructor<?> ctor = resolvedCtor.first;
params = resolvedCtor.second;

return ctor.newInstance(params);
}
catch (ClassNotFoundException e) {
Expand Down Expand Up @@ -343,14 +348,14 @@ private Object makeInstantiation(ServiceInstantiation definition, Cellar cellar)
private Object makeStaticCall(ServiceProvider definition, Cellar cellar) throws VaultInstantiationException
{
try {
Class c = this.classLoader != null
Class<?> c = this.classLoader != null
? Class.forName(definition.getType(), true, this.classLoader)
: Class.forName(definition.getType())
;

String methodName = definition.getMethod();
Object[] args = definition.getMethodArgs();
Class[] argTypes = new Class[args.length];
Class<?>[] argTypes = new Class[args.length];
for (int i = 0; i < args.length; ++i)
{
Object o = args[i];
Expand All @@ -362,7 +367,10 @@ private Object makeStaticCall(ServiceProvider definition, Cellar cellar) throws
}
argTypes[i] = o != null ? o.getClass() : null;
}
Method method = ClassUtils.getMethod(c, methodName, argTypes);

Pair<Method, Object[]> resolvedCtor = LenientClassUtils.getLenientMethod(c, methodName, argTypes, args);
Method method = resolvedCtor.first;
args = resolvedCtor.second;

if (!Modifier.isStatic(method.getModifiers()))
throw new VaultInstantiationException("The provider specified for service \""+definition.getIdentifier()+"\" has to be a static method.");
Expand Down Expand Up @@ -402,7 +410,7 @@ private void makeInvocations(ServiceDefinition definition, Object instance, Cell
for (Invocation invocation : definition.getInvocations())
{
Object[] params = invocation.getParams();
Class[] paramTypes = new Class[params.length];
Class<?>[] paramTypes = new Class[params.length];
for (int i = 0 ; i < params.length ; ++i)
{
Object o = params[i];
Expand All @@ -415,7 +423,10 @@ private void makeInvocations(ServiceDefinition definition, Object instance, Cell
paramTypes[i] = o != null ? o.getClass() : null;
}

Method method = ClassUtils.getMethod(instance.getClass(), invocation.getMethodName(), paramTypes);
Pair<Method, Object[]> resolvedCtor = LenientClassUtils.getLenientMethod(instance.getClass(), invocation.getMethodName(), paramTypes, params);
Method method = resolvedCtor.first;
params = resolvedCtor.second;

method.invoke(instance, params);
}
}
Expand Down
Loading

0 comments on commit a9272c8

Please sign in to comment.