Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for specifying Feature processing order #4540

Merged
merged 1 commit into from
Sep 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -109,19 +109,34 @@ private static final class FeatureRegistration {
private final Class<? extends Feature> featureClass;
private final Feature feature;
private final RuntimeType runtimeType;
private final int priority;

private FeatureRegistration(final Class<? extends Feature> featureClass) {
private FeatureRegistration(final Class<? extends Feature> featureClass, int priority) {
this.featureClass = featureClass;
this.feature = null;
final ConstrainedTo runtimeTypeConstraint = featureClass.getAnnotation(ConstrainedTo.class);
this.runtimeType = runtimeTypeConstraint == null ? null : runtimeTypeConstraint.value();
this.priority = priority(featureClass, priority);
}

private FeatureRegistration(final Feature feature) {
private FeatureRegistration(final Feature feature, int priority) {
this.featureClass = feature.getClass();
this.feature = feature;
final ConstrainedTo runtimeTypeConstraint = featureClass.getAnnotation(ConstrainedTo.class);
this.runtimeType = runtimeTypeConstraint == null ? null : runtimeTypeConstraint.value();
this.priority = priority(featureClass, priority);
}

private static int priority(Class<? extends Feature> featureClass, int priority) {
if (priority != ContractProvider.NO_PRIORITY) {
return priority;
}
final Priority priorityAnnotation = featureClass.getAnnotation(Priority.class);
if (priorityAnnotation != null) {
return priorityAnnotation.value();
} else {
return Priorities.USER;
}
}

/**
Expand Down Expand Up @@ -400,7 +415,7 @@ public CommonConfig property(final String name, final Object value) {
public CommonConfig register(final Class<?> componentClass) {
checkComponentClassNotNull(componentClass);
if (componentBag.register(componentClass, getModelEnhancer(componentClass))) {
processFeatureRegistration(null, componentClass);
processFeatureRegistration(null, componentClass, ContractProvider.NO_PRIORITY);
}

return this;
Expand All @@ -410,7 +425,7 @@ public CommonConfig register(final Class<?> componentClass) {
public CommonConfig register(final Class<?> componentClass, final int bindingPriority) {
checkComponentClassNotNull(componentClass);
if (componentBag.register(componentClass, bindingPriority, getModelEnhancer(componentClass))) {
processFeatureRegistration(null, componentClass);
processFeatureRegistration(null, componentClass, bindingPriority);
}

return this;
Expand All @@ -424,7 +439,7 @@ public CommonConfig register(final Class<?> componentClass, final Class<?>... co
return this;
}
if (componentBag.register(componentClass, asNewIdentitySet(contracts), getModelEnhancer(componentClass))) {
processFeatureRegistration(null, componentClass);
processFeatureRegistration(null, componentClass, ContractProvider.NO_PRIORITY);
}

return this;
Expand All @@ -434,7 +449,7 @@ public CommonConfig register(final Class<?> componentClass, final Class<?>... co
public CommonConfig register(final Class<?> componentClass, final Map<Class<?>, Integer> contracts) {
checkComponentClassNotNull(componentClass);
if (componentBag.register(componentClass, contracts, getModelEnhancer(componentClass))) {
processFeatureRegistration(null, componentClass);
processFeatureRegistration(null, componentClass, ContractProvider.NO_PRIORITY);
}

return this;
Expand All @@ -446,7 +461,7 @@ public CommonConfig register(final Object component) {

final Class<?> componentClass = component.getClass();
if (componentBag.register(component, getModelEnhancer(componentClass))) {
processFeatureRegistration(component, componentClass);
processFeatureRegistration(component, componentClass, ContractProvider.NO_PRIORITY);
}

return this;
Expand All @@ -457,7 +472,7 @@ public CommonConfig register(final Object component, final int bindingPriority)
checkProviderNotNull(component);
final Class<?> componentClass = component.getClass();
if (componentBag.register(component, bindingPriority, getModelEnhancer(componentClass))) {
processFeatureRegistration(component, componentClass);
processFeatureRegistration(component, componentClass, bindingPriority);
}

return this;
Expand All @@ -472,7 +487,7 @@ public CommonConfig register(final Object component, final Class<?>... contracts
return this;
}
if (componentBag.register(component, asNewIdentitySet(contracts), getModelEnhancer(componentClass))) {
processFeatureRegistration(component, componentClass);
processFeatureRegistration(component, componentClass, ContractProvider.NO_PRIORITY);
}

return this;
Expand All @@ -483,19 +498,19 @@ public CommonConfig register(final Object component, final Map<Class<?>, Integer
checkProviderNotNull(component);
final Class<?> componentClass = component.getClass();
if (componentBag.register(component, contracts, getModelEnhancer(componentClass))) {
processFeatureRegistration(component, componentClass);
processFeatureRegistration(component, componentClass, ContractProvider.NO_PRIORITY);
}

return this;
}

private void processFeatureRegistration(final Object component, final Class<?> componentClass) {
private void processFeatureRegistration(final Object component, final Class<?> componentClass, int priority) {
final ContractProvider model = componentBag.getModel(componentClass);
if (model.getContracts().contains(Feature.class)) {
@SuppressWarnings("unchecked")
final FeatureRegistration registration = (component != null)
? new FeatureRegistration((Feature) component)
: new FeatureRegistration((Class<? extends Feature>) componentClass);
? new FeatureRegistration((Feature) component, priority)
: new FeatureRegistration((Class<? extends Feature>) componentClass, priority);
newFeatureRegistrations.add(registration);
}
}
Expand Down Expand Up @@ -524,7 +539,7 @@ public CommonConfig loadFrom(final Configuration config) {
this.enabledFeatureClasses.clear();

componentBag.clear();
resetRegistrations();
resetFeatureRegistrations();

for (final Class<?> clazz : config.getClasses()) {
if (Feature.class.isAssignableFrom(clazz) && config.isEnabled((Class<? extends Feature>) clazz)) {
Expand Down Expand Up @@ -629,7 +644,7 @@ public void configureMetaProviders(InjectionManager injectionManager, ManagedObj
// Next, register external meta objects
configureExternalObjects(injectionManager, configuredExternals);
// Configure all features
configureFeatures(injectionManager, new HashSet<>(), resetRegistrations(), finalizer);
configureFeatures(injectionManager, new HashSet<>(), resetFeatureRegistrations(), finalizer);
// Next, register external meta objects registered by features
configureExternalObjects(injectionManager, configuredExternals);
// At last, configure any new binders added by features
Expand Down Expand Up @@ -718,16 +733,17 @@ private void configureFeatures(InjectionManager injectionManager,
if (providerModel != null) {
ProviderBinder.bindProvider(feature, providerModel, injectionManager);
}
configureFeatures(injectionManager, processed, resetRegistrations(), managedObjectsFinalizer);
configureFeatures(injectionManager, processed, resetFeatureRegistrations(), managedObjectsFinalizer);
enabledFeatureClasses.add(registration.getFeatureClass());
enabledFeatures.add(feature);
}
}
}

private List<FeatureRegistration> resetRegistrations() {
private List<FeatureRegistration> resetFeatureRegistrations() {
final List<FeatureRegistration> result = new ArrayList<>(newFeatureRegistrations);
newFeatureRegistrations.clear();
Collections.sort(result, (o1, o2) -> o1.priority < o2.priority ? -1 : 1);
return result;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2018 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand Down Expand Up @@ -57,6 +57,7 @@
import org.junit.Ignore;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
Expand Down Expand Up @@ -997,4 +998,72 @@ public void testFeatureInjectionsBindInFeature() throws Exception {
assertThat("Feature class not injected", config.getProperty("class-injected").toString(), is("true"));
}

// ===========================================================================================================================

@Test
public void testFeatureBindingPriority() {
final InjectionManager injectionManager = Injections.createInjectionManager();
final ManagedObjectsFinalizer finalizer = new ManagedObjectsFinalizer(injectionManager);
config.register(new OrderedFeature(Priorities.USER){}, Priorities.USER);
config.register(new OrderedFeature(Priorities.USER - 100){}, Priorities.USER - 100);
config.register(new OrderedFeature(Priorities.USER + 100){}, Priorities.USER + 100);
config.configureMetaProviders(injectionManager, finalizer);
int value = (int) config.getProperty(OrderedFeature.PROPERTY_NAME);

assertEquals(Priorities.USER + 100, value);
}

private static class OrderedFeature implements Feature {
private final int orderId;
private static final String PROPERTY_NAME = "ORDER_ID";

private OrderedFeature(int orderId) {
this.orderId = orderId;
}

@Override
public boolean configure(FeatureContext context) {
Integer previousId = (Integer) context.getConfiguration().getProperty(PROPERTY_NAME);
if (previousId != null) {
assertThat(previousId, lessThan(orderId));
}
context.property(PROPERTY_NAME, orderId);
return false;
}
}

@Test
public void testFeatureAnnotatedPriority() {
final InjectionManager injectionManager = Injections.createInjectionManager();
final ManagedObjectsFinalizer finalizer = new ManagedObjectsFinalizer(injectionManager);
config.register(PriorityFeature1.class);
config.register(PriorityFeature2.class);
config.register(PriorityFeature3.class);
config.configureMetaProviders(injectionManager, finalizer);
int value = (int) config.getProperty(OrderedFeature.PROPERTY_NAME);

assertEquals(Priorities.USER + 100, value);
}

@Priority(Priorities.USER)
private static class PriorityFeature1 extends OrderedFeature {
private PriorityFeature1() {
super(Priorities.USER);
}
}

@Priority(Priorities.USER - 100)
private static class PriorityFeature2 extends OrderedFeature {
private PriorityFeature2() {
super(Priorities.USER - 100);
}
}

@Priority(Priorities.USER + 100)
private static class PriorityFeature3 extends OrderedFeature {
private PriorityFeature3() {
super(Priorities.USER + 100);
}
}

}