#432 Injector - prevent static field injection, add more tests
This commit is contained in:
@@ -253,8 +253,8 @@ public class AuthMe extends JavaPlugin {
|
||||
initializer.provide(DataFolder.class, getDataFolder());
|
||||
|
||||
// Register elements we instantiate manually
|
||||
initializer.register(newSettings);
|
||||
initializer.register(messages);
|
||||
initializer.register(NewSetting.class, newSettings);
|
||||
initializer.register(Messages.class, messages);
|
||||
initializer.register(DataSource.class, database);
|
||||
initializer.provide(BaseCommands.class, CommandInitializer.buildCommands(initializer));
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
@@ -51,20 +50,6 @@ public class AuthMeServiceInitializer {
|
||||
return get(clazz, new HashSet<Class<?>>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an instantiation by its type.
|
||||
*
|
||||
* @param object the object to register
|
||||
* @throws IllegalStateException if an object of the same type has already been registered
|
||||
*/
|
||||
public void register(Object object) {
|
||||
if (object instanceof Type) {
|
||||
throw new IllegalStateException("You tried to register a Type object: '" + object
|
||||
+ "'. This likely indicates an error. Please use register(Class<T>, T) if really desired.");
|
||||
}
|
||||
storeObject(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an object with a custom class (supertype). Use this for example to specify a
|
||||
* concrete implementation of an interface or an abstract class.
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.lang.annotation.Target;
|
||||
/**
|
||||
* Annotation to denote the collection of AuthMe commands.
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Target({ElementType.PARAMETER, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface BaseCommands {
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package fr.xephi.authme.initialization;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -36,6 +38,7 @@ class ConstructorInjection<T> implements Injection<T> {
|
||||
|
||||
@Override
|
||||
public T instantiateWith(Object... values) {
|
||||
validateNoNullValues(values);
|
||||
try {
|
||||
return constructor.newInstance(values);
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
@@ -60,7 +63,7 @@ class ConstructorInjection<T> implements Injection<T> {
|
||||
*
|
||||
* @param clazz the class to process
|
||||
* @param <T> the class' type
|
||||
* @return injection constructor for the class
|
||||
* @return injection constructor for the class, null if not applicable
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> Constructor<T> getInjectionConstructor(Class<T> clazz) {
|
||||
@@ -74,4 +77,10 @@ class ConstructorInjection<T> implements Injection<T> {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void validateNoNullValues(Object[] array) {
|
||||
for (Object entry : array) {
|
||||
Preconditions.checkNotNull(entry);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.lang.annotation.Target;
|
||||
/**
|
||||
* Annotation for specifying the plugin's data folder.
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Target({ElementType.PARAMETER, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface DataFolder {
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -57,6 +58,7 @@ class FieldInjection<T> implements Injection<T> {
|
||||
|
||||
for (int i = 0; i < fields.length; ++i) {
|
||||
try {
|
||||
Preconditions.checkNotNull(values[i]);
|
||||
fields[i].set(instance, values[i]);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new UnsupportedOperationException(e);
|
||||
@@ -65,15 +67,15 @@ class FieldInjection<T> implements Injection<T> {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private static Class<?> getFirstNonInjectAnnotation(Field field) {
|
||||
for (Annotation annotation : field.getAnnotations()) {
|
||||
if (annotation.annotationType() != Inject.class) {
|
||||
return annotation.annotationType();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a provider for a {@code FieldInjection<T>} instance, i.e. a provides an object
|
||||
* with which field injection can be performed on the given class if applicable. The provided
|
||||
* value is {@code null} if field injection cannot be applied to the class.
|
||||
*
|
||||
* @param clazz the class to provide field injection for
|
||||
* @param <T> the class' type
|
||||
* @return field injection provider for the given class
|
||||
*/
|
||||
public static <T> Provider<FieldInjection<T>> provide(final Class<T> clazz) {
|
||||
return new Provider<FieldInjection<T>>() {
|
||||
@Override
|
||||
@@ -92,6 +94,10 @@ class FieldInjection<T> implements Injection<T> {
|
||||
List<Field> fields = new ArrayList<>();
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
if (field.isAnnotationPresent(Inject.class)) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
throw new IllegalStateException(String.format("Field '%s' in class '%s' is static but "
|
||||
+ "annotated with @Inject", field.getName(), clazz.getSimpleName()));
|
||||
}
|
||||
field.setAccessible(true);
|
||||
fields.add(field);
|
||||
}
|
||||
@@ -99,6 +105,15 @@ class FieldInjection<T> implements Injection<T> {
|
||||
return fields;
|
||||
}
|
||||
|
||||
private static Class<?> getFirstNonInjectAnnotation(Field field) {
|
||||
for (Annotation annotation : field.getAnnotations()) {
|
||||
if (annotation.annotationType() != Inject.class) {
|
||||
return annotation.annotationType();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static <T> Constructor<T> getDefaultConstructor(Class<T> clazz) {
|
||||
try {
|
||||
Constructor<?> defaultConstructor = clazz.getDeclaredConstructor();
|
||||
|
||||
Reference in New Issue
Block a user