/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat, Inc., and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* 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 org.jboss.weld.injection;
import static org.jboss.weld.injection.Exceptions.rethrowException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.security.AccessController;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.TransientReference;
import javax.enterprise.inject.spi.AnnotatedConstructor;
import javax.enterprise.inject.spi.Bean;
import org.jboss.weld.annotated.enhanced.ConstructorSignature;
import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedConstructor;
import org.jboss.weld.construction.api.AroundConstructCallback;
import org.jboss.weld.construction.api.ConstructionHandle;
import org.jboss.weld.contexts.CreationalContextImpl;
import org.jboss.weld.exceptions.WeldException;
import org.jboss.weld.manager.BeanManagerImpl;
import org.jboss.weld.security.GetAccessibleCopyOfMember;
import org.jboss.weld.util.collections.Arrays2;
import org.jboss.weld.util.reflection.Reflections;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* High-level representation of an injected constructor. This class does not need to be serializable because it is never injected.
*
* @author Pete Muir
* @author Jozef Hartinger
*
* @param <T>
*/
@SuppressFBWarnings("EQ_DOESNT_OVERRIDE_EQUALS")
public class ConstructorInjectionPoint<T> extends AbstractCallableInjectionPoint<T, T, Constructor<T>> {
private final AnnotatedConstructor<T> constructor;
private final ConstructorSignature signature;
private final Constructor<T> accessibleConstructor;
protected ConstructorInjectionPoint(EnhancedAnnotatedConstructor<T> constructor, Bean<T> declaringBean, Class<?> declaringComponentClass, InjectionPointFactory factory, BeanManagerImpl manager) {
super(constructor, declaringBean, declaringComponentClass, false, factory, manager);
this.constructor = constructor.slim();
this.signature = constructor.getSignature();
this.accessibleConstructor = AccessController.doPrivileged(new GetAccessibleCopyOfMember<Constructor<T>>(constructor.getJavaMember()));
}
public T newInstance(BeanManagerImpl manager, CreationalContext<?> ctx) {
CreationalContext<?> transientReferenceContext = null;
if (hasTransientReferenceParameter) {
transientReferenceContext = manager.createCreationalContext(null);
}
try {
Object[] parameterValues = getParameterValues(manager, ctx, transientReferenceContext);
if (ctx instanceof CreationalContextImpl<?>) {
CreationalContextImpl<T> weldCtx = Reflections.cast(ctx);
return invokeAroundConstructCallbacks(parameterValues, weldCtx);
} else {
return newInstance(parameterValues);
}
} finally {
if (hasTransientReferenceParameter) {
transientReferenceContext.release();
}
}
}
private T invokeAroundConstructCallbacks(Object[] parameters, CreationalContextImpl<T> ctx) {
final List<AroundConstructCallback<T>> callbacks = ctx.getAroundConstructCallbacks();
if (callbacks.isEmpty()) {
return newInstance(parameters);
}
final Iterator<AroundConstructCallback<T>> iterator = callbacks.iterator();
return invokeAroundConstructCallback(iterator.next(), new ConstructionHandle<T>() {
@Override
public T proceed(Object[] parameters, Map<String, Object> data) {
if (iterator.hasNext()) {
return invokeAroundConstructCallback(iterator.next(), this, getComponentConstructor(), parameters, data);
} else {
return newInstance(parameters);
}
}
}, getComponentConstructor(), parameters, new HashMap<String, Object>());
}
private T invokeAroundConstructCallback(AroundConstructCallback<T> callback, ConstructionHandle<T> ctx, AnnotatedConstructor<T> constructor, Object[] parameters, Map<String, Object> data) {
try {
return callback.aroundConstruct(ctx, constructor, parameters, data);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new WeldException(e);
}
}
protected T newInstance(Object[] parameterValues) {
try {
return accessibleConstructor.newInstance(parameterValues);
} catch (IllegalArgumentException e) {
rethrowException(e);
} catch (InstantiationException e) {
rethrowException(e);
} catch (IllegalAccessException e) {
rethrowException(e);
} catch (InvocationTargetException e) {
rethrowException(e);
}
return null;
}
/**
* Helper method for getting the current parameter values from a list of annotated parameters.
*
* @param parameters The list of annotated parameter to look up
* @param manager The Bean manager
* @return The object array of looked up values
*/
public Object[] getParameterValues(BeanManagerImpl manager, CreationalContext<?> ctx, CreationalContext<?> transientReference) {
if (getInjectionPoints().isEmpty()) {
return Arrays2.EMPTY_ARRAY;
}
Object[] parameterValues = new Object[getParameterInjectionPoints().size()];
List<ParameterInjectionPoint<?, T>> parameters = getParameterInjectionPoints();
for (int i = 0; i < parameterValues.length; i++) {
ParameterInjectionPoint<?, ?> param = parameters.get(i);
if (hasTransientReferenceParameter && param.getAnnotated().isAnnotationPresent(TransientReference.class)) {
parameterValues[i] = param.getValueToInject(manager, transientReference);
} else {
parameterValues[i] = param.getValueToInject(manager, ctx);
}
}
return parameterValues;
}
public AnnotatedConstructor<T> getAnnotated() {
return constructor;
}
public ConstructorSignature getSignature() {
return signature;
}
public AnnotatedConstructor<T> getComponentConstructor() {
return constructor;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((constructor == null) ? 0 : constructor.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ConstructorInjectionPoint<?> other = (ConstructorInjectionPoint<?>) obj;
if (constructor == null) {
if (other.constructor != null) {
return false;
}
} else if (!constructor.equals(other.constructor)) {
return false;
}
return true;
}
}