Package org.agorava.cdi.extensions

Source Code of org.agorava.cdi.extensions.AgoravaExtension

/*
* Copyright 2014 Agorava
*
* 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.agorava.cdi.extensions;

import org.agorava.AgoravaConstants;
import org.agorava.AgoravaContext;
import org.agorava.api.atinject.Current;
import org.agorava.api.atinject.Generic;
import org.agorava.api.atinject.InjectWithQualifier;
import org.agorava.api.atinject.ProviderRelated;
import org.agorava.api.exception.AgoravaException;
import org.agorava.api.oauth.OAuth;
import org.agorava.api.oauth.OAuthService;
import org.agorava.api.oauth.OAuthSession;
import org.agorava.api.oauth.application.OAuthAppSettings;
import org.agorava.api.oauth.application.OAuthAppSettingsBuilder;
import org.agorava.api.oauth.application.OAuthApplication;
import org.agorava.api.storage.GlobalRepository;
import org.agorava.cdi.CurrentLiteral;
import org.agorava.cdi.resolver.ApplicationResolver;
import org.agorava.oauth.settings.PropertyOAuthAppSettingsBuilder;
import org.agorava.spi.ProviderConfigOauth;
import org.apache.deltaspike.core.api.config.ConfigResolver;
import org.apache.deltaspike.core.api.literal.AnyLiteral;
import org.apache.deltaspike.core.util.bean.BeanBuilder;
import org.apache.deltaspike.core.util.bean.WrappingBeanBuilder;
import org.apache.deltaspike.core.util.metadata.builder.AnnotatedTypeBuilder;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.agorava.cdi.extensions.AnnotationUtils.getAnnotationsWithMeta;
import static org.agorava.cdi.extensions.AnnotationUtils.getSingleProviderRelatedQualifier;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import javax.enterprise.context.Dependent;
import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AfterDeploymentValidation;
import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.AnnotatedConstructor;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMember;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;
import javax.enterprise.inject.spi.BeforeBeanDiscovery;
import javax.enterprise.inject.spi.Extension;
import javax.enterprise.inject.spi.ProcessAnnotatedType;
import javax.enterprise.inject.spi.ProcessBean;
import javax.enterprise.inject.spi.ProcessProducer;
import javax.enterprise.inject.spi.ProcessProducerMethod;
import javax.enterprise.inject.spi.Producer;
import javax.inject.Named;

/**
* Agorava CDI extension to discover existing module and configured modules
*
* @author Antoine Sabot-Durand
*/
public class AgoravaExtension extends AgoravaContext implements Extension, Serializable {

    private static final long serialVersionUID = 1L;
    private static Logger log = Logger.getLogger(AgoravaExtension.class.getName());
    private static Bean osb;
    private Set<Annotation> providerQualifiersConfigured = new HashSet<Annotation>();
    private Map<OAuth.OAuthVersion, Class<? extends OAuthService>> version2ServiceClass = new HashMap<OAuth.OAuthVersion,
            Class<? extends OAuthService>>();
    private Map<Annotation, OAuth.OAuthVersion> providerQualifiers2Version = new HashMap<Annotation, OAuth.OAuthVersion>();
    private Set<Class<?>> genericClasses = new HashSet<Class<?>>();

    /**
     * @return the set of all service's qualifiers present in the application
     */
    public static Collection<Annotation> getServicesQualifiersAvailable() {
        return getServicesToQualifier().values();
    }

    //-------------------- Utilities -------------------------------------

    void applyQualifier(Annotation qual, AnnotatedTypeBuilder<?> atb) {
        AnnotatedType<?> at = atb.create();

        //do a loop on all field to replace annotation mark by CDI annotations
        for (AnnotatedField af : at.getFields())
            if (af.isAnnotationPresent(InjectWithQualifier.class)) {
                atb.addToField(af, InjectLiteral.instance);
                atb.addToField(af, qual);

            }

        //loop on constructors to do the same
        for (AnnotatedConstructor ac : at.getConstructors()) {
            Annotation[][] pa = ac.getJavaMember().getParameterAnnotations();
            //loop on args to detect marked param
            for (int i = 0; i < pa.length; i++)
                for (int j = 0; j < pa[i].length; j++)
                    if (pa[i][j].equals(InjectWithQualifierLiteral.instance)) {
                        atb.addToConstructor(ac, InjectLiteral.instance);
                        atb.addToConstructorParameter(ac.getJavaMember(), i, qual);
                    }

        }

        //loop on other methods (setters)
        for (AnnotatedMethod am : at.getMethods())
            if (am.isAnnotationPresent(InjectWithQualifierLiteral.class)) {
                atb.addToMethod(am, InjectLiteral.instance);
                atb.addToMethod(am, qual);
            }

    }

    //----------------- Before Bean discovery Phase ----------------------------------

    /**
     * This observer is started at the beginning of the extension
     *
     * @param bbd
     */
    public void launchExtension(@Observes BeforeBeanDiscovery bbd) {
        log.info("Starting Agorava Framework initialization");
    }

    //----------------- Process AnnotatedType Phase ----------------------------------

    public void processOauthService(@Observes ProcessAnnotatedType<? extends OAuthService> pat) {
        pat.veto();
        AnnotatedType<? extends OAuthService> at = pat.getAnnotatedType();
        Class<? extends OAuthService> clazz = (Class<? extends OAuthService>) at.getBaseType();
        OAuth qualOauth = at.getAnnotation(OAuth.class);
        if (qualOauth != null)
            version2ServiceClass.put(qualOauth.value(), clazz);
    }

    public void vetoOauthSession(@Observes ProcessAnnotatedType<OAuthSession> pat) {
        pat.veto();
    }

    public void processGenericBeanAnnotated(@Observes ProcessAnnotatedType<?> pat) {

        AnnotatedType<?> at = pat.getAnnotatedType();
        if (at.isAnnotationPresent(Generic.class) && !(at.isAnnotationPresent(OAuth.class))) {
            genericClasses.add(at.getJavaClass());
            pat.veto();
        }
    }

    //----------------- Process Producer Phase ----------------------------------

    /**
     * This observer decorates the produced {@link OAuthAppSettings} by injecting
     * its own qualifier and service name
     * and build the list of Qualifiers bearing the ProviderRelated meta annotation (configured services)
     *
     * @param pp the Process producer event
     */
    public void processOAuthSettingsProducer(@Observes final ProcessProducer<?, OAuthAppSettings> pp) {
        final AnnotatedMember<OAuthAppSettings> annotatedMember = (AnnotatedMember<OAuthAppSettings>) pp.getAnnotatedMember();

        log.log(INFO, "Qualifiers configured {0}", providerQualifiersConfigured);
        Annotation qual = null;
        try {
            qual = getSingleProviderRelatedQualifier(annotatedMember, false);
        } catch (AgoravaException e) {
            pp.addDefinitionError(new AgoravaException("OAuthAppSettings producers should be annotated with a Service " +
                    "Provider on " + annotatedMember.getJavaMember().getName() + " in " + annotatedMember.getJavaMember()
                    .getDeclaringClass()));
        }

        if (annotatedMember.isAnnotationPresent(OAuthApplication.class)) {
            if (annotatedMember instanceof AnnotatedField) {

                final OAuthApplication app = annotatedMember.getAnnotation(OAuthApplication.class);

                OAuthAppSettingsBuilder builderOAuthApp = null;
                Class<? extends OAuthAppSettingsBuilder> builderClass = app.builder();
                if (builderClass == OAuthAppSettingsBuilder.class) {
                    log.info("You didn't provide a Concrete OAuthAppSettingsBuilder class using the default " +
                            "PropertyOAuthAppSettingsBuilder class");
                    builderClass = PropertyOAuthAppSettingsBuilder.class;
                }
                try {
                    builderOAuthApp = builderClass.newInstance();
                } catch (Exception e) {
                    pp.addDefinitionError(new AgoravaException("Unable to create Settings Builder with class " +
                            builderClass, e));
                }

                builderOAuthApp.qualifier(qual)
                        .params(app.params());

                pp.setProducer(new OAuthAppSettingsProducerWithBuilder(builderOAuthApp, qual));
            } else
                pp.addDefinitionError(new AgoravaException("@OAuthApplication are only supported on Field. Agorava cannot " +
                        "process producer " + annotatedMember.getJavaMember().getName() + " in class " + annotatedMember
                        .getJavaMember().getDeclaringClass()));
        } else {
            final Producer<OAuthAppSettings> oldProducer = pp.getProducer();
            pp.setProducer(new OAuthAppSettingsProducerDecorator(oldProducer, qual));
        }

        log.log(INFO, "Found settings for {0}", qual);
        providerQualifiersConfigured.add(qual);
    }

    //----------------- Process Bean Phase ----------------------------------

    /*
     * This does practically not do much anymore after the discovery was moved
     * to AfterDeploymentValidation. see https://issues.jboss.org/browse/CDI-274
     * Kept around to do simple deployment validation of ProviderRelated qualifier.
     */
    private void CommonsProcessOAuthTier(ProcessBean<? extends ProviderConfigOauth> pb) {

        Annotated annotated = pb.getAnnotated();
        Set<Annotation> qualifiers = getAnnotationsWithMeta(annotated, ProviderRelated.class);
        if (qualifiers.size() != 1)
            throw new AgoravaException("A RemoteService bean should have one and only one service related Qualifier : " + pb
                    .getAnnotated().toString());

        Class<? extends ProviderConfigOauth> clazz = (Class<? extends ProviderConfigOauth>) pb.getBean().getBeanClass();
        try {
            providerQualifiers2Version.put(getSingleProviderRelatedQualifier(qualifiers, true),
                    clazz.newInstance().getOAuthVersion());
        } catch (Exception e) {
            throw new AgoravaException("Error while retrieving version of OAuth in tier config", e);
        }
    }

    public void processRemoteServiceRoot(@Observes ProcessBean<? extends ProviderConfigOauth> pb) {
        CommonsProcessOAuthTier(pb);
    }


    private void captureOauthSessionProducer(@Observes ProcessProducerMethod<OAuthSession, ?> pb) {
        Annotated at = pb.getAnnotated();

        Bean<?> bean = pb.getBean();

        if (bean.getQualifiers().contains(CurrentLiteral.INSTANCE)) {
            Set<Annotation> providerRelatedAnnotations = getAnnotationsWithMeta(bean.getQualifiers(), ProviderRelated.class);

            if (!providerRelatedAnnotations.isEmpty()) {
                throw new AgoravaException("OAuthSession cannot have @Current annotation and a provider " +
                        "related annotation. Error on bean " + pb.getBean().toString());
            }
            if (!pb.getAnnotated().isAnnotationPresent(Named.class)) {
                log.warning("@Current OAuthSession won't be accessible from UI");
            }
        }
        if (!pb.getAnnotated().isAnnotationPresent(Current.class)) {
            osb = pb.getBean();
        }
    }
   
    /*
    private void captureGenericOAuthProvider(@Observes ProcessBean<? extends OAuthProvider> pb) {
        Bean<? extends OAuthProvider> bean = pb.getBean();

        if (bean.getQualifiers().contains(GenericBeanLiteral.INSTANCE)) {
            for (Annotation annotation : bean.getQualifiers()) {
                if (annotation instanceof OAuth) {
                    version2ServiceClass.put(((OAuth) annotation).value(), bean);

                }
            }
        }
    }*/

    //----------------- Process After Bean Discovery Phase ----------------------------------

    private <T> void beanRegisterer(Class<T> clazz, Annotation qual, Class<? extends Annotation> scope, AfterBeanDiscovery abd,
            BeanManager beanManager, Type... types) {

        AnnotatedTypeBuilder<T> atb = new AnnotatedTypeBuilder<T>()
                .readFromType(clazz)
                .addToClass(qual)
                .setJavaClass(clazz);

        applyQualifier(qual, atb);

        BeanBuilder<T> providerBuilder = new BeanBuilder<T>(beanManager)
                .readFromType(atb.create())
                .passivationCapable(true)
                .addTypes(types);

        if (scope != null) {
            providerBuilder.scope(scope);
        }
        Bean<T> newBean = providerBuilder.create();
        abd.addBean(newBean);
    }

    /**
     * After all {@link OAuthAppSettings} were discovered we get their bean to
     * retrieve the actual name of Social Media
     * and associates it with the corresponding Qualifier
     *
     * @param abd
     * @param beanManager
     */
    public void registerGenericBeans(@Observes AfterBeanDiscovery abd, BeanManager beanManager) {

        for (Annotation qual : providerQualifiersConfigured) {
            for (Class<?> aClass : genericClasses) {
                beanRegisterer(aClass, qual, Dependent.class, abd, beanManager);

            }

            OAuth.OAuthVersion version = providerQualifiers2Version.get(qual);
            if (version == null) {
                abd.addDefinitionError(new AgoravaException("There is no OAuth version associated to provider related " +
                        "Qualifier " +
                        "" + qual));
            }

            Class clazz = version2ServiceClass.get(version);
            if (clazz == null) {
                abd.addDefinitionError(new AgoravaException("There is no OAuth Service class for OAuth version " + version));
            }

            beanRegisterer(clazz, qual, Dependent.class, abd, beanManager, OAuthService.class);
        }

        // Adding all Provider Related qualifier on Session producer
        if (osb == null) {
            abd.addDefinitionError(new AgoravaException("Application didn't provided OAuthSession bean. You should add one " +
                    "via a producer or activate automatic management by adding " + ApplicationResolver.RESOLVER +
                    "=<application|session|request|cookie> in " +
                    "agorava.properties file"));
        } else {
            WrappingBeanBuilder<OAuthSession> wbp = new WrappingBeanBuilder<OAuthSession>(osb, beanManager);
            wbp.readFromType(beanManager.createAnnotatedType(OAuthSession.class))
                    .scope(Dependent.class);
            for (Annotation qual : providerQualifiersConfigured) {
                wbp.qualifiers(qual);
                Bean res = wbp.create();
                abd.addBean(res);
            }

        }

    }

    //--------------------- After Deployment validation phase

    public void endOfExtension(@Observes AfterDeploymentValidation adv, BeanManager bm) {

        registerServiceNames(bm);

        new BeanResolverCdi();
        Bean<?> bean = bm.getBeans(GlobalRepository.class).iterator().next();

        bm.getReference(bean, GlobalRepository.class, bm.createCreationalContext(bean));
        producerScope = ConfigResolver.getPropertyValue("producerScope", "");
        internalCallBack = ConfigResolver.getPropertyValue(AgoravaConstants.INTERN_CALLBACK_PARAM);
        if (internalCallBack == null) {
            log.warning("No internal callback defined, it's defaulted to home page");
            internalCallBack = "/";
        }

        log.info("Agorava initialization complete");
    }

    private void registerServiceNames(BeanManager beanManager) {
        Set<Bean<?>> beans = beanManager.getBeans(ProviderConfigOauth.class, new AnyLiteral());

        for (Bean<?> bean : beans) {
            Annotation qual = getSingleProviderRelatedQualifier(bean.getQualifiers(), false);
            CreationalContext<?> ctx = beanManager.createCreationalContext(null);
            final ProviderConfigOauth tierConfig = (ProviderConfigOauth) beanManager.getReference(bean,
                    ProviderConfigOauth.class, ctx);
            String name = tierConfig.getProviderName();
            getServicesToQualifier().put(name, qual);
            ctx.release();
        }
        if (providerQualifiersConfigured.size() != getServicesToQualifier().size())
            log.log(WARNING, "Some Service modules present in the application are not configured so won't be available");
        //TODO:list the service without config

    }


}
TOP

Related Classes of org.agorava.cdi.extensions.AgoravaExtension

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.