/***
* Copyright (c) 2009 Caelum - www.caelum.com.br/opensource All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package br.com.caelum.vraptor.proxy;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import br.com.caelum.vraptor.ioc.ApplicationScoped;
/**
* Java Reflection implementation for {@link InstanceCreator}.
*
* @author Fabio Kung
* @since 3.4.0 (refactored from old DefaultProxifier from 3.0)
*/
@ApplicationScoped
public class ReflectionInstanceCreator
implements InstanceCreator {
private final Logger logger = LoggerFactory.getLogger(ReflectionInstanceCreator.class);
public <T> T instanceFor(Class<T> clazz) {
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
Constructor<?> defaultConstructor = findDefaultConstructor(constructors);
if (defaultConstructor != null) {
logger.debug("Default constructor found in {} ", clazz);
return useDefaultConstructor(clazz);
} else {
if (logger.isDebugEnabled()) {
logger.debug("No default constructor found for {}. Trying to create the "
+ "proxy with other constructors (there are {}).", clazz, constructors.length);
}
return tryAllConstructors(clazz, constructors);
}
}
private static <T> T useDefaultConstructor(Class<T> clazz) {
try {
return clazz.newInstance();
} catch (Exception e) {
throw new ProxyCreationException(e);
}
}
private <T> T tryAllConstructors(Class<T> type, Constructor<?>[] constructors) {
List<Throwable> problems = new ArrayList<Throwable>();
for (Constructor<?> constructor : constructors) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
Object[] parameterValues = proxyParameters(parameterTypes);
if (logger.isDebugEnabled()) {
Object[] params = { Arrays.toString(parameterTypes), Arrays.toString(parameterValues) };
logger.debug("trying constructor with following parameters types: {} values are going to be: {}", params);
}
try {
Object newInstance = constructor.newInstance(parameterValues);
return type.cast(newInstance);
} catch (Throwable e) {
logger.debug("Problem while calling constructor with parameters {}. Trying next.", constructor.getParameterTypes(), e);
problems.add(e);
continue; // try next constructor
}
}
String message = String.format("Tried to instantiate type: %s %d times, but none of the attempts worked. "
+ "The exceptions are: %s", type, constructors.length, problems);
throw new ProxyCreationException(message);
}
/**
* By now, we are always passing null as constructor parameters. If needed, we can create proxies for each
* parameter, changing this method.
*
* @param parameterTypes of the constructor to be called, in order.
* @return parameter instances for the given types.
*/
private static Object[] proxyParameters(Class<?>[] parameterTypes) {
return new Object[parameterTypes.length];
}
/**
* @param constructors from the type to be proxified
* @return null when there isn't a default (null) constructor
*/
private static Constructor<?> findDefaultConstructor(Constructor<?>[] constructors) {
for (Constructor<?> constructor : constructors) {
if (constructor.getParameterTypes().length == 0) {
return constructor;
}
}
return null;
}
}