Skip to content

Commit

Permalink
Merge pull request #110 from rchodava/rc/new-wiring
Browse files Browse the repository at this point in the history
A fresh new and simpler approach to Wirings as a combination of a PropertySourceChain and a FactoryChain
  • Loading branch information
rchodava committed Apr 18, 2017
2 parents 6c3c989 + b4af376 commit 4192037
Show file tree
Hide file tree
Showing 48 changed files with 2,576 additions and 1,427 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package foundation.stack.datamill.configuration;

import foundation.stack.datamill.configuration.impl.AbstractSource;

import java.util.Optional;

/**
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public class DelegatingPropertySource extends AbstractSource {
private volatile PropertySource delegate;

public DelegatingPropertySource(PropertySource delegate) {
setDelegate(delegate);
}

public DelegatingPropertySource() {
this(null);
}

public void setDelegate(PropertySource delegate) {
this.delegate = delegate;
}

@Override
protected Optional<String> getOptional(String name) {
return delegate != null ? delegate.get(name) : Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package foundation.stack.datamill.configuration;

import rx.functions.Func2;

/**
* A factory for constructing objects of particular types. Use {@link FactoryChains} to create factories and compose
* them together into chains. Note that the contract of a factory is to return a fully constructed instance of the
* requested type (it can use the provided {@link Wiring} to construct the instance, or return null if the factory
* cannot construct an instance.
*
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
@FunctionalInterface
public interface Factory<T, R extends T> extends Func2<Wiring, Class<? extends T>, R> {
static Factory<?, ?> wrap(TypeLessFactory<?> factory) {
return (w, c) -> factory.call(w);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package foundation.stack.datamill.configuration;

/**
* A chain of object factories used by {@link Wiring}s to create objects. Use {@link FactoryChains} to create
* {@link FactoryChain}s.
*
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public interface FactoryChain extends QualifyingFactory<Object, Object> {
/**
* Remove a factory from the chain, returning a chain without the specified factory.
*/
FactoryChain exclude(QualifyingFactory<?, ?> factory);

/**
* Add a factory which will be used to attempt construction of instances for all types.
*/
<T, R extends T> FactoryChain thenForAny(Factory<T, R> factory);

/**
* Add a factory which will be used to attempt construction of instances of concrete classes.
*/
FactoryChain thenForAnyConcreteClass();

/**
* Add a factory for a type, and it's super-classes and interfaces. Note that this factory will be invoked if the
* type requested is exactly the specified type, or one of it's super-classes or interfaces.
*/
<T> FactoryChain thenForSuperOf(Class<T> type, Factory<T, ?> factory);

/**
* Add a factory for a specific type. Note that this factory is only invoked if the type being constructed matches
* the exact type specified.
*
* @param type Exact type for which the specified factory will be used.
*/
<T, R extends T> FactoryChain thenForType(Class<T> type, Factory<T, R> factory);

/**
* @see #thenForAny(Factory)
*/
<R> FactoryChain thenForAny(TypeLessFactory<R> factory);

/**
* @see #thenForSuperOf(Class, Factory)
*/
<T> FactoryChain thenForSuperOf(Class<T> type, TypeLessFactory<?> factory);

/**
* @see #thenForType(Class, Factory)
*/
<T, R extends T> FactoryChain thenForType(Class<T> type, TypeLessFactory<R> factory);

/**
* Add a qualifying factory which will be used to attempt construction of instances for all types.
*
* @see #thenForAny(Factory)
*/
<T, R extends T> FactoryChain thenForAny(QualifyingFactory<T, R> factory);

/**
* Add a qualifying factory for a type, and it's super-classes and interfaces.
*
* @see #thenForSuperOf(Class, Factory)
*/
<T> FactoryChain thenForSuperOf(Class<T> type, QualifyingFactory<T, ?> factory);

/**
* Add a qualifying factory for a specific type.
*
* @see #thenForType(Class, Factory)
*/
<T, R extends T> FactoryChain thenForType(Class<T> type, QualifyingFactory<T, R> factory);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package foundation.stack.datamill.configuration;

import foundation.stack.datamill.configuration.impl.ConcreteClassFactory;
import foundation.stack.datamill.configuration.impl.FactoryChainImpl;

/**
* <p>
* Use this class to create{@link FactoryChain}s. For example, consider a chain composed and used as follows:
* <p/>
* <pre>
* chain = FactoryChains.forType(DatabaseClient.class, w -> createDatabaseClient())
* .thenForSuperOf(UserRepository.class, w -> new UserRepository(...))
* .thenForAny(Core.FACTORY)
* .thenForAnyConcreteClass();
* </pre>
* <p>
* This chain will first check if the class requested is a DatabaseClient, and if so, will construct it. Then, it will
* check if the class requested is a super-class or interface of the UserRepository class, and if so will construct it.
* Then, it will delegate to the Core.FACTORY factory chain. Finally, if none of those factories are able to construct
* the requested instance, the chain will ask the concrete class factory to construct an instance.
* <p/>
* <p>
* Note that if you do not add the concrete class factory at the end of a chain, your chain will only construct the
* exact types for which your factories can construct instances. Thus, it is often useful to add the concrete class
* factory to the end of a factory chain.
* </p>
* <p>
* Note that it is easy to construct factory chains that result in infinite recursion. For example, consider the
* following:
* </p>
* <pre>
* chain = FactoryChains.forSuperOf(EmailService.class, w -> w.singleton(EmailService.class))
* .thenForAnyConcreteClass();
*
* new Wiring(chain).singleton(EmailService.class);
* </pre>
* <p>
* If the chain is used in this way to construct an instance of the EmailService class (a concrete class), the first
* factory in the chain is used. This results in turn to a call to the wiring to construct a singleton instance of
* EmailService. The Wiring will use the same chain, starting at the beginning of the chain. This will again result
* in the first factory in the chain to be used, and would lead to an infinite recursion. To solve this, when a particular
* factory in the chain in turn uses the Wiring to construct an instance, the factory will be excluded from the chain in
* the next call. This will essentially break the infinite recursion - the call to construct the EmailService from the
* first factory in the chain will go directly to the concrete class factory in the second pass through the chain.
* </p>
* <p>
* Note that factory chains are immutable, and each of the chaining methods returns a new chain with the additional
* factory added. This allows you to have common chains which you can combine in different ways, because each
* of those combinations is a new chain.
* </p>
*
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public class FactoryChains {
/**
* @see FactoryChain#thenForAnyConcreteClass()
*/
public static FactoryChain forAnyConcreteClass() {
return forAny(ConcreteClassFactory.instance());
}

/**
* @see FactoryChain#thenForAny(Factory)
*/
public static <T, R extends T> FactoryChain forAny(Factory<T, R> factory) {
return new FactoryChainImpl(FactoryChainImpl.tautology(), factory);
}

/**
* @see FactoryChain#thenForSuperOf(Class, Factory)
*/
public static FactoryChain forSuperOf(Class<?> type, Factory<?, ?> factory) {
return new FactoryChainImpl(FactoryChainImpl.isSuperOf(type), factory);
}

/**
* @see FactoryChain#thenForType(Class, Factory)
*/
public static <T, R extends T> FactoryChain forType(Class<T> type, Factory<T, R> factory) {
return new FactoryChainImpl(FactoryChainImpl.isExactType(type), factory);
}

/**
* @see FactoryChain#thenForAny(QualifyingFactory)
*/
public static <T, R extends T> FactoryChain forAny(QualifyingFactory<T, R> factory) {
return new FactoryChainImpl(FactoryChainImpl.tautology(), factory);
}

/**
* @see FactoryChain#thenForSuperOf(Class, QualifyingFactory)
*/
public static FactoryChain forSuperOf(Class<?> type, QualifyingFactory<?, ?> factory) {
return new FactoryChainImpl(FactoryChainImpl.isSuperOf(type), factory);
}

/**
* @see FactoryChain#thenForType(Class, QualifyingFactory)
*/
public static <T, R extends T> FactoryChain forType(Class<T> type, QualifyingFactory<T, R> factory) {
return new FactoryChainImpl(FactoryChainImpl.isExactType(type), factory);
}

/**
* @see FactoryChain#thenForAny(TypeLessFactory)
*/
public static <R> FactoryChain forAny(TypeLessFactory<R> factory) {
return new FactoryChainImpl(FactoryChainImpl.tautology(), factory);
}

/**
* @see FactoryChain#thenForSuperOf(Class, TypeLessFactory)
*/
public static FactoryChain forSuperOf(Class<?> type, TypeLessFactory<?> factory) {
return new FactoryChainImpl(FactoryChainImpl.isSuperOf(type), factory);
}

/**
* @see FactoryChain#thenForType(Class, TypeLessFactory)
*/
public static <T, R extends T> FactoryChain forType(Class<T> type, TypeLessFactory<R> factory) {
return new FactoryChainImpl(FactoryChainImpl.isExactType(type), factory);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package foundation.stack.datamill.configuration;

/**
* Used in defining an immediate immutable set of properties.
*
* @see PropertySources
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public interface ImmediatePropertySource {
ImmediatePropertySource put(String name, String value);

/**
* Add a formatted value to the immediate set of properties. Uses the
* {@link java.text.MessageFormat#format(String, Object...)} method to format the value.
*/
ImmediatePropertySource put(String name, String format, Object... arguments);
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
import java.lang.annotation.*;

/**
* Annotation to be used to add a name qualifier to constructor parameters.
* Annotation to be used to request a named property to be injected into a constructor parameter.
*
* @see Wiring
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Target({ElementType.PARAMETER})
public @interface Named {
String value();
}

This file was deleted.

0 comments on commit 4192037

Please sign in to comment.