
com.zandero.rest.data.ClassFactory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rest.vertx Show documentation
Show all versions of rest.vertx Show documentation
JAX-RS REST annotation processor for vert.x verticals
The newest version!
package com.zandero.rest.data;
import com.zandero.rest.annotation.*;
import com.zandero.rest.bean.*;
import com.zandero.rest.cache.*;
import com.zandero.rest.exception.*;
import com.zandero.rest.injection.*;
import com.zandero.utils.*;
import io.vertx.ext.web.*;
import org.slf4j.*;
import javax.ws.rs.core.*;
import java.lang.reflect.*;
import java.util.*;
import static com.zandero.rest.data.ClassUtils.*;
/**
* A class factory utility
*/
public class ClassFactory {
private final static Logger log = LoggerFactory.getLogger(ClassFactory.class);
private static final Set INJECTION_ANNOTATIONS = ArrayUtils.toSet("Inject", "Injection", "InjectionProvider");
@SuppressWarnings("unchecked")
public static Object getClassInstance(Class> clazz,
ClassCache classCache,
InjectionProvider provider,
RoutingContext context) throws ClassFactoryException, ContextException {
if (clazz == null) {
return null;
}
// only use cache if no @Context is needed (TODO: join this two calls into one!)
boolean hasContext = ContextProviderCache.hasContext(clazz); // TODO: move this method somewhere else
boolean cacheIt = clazz.getAnnotation(NoCache.class) == null; // caching disabled / enabled
Object instance = null;
if (!hasContext && cacheIt) { // no Context ... we can get it from cache
instance = classCache.getInstanceByType(clazz);
}
if (instance == null) {
instance = newInstanceOf(clazz, provider, context);
if (!hasContext && cacheIt) { // no context .. we can cache this instance
classCache.registerInstance(instance);
}
}
return instance;
}
public static Object getClassInstanceByName(Class> clazz,
String name,
ClassCache classCache,
InjectionProvider provider,
RoutingContext context) throws ClassFactoryException, ContextException {
if (clazz == null) {
return null;
}
// only use cache if no @Context is needed (TODO: join this two calls into one!)
boolean hasContext = ContextProviderCache.hasContext(clazz); // TODO: move this method somewhere else
boolean cacheIt = clazz.getAnnotation(NoCache.class) == null; // caching disabled / enabled
Object instance = null;
if (!hasContext && cacheIt) { // no Context ... we can get it from cache
instance = classCache.getInstanceByName(name);
}
if (instance == null) {
instance = newInstanceOf(clazz, provider, context);
if (!hasContext && cacheIt) { // no context .. we can cache this instance
classCache.registerInstanceByName(name, instance);
}
}
return instance;
}
// TODO: improve with additional context provider
public static Object newInstanceOf(Class> clazz,
InjectionProvider provider,
RoutingContext context) throws ClassFactoryException, ContextException {
if (clazz == null) {
return null;
}
Object instance;
boolean canBeInjected = InjectionProvider.canBeInjected(clazz);
boolean hasInjection = InjectionProvider.hasInjection(clazz);
if (provider == null || (!hasInjection && !canBeInjected)) {
SuppressCheck suppress = clazz.getAnnotation(SuppressCheck.class);
if (hasInjection &&
(suppress == null || !INJECTION_ANNOTATIONS.contains(suppress.value()))) {
log.warn(clazz.getName() + " uses @Inject but no InjectionProvider registered!");
}
instance = newInstanceOf(clazz, context);
} else {
try {
instance = provider.getInstance(clazz);
if (instance == null) {
throw new ClassFactoryException("Failed to getInstance class of type: " + clazz.getName() + ", with injector: " +
provider.getClass().getName() + "!", null);
}
} catch (Throwable e) {
throw new ClassFactoryException("Failed to getInstance class of type: " + clazz.getName() + ", with injector: " +
provider.getClass().getName() + "!", e);
}
}
// TODO: remove this or move this method out of ClassFactory ... use some other means to inject context ...
if (ContextProviderCache.hasContext(clazz)) {
ContextProviderCache.injectContext(instance, context);
}
return instance;
}
public static Object newInstanceOf(Class> clazz, RoutingContext context) throws ClassFactoryException {
if (clazz == null) {
return null;
}
try {
for (Constructor> c : clazz.getDeclaredConstructors()) {
boolean isAccessible = c.isAccessible();
if (!isAccessible) {
c.setAccessible(true);
}
// initialize with empty constructor
Object instance;
if (c.getParameterCount() == 0) {
instance = c.newInstance();
} else {
instance = constructWithContext(c, context);
}
if (!isAccessible) {
c.setAccessible(false);
}
if (instance != null) { // managed to create new object instance ...
return instance;
}
}
} catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
log.error("Failed to instantiate class '" + clazz.getName() + "' " + e.getMessage(), e);
throw new ClassFactoryException("Failed to instantiate class of type: " + clazz.getName() + ", class needs empty constructor!", e);
}
throw new ClassFactoryException("Failed to instantiate class of type: " + clazz.getName() + ", class needs empty constructor!", null);
}
/**
* Creates new instance of object using context as provider for contructor parameters
*
* @param constructor to be used
* @param context to extract parameters from
* @return instance or null;
*/
private static Object constructWithContext(Constructor> constructor, RoutingContext context) throws ClassFactoryException {
// Try to initialize class from context if arguments fit
if (context != null) {
BeanDefinition definition = new BeanDefinition(constructor);
if (definition.size() == constructor.getParameterCount()) {
Object[] params = new Object[definition.size()];
String[] values = new String[params.length];
try {
for (int index = 0; index < params.length; index++) {
MethodParameter parameter = definition.get(index);
values[index] = ArgumentProvider.getValue(null, parameter, context, parameter.getDefaultValue());
}
for (int index = 0; index < params.length; index++) {
MethodParameter parameter = definition.get(index);
params[index] = stringToPrimitiveType(values[index], parameter.getDataType());
}
// TODO: log params before invoking
log.info("Invoking: " + describeConstructor(constructor, values));
return constructor.newInstance(params);
} catch (IllegalAccessException | InstantiationException | InvocationTargetException | ClassFactoryException e) {
String error = "Failed to instantiate class, with constructor: " +
describeConstructor(constructor, values) + ". " + e.getMessage();
log.error(error, e);
throw new ClassFactoryException(error, e);
}
}
}
return null;
}
private static String describeConstructor(Constructor> constructor, Object[] params) {
assert constructor != null;
StringBuilder builder = new StringBuilder();
builder.append(constructor.getName())
.append("(");
if (constructor.getParameterCount() > 0) {
for (int i = 0; i < constructor.getParameterCount(); i++) {
Object paramValue = params.length > i ? params[i] : null;
Parameter param = constructor.getParameters()[i];
builder.append(param.getType().getSimpleName())
.append(" ")
.append(param.getName())
.append("=")
.append(paramValue);
if (i + 1 < constructor.getParameterCount()) {
builder.append(", ");
}
}
}
builder.append(")");
return builder.toString();
}
public static Object newInstanceOf(Class> clazz) throws ClassFactoryException {
return newInstanceOf(clazz, null);
}
// TODO : move media type specific into a new class that Reader, Writer factory derives from
public static Object get(Class> type,
ClassCache classCache,
Class> byDefinition,
InjectionProvider provider,
RoutingContext routeContext,
MediaType[] mediaTypes) throws ClassFactoryException,
ContextException {
Class> clazz = byDefinition;
// No class defined ... try by type
if (clazz == null) {
clazz = classCache.getInstanceFromType(type);
}
// try with media type ...
if (clazz == null && mediaTypes != null) {
for (MediaType mediaType : mediaTypes) {
clazz = classCache.getInstanceFromMediaType(mediaType);
if (clazz != null) {
break;
}
}
}
if (clazz != null) {
return getClassInstance(clazz, classCache, provider, routeContext);
}
// 3. find cached instance ... if any
return classCache.getInstanceByName(type.getName());
}
/*
// MediaType
public static Object get(Class> type,
ClassCache classCache,
Class> byDefinition,
InjectionProvider provider,
RoutingContext routeContext,
jakarta.ws.rs.core.MediaType[] mediaTypes) throws ClassFactoryException,
ContextException {
Class> clazz = byDefinition;
// No class defined ... try by type
if (clazz == null) {
clazz = classCache.getInstanceFromType(type);
}
// try with media type ...
if (clazz == null && mediaTypes != null) {
for (jakarta.ws.rs.core.MediaType mediaType : mediaTypes) {
clazz = classCache.getInstanceFromMediaType(mediaType);
if (clazz != null) {
break;
}
}
}
if (clazz != null) {
return getClassInstance(clazz, classCache, provider, routeContext);
}
// 3. find cached instance ... if any
return classCache.getInstanceByName(type.getName());
}*/
public static Object get(String mediaType,
ClassCache classCache,
RoutingContext routeContext) throws ClassFactoryException,
ContextException {
Class> clazz = classCache.getInstanceFromMediaType(MediaTypeHelper.valueOf(mediaType));
return getClassInstance(clazz, classCache, null, routeContext);
}
/*public Class extends T> get(Class> type) {
if (type == null) {
return null;
}
// try to find appropriate class if mapped (by type)
for (Class> key : classTypes.keySet()) {
if (key.isInstance(type) || key.isAssignableFrom(type)) {
return classTypes.get(key);
}
}
return null;
}*/
/**
* Aims to construct given type utilizing a constructor that takes String or other primitive type values
*
* @param type to be constructed
* @param fromValue constructor param
* @param type of value
* @return class object
* @throws ClassFactoryException in case type could not be constructed
*/
public static Object constructType(Class type, String fromValue) throws ClassFactoryException {
Assert.notNull(type, "Missing type!");
// a primitive or "simple" type
if (isPrimitiveType(type)) {
return stringToPrimitiveType(fromValue, type);
}
// have a constructor that accepts a single argument (String or any other primitive type that can be converted from String)
Pair result = constructViaConstructor(type, fromValue);
if (result.getKey()) {
return result.getValue();
}
result = constructViaMethod(type, fromValue);
if (result.getKey()) {
return result.getValue();
}
//
// have a registered implementation of ParamConverterProvider JAX-RS extension SPI that returns a ParamConverter instance capable of a "from string" conversion for the type.
//
// Be List, Set or SortedSet, where T satisfies 2, 3 or 4 above. The resulting collection is read-only.
/*if (type.isAssignableFrom(List.class) ||
type.isAssignableFrom(Set.class) ||
type.isAssignableFrom(SortedSet.class))*/
throw new ClassFactoryException("Could not construct: " + type + " with default value: '" + fromValue + "', " +
"must provide String only or primitive type constructor, " +
"static fromString() or valueOf() methods!", null);
}
/**
* have a constructor that accepts a single argument
* (String or any other primitive type that can be converted from String)
* Pair(success, object)
*/
static Pair constructViaConstructor(Class type, String fromValue) {
Constructor>[] allConstructors = type.getDeclaredConstructors();
for (Constructor> ctor : allConstructors) {
Class>[] pType = ctor.getParameterTypes();
if (pType.length == 1) { // ok ... match ... might be possible to use
try {
for (Class> primitive : ClassUtils.PRIMITIVE_TYPE) {
if (pType[0].isAssignableFrom(primitive)) {
Object value = stringToPrimitiveType(fromValue, primitive);
return new Pair(true, ctor.newInstance(value));
}
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException | ClassFactoryException e) {
//continue; // try next one ... if any
log.warn("Failed constructing: " + ctor, e);
}
}
}
return new Pair<>(false, null);
}
/**
* Constructs type via static method fromString(String value) or valueOf(String value)
*
* @param type to be constructed
* @param fromValue value to take
* @param class type
* @return Object of type or null if failed to construct
*/
static Pair constructViaMethod(Class type, String fromValue) {
// Try to usse fromString before valueOf (enums have valueOf already defined) - in case we override fromString()
List methods = ClassUtils.getMethods(type, "fromString", "valueOf");
if (!methods.isEmpty()) {
for (Method method : methods) {
try {
Object value = method.invoke(null, fromValue);
return new Pair(true, value);
} catch (IllegalAccessException | InvocationTargetException e) {
// failed with this one ... try the others ...
log.warn("Failed invoking static method: " + method.getName());
}
}
}
return new Pair<>(false, null);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy