/**
* Copyright (C) 2010 hprange <hprange@gmail.com>
*
* 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 com.webobjects.foundation;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.google.inject.Binding;
import com.google.inject.BindingAnnotation;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Scopes;
import com.google.inject.util.Providers;
import com.webobjects.appserver.WOComponent;
import com.webobjects.appserver.WODirectAction;
import com.webobjects.appserver.WOSession;
import com.webobjects.eocontrol.EOEnterpriseObject;
import com.woinject.InjectableApplication;
import com.woinject.WOInjectException;
/**
* The InstantiatorInterceptor class creates objects and injects their members
* automatically. Special WebObjects types (components, enterprise objects,
* direct actions and sessions) are instantiated using the Guice injector and
* are subject to AOP interceptors. This class is private and is not intended to
* be used directly by users.
*
* @author <a href="mailto:hprange@gmail.com.br">Henrique Prange</a>
* @since 1.0
*/
class InstantiationInterceptor {
/**
* Special binding annotation used by WOInject to avoid binding conflicts.
*
* @author <a href="mailto:hprange@gmail.com.br">Henrique Prange</a>
* @since 1.0
*/
@BindingAnnotation
@Target({ FIELD, PARAMETER, METHOD })
@Retention(RUNTIME)
private @interface WOInjectBinding {
}
private static final Logger LOGGER = LoggerFactory.getLogger(InstantiationInterceptor.class);
private static <T> Constructor<T> findConstructor(final Class<T> type, final Class<?>[] parameterTypes, boolean shouldThrow, boolean shouldLog) {
try {
return type.getConstructor(parameterTypes);
} catch (Exception exception) {
return handleException(type, exception, shouldThrow, shouldLog);
}
}
private static <T, N> N handleException(Class<T> type, Exception exception, boolean shouldThrow, boolean shouldLog) {
if (shouldLog) {
LOGGER.error("The instantiation of " + type.getName() + " class has failed.", exception);
}
if (!shouldThrow) {
return null;
}
throw new WOInjectException("The instantiation of " + type.getName() + " class has failed.", exception);
}
private static Injector injector() {
InjectableApplication application = InjectableApplication.application();
return application == null ? null : application.injector();
}
/**
* Instantiate the object represented by the type parameter. The object is
* created by the injector defined in the {@link InjectableApplication}
* class if the type is assignable from <code>WOComponent</code>,
* <code>EOEnterpriseObject</code>, <code>WOSession</code> or
* <code>WODirectAction</code>.
* <p>
* Other types of objects are instantiated by reflection, and their members
* are injected using the {@link Injector#injectMembers(Object)} method.
* Those objects are not subject to AOP interceptors.
*
* @see _NSUtilities#instantiateObject(Class, Class[], Object[], boolean,
* boolean)
* @see Injector#injectMembers(Object)
*/
public static <T> T instantiateObject(final Class<T> type, final Class<?>[] parameterTypes, final Object[] parameters, boolean shouldThrow, boolean shouldLog) {
Constructor<T> constructor = findConstructor(type, parameterTypes, shouldThrow, shouldLog);
if (constructor == null) {
return null;
}
return instantiateObject(constructor, type, parameters, shouldThrow, shouldLog);
}
/**
* Instantiate the object represented by the type parameter using the
* specified constructor. The object is created by the injector defined in
* the {@link InjectableApplication} class if the type is assignable from
* <code>WOComponent</code>, <code>EOEnterpriseObject</code>,
* <code>WOSession</code> or <code>WODirectAction</code>.
* <p>
* Other types of objects are instantiated by reflection, and their members
* are injected using the {@link Injector#injectMembers(Object)} method.
* Those objects are not subject to AOP interceptors.
*
* @see _NSUtilities#instantiateObject(Class, Class[], Object[], boolean,
* boolean)
* @see Injector#injectMembers(Object)
*/
public static <T> T instantiateObject(final Constructor<T> constructor, final Class<T> type, final Object[] parameters, boolean shouldThrow, boolean shouldLog) {
Injector injector = injector();
if (injector == null) {
return instantiateObjectByReflection(type, constructor, parameters, shouldThrow, shouldLog);
}
if (!(WOSession.class.isAssignableFrom(type) || WOComponent.class.isAssignableFrom(type) || EOEnterpriseObject.class.isAssignableFrom(type) || WODirectAction.class.isAssignableFrom(type))) {
T object = instantiateObjectByReflection(type, constructor, parameters, shouldThrow, shouldLog);
if (object != null) {
injector.injectMembers(object);
}
return object;
}
Module woinjectModule = new AbstractModule() {
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
protected void configure() {
Binder binder = binder().withSource(type);
int i = 0;
if (parameters != null) {
for (Class<?> parameterType : constructor.getParameterTypes()) {
binder.bind(parameterType).toProvider((Provider) Providers.of(parameters[i++]));
}
}
binder.bind(Key.get(type, WOInjectBinding.class)).toConstructor(constructor).in(Scopes.NO_SCOPE);
}
};
try {
Injector forCreate = injector.createChildInjector(woinjectModule);
Binding<T> binding = forCreate.getBinding(Key.get(type, WOInjectBinding.class));
return binding.getProvider().get();
} catch (Exception exception) {
return handleException(type, exception, shouldThrow, shouldLog);
}
}
private static <T> T instantiateObjectByReflection(Class<T> type, Constructor<T> constructor, Object[] parameters, boolean shouldThrow, boolean shouldLog) {
try {
return constructor.newInstance(parameters);
} catch (Exception exception) {
return handleException(type, exception, shouldThrow, shouldLog);
}
}
}