Skip to content

Commit d9cf33b

Browse files
authored
Merge pull request #263 from adrienlauer/provides
Add @provide annotation
2 parents f6744be + 1fff9fc commit d9cf33b

File tree

38 files changed

+499
-80
lines changed

38 files changed

+499
-80
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# Version 3.8.5 (2019-03-22)
2+
3+
* [new] The `@Provide` annotation allows to register JSR-330 providers for producing injectable instances of a specific type.
4+
15
# Version 3.8.4 (2019-03-12)
26

37
* [fix] Fix Jansi loading on unsupported platforms.

cli/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<parent>
1515
<groupId>org.seedstack.seed</groupId>
1616
<artifactId>seed</artifactId>
17-
<version>3.8.4-SNAPSHOT</version>
17+
<version>3.8.5-SNAPSHOT</version>
1818
</parent>
1919

2020
<artifactId>seed-cli</artifactId>

core/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
<parent>
1515
<groupId>org.seedstack.seed</groupId>
1616
<artifactId>seed</artifactId>
17-
<version>3.8.4-SNAPSHOT</version>
17+
<version>3.8.5-SNAPSHOT</version>
1818
</parent>
1919

2020
<artifactId>seed-core</artifactId>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright © 2013-2019, The SeedStack authors <http://seedstack.org>
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
package org.seedstack.seed.core.internal;
9+
10+
import static com.google.common.base.Preconditions.checkNotNull;
11+
12+
import com.google.inject.Binder;
13+
import java.lang.annotation.Annotation;
14+
import java.util.Optional;
15+
import javax.inject.Qualifier;
16+
import org.seedstack.shed.reflect.AnnotationPredicates;
17+
import org.seedstack.shed.reflect.Annotations;
18+
19+
abstract class Bindable<T> {
20+
final Class<? extends T> target;
21+
final Annotation qualifier;
22+
23+
Bindable(Class<? extends T> target) {
24+
this.target = checkNotNull(target, "Binding target should not be null");
25+
this.qualifier = findQualifier(this.target).orElse(null);
26+
}
27+
28+
abstract void apply(Binder binder);
29+
30+
private Optional<Annotation> findQualifier(Class<? extends T> target) {
31+
return Annotations.on(target)
32+
.traversingSuperclasses()
33+
.traversingInterfaces()
34+
.findAll()
35+
.filter(AnnotationPredicates.annotationAnnotatedWith(Qualifier.class, false))
36+
.findFirst();
37+
}
38+
}

core/src/main/java/org/seedstack/seed/core/internal/BindingDefinition.java

Lines changed: 21 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,69 +5,47 @@
55
* License, v. 2.0. If a copy of the MPL was not distributed with this
66
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
77
*/
8-
package org.seedstack.seed.core.internal;
98

10-
import static com.google.common.base.Preconditions.checkNotNull;
9+
package org.seedstack.seed.core.internal;
1110

1211
import com.google.inject.Binder;
1312
import com.google.inject.binder.AnnotatedBindingBuilder;
14-
import java.lang.annotation.Annotation;
15-
import java.util.Optional;
16-
import javax.inject.Qualifier;
1713
import org.seedstack.seed.SeedException;
18-
import org.seedstack.shed.reflect.AnnotationPredicates;
19-
import org.seedstack.shed.reflect.Annotations;
2014
import org.slf4j.Logger;
2115
import org.slf4j.LoggerFactory;
2216

23-
public class BindingDefinition<E> {
17+
class BindingDefinition<T> extends Bindable<T> {
2418
private static final Logger LOGGER = LoggerFactory.getLogger(BindingDefinition.class);
25-
private final Class<E> key;
26-
private final Class<? extends E> target;
27-
private final Annotation qualifier;
28-
29-
public BindingDefinition(Class<? extends E> target) {
30-
this(target, null);
31-
}
19+
private final Class<T> from;
3220

33-
public BindingDefinition(Class<? extends E> target, Class<E> fromKey) {
34-
this.target = checkNotNull(target, "Binding target should not be null");
35-
this.key = fromKey;
36-
this.qualifier = findQualifier(this.target).orElse(null);
21+
BindingDefinition(Class<? extends T> target, Class<T> from) {
22+
super(target);
23+
this.from = from;
3724
}
3825

3926
public void apply(Binder binder) {
40-
if (key != null) {
41-
AnnotatedBindingBuilder<?> bind = binder.bind(key);
42-
if (!key.isAssignableFrom(target)) {
27+
if (from != null) {
28+
AnnotatedBindingBuilder<T> bind = binder.bind(from);
29+
if (!from.isAssignableFrom(target)) {
4330
throw SeedException.createNew(CoreErrorCode.INVALID_BINDING)
44-
.put("key", key)
31+
.put("key", from)
4532
.put("target", target);
4633
}
4734
if (qualifier != null) {
48-
LOGGER.trace("Binding {} annotated with {} to {}", key.getName(), qualifier, target.getName());
49-
bind.annotatedWith(qualifier).to(getExtendingClass(target));
35+
LOGGER.debug("Binding {} annotated with {} to {}", from.getName(), qualifier, target.getName());
36+
bind.annotatedWith(qualifier).to(target);
5037
} else {
51-
LOGGER.trace("Binding {} to {}", key.getName(), target.getName());
52-
bind.to(getExtendingClass(target));
38+
LOGGER.debug("Binding {} to {}", from.getName(), target.getName());
39+
bind.to(target);
5340
}
5441
} else {
55-
LOGGER.trace("Binding {} to itself", target.getName());
56-
binder.bind(target);
42+
if (qualifier != null) {
43+
LOGGER.debug("Binding {} annotated with {} to itself", target.getName(), qualifier);
44+
binder.bind(target).annotatedWith(qualifier);
45+
} else {
46+
LOGGER.debug("Binding {} to itself", target.getName());
47+
binder.bind(target);
48+
}
5749
}
5850
}
59-
60-
private Optional<Annotation> findQualifier(Class<? extends E> target) {
61-
return Annotations.on(target)
62-
.traversingSuperclasses()
63-
.traversingInterfaces()
64-
.findAll()
65-
.filter(AnnotationPredicates.annotationAnnotatedWith(Qualifier.class, false))
66-
.findFirst();
67-
}
68-
69-
@SuppressWarnings("unchecked")
70-
private <C extends Class<?>> C getExtendingClass(Class<?> aClass) {
71-
return (C) aClass;
72-
}
7351
}

core/src/main/java/org/seedstack/seed/core/internal/CoreModule.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* License, v. 2.0. If a copy of the MPL was not distributed with this
66
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
77
*/
8+
89
package org.seedstack.seed.core.internal;
910

1011
import com.google.inject.AbstractModule;
@@ -17,10 +18,10 @@
1718
class CoreModule extends AbstractModule {
1819
private final Logger LOGGER = LoggerFactory.getLogger(CoreModule.class);
1920
private final Collection<? extends Module> modules;
20-
private final Set<BindingDefinition> bindings;
21+
private final Set<Bindable> bindings;
2122
private final boolean overriding;
2223

23-
CoreModule(Collection<? extends Module> modules, Set<BindingDefinition> bindings, boolean overriding) {
24+
CoreModule(Collection<? extends Module> modules, Set<Bindable> bindings, boolean overriding) {
2425
this.modules = modules;
2526
this.bindings = bindings;
2627
this.overriding = overriding;

core/src/main/java/org/seedstack/seed/core/internal/CorePlugin.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* License, v. 2.0. If a copy of the MPL was not distributed with this
66
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
77
*/
8+
89
package org.seedstack.seed.core.internal;
910

1011
import com.google.common.base.Strings;
@@ -16,6 +17,7 @@
1617
import java.util.HashSet;
1718
import java.util.Set;
1819
import java.util.stream.Collectors;
20+
import javax.inject.Provider;
1921
import org.kametic.specifications.Specification;
2022
import org.seedstack.seed.core.internal.utils.SpecificationBuilder;
2123
import org.seedstack.shed.reflect.Classes;
@@ -32,10 +34,12 @@ public class CorePlugin extends AbstractSeedPlugin {
3234
InstallResolver.INSTANCE).build();
3335
private static final Specification<Class<?>> bindSpecification = new SpecificationBuilder<>(
3436
BindResolver.INSTANCE).build();
37+
private static final Specification<Class<?>> providerSpecification = new SpecificationBuilder<>(
38+
ProvideResolver.INSTANCE).build();
3539
private final Set<Class<? extends Module>> modules = new HashSet<>();
3640
private final Set<Class<? extends Module>> overridingModules = new HashSet<>();
37-
private final Set<BindingDefinition> bindings = new HashSet<>();
38-
private final Set<BindingDefinition> overridingBindings = new HashSet<>();
41+
private final Set<Bindable> bindings = new HashSet<>();
42+
private final Set<Bindable> overridingBindings = new HashSet<>();
3943

4044
@Override
4145
public String name() {
@@ -52,10 +56,10 @@ public Collection<ClasspathScanRequest> classpathScanRequests() {
5256
return classpathScanRequestBuilder()
5357
.specification(installSpecification)
5458
.specification(bindSpecification)
59+
.specification(providerSpecification)
5560
.build();
5661
}
5762

58-
@SuppressWarnings("unchecked")
5963
@Override
6064
public InitState initialize(InitContext initContext) {
6165
String autodetectModules = initContext.kernelParam(AUTODETECT_MODULES_KERNEL_PARAM);
@@ -65,6 +69,7 @@ public InitState initialize(InitContext initContext) {
6569
String autodetectBindings = initContext.kernelParam(AUTODETECT_BINDINGS_KERNEL_PARAM);
6670
if (Strings.isNullOrEmpty(autodetectBindings) || Boolean.parseBoolean(autodetectBindings)) {
6771
detectBindings(initContext);
72+
detectProviders(initContext);
6873
}
6974
return InitState.INITIALIZED;
7075
}
@@ -101,6 +106,18 @@ private void detectBindings(InitContext initContext) {
101106
}));
102107
}
103108

109+
@SuppressWarnings("unchecked")
110+
private void detectProviders(InitContext initContext) {
111+
initContext.scannedTypesBySpecification().get(providerSpecification)
112+
.forEach(candidate -> ProvideResolver.INSTANCE.apply(candidate).ifPresent(annotation -> {
113+
if (annotation.override()) {
114+
overridingBindings.add(new ProviderDefinition<>((Class<Provider<Object>>) candidate));
115+
} else {
116+
bindings.add(new ProviderDefinition<>((Class<Provider<Object>>) candidate));
117+
}
118+
}));
119+
}
120+
104121
@Override
105122
public Object nativeUnitModule() {
106123
return new CoreModule(
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright © 2013-2019, The SeedStack authors <http://seedstack.org>
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
9+
package org.seedstack.seed.core.internal;
10+
11+
import org.seedstack.seed.Provide;
12+
import org.seedstack.shed.reflect.StandardAnnotationResolver;
13+
14+
class ProvideResolver extends StandardAnnotationResolver<Class<?>, Provide> {
15+
static ProvideResolver INSTANCE = new ProvideResolver();
16+
17+
private ProvideResolver() {
18+
// no external instantiation allowed
19+
}
20+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright © 2013-2019, The SeedStack authors <http://seedstack.org>
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
package org.seedstack.seed.core.internal;
9+
10+
import static com.google.common.base.Preconditions.checkNotNull;
11+
12+
import com.google.inject.Binder;
13+
import com.google.inject.TypeLiteral;
14+
import com.google.inject.binder.AnnotatedBindingBuilder;
15+
import java.lang.reflect.ParameterizedType;
16+
import java.lang.reflect.Type;
17+
import javax.inject.Provider;
18+
import net.jodah.typetools.TypeResolver;
19+
import org.slf4j.Logger;
20+
import org.slf4j.LoggerFactory;
21+
22+
class ProviderDefinition<T, P extends Provider<T>> extends Bindable<P> {
23+
private static final Logger LOGGER = LoggerFactory.getLogger(ProviderDefinition.class);
24+
private final Type from;
25+
26+
ProviderDefinition(Class<P> provider) {
27+
super(provider);
28+
Type type = checkNotNull(TypeResolver.resolveGenericType(Provider.class, provider),
29+
"Unable to resolve generic type of provider " + provider.getName());
30+
if (type instanceof ParameterizedType) {
31+
from = ((ParameterizedType) type).getActualTypeArguments()[0];
32+
} else {
33+
throw new IllegalArgumentException("A generic type is required for provider " + provider.getName());
34+
}
35+
}
36+
37+
@SuppressWarnings("unchecked")
38+
public void apply(Binder binder) {
39+
AnnotatedBindingBuilder<T> bind = binder.bind((TypeLiteral<T>) TypeLiteral.get(from));
40+
if (qualifier != null) {
41+
LOGGER.debug("Binding {} annotated with {} to provider {}",
42+
from.getTypeName(),
43+
qualifier,
44+
target.getName());
45+
bind.annotatedWith(qualifier).toProvider(target);
46+
} else {
47+
LOGGER.debug("Binding {} to provider {}", from.getTypeName(), target.getName());
48+
bind.toProvider(target);
49+
}
50+
}
51+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright © 2013-2019, The SeedStack authors <http://seedstack.org>
3+
*
4+
* This Source Code Form is subject to the terms of the Mozilla Public
5+
* License, v. 2.0. If a copy of the MPL was not distributed with this
6+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7+
*/
8+
package custom;
9+
10+
import java.io.File;
11+
import java.util.Map;
12+
import javax.inject.Provider;
13+
import org.seedstack.coffig.Coffig;
14+
import org.seedstack.seed.Application;
15+
import org.seedstack.seed.ClassConfiguration;
16+
import org.seedstack.seed.Provide;
17+
18+
@Provide(override = true)
19+
public class CustomApplicationProvider implements Provider<Application> {
20+
@Override
21+
public Application get() {
22+
return new Application() {
23+
@Override
24+
public String getName() {
25+
return "custom";
26+
}
27+
28+
@Override
29+
public String getId() {
30+
return null;
31+
}
32+
33+
@Override
34+
public String getVersion() {
35+
return null;
36+
}
37+
38+
@Override
39+
public File getStorageLocation(String context) {
40+
return null;
41+
}
42+
43+
@Override
44+
public boolean isStorageEnabled() {
45+
return false;
46+
}
47+
48+
@Override
49+
public Coffig getConfiguration() {
50+
return null;
51+
}
52+
53+
@Override
54+
public <T> ClassConfiguration<T> getConfiguration(Class<T> someClass) {
55+
return null;
56+
}
57+
58+
@Override
59+
public String substituteWithConfiguration(String value) {
60+
return null;
61+
}
62+
63+
@Override
64+
public Map<String, String> getKernelParameters() {
65+
return null;
66+
}
67+
68+
@Override
69+
public String[] getArguments() {
70+
return new String[0];
71+
}
72+
};
73+
}
74+
}

0 commit comments

Comments
 (0)