public class ExtensionInitializationException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    public ExtensionInitializationException(String message) {
        super(message);
    }

    public ExtensionInitializationException(Throwable cause) {
        super(cause);
    }

    public ExtensionInitializationException(String message, Throwable cause) {
        super(message, cause);
    }

} public class InjectableResolverParameter implements InjectionPoint, ResolverParameter<Object> {

    private final AnnotatedParameter<?> parameter;
    private BeanManager beanManager;
    private Bean<?> resolverBean;

    public InjectableResolverParameter(AnnotatedParameter<?> parameter) {
        this.parameter = parameter;
    }

    /**
     * See {@link InjectionPoint#getAnnotated()}
     */
    @Override
    public Annotated getAnnotated() {
        return parameter;
    }

    /**
     * See {@link InjectionPoint#getBean()}
     */
    @Override
    public Bean<?> getBean() {
        return resolverBean;
    }

    /**
     * See {@link InjectionPoint#getMember()}
     */
    @Override
    public Member getMember() {
        return parameter.getDeclaringCallable().getJavaMember();
    }

    /**
     * See {@link InjectionPoint#getQualifiers()}
     */
    @Override
    public Set<Annotation> getQualifiers() {
        Set<Annotation> qualifiers = new HashSet<Annotation>();
        for (Annotation annotation : parameter.getAnnotations()) {
            if (beanManager.isQualifier(annotation.annotationType())) {
                qualifiers.add(annotation);
            }
        }
        return qualifiers;
    }

    /**
     * See {@link InjectionPoint#getType()}
     */
    @Override
    public Type getType() {
        return parameter.getBaseType();
    }

    /**
     * See {@link InjectionPoint#isDelegate()}
     */
    @Override
    public boolean isDelegate() {
        return false;
    }

    /**
     * See {@link InjectionPoint#isTransient()}
     */
    @Override
    public boolean isTransient() {
        return false;
    }

    /**
     * see {@link ResolverParameter#resolve(String, String, CreationalContext)}
     */
    @Override
    public <T> Object resolve(ResolverContext resolverContext, CreationalContext<T> ctx) {
        resolverBean = resolverContext.getResolverBean();
        beanManager = resolverContext.getBeanManager();
        return beanManager.getInjectableReference(this, ctx);
    }

} public interface ResolverParameter<R> {

    /**
     * Resolves the value for a given resolver method parameter
     * 
     * @param resolverContext
     *            The resolver context
     * @param ctx
     *            The CDI creational context
     * @return
     *            The value to be injected into the property resolver method parameter
     */
    <T> R resolve(ResolverContext resolverContext, CreationalContext<T> ctx);

} public static class ResolverInstance {

    private final Object resolverInstance;
    private final Bean<?> resolverBean;
    private final BeanManager activeBeanManager;

    public ResolverInstance(Object resolverInstance, Bean<?> resolverBean,BeanManager activeBeanManager) {
        this.resolverInstance = resolverInstance;
        this.resolverBean = resolverBean;
        this.activeBeanManager = activeBeanManager;
    }

    public Object getResolverInstance() {
        return resolverInstance;
    }

    public Bean<?> getResolverBean() {
        return resolverBean;
    }

    public BeanManager getActiveBeanManager() {
        return activeBeanManager;
    }

} public class ResolverContext {

    private final String key;
    private final String bundleName;
    private Bean<?> resolverBean;
    private BeanManager beanManager;

    private ResolverContext(String key, String bundleName) {
        this.key = key;
        this.bundleName = bundleName;
    }

    /**
     * Creates a {@link com.byteslounge.cdi.resolver.context.ResolverContext} instance.
     * 
     * @param key the property key to be associated with the created context
     * @param bundleName the resource bundle name to be associated with the created context
     * 
     * @return the newly created {@link com.byteslounge.cdi.resolver.context.ResolverContext} instance
     */
    public static ResolverContext create(String key, String bundleName) {
        return new ResolverContext(key, bundleName);
    }

    /**
     * Gets the key associated with this {@link com.byteslounge.cdi.resolver.context.ResolverContext} instance
     * 
     * @return the key associated with this {@link com.byteslounge.cdi.resolver.context.ResolverContext} instance
     */
    public String getKey() {
        return key;
    }

    /**
     * Gets the resource bundle name associated with this {@link com.byteslounge.cdi.resolver.context.ResolverContext} instance
     * 
     * @return the resource bundle name associated with this {@link com.byteslounge.cdi.resolver.context.ResolverContext} instance
     */
    public String getBundleName() {
        return bundleName;
    }

    /**
     * Sets the resolver bean associated with the current resolving execution
     * 
     * @param resolverBean the property key to be associated with the created context
     */
    public void setResolverBean(Bean<?> resolverBean) {
        this.resolverBean = resolverBean;
    }

    /**
     * Gets the resolver bean associated with the current resolving execution
     * 
     * @return the resolver bean associated with the current resolving execution
     */
    public Bean<?> getResolverBean() {
        return resolverBean;
    }

    /**
     * Gets the bean manager associated with the current resolving execution
     * 
     * @return the bean manager associated with the current resolving execution
     */
    public BeanManager getBeanManager() {
        return beanManager;
    }

    /**
     * Sets the bean manager associated with the current resolving execution
     * 
     * @param beanManager the bean manager associated with the current resolving execution
     */
    public void setBeanManager(BeanManager beanManager) {
        this.beanManager = beanManager;
    }

} public class BundleResolverParameterExtractor extends
        ProvidedResolverParameterExtractor<BundlePropertyResolverParameter> {

    public BundleResolverParameterExtractor() {
        super(PropertyBundle.class, new BundlePropertyResolverParameter());
    }

} public class KeyResolverParameterExtractor extends ProvidedResolverParameterExtractor<KeyPropertyResolverParameter> {

    public KeyResolverParameterExtractor() {
        super(PropertyKey.class, new KeyPropertyResolverParameter());
    }

} public class LocaleResolverParameterExtractor extends
        ProvidedResolverParameterExtractor<LocalePropertyResolverParameter> {

    public LocaleResolverParameterExtractor(ResolverBean<Locale> localeResolverBean) {
        super(PropertyLocale.class, new LocalePropertyResolverParameter(localeResolverBean));
    }

} public interface ResolverParameterExtractor<T extends ResolverParameter<?>> {

    /**
     * Returns the resolver that will be used to extract a given property resolver method parameter
     * 
     * @param parameter
     *            The parameter type being checked
     * @return the resolver for the given parameter
     */
    T extract(AnnotatedParameter<?> parameter);

} public class MessageUtils {

    private MessageUtils() {
    }

    /**
     * Builds a textual representation of an {@link AnnotatedMethod}
     * 
     * @param method
     *            The {@link AnnotatedMethod} instance
     * @return A textual representation of the {@link AnnotatedMethod}
     */
    public static String getMethodDefinition(AnnotatedMethod<?> method) {
        return method.getJavaMember().getDeclaringClass().getName() + "." + method.getJavaMember().getName() + "()";
    }

}
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;
/*
 * 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;





/**
 * 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) {