/*
* Copyright 2015 byteslounge.com (Gonçalo Marques).
*
* 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.byteslounge.cdi.resolver.bean;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedParameter;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.BeanManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.byteslounge.cdi.exception.ExtensionInitializationException;
import com.byteslounge.cdi.extension.param.InjectableResolverParameter;
import com.byteslounge.cdi.extension.param.ResolverParameter;
import com.byteslounge.cdi.resolver.bean.ResolverInstanceLazyInitializer.ResolverInstance;
import com.byteslounge.cdi.resolver.context.ResolverContext;
import com.byteslounge.cdi.resolver.extractor.BundleResolverParameterExtractor;
import com.byteslounge.cdi.resolver.extractor.KeyResolverParameterExtractor;
import com.byteslounge.cdi.resolver.extractor.LocaleResolverParameterExtractor;
import com.byteslounge.cdi.resolver.extractor.ResolverParameterExtractor;
import com.byteslounge.cdi.utils.MessageUtils;
/**
* Represents the base class used to wrap an extension's resolver method.
*
* @author Gonçalo Marques
* @since 1.1.0
*/
public abstract class AbstractResolverBean<T> implements ResolverBean<T> {
private static final Logger logger = LoggerFactory.getLogger(AbstractResolverBean.class);
private final Class<? extends Annotation> typeToSearch;
private final Class<?> defaulResolverClass;
private final String resolverDescription;
private AnnotatedMethod<?> providedResolverMethod = null;
private List<ResolverParameter<?>> resolverParameters;
private ResolverBean<Locale> localeResolverBean;
protected AnnotatedMethod<?> resolverMethod = null;
private ResolverInstanceLazyInitializer lazyInitializer;
public AbstractResolverBean(Class<? extends Annotation> typeToSearch, String resolverDescription,
Class<?> defaulResolverClass) {
this.typeToSearch = typeToSearch;
this.resolverDescription = resolverDescription;
this.defaulResolverClass = defaulResolverClass;
}
/**
* Validates an extension's resolver method for correctness
* in what matters to the method configuration.
*
* @param resolverMethod The resolver method being verified
*/
abstract void validate(AnnotatedMethod<?> resolverMethod);
/**
* @see {@link com.byteslounge.cdi.resolver.bean.ResolverBean#process(AnnotatedType)}
*/
@Override
public void process(AnnotatedType<?> at) {
for (AnnotatedMethod<?> method : at.getMethods()) {
if (method.isAnnotationPresent(typeToSearch)) {
if (method.getJavaMember().getDeclaringClass().equals(defaulResolverClass)) {
resolverMethod = method;
if (logger.isDebugEnabled()) {
logger.debug("Found default " + resolverDescription + "resolver method: "
+ MessageUtils.getMethodDefinition(method));
}
} else {
if (providedResolverMethod != null) {
String errorMessage = "Found multiple provided " + resolverDescription + " resolver methods: "
+ MessageUtils.getMethodDefinition(providedResolverMethod) + ", "
+ MessageUtils.getMethodDefinition(method);
logger.error(errorMessage);
throw new ExtensionInitializationException(errorMessage);
}
providedResolverMethod = method;
if (logger.isDebugEnabled()) {
logger.debug("Found provided " + resolverDescription + "resolver method: "
+ MessageUtils.getMethodDefinition(providedResolverMethod));
}
}
}
}
}
/**
* @see {@link com.byteslounge.cdi.resolver.bean.ResolverBean#initialize(BeanManager)}
*/
@Override
public void initialize(final BeanManager beanManager) {
validate();
lazyInitializer = new ResolverInstanceLazyInitializer(beanManager, resolverMethod.getJavaMember()
.getDeclaringClass());
resolverParameters = new ArrayList<>();
List<ResolverParameterExtractor<? extends ResolverParameter<?>>> extractors = Arrays
.asList(new ResolverParameterExtractor<?>[] { new KeyResolverParameterExtractor(),
new LocaleResolverParameterExtractor(localeResolverBean),
new BundleResolverParameterExtractor(),
new ResolverParameterExtractor<InjectableResolverParameter>() {
@Override
public InjectableResolverParameter extract(AnnotatedParameter<?> parameter) {
return new InjectableResolverParameter(parameter);
}
} });
for (final AnnotatedParameter<?> parameter : resolverMethod.getParameters()) {
for (ResolverParameterExtractor<? extends ResolverParameter<?>> extractor : extractors) {
ResolverParameter<?> resolverParameter = extractor.extract(parameter);
if (resolverParameter != null) {
resolverParameters.add(resolverParameter);
break;
}
}
}
logger.info("Configured " + resolverDescription + " resolver method: "
+ MessageUtils.getMethodDefinition(resolverMethod));
}
/**
* @see {@link com.byteslounge.cdi.resolver.bean.ResolverBean#invoke(ResolverContext, CreationalContext)}
*/
@Override
@SuppressWarnings("unchecked")
public T invoke(ResolverContext resolverContext, CreationalContext<?> ctx) {
ResolverInstance resolverInstance = lazyInitializer.get();
resolverContext.setResolverBean(resolverInstance.getResolverBean());
resolverContext.setBeanManager(resolverInstance.getActiveBeanManager());
List<Object> parameters = new ArrayList<>();
if (logger.isDebugEnabled()) {
logger.debug("About to resolve parameters of " + resolverDescription + " resolver method");
}
for (ResolverParameter<?> parameter : resolverParameters) {
parameters.add(parameter.resolve(resolverContext, ctx));
}
if (logger.isDebugEnabled()) {
logger.debug("Parameters resolved. Invoking " + resolverDescription + " resolver method.");
}
try {
return (T) resolverMethod.getJavaMember().invoke(resolverInstance.getResolverInstance(),
parameters.toArray());
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new RuntimeException("Could not invoke resolver", e);
}
}
/**
* Validates if the extension's resolver method represented by this bean was properly found
*
*/
private void validate() {
if (providedResolverMethod != null) {
resolverMethod = providedResolverMethod;
}
if (resolverMethod == null) {
String errorMessage = "Could not find any " + resolverDescription + " resolver method.";
logger.error(errorMessage);
throw new ExtensionInitializationException(errorMessage);
}
validate(resolverMethod);
}
/**
* Sets a Locale resolver bean to be used as a helper for the current resolver bean, if required
*
* @param localeResolverBean the Locale resolver bean
*/
public void setLocaleResolverBean(ResolverBean<Locale> localeResolverBean) {
this.localeResolverBean = localeResolverBean;
}
}