/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2011, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.as.jpa.processor;

import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.PersistenceContexts;
import javax.persistence.PersistenceUnit;
import javax.persistence.PersistenceUnits;
import javax.persistence.SynchronizationType;
import javax.persistence.spi.PersistenceUnitTransactionType;

import org.jboss.as.ee.component.Attachments;
import org.jboss.as.ee.component.BindingConfiguration;
import org.jboss.as.ee.component.EEApplicationClasses;
import org.jboss.as.ee.component.EEModuleClassDescription;
import org.jboss.as.ee.component.EEModuleDescription;
import org.jboss.as.ee.component.FieldInjectionTarget;
import org.jboss.as.ee.component.InjectionSource;
import org.jboss.as.ee.component.InjectionTarget;
import org.jboss.as.ee.component.LookupInjectionSource;
import org.jboss.as.ee.component.MethodInjectionTarget;
import org.jboss.as.ee.component.ResourceInjectionConfiguration;
import org.jboss.as.ee.structure.SpecDescriptorPropertyReplacement;
import org.jboss.as.jpa.config.JPADeploymentSettings;
import org.jboss.as.jpa.container.PersistenceUnitSearch;
import org.jboss.as.jpa.injectors.PersistenceContextInjectionSource;
import org.jboss.as.jpa.injectors.PersistenceUnitInjectionSource;
import org.jboss.as.jpa.messages.JpaLogger;
import org.jboss.as.jpa.service.PersistenceUnitServiceImpl;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.as.server.deployment.DeploymentUtils;
import org.jboss.as.server.deployment.JPADeploymentMarker;
import org.jboss.as.server.deployment.annotation.CompositeIndex;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.FieldInfo;
import org.jboss.jandex.MethodInfo;
import org.jboss.msc.service.ServiceName;
import org.jipijapa.plugin.spi.PersistenceUnitMetadata;

import static org.jboss.as.jpa.messages.JpaLogger.ROOT_LOGGER;

/**
 * Handle PersistenceContext and PersistenceUnit annotations.
 *
 * @author Scott Marlow (based on ResourceInjectionAnnotationParsingProcessor)
 */
public class JPAAnnotationProcessor implements DeploymentUnitProcessor {

    private static final DotName PERSISTENCE_CONTEXT_ANNOTATION_NAME = DotName.createSimple(PersistenceContext.class.getName());
    private static final DotName PERSISTENCE_CONTEXTS_ANNOTATION_NAME =  DotName.createSimple(PersistenceContexts.class.getName());
    private static final DotName PERSISTENCE_UNIT_ANNOTATION_NAME = DotName.createSimple(PersistenceUnit.class.getName());
    private static final DotName PERSISTENCE_UNITS_ANNOTATION_NAME = DotName.createSimple(PersistenceUnits.class.getName());

    private static final String ENTITY_MANAGER_CLASS = "javax.persistence.EntityManager";
    private static final String ENTITY_MANAGERFACTORY_CLASS = "javax.persistence.EntityManagerFactory";

    @Override
    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        final EEModuleDescription eeModuleDescription = deploymentUnit.getAttachment(Attachments.EE_MODULE_DESCRIPTION);
        final CompositeIndex index = deploymentUnit.getAttachment(org.jboss.as.server.deployment.Attachments.COMPOSITE_ANNOTATION_INDEX);
        final EEApplicationClasses applicationClasses = deploymentUnit.getAttachment(Attachments.EE_APPLICATION_CLASSES_DESCRIPTION);

        // @PersistenceContext
        List<AnnotationInstance> persistenceContexts = index.getAnnotations(PERSISTENCE_CONTEXT_ANNOTATION_NAME);
        // create binding and injection configurations out of the @PersistenceContext annotations
        this.processPersistenceAnnotations(deploymentUnit, eeModuleDescription, persistenceContexts, applicationClasses);

        // @PersistenceContexts
        List<AnnotationInstance> collectionPersistenceContexts = index.getAnnotations(PERSISTENCE_CONTEXTS_ANNOTATION_NAME);
        // create binding and injection configurations out of the @PersistenceContext annotations
        processPersistenceAnnotations(deploymentUnit, eeModuleDescription, collectionPersistenceContexts, applicationClasses);

        // @PersistenceUnits
        List<AnnotationInstance> collectionPersistenceunits = index.getAnnotations(PERSISTENCE_UNITS_ANNOTATION_NAME);
        processPersistenceAnnotations(deploymentUnit, eeModuleDescription, collectionPersistenceunits, applicationClasses);

        // @PersistenceUnit
        List<AnnotationInstance> persistenceUnits = index.getAnnotations(PERSISTENCE_UNIT_ANNOTATION_NAME);
        // create binding and injection configurations out of the @PersistenceUnit annotations
        this.processPersistenceAnnotations(deploymentUnit, eeModuleDescription, persistenceUnits, applicationClasses);

        // if we found any @PersistenceContext or @PersistenceUnit annotations then mark this as a JPA deployment
        if (!persistenceContexts.isEmpty() || !persistenceUnits.isEmpty() ||
                !collectionPersistenceContexts.isEmpty() || !collectionPersistenceunits.isEmpty()) {
            JPADeploymentMarker.mark(deploymentUnit);
        }
    }

    @Override
    public void undeploy(DeploymentUnit context) {

    }

    private void processPersistenceAnnotations(final DeploymentUnit deploymentUnit, final EEModuleDescription eeModuleDescription, List<AnnotationInstance> persistenceContexts, final EEApplicationClasses applicationClasses) throws
        DeploymentUnitProcessingException {

        for (AnnotationInstance annotation : persistenceContexts) {
            ClassInfo declaringClass;
            final AnnotationTarget annotationTarget = annotation.target();
            if (annotationTarget instanceof FieldInfo) {
                FieldInfo fieldInfo = (FieldInfo) annotationTarget;
                declaringClass = fieldInfo.declaringClass();
                EEModuleClassDescription eeModuleClassDescription = eeModuleDescription.addOrGetLocalClassDescription(declaringClass.name().toString());
                this.processField(deploymentUnit, annotation, fieldInfo, eeModuleClassDescription);
            } else if (annotationTarget instanceof MethodInfo) {
                MethodInfo methodInfo = (MethodInfo) annotationTarget;
                declaringClass = methodInfo.declaringClass();
                EEModuleClassDescription eeModuleClassDescription = eeModuleDescription.addOrGetLocalClassDescription(declaringClass.name().toString());
                this.processMethod(deploymentUnit, annotation, methodInfo, eeModuleClassDescription);
            } else if (annotationTarget instanceof ClassInfo) {
                declaringClass = (ClassInfo) annotationTarget;
                EEModuleClassDescription eeModuleClassDescription = eeModuleDescription.addOrGetLocalClassDescription(declaringClass.name().toString());
                this.processClass(deploymentUnit, annotation, eeModuleClassDescription);
            }
        }
    }

    private void processField(final DeploymentUnit deploymentUnit, final AnnotationInstance annotation, final FieldInfo fieldInfo,
                              final EEModuleClassDescription eeModuleClassDescription) throws
        DeploymentUnitProcessingException {

        final String fieldName = fieldInfo.name();
        final AnnotationValue declaredNameValue = annotation.value("name");
        final String declaredName = declaredNameValue != null ? declaredNameValue.asString() : null;
        final String localContextName;
        if (declaredName == null || declaredName.isEmpty()) {
            localContextName = fieldInfo.declaringClass().name().toString() + "/" + fieldName;
        } else {
            localContextName = declaredName;
        }

        //final AnnotationValue declaredTypeValue = annotation.value("type");
        final DotName declaredTypeDotName = fieldInfo.type().name();
        final DotName injectionTypeDotName = declaredTypeDotName == null || declaredTypeDotName.toString().equals(Object.class.getName()) ? fieldInfo.type().name() : declaredTypeDotName;

        final String injectionType = injectionTypeDotName.toString();
        final InjectionSource bindingSource = this.getBindingSource(deploymentUnit, annotation, injectionType, eeModuleClassDescription);
        if (bindingSource != null) {
            final BindingConfiguration bindingConfiguration = new BindingConfiguration(localContextName, bindingSource);
            eeModuleClassDescription.getBindingConfigurations().add(bindingConfiguration);

            // setup the injection target
            final InjectionTarget injectionTarget = new FieldInjectionTarget(fieldInfo.declaringClass().name().toString(), fieldName, fieldInfo.type().name().toString());
            // source is always local ENC jndi
            final InjectionSource injectionSource = new LookupInjectionSource(localContextName);
            final ResourceInjectionConfiguration injectionConfiguration = new ResourceInjectionConfiguration(injectionTarget, injectionSource);
            eeModuleClassDescription.addResourceInjection(injectionConfiguration);
        }
    }

    private void processMethod(final DeploymentUnit deploymentUnit, final AnnotationInstance annotation, final MethodInfo methodInfo,
                               final EEModuleClassDescription eeModuleClassDescription) throws
        DeploymentUnitProcessingException {

        final String methodName = methodInfo.name();
        if (!methodName.startsWith("set") || methodInfo.args().length != 1) {
            eeModuleClassDescription.setInvalid(JpaLogger.ROOT_LOGGER.setterMethodOnlyAnnotation(annotation.name().toString(), methodInfo));
            return;
        }

        final String contextNameSuffix = methodName.substring(3, 4).toLowerCase(Locale.ENGLISH) + methodName.substring(4);
        final AnnotationValue declaredNameValue = annotation.value("name");
        final String declaredName = declaredNameValue != null ? declaredNameValue.asString() : null;
        final String localContextName;
        if (declaredName == null || declaredName.isEmpty()) {
            localContextName = methodInfo.declaringClass().name().toString() + "/" + contextNameSuffix;
        } else {
            localContextName = declaredName;
        }

        final String injectionType = methodInfo.args()[0].name().toString();
        final InjectionSource bindingSource = this.getBindingSource(deploymentUnit, annotation, injectionType, eeModuleClassDescription);
        if (bindingSource != null) {
            final BindingConfiguration bindingConfiguration = new BindingConfiguration(localContextName, bindingSource);
            eeModuleClassDescription.getBindingConfigurations().add(bindingConfiguration);

            // setup the injection configuration
            final InjectionTarget injectionTarget = new MethodInjectionTarget(methodInfo.declaringClass().name().toString(), methodName, methodInfo.args()[0].name().toString());
            // source is always local ENC jndi name
            final InjectionSource injectionSource = new LookupInjectionSource(localContextName);
            final ResourceInjectionConfiguration injectionConfiguration = new ResourceInjectionConfiguration(injectionTarget, injectionSource);

            eeModuleClassDescription.addResourceInjection(injectionConfiguration);
        }
    }

    private void processClass(final DeploymentUnit deploymentUnit, final AnnotationInstance annotation,
                              final EEModuleClassDescription eeModuleClassDescription) throws
        DeploymentUnitProcessingException {

        bindClassSources(deploymentUnit, annotation, eeModuleClassDescription);
    }

    private void bindClassSources(final DeploymentUnit deploymentUnit, final AnnotationInstance annotation, final EEModuleClassDescription classDescription)
        throws DeploymentUnitProcessingException {

        // handle PersistenceContext and PersistenceUnit annotations
        if (isPersistenceContext(annotation) ||
                isPersistenceUnit(annotation)) {
            String injectionTypeName = getClassLevelInjectionType(annotation);
            InjectionSource injectionSource = getBindingSource(deploymentUnit, annotation, injectionTypeName, classDescription);
            if (injectionSource != null) {
                final AnnotationValue nameValue = annotation.value("name");
                if (nameValue == null || nameValue.asString().isEmpty()) {
                    classDescription.setInvalid(JpaLogger.ROOT_LOGGER.classLevelAnnotationParameterRequired(annotation.name().toString(), classDescription.getClassName(), "name"));
                    return;
                }
                final String name = nameValue.asString();

                final BindingConfiguration bindingConfiguration = new BindingConfiguration(name, injectionSource);
                classDescription.getBindingConfigurations().add(bindingConfiguration);
            }
        } else if (isPersistenceUnits(annotation)) {
            // handle PersistenceUnits (array of PersistenceUnit)
            AnnotationValue containedPersistenceUnits = annotation.value("value");
            AnnotationInstance[] arrayPersistenceUnits;
            if (containedPersistenceUnits != null &&
                (arrayPersistenceUnits = containedPersistenceUnits.asNestedArray()) != null) {
                for (int source = 0; source < arrayPersistenceUnits.length; source++) {
                    String injectionTypeName = getClassLevelInjectionType(arrayPersistenceUnits[source]);
                    InjectionSource injectionSource = getBindingSource(deploymentUnit, arrayPersistenceUnits[source], injectionTypeName, classDescription);
                    if (injectionSource != null) {
                        final AnnotationValue nameValue = arrayPersistenceUnits[source].value("name");
                        if (nameValue == null || nameValue.asString().isEmpty()) {
                            classDescription.setInvalid(JpaLogger.ROOT_LOGGER.classLevelAnnotationParameterRequired(arrayPersistenceUnits[source].name().toString(), classDescription.getClassName(), "name"));
                            return;
                        }
                        final String name = nameValue.asString();

                        final BindingConfiguration bindingConfiguration = new BindingConfiguration(name, injectionSource);
                        classDescription.getBindingConfigurations().add(bindingConfiguration);
                    }
                }
            }
        } else if (isPersistenceContexts(annotation)) {
            // handle PersistenceContexts (array of PersistenceContext)
            AnnotationValue containedPersistenceContexts = annotation.value("value");
            AnnotationInstance[] arrayPersistenceContexts;
            if (containedPersistenceContexts != null &&
                (arrayPersistenceContexts = containedPersistenceContexts.asNestedArray()) != null) {
                for (int source = 0; source < arrayPersistenceContexts.length; source++) {
                    String injectionTypeName = getClassLevelInjectionType(arrayPersistenceContexts[source]);
                    InjectionSource injectionSource = getBindingSource(deploymentUnit, arrayPersistenceContexts[source], injectionTypeName, classDescription);
                    if (injectionSource != null) {
                        final AnnotationValue nameValue = arrayPersistenceContexts[source].value("name");
                        if (nameValue == null || nameValue.asString().isEmpty()) {
                            classDescription.setInvalid(JpaLogger.ROOT_LOGGER.classLevelAnnotationParameterRequired(arrayPersistenceContexts[source].name().toString(), classDescription.getClassName(), "name"));
                            return;
                        }
                        final String name = nameValue.asString();

                        final BindingConfiguration bindingConfiguration = new BindingConfiguration(name, injectionSource);
                        classDescription.getBindingConfigurations().add(bindingConfiguration);
                    }
                }
            }
        }
    }

    private InjectionSource getBindingSource(final DeploymentUnit deploymentUnit, final AnnotationInstance annotation, String injectionTypeName, final EEModuleClassDescription classDescription)
        throws DeploymentUnitProcessingException {
        PersistenceUnitMetadata pu = getPersistenceUnit(deploymentUnit, annotation, classDescription);
        if (pu == null) {
            return null;
        }
        String scopedPuName = pu.getScopedPersistenceUnitName();
        ServiceName puServiceName = getPuServiceName(scopedPuName);
        if (isPersistenceContext(annotation)) {
            if (pu.getTransactionType() == PersistenceUnitTransactionType.RESOURCE_LOCAL) {
                classDescription.setInvalid(JpaLogger.ROOT_LOGGER.cannotInjectResourceLocalEntityManager());
                return null;
            }
            AnnotationValue pcType = annotation.value("type");
            PersistenceContextType type = (pcType == null || PersistenceContextType.TRANSACTION.name().equals(pcType.asString()))
                ? PersistenceContextType.TRANSACTION : PersistenceContextType.EXTENDED;

            AnnotationValue stType = annotation.value("synchronization");
            SynchronizationType synchronizationType =
                    (stType == null || SynchronizationType.SYNCHRONIZED.name().equals(stType.asString()))?
                            SynchronizationType.SYNCHRONIZED: SynchronizationType.UNSYNCHRONIZED;

            Map<String, String> properties;
            AnnotationValue value = annotation.value("properties");
            AnnotationInstance[] props = value != null ? value.asNestedArray() : null;
            if (props != null) {
                properties = new HashMap<>();
                for (int source = 0; source < props.length; source++) {
                    properties.put(props[source].value("name").asString(), props[source].value("value").asString());
                }
            } else {
                properties = null;
            }
            // get deployment settings from top level du (jboss-all.xml is only parsed at the top level).
            final JPADeploymentSettings jpaDeploymentSettings = DeploymentUtils.getTopDeploymentUnit(deploymentUnit).getAttachment(JpaAttachments.DEPLOYMENT_SETTINGS_KEY);
            return new PersistenceContextInjectionSource(type, synchronizationType , properties, puServiceName, deploymentUnit.getServiceRegistry(), scopedPuName, injectionTypeName, pu, jpaDeploymentSettings);
        } else {
            return new PersistenceUnitInjectionSource(puServiceName, deploymentUnit.getServiceRegistry(), injectionTypeName, pu);
        }
    }

    private boolean isPersistenceContext(final AnnotationInstance annotation) {
        return annotation.name().local().equals("PersistenceContext");
    }

    private boolean isPersistenceUnit(final AnnotationInstance annotation) {
        return annotation.name().local().equals("PersistenceUnit");
    }

    private boolean isPersistenceContexts(final AnnotationInstance annotation) {
        return annotation.name().local().equals("PersistenceContexts");
    }

    private boolean isPersistenceUnits(final AnnotationInstance annotation) {
        return annotation.name().local().equals("PersistenceUnits");
    }

    /**
     * Based on the the annotation type, its either entitymanager or entitymanagerfactory
     *
     * @param annotation
     * @return
     */
    private String getClassLevelInjectionType(final AnnotationInstance annotation) {
        boolean isPC = annotation.name().local().equals("PersistenceContext");
        return isPC ? ENTITY_MANAGER_CLASS : ENTITY_MANAGERFACTORY_CLASS;
    }

    private PersistenceUnitMetadata getPersistenceUnit(final DeploymentUnit deploymentUnit, final AnnotationInstance annotation, EEModuleClassDescription classDescription)
        throws DeploymentUnitProcessingException {

        final AnnotationValue puName = annotation.value("unitName");
        String searchName = null;   // note:  a null searchName will match the first PU definition found

        if (puName != null && (searchName = puName.asString()) != null) {
            searchName = SpecDescriptorPropertyReplacement.propertyReplacer(deploymentUnit).replaceProperties(searchName);
        }
        ROOT_LOGGER.debugf("persistence unit search for unitName=%s referenced from class=%s (annotation=%s)", searchName, classDescription.getClassName(), annotation.toString());
        PersistenceUnitMetadata pu = PersistenceUnitSearch.resolvePersistenceUnitSupplier(deploymentUnit, searchName);
        if (null == pu) {
            classDescription.setInvalid(JpaLogger.ROOT_LOGGER.persistenceUnitNotFound(searchName, deploymentUnit));
            return null;
        }
        return pu;
    }

    private ServiceName getPuServiceName(String scopedPuName)
        throws DeploymentUnitProcessingException {

        return PersistenceUnitServiceImpl.getPUServiceName(scopedPuName);
    }

}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2015, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.as.webservices.deployers;

import static org.jboss.as.server.deployment.Attachments.DEPLOYMENT_ROOT;
import static org.jboss.as.webservices.util.ASHelper.getJaxwsEjbs;
import static org.jboss.as.webservices.util.ASHelper.getJaxwsPojos;
import static org.jboss.as.webservices.util.ASHelper.getOptionalAttachment;
import static org.jboss.as.webservices.util.ASHelper.isJaxwsEndpoint;
import static org.jboss.as.webservices.util.WSAttachmentKeys.WS_ENDPOINT_HANDLERS_MAPPING_KEY;

import java.util.Set;

import javax.jws.WebService;
import javax.xml.ws.WebServiceProvider;

import org.jboss.as.ee.component.ComponentDescription;
import org.jboss.as.ee.component.DeploymentDescriptorEnvironment;
import org.jboss.as.ee.component.EEModuleClassDescription;
import org.jboss.as.ee.component.EEModuleDescription;
import org.jboss.as.ee.metadata.ClassAnnotationInformation;
import org.jboss.as.ee.structure.DeploymentType;
import org.jboss.as.ee.structure.DeploymentTypeMarker;
import org.jboss.as.server.deployment.Attachments;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.annotation.CompositeIndex;
import org.jboss.as.webservices.injection.WSEndpointHandlersMapping;
import org.jboss.as.webservices.metadata.model.EJBEndpoint;
import org.jboss.as.webservices.metadata.model.POJOEndpoint;
import org.jboss.as.webservices.util.ASHelper;
import org.jboss.as.webservices.util.WSAttachmentKeys;
import org.jboss.jandex.ClassInfo;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.msc.service.ServiceName;
import org.jboss.vfs.VirtualFile;
import org.jboss.wsf.spi.metadata.config.EndpointConfig;
import org.jboss.wsf.spi.metadata.webservices.JBossWebservicesMetaData;

/**
 * @author Richard Opalka
 * @author Jim Ma
 * @author Alessio Soldano
 */
public final class WSIntegrationProcessorJAXWS_HANDLER extends AbstractIntegrationProcessorJAXWS {

    public WSIntegrationProcessorJAXWS_HANDLER() {
    }

    @Override
    protected void processAnnotation(final DeploymentUnit unit, final EEModuleDescription moduleDescription) throws DeploymentUnitProcessingException {
        final WSEndpointHandlersMapping mapping = getOptionalAttachment(unit, WS_ENDPOINT_HANDLERS_MAPPING_KEY);
        final VirtualFile root = unit.getAttachment(DEPLOYMENT_ROOT).getRoot();
        final JBossWebservicesMetaData jbossWebservicesMD = unit.getAttachment(WSAttachmentKeys.JBOSS_WEBSERVICES_METADATA_KEY);
        final CompositeIndex index = unit.getAttachment(Attachments.COMPOSITE_ANNOTATION_INDEX);
        final boolean war = DeploymentTypeMarker.isType(DeploymentType.WAR, unit);
        final JBossWebMetaData jwmd = ASHelper.getJBossWebMetaData(unit);
        for (EEModuleClassDescription classDescription : moduleDescription.getClassDescriptions()) {
            ClassInfo classInfo = null;
            ClassAnnotationInformation<WebService, WebServiceAnnotationInfo> annotationInfo = classDescription
                    .getAnnotationInformation(WebService.class);
            if (annotationInfo != null) {
                classInfo = (ClassInfo) annotationInfo.getClassLevelAnnotations().get(0).getTarget();
            }
            final ClassAnnotationInformation<WebServiceProvider, WebServiceProviderAnnotationInfo> providreInfo = classDescription
                        .getAnnotationInformation(WebServiceProvider.class);
            if (providreInfo != null) {
                classInfo = (ClassInfo) providreInfo.getClassLevelAnnotations().get(0).getTarget();
            }

            if (classInfo != null && isJaxwsEndpoint(classInfo, index, false)) {
                final String endpointClassName = classInfo.name().toString();
                final ConfigResolver configResolver = new ConfigResolver(classInfo, jbossWebservicesMD, jwmd, root, war);
                final EndpointConfig config = configResolver.resolveEndpointConfig();
                if (config != null) {
                    registerConfigMapping(endpointClassName, config, unit);
                }
                final Set<String> handlers = getHandlers(endpointClassName, config, configResolver, mapping);
                if (!handlers.isEmpty()) {
                    if (isEjb3(classInfo)) {
                        for (final EJBEndpoint ejbEndpoint : getJaxwsEjbs(unit)) {
                            if (endpointClassName.equals(ejbEndpoint.getClassName())) {
                                for (final String handlerClassName : handlers) {
                                    final String ejbEndpointName = ejbEndpoint.getName();
                                    final String handlerName = ejbEndpointName + "-" + handlerClassName;
                                    final ComponentDescription jaxwsHandlerDescription = createComponentDescription(unit,
                                            handlerName, handlerClassName, ejbEndpointName);
                                    propagateNamingContext(jaxwsHandlerDescription, ejbEndpoint);
                                }
                            }
                        }
                    } else {
                        for (final POJOEndpoint pojoEndpoint : getJaxwsPojos(unit)) {
                            if (endpointClassName.equals(pojoEndpoint.getClassName())) {
                                for (final String handlerClassName : handlers) {
                                    final String pojoEndpointName = pojoEndpoint.getName();
                                    final String handlerName = pojoEndpointName + "-" + handlerClassName;
                                    createComponentDescription(unit, handlerName, handlerClassName, pojoEndpointName);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    //TODO this could be moved to a separate DeploymentUnitProcessor operating on endpoints (together with the rest of the config resolution mechanism)
    private void registerConfigMapping(String endpointClassName, EndpointConfig config, DeploymentUnit unit) {
        WSEndpointConfigMapping mapping = unit.getAttachment(WSAttachmentKeys.WS_ENDPOINT_CONFIG_MAPPING_KEY);
        if (mapping == null) {
            mapping = new WSEndpointConfigMapping();
            unit.putAttachment(WSAttachmentKeys.WS_ENDPOINT_CONFIG_MAPPING_KEY, mapping);
        }
        mapping.registerEndpointConfig(endpointClassName, config);
    }

    private Set<String> getHandlers(String endpointClassName, EndpointConfig config, ConfigResolver resolver, WSEndpointHandlersMapping mapping) {
        Set<String> handlers = resolver.getAllHandlers(config); //handlers from the resolved endpoint configuration
        if (mapping != null) {
            Set<String> hch = mapping.getHandlers(endpointClassName); // handlers from @HandlerChain
            if (hch != null) {
                handlers.addAll(hch);
            }
        }
        return handlers;
    }

    private static void propagateNamingContext(final ComponentDescription jaxwsHandlerDescription, final EJBEndpoint ejbEndpoint) {
        final ServiceName ejbContextServiceName = ejbEndpoint.getContextServiceName();
        final DeploymentDescriptorEnvironment ejbEnv = ejbEndpoint.getDeploymentDescriptorEnvironment();
        // configure JAXWS EJB3 handler to be able to see EJB3 environment
        jaxwsHandlerDescription.setContextServiceName(ejbContextServiceName);
        jaxwsHandlerDescription.setDeploymentDescriptorEnvironment(ejbEnv);
        jaxwsHandlerDescription.addDependency(ejbContextServiceName);
    }

}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2010, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.as.weld;

import static org.jboss.as.weld.WeldResourceDefinition.REQUIRE_BEAN_DESCRIPTOR_ATTRIBUTE;

import java.util.ServiceLoader;
import java.util.function.Consumer;

import org.jboss.as.controller.AbstractBoottimeAddStepHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.controller.registry.Resource.NoSuchResourceException;
import org.jboss.as.server.AbstractDeploymentChainStep;
import org.jboss.as.server.DeploymentProcessorTarget;
import org.jboss.as.server.deployment.Phase;
import org.jboss.as.server.deployment.jbossallxml.JBossAllXmlParserRegisteringProcessor;
import org.jboss.as.weld.deployment.CdiAnnotationProcessor;
import org.jboss.as.weld.deployment.processors.BeanArchiveProcessor;
import org.jboss.as.weld.deployment.processors.BeanDefiningAnnotationProcessor;
import org.jboss.as.weld.deployment.processors.BeansXmlProcessor;
import org.jboss.as.weld.deployment.processors.DevelopmentModeProcessor;
import org.jboss.as.weld.deployment.processors.ExternalBeanArchiveProcessor;
import org.jboss.as.weld.deployment.processors.WebIntegrationProcessor;
import org.jboss.as.weld.deployment.processors.WeldBeanManagerServiceProcessor;
import org.jboss.as.weld.deployment.processors.WeldComponentIntegrationProcessor;
import org.jboss.as.weld.deployment.processors.WeldConfigurationProcessor;
import org.jboss.as.weld.deployment.processors.WeldDependencyProcessor;
import org.jboss.as.weld.deployment.processors.WeldDeploymentCleanupProcessor;
import org.jboss.as.weld.deployment.processors.WeldDeploymentProcessor;
import org.jboss.as.weld.deployment.processors.WeldImplicitDeploymentProcessor;
import org.jboss.as.weld.deployment.processors.WeldPortableExtensionProcessor;
import org.jboss.as.weld.services.TCCLSingletonService;
import org.jboss.as.weld.services.bootstrap.WeldExecutorServices;
import org.jboss.as.weld.spi.DeploymentUnitProcessorProvider;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceController.Mode;
import org.jboss.weld.manager.api.ExecutorServices;
import org.wildfly.security.manager.WildFlySecurityManager;

/**
 * The weld subsystem add update handler.
 *
 * @author Stuart Douglas
 * @author Emanuel Muckenhuber
 * @author Tomasz Adamski
 */
class WeldSubsystemAdd extends AbstractBoottimeAddStepHandler {

    static final WeldSubsystemAdd INSTANCE = new WeldSubsystemAdd();

    @Override
    protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
        WeldResourceDefinition.REQUIRE_BEAN_DESCRIPTOR_ATTRIBUTE.validateAndSet(operation, model);
        WeldResourceDefinition.NON_PORTABLE_MODE_ATTRIBUTE.validateAndSet(operation, model);
        WeldResourceDefinition.DEVELOPMENT_MODE_ATTRIBUTE.validateAndSet(operation, model);
        WeldResourceDefinition.THREAD_POOL_SIZE_ATTRIBUTE.validateAndSet(operation, model);
    }

    @Override
    protected void performBoottime(final OperationContext context, ModelNode operation,Resource resource) throws OperationFailedException {

        final ModelNode model = resource.getModel();
        final boolean requireBeanDescriptor = REQUIRE_BEAN_DESCRIPTOR_ATTRIBUTE.resolveModelAttribute(context, model).asBoolean();
        final boolean nonPortableMode = WeldResourceDefinition.NON_PORTABLE_MODE_ATTRIBUTE.resolveModelAttribute(context, model).asBoolean();
        final boolean developmentMode = WeldResourceDefinition.DEVELOPMENT_MODE_ATTRIBUTE.resolveModelAttribute(context, model).asBoolean();
        final int threadPoolSize = WeldResourceDefinition.THREAD_POOL_SIZE_ATTRIBUTE.resolveModelAttribute(context, model)
                .asInt(WeldExecutorServices.DEFAULT_BOUND);

        context.addStep(new AbstractDeploymentChainStep() {
            @Override
            protected void execute(DeploymentProcessorTarget processorTarget) {
                final JBossAllXmlParserRegisteringProcessor<?> jbossAllParsers = JBossAllXmlParserRegisteringProcessor.builder()
                    .addParser(WeldJBossAll10Parser.ROOT_ELEMENT, WeldJBossAllConfiguration.ATTACHMENT_KEY, WeldJBossAll10Parser.INSTANCE)
                    .addParser(WeldJBossAll11Parser.ROOT_ELEMENT, WeldJBossAllConfiguration.ATTACHMENT_KEY, WeldJBossAll11Parser.INSTANCE)
                    .build();
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.STRUCTURE, Phase.STRUCTURE_REGISTER_JBOSS_ALL_WELD, jbossAllParsers);
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.PARSE, Phase.PARSE_WELD_CONFIGURATION, new WeldConfigurationProcessor(requireBeanDescriptor, nonPortableMode, developmentMode));
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.PARSE, Phase.PARSE_CDI_ANNOTATIONS, new CdiAnnotationProcessor());
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.PARSE, Phase.PARSE_CDI_BEAN_DEFINING_ANNOTATIONS, new BeanDefiningAnnotationProcessor());
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.PARSE, Phase.PARSE_WELD_DEPLOYMENT, new BeansXmlProcessor());
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.PARSE, Phase.PARSE_WELD_IMPLICIT_DEPLOYMENT_DETECTION, new WeldImplicitDeploymentProcessor());
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.DEPENDENCIES, Phase.DEPENDENCIES_WELD, new WeldDependencyProcessor());
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.POST_MODULE, Phase.POST_MODULE_WELD_WEB_INTEGRATION, new WebIntegrationProcessor());
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.POST_MODULE, Phase.POST_MODULE_WELD_DEVELOPMENT_MODE, new DevelopmentModeProcessor());
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.POST_MODULE, Phase.POST_MODULE_WELD_BEAN_ARCHIVE, new BeanArchiveProcessor());
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.POST_MODULE, Phase.POST_MODULE_WELD_EXTERNAL_BEAN_ARCHIVE, new ExternalBeanArchiveProcessor());
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.POST_MODULE, Phase.POST_MODULE_WELD_PORTABLE_EXTENSIONS, new WeldPortableExtensionProcessor());
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.POST_MODULE, Phase.POST_MODULE_WELD_COMPONENT_INTEGRATION, new WeldComponentIntegrationProcessor());
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.INSTALL, Phase.INSTALL_WELD_DEPLOYMENT, new WeldDeploymentProcessor(checkJtsEnabled(context)));
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.INSTALL, Phase.INSTALL_WELD_BEAN_MANAGER, new WeldBeanManagerServiceProcessor());
                // note that we want to go one step before Phase.CLEANUP_EE because we use metadata that it then cleans up
                processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, Phase.CLEANUP, Phase.CLEANUP_EE - 1, new WeldDeploymentCleanupProcessor());

                // Add additional deployment processors
                ServiceLoader<DeploymentUnitProcessorProvider> processorProviders = ServiceLoader.load(DeploymentUnitProcessorProvider.class,
                        WildFlySecurityManager.getClassLoaderPrivileged(WeldSubsystemAdd.class));
                for (DeploymentUnitProcessorProvider provider : processorProviders) {
                    processorTarget.addDeploymentProcessor(WeldExtension.SUBSYSTEM_NAME, provider.getPhase(), provider.getPriority(), provider.getProcessor());
                }
            }
        }, OperationContext.Stage.RUNTIME);

        context.getServiceTarget().addService(TCCLSingletonService.SERVICE_NAME).setInstance(new TCCLSingletonService()).setInitialMode(Mode.ON_DEMAND).install();

        ServiceBuilder<?> builder = context.getServiceTarget().addService(WeldExecutorServices.SERVICE_NAME);
        final Consumer<ExecutorServices> executorServicesConsumer = builder.provides(WeldExecutorServices.SERVICE_NAME);
        builder.setInstance(new WeldExecutorServices(executorServicesConsumer, threadPoolSize));
        builder.setInitialMode(Mode.ON_DEMAND);
        builder.install();
    }

    // Synchronization objects created by iiop ejb beans require wrapping by JTSSychronizationWrapper to work correctly
    // (WFLY-3538). This hack is used obtain jts configuration in order to avoid doing this in non-jts environments when it is
    // not necessary.
    private boolean checkJtsEnabled(final OperationContext context) {
        try {
            final ModelNode jtsNode = context.readResourceFromRoot(PathAddress.pathAddress("subsystem", "transactions"), false)
                    .getModel().get("jts");
            return jtsNode.isDefined() ? jtsNode.asBoolean() : false;
        } catch (NoSuchResourceException ex) {
            return false;
        }
    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2011, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.as.naming.service;

import javax.naming.NamingException;

import org.jboss.as.naming.ServiceBasedNamingStore;
import org.jboss.as.naming.WritableServiceBasedNamingStore;
import org.jboss.as.naming.logging.NamingLogger;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceRegistry;
import org.jboss.msc.service.ServiceTarget;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;

/**
 * Service responsible for managing the creation and life-cycle of a service based naming store.
 *
 * @author John E. Bailey
 * @author Stuart Douglas
 * @author Eduardo Martins
 */
public class NamingStoreService implements Service<ServiceBasedNamingStore> {

    private final boolean readOnly;
    private volatile ServiceBasedNamingStore store;

    public NamingStoreService() {
        this(false);
    }

    public NamingStoreService(boolean readOnly) {
        this.readOnly = readOnly;
    }

    /**
     * Creates the naming store if not provided by the constructor.
     *
     * @param context The start context
     * @throws StartException If any problems occur creating the context
     */
    public void start(final StartContext context) throws StartException {
        if(store == null) {
            final ServiceRegistry serviceRegistry = context.getController().getServiceContainer();
            final ServiceName serviceNameBase = context.getController().getName();
            final ServiceTarget serviceTarget = context.getChildTarget();
            store = readOnly ? new ServiceBasedNamingStore(serviceRegistry, serviceNameBase) : new WritableServiceBasedNamingStore(serviceRegistry, serviceNameBase, serviceTarget);
        }
    }

    /**
     * Destroys the naming store.
     *
     * @param context The stop context
     */
    public void stop(StopContext context) {
        if(store != null) {
            try {
                store.close();
                store = null;
            } catch (NamingException e) {
                throw NamingLogger.ROOT_LOGGER.failedToDestroyRootContext(e);
            }
        }
    }

    /**
     * Get the context value.
     *
     * @return The naming store
     * @throws IllegalStateException
     */
    public ServiceBasedNamingStore getValue() throws IllegalStateException {
        return store;
    }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the @authors tag. All rights reserved.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU Lesser General Public License, v. 2.1.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License,
 * v.2.1 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 */
package org.jboss.as.webservices.dmr;

import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.operations.validation.ModelTypeValidator;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
import org.jboss.ws.common.Messages;
import org.jboss.ws.common.utils.AddressUtils;

/**
 * Validates addresses using the JBossWS common address utilities
 *
 * @author Alessio Soldano
 */
public class AddressValidator extends ModelTypeValidator {

    public AddressValidator(final boolean nullable, final boolean allowExpressions) {
        super(ModelType.STRING, nullable, allowExpressions, true);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void validateParameter(String parameterName, ModelNode value) throws OperationFailedException {
        super.validateParameter(parameterName, value);
        if (value.isDefined() && value.getType() != ModelType.EXPRESSION) {
            String address = value.asString();
            if (address.startsWith("[") && address.endsWith("]")) {
                address = address.substring(1, address.length() - 1);
            }
            if (!AddressUtils.isValidAddress(address)) {
                throw new OperationFailedException(Messages.MESSAGES.invalidAddressProvided(address));
            }
        }
    }

}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2010, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.as.security.deployment;

import org.jboss.as.security.ModuleName;
import org.jboss.as.security.remoting.RemotingLoginModule;
import org.jboss.as.server.deployment.Attachments;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.as.server.deployment.module.ModuleDependency;
import org.jboss.as.server.deployment.module.ModuleSpecification;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoader;
import org.jboss.modules.filter.PathFilters;

/**
 * Adds a security subsystem dependency to deployments
 *
 * @author Anil.Saldhana@redhat.com
 * @since Jan 14, 2011
 */
public class SecurityDependencyProcessor implements DeploymentUnitProcessor {

    public static final ModuleIdentifier PICKETBOX_ID = ModuleIdentifier.create(ModuleName.PICKETBOX.getName(),
            ModuleName.PICKETBOX.getSlot());
    public static final ModuleIdentifier REMOTING_LOGIN_MODULE = ModuleIdentifier.create("org.jboss.as.security");

    public static final ModuleIdentifier AUTH_MESSAGE_API = ModuleIdentifier.create("javax.security.auth.message.api");
    public static final ModuleIdentifier JACC_API = ModuleIdentifier.create("javax.security.jacc.api");

    /** {@inheritDoc} */
    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        final ModuleLoader moduleLoader = Module.getBootModuleLoader();
        final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, PICKETBOX_ID, false, false, false, false));

        //add the remoting login module
        final ModuleDependency remoting = new ModuleDependency(moduleLoader, REMOTING_LOGIN_MODULE, false, false, false, false);
        remoting.addImportFilter(PathFilters.is(RemotingLoginModule.class.getName().replace(".","/")), true);
        moduleSpecification.addSystemDependency(remoting);

        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, JACC_API, false, false, true, false));
        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, AUTH_MESSAGE_API, false, false, true, false));
    }

    /** {@inheritDoc} */
    public void undeploy(DeploymentUnit context) {
    }

}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2012, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.wildfly.extension.picketlink.federation.service;

import org.jboss.as.controller.OperationContext;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.web.common.WarMetaData;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceRegistry;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedValue;
import org.picketlink.common.constants.GeneralConstants;
import org.picketlink.common.constants.JBossSAMLURIConstants;
import org.picketlink.common.exceptions.ConfigurationException;
import org.picketlink.config.federation.KeyValueType;
import org.picketlink.config.federation.PicketLinkType;
import org.picketlink.config.federation.ProviderType;
import org.picketlink.config.federation.STSType;
import org.picketlink.config.federation.TokenProviderType;
import org.picketlink.config.federation.handler.Handler;
import org.picketlink.config.federation.handler.Handlers;
import org.picketlink.config.federation.parsers.STSConfigParser;
import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2Handler;
import org.picketlink.identity.federation.web.handlers.saml2.RolesGenerationHandler;
import org.picketlink.identity.federation.web.handlers.saml2.SAML2AuthenticationHandler;
import org.picketlink.identity.federation.web.handlers.saml2.SAML2EncryptionHandler;
import org.picketlink.identity.federation.web.handlers.saml2.SAML2LogOutHandler;
import org.picketlink.identity.federation.web.handlers.saml2.SAML2SignatureValidationHandler;
import org.wildfly.extension.picketlink.federation.config.ProviderConfiguration;
import org.wildfly.extension.picketlink.federation.metrics.PicketLinkSubsystemMetrics;
import org.wildfly.extension.picketlink.logging.PicketLinkLogger;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import static org.wildfly.extension.picketlink.logging.PicketLinkLogger.ROOT_LOGGER;

/**
 * @author Pedro Silva
 */
public abstract class EntityProviderService<T extends PicketLinkFederationService<T>, C extends ProviderConfiguration> implements PicketLinkFederationService<T> {

    private final InjectedValue<FederationService> federationService = new InjectedValue<FederationService>();
    private final PicketLinkType picketLinkType;
    private volatile PicketLinkSubsystemMetrics metrics;
    private final Set<Handler> handlers = new LinkedHashSet<Handler>();

    public EntityProviderService(C configuration) {
        this.picketLinkType = createPicketLinkType(configuration);
    }

    @Override
    public void start(StartContext context) throws StartException {
        ROOT_LOGGER.debugf("Starting service for %s.", getConfiguration().getAlias());
    }

    @Override
    public void stop(StopContext context) {
        ROOT_LOGGER.debugf("Stopping service for %s.", getConfiguration().getAlias());
        context.getController().setMode(ServiceController.Mode.REMOVE);
    }

    @Override
    public void configure(DeploymentUnit deploymentUnit) {
        configureHandlers();
        configureWarMetadata(deploymentUnit);
        configureWebComponents(deploymentUnit);
        doConfigureDeployment(deploymentUnit);
        configureTokenProviders();
        configureKeyProvider();
    }

    @Override
    public PicketLinkSubsystemMetrics getMetrics() {
        if (this.metrics == null) {
            synchronized (this) {
                if (this.metrics == null) {
                    try {
                        this.metrics = new PicketLinkSubsystemMetrics(((ProviderConfiguration) getPicketLinkType().getIdpOrSP()).getSecurityDomain());
                    } catch (ConfigurationException e) {
                        ROOT_LOGGER.federationErrorCollectingMetric(e);
                    }
                }
            }
        }

        return this.metrics;
    }

    protected List<Class<? extends SAML2Handler>> getDefaultHandlers() {
        List<Class<? extends SAML2Handler>> defaultHandlers = new ArrayList<Class<? extends SAML2Handler>>();

        defaultHandlers.add(SAML2LogOutHandler.class);
        defaultHandlers.add(SAML2AuthenticationHandler.class);
        defaultHandlers.add(RolesGenerationHandler.class);
        defaultHandlers.add(SAML2EncryptionHandler.class);
        defaultHandlers.add(SAML2SignatureValidationHandler.class);

        return defaultHandlers;
    }

    private void configureKeyProvider() {
        getConfiguration().setKeyProvider(getFederationService().getValue().getKeyProviderType());
    }

    /**
     *
 Configure the STS Token Providers.

     */
    private void configureTokenProviders() {
        STSType stsType = getFederationService().getValue().getStsType();

        if (stsType != null) {
            int tokenTimeout = stsType.getTokenTimeout();
            int clockSkew = stsType.getClockSkew();

            STSType providerStsType = getPicketLinkType().getStsType();

            providerStsType.setTokenTimeout(tokenTimeout);
            providerStsType.setClockSkew(clockSkew);

            List<TokenProviderType> tokenProviders = providerStsType.getTokenProviders().getTokenProvider();

            for (TokenProviderType tokenProviderType : tokenProviders) {
                if (tokenProviderType.getTokenType().equals(JBossSAMLURIConstants.ASSERTION_NSURI.get())) {
                    KeyValueType keyValueTypeTokenTimeout = new KeyValueType();

                    keyValueTypeTokenTimeout.setKey(GeneralConstants.ASSERTIONS_VALIDITY);
                    keyValueTypeTokenTimeout.setValue(String.valueOf(tokenTimeout));

                    KeyValueType keyValueTypeClockSkew = new KeyValueType();

                    keyValueTypeClockSkew.setKey(GeneralConstants.CLOCK_SKEW);
                    keyValueTypeClockSkew.setValue(String.valueOf(clockSkew));

                    tokenProviderType.add(keyValueTypeTokenTimeout);
                    tokenProviderType.add(keyValueTypeClockSkew);
                }
            }
        }
    }

    /**
     *
 Configure the SAML Handlers.

     */
    private void configureHandlers() {
        Handlers actualHandlers = new Handlers();

        actualHandlers.setHandlers(new ArrayList<Handler>());

        if (this.handlers.isEmpty()) {
            for (Class<? extends SAML2Handler> commonHandlerClass : getDefaultHandlers()) {
                addHandler(commonHandlerClass, actualHandlers);
            }
        } else {
            for (Handler handler : this.handlers) {
                actualHandlers.add(handler);
            }
        }

        getPicketLinkType().setHandlers(actualHandlers);
    }

    private void configureWarMetadata(DeploymentUnit deploymentUnit) {
        WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);

        warMetaData.getMergedJBossWebMetaData().setSecurityDomain(this.getConfiguration().getSecurityDomain());
    }

    protected abstract void configureWebComponents(DeploymentUnit deploymentUnit);

    /**
     *
 Subclasses should implement this method to configureDeployment a specific PicketLink Provider type. Eg.: Identity Provider or Service
     * Provider.

     *
     * @param deploymentUnit
     */
    protected abstract void doConfigureDeployment(DeploymentUnit deploymentUnit);

    @SuppressWarnings("unchecked")
    @Override
    public T getValue() throws IllegalStateException, IllegalArgumentException {
        return (T) this;
    }

    private PicketLinkType createPicketLinkType(C configuration) {
        PicketLinkType picketLinkType = new PicketLinkType();

        picketLinkType.setStsType(createSTSType());
        picketLinkType.setHandlers(new Handlers());
        picketLinkType.setEnableAudit(true);

        picketLinkType.setIdpOrSP((ProviderType) configuration);

        return picketLinkType;
    }

    public void addHandler(final Handler handler) {
        for (Handler actualHandler : new ArrayList<Handler>(this.handlers)) {
            if (actualHandler.getClazz().equals(handler.getClazz())) {
                return;
            }
        }

        this.handlers.add(handler);
    }

    void addHandler(Class<? extends SAML2Handler> handlerClassName, Handlers handlers) {
        for (Handler handler : handlers.getHandler()) {
            if (handler.getClazz().equals(handlerClassName.getName())) {
                return;
            }
        }

        Handler handler = new Handler();

        handler.setClazz(handlerClassName.getName());

        handlers.add(handler);
    }

    public void removeHandler(String handlerType) {
        for (Handler handler : new ArrayList<Handler>(this.handlers)) {
            if (handler.getClazz().equals(handlerType)) {
                this.handlers.remove(handler);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public C getConfiguration() {
        return (C) getPicketLinkType().getIdpOrSP();
    }

    private STSType createSTSType() {
        STSType stsType = null;

        InputStream stream = null;

        try {
            URL url = getClass().getClassLoader().getResource("core-sts.xml");

            if (url == null) {
                url = Thread.currentThread().getContextClassLoader().getResource("core-sts");
            }

            if (url != null) {
                stream = url.openStream();
                stsType = (STSType) new STSConfigParser().parse(stream);
            }
        } catch (Exception e) {
            throw PicketLinkLogger.ROOT_LOGGER.federationCouldNotParseSTSConfig(e);
        } finally {
            try {
                if (stream != null) {
                    stream.close();
                }
            } catch (IOException ignored) {
            }
        }

        return stsType;
    }

    public InjectedValue<FederationService> getFederationService() {
        return this.federationService;
    }

    PicketLinkType getPicketLinkType() {
        return this.picketLinkType;
    }

    public void setConfiguration(C configuration) {
        this.picketLinkType.setIdpOrSP((ProviderType) configuration);
    }

    public void removeHandlerParameter(String handlerType, String handlerParameterName) {
        for (Handler handler : this.handlers) {
            if (handler.getClazz().equals(handlerType)) {
                for (KeyValueType kv : new ArrayList<KeyValueType>(handler.getOption())) {
                    if (kv.getKey().equals(handlerParameterName)) {
                        handler.remove(kv);
                    }
                }
            }
        }
    }

    public void addHandlerParameter(String handlerType, KeyValueType keyValueType) {
        removeHandlerParameter(handlerType, keyValueType.getKey());

        for (Handler handler : this.handlers) {
            if (handler.getClazz().equals(handlerType)) {
                handler.add(keyValueType);
            }
        }
    }

    public static EntityProviderService getService(OperationContext context, String alias) {
        // We assume the mgmt ops that trigger IdentityProviderAddHandler or ServiceProviderAddHandler
        // run before the OperationStepHandler that triggers deploy. If not, that's a user mistake.
        // Since those handlers run first, we can count on MSC having services *registered* even
        // though we cannot count on them being *started*.
        ServiceRegistry serviceRegistry = context.getServiceRegistry(false);
        ServiceController<EntityProviderService> service = (ServiceController<EntityProviderService>) serviceRegistry.getService(IdentityProviderService.createServiceName(alias));

        if (service == null) {
            service = (ServiceController<EntityProviderService>) serviceRegistry.getService(ServiceProviderService.createServiceName(alias));
        }

        if (service == null) {
            return null;
        }

        return service.getValue();
    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2012, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.as.ejb3.component.allowedmethods;

import org.jboss.as.ee.component.Component;
import org.jboss.as.ee.component.interceptors.InvocationType;
import org.jboss.as.ejb3.logging.EjbLogger;
import org.jboss.as.ejb3.component.EJBComponent;
import org.jboss.as.ejb3.component.stateful.CurrentSynchronizationCallback;
import org.jboss.as.ejb3.context.CurrentInvocationContext;
import org.jboss.invocation.InterceptorContext;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * This class and its subclasses can be used to determine if a given method
 * is allowed to be invoked.
 *
 * @see CurrentInvocationContext
 * @see CurrentSynchronizationCallback
 *
 * @author Stuart Douglas
 */
public class AllowedMethodsInformation {

    public static final AllowedMethodsInformation INSTANCE_BMT = new AllowedMethodsInformation(true);
    public static final AllowedMethodsInformation INSTANCE_CMT = new AllowedMethodsInformation(false);

    private final Set<DeniedMethodKey> denied;
    private final Set<DeniedSyncMethodKey> deniedSyncMethods;
    private final boolean beanManagedTransaction;

    protected AllowedMethodsInformation(boolean beanManagedTransaction) {
        this.beanManagedTransaction = beanManagedTransaction;
        final Set<DeniedMethodKey> denied = new HashSet<DeniedMethodKey>();
        add(denied, InvocationType.SET_ENTITY_CONTEXT, MethodType.TIMER_SERVICE_METHOD);
        add(denied, InvocationType.SET_ENTITY_CONTEXT, MethodType.TIMER_SERVICE_METHOD);
        add(denied, InvocationType.SET_ENTITY_CONTEXT, MethodType.GET_PRIMARY_KEY);
        add(denied, InvocationType.SET_ENTITY_CONTEXT, MethodType.GET_TIMER_SERVICE);
        add(denied, InvocationType.SET_ENTITY_CONTEXT, MethodType.IS_CALLER_IN_ROLE);
        add(denied, InvocationType.SET_ENTITY_CONTEXT, MethodType.GET_CALLER_PRINCIPLE);

        add(denied, InvocationType.HOME_METHOD, MethodType.TIMER_SERVICE_METHOD);
        add(denied, InvocationType.HOME_METHOD, MethodType.GET_PRIMARY_KEY);

        add(denied, InvocationType.ENTITY_EJB_CREATE, MethodType.TIMER_SERVICE_METHOD);
        add(denied, InvocationType.ENTITY_EJB_CREATE, MethodType.GET_PRIMARY_KEY);

        setup(denied);
        this.denied = Collections.unmodifiableSet(denied);

        final Set<DeniedSyncMethodKey> deniedSync = new HashSet<DeniedSyncMethodKey>();
        add(deniedSync, CurrentSynchronizationCallback.CallbackType.AFTER_COMPLETION, MethodType.TIMER_SERVICE_METHOD);
        add(deniedSync, CurrentSynchronizationCallback.CallbackType.AFTER_COMPLETION, MethodType.GET_ROLLBACK_ONLY);
        add(deniedSync, CurrentSynchronizationCallback.CallbackType.AFTER_COMPLETION, MethodType.SET_ROLLBACK_ONLY);

        this.deniedSyncMethods = Collections.unmodifiableSet(deniedSync);

    }

    protected void setup(Set<DeniedMethodKey> denied) {

    }

    protected static void add(Set<DeniedMethodKey> otherDenied, InvocationType setEntityContext, MethodType timerServiceMethod) {
        otherDenied.add(new DeniedMethodKey(setEntityContext, timerServiceMethod));
    }

    protected static void add(Set<DeniedSyncMethodKey> otherDenied, CurrentSynchronizationCallback.CallbackType callbackType, MethodType timerServiceMethod) {
        otherDenied.add(new DeniedSyncMethodKey(callbackType, timerServiceMethod));
    }

    /**
     * Checks that the current method
     */
    public static void checkAllowed(final MethodType methodType) {

        final InterceptorContext context = CurrentInvocationContext.get();
        if (context == null) {
            return;
        }

        final Component component = context.getPrivateData(Component.class);
        if (!(component instanceof EJBComponent)) {
            return;
        }
        final InvocationType invocationType = context.getPrivateData(InvocationType.class);

        ((EJBComponent) component).getAllowedMethodsInformation().realCheckPermission(methodType, invocationType);

    }

    /**
     * transaction sync is not affected by the current invocation, as multiple ejb methods may be invoked from afterCompletion
     */
    private void checkTransactionSync(MethodType methodType) {
        //first we have to check the synchronization status
        //as the sync is not affected by the current invocation
        final CurrentSynchronizationCallback.CallbackType currentSync = CurrentSynchronizationCallback.get();
        if (currentSync != null) {
            if (deniedSyncMethods.contains(new DeniedSyncMethodKey(currentSync, methodType))) {
                throwException(methodType, currentSync);
            }
        }
    }

    protected void realCheckPermission(MethodType methodType, InvocationType invocationType) {
        checkTransactionSync(methodType);
        if (invocationType != null) {
            if (denied.contains(new DeniedMethodKey(invocationType, methodType))) {
                throwException(methodType, invocationType);
            }
        }
        if (invocationType != InvocationType.CONCURRENT_CONTEXT && !beanManagedTransaction && methodType == MethodType.GET_USER_TRANSACTION) {
            throw EjbLogger.ROOT_LOGGER.unauthorizedAccessToUserTransaction();
        }
    }

    /**
     * throw an exception when a method cannot be invoked
     *
     * @param methodType     the method
     * @param invocationType the type of invocation that caused it to be disabled
     */
    protected void throwException(MethodType methodType, InvocationType invocationType) {
        throw EjbLogger.ROOT_LOGGER.cannotCallMethod(methodType.getLabel(), invocationType.getLabel());
    }

    /**
     * throw an exception when a method cannot be invoked
     *
     * @param methodType the method
     * @param callback   the type of invocation that caused it to be disabled
     */
    protected void throwException(MethodType methodType, CurrentSynchronizationCallback.CallbackType callback) {
        throw EjbLogger.ROOT_LOGGER.cannotCallMethod(methodType.getLabel(), callback.name());
    }

    private static class DeniedSyncMethodKey {
        private final CurrentSynchronizationCallback.CallbackType callbackType;
        private final MethodType methodType;

        public DeniedSyncMethodKey(CurrentSynchronizationCallback.CallbackType callbackType, MethodType methodType) {
            this.callbackType = callbackType;
            this.methodType = methodType;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            DeniedSyncMethodKey that = (DeniedSyncMethodKey) o;

            if (callbackType != that.callbackType) return false;
            if (methodType != that.methodType) return false;

            return true;
        }

        @Override
        public int hashCode() {
            int result = callbackType != null ? callbackType.hashCode() : 0;
            result = 31 * result + (methodType != null ? methodType.hashCode() : 0);
            return result;
        }
    }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2010, Red Hat Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.jboss.as.ejb3.deployment.processors.merging;

import java.util.List;

import org.jboss.as.ee.component.EEApplicationClasses;
import org.jboss.as.ee.component.EEModuleClassDescription;
import org.jboss.as.ee.metadata.ClassAnnotationInformation;
import org.jboss.as.ee.structure.Attachments;
import org.jboss.as.ejb3.component.EJBComponentDescription;
import org.jboss.as.ejb3.deployment.EjbDeploymentAttachmentKeys;
import org.jboss.as.ejb3.security.metadata.EJBBoundSecurityMetaData;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.reflect.DeploymentReflectionIndex;
import org.jboss.ejb3.annotation.SecurityDomain;
import org.jboss.metadata.ear.jboss.JBossAppMetaData;
import org.jboss.metadata.ear.spec.EarMetaData;
import org.jboss.metadata.ejb.spec.AssemblyDescriptorMetaData;
import org.jboss.metadata.ejb.spec.EjbJarMetaData;

import static org.jboss.as.ejb3.logging.EjbLogger.ROOT_LOGGER;

/**
 * @author Stuart Douglas
 */
public class SecurityDomainMergingProcessor extends AbstractMergingProcessor<EJBComponentDescription> {

    public SecurityDomainMergingProcessor() {
        super(EJBComponentDescription.class);
    }

    @Override
    protected void handleAnnotations(final DeploymentUnit deploymentUnit, final EEApplicationClasses applicationClasses, final DeploymentReflectionIndex deploymentReflectionIndex, final Class<?> componentClass, final EJBComponentDescription description) throws DeploymentUnitProcessingException {
        final EEModuleClassDescription clazz = applicationClasses.getClassByName(componentClass.getName());
        //we only care about annotations on the bean class itself
        if (clazz == null) {
            return;
        }
        final ClassAnnotationInformation<SecurityDomain, String> securityDomain = clazz.getAnnotationInformation(SecurityDomain.class);
        if (securityDomain == null) {
            return;
        }
        if (!securityDomain.getClassLevelAnnotations().isEmpty()) {
            if (ROOT_LOGGER.isDebugEnabled()) {
                ROOT_LOGGER.debug("EJB " + description.getEJBName() + " is part of security domain " + securityDomain.getClassLevelAnnotations().get(0));
            }
            description.setSecurityDomain(securityDomain.getClassLevelAnnotations().get(0));
        }
    }

    @Override
    protected void handleDeploymentDescriptor(final DeploymentUnit deploymentUnit, final DeploymentReflectionIndex deploymentReflectionIndex, final Class<?> componentClass, final EJBComponentDescription description) throws DeploymentUnitProcessingException {
        String securityDomain = getJBossAppSecurityDomain(deploymentUnit);
        String globalSecurityDomain = null;
        final EjbJarMetaData ejbJarMetaData = deploymentUnit.getAttachment(EjbDeploymentAttachmentKeys.EJB_JAR_METADATA);
        if (ejbJarMetaData != null) {
            final AssemblyDescriptorMetaData assemblyMetadata = ejbJarMetaData.getAssemblyDescriptor();
            if (assemblyMetadata != null) {
                final List<EJBBoundSecurityMetaData> securityMetaDatas = assemblyMetadata.getAny(EJBBoundSecurityMetaData.class);
                if (securityMetaDatas != null) {
                    for (final EJBBoundSecurityMetaData securityMetaData : securityMetaDatas) {
                        if (securityMetaData.getEjbName().equals(description.getComponentName())) {
                            securityDomain = securityMetaData.getSecurityDomain();
                            break;
                        }
                        // check global security domain
                        if (securityMetaData.getEjbName().equals("*")) {
                            globalSecurityDomain = securityMetaData.getSecurityDomain();
                            continue;
                        }
                    }
                }
            }
        }
        if (securityDomain != null)
            description.setSecurityDomain(securityDomain);
        else if (globalSecurityDomain != null)
            description.setSecurityDomain(globalSecurityDomain);
    }

    /**
     * Try to obtain the security domain configured in jboss-app.xml at the ear level if available
     *
     * @param deploymentUnit
     * @return
     */
    private String getJBossAppSecurityDomain(final DeploymentUnit deploymentUnit) {
        String securityDomain = null;
        DeploymentUnit parent = deploymentUnit.getParent();
        if (parent != null) {
            final EarMetaData jbossAppMetaData = parent.getAttachment(Attachments.EAR_METADATA);
            if (jbossAppMetaData instanceof JBossAppMetaData) {
                securityDomain = ((JBossAppMetaData) jbossAppMetaData).getSecurityDomain();
            }
        }
        return securityDomain;
    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2017, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.wildfly.extension.undertow.deployment;

import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.servlet.AsyncListener;

import org.jboss.as.ee.component.Attachments;
import org.jboss.as.ee.component.ComponentDescription;
import org.jboss.as.ee.component.EEApplicationClasses;
import org.jboss.as.ee.component.EEModuleDescription;
import org.jboss.as.ee.structure.DeploymentType;
import org.jboss.as.ee.structure.DeploymentTypeMarker;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.as.server.deployment.annotation.CompositeIndex;
import org.wildfly.extension.undertow.logging.UndertowLogger;
import org.jboss.as.web.common.WarMetaData;
import org.jboss.as.web.common.WebComponentDescription;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.metadata.web.spec.FilterMetaData;
import org.jboss.metadata.web.spec.ListenerMetaData;
import org.jboss.metadata.web.spec.ServletMetaData;
import org.jboss.metadata.web.spec.TagMetaData;
import org.jboss.metadata.web.spec.TldMetaData;

/**
 * Processor that figures out what type of component a servlet/listener is, and registers the appropriate metadata.
 * The different types are:
 *

 *
Managed Bean - If the servlet is annotated with the ManagedBean annotation

 *
CDI Bean - If the servlet is deployed in a bean archive

 *
EE Component - If this is an EE deployment and the servlet is not one of the above

 *
Normal Servlet - If the EE subsystem is disabled

 *

 *

 * For ManagedBean Servlets no action is necessary at this stage, as the servlet is already registered as a component.
 * For CDI and EE components a component definition is added to the deployment.
 *

 * For now we are just using managed bean components as servlets. We may need a custom component type in future.
 */
public class WebComponentProcessor implements DeploymentUnitProcessor {

    /**
     * Tags in these packages do not need to be computerized
     */
    private static final String[] BUILTIN_TAGLIBS = {"org.apache.taglibs.standard", "com.sun.faces.taglib.jsf_core",  "com.sun.faces.ext.taglib", "com.sun.faces.taglib.html_basic",};

    /**
     * Dotname for AsyncListener, which can be injected dynamically.
     */
    private static final DotName ASYNC_LISTENER_INTERFACE = DotName.createSimple(AsyncListener.class.getName());

    @Override
    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();

        if (!DeploymentTypeMarker.isType(DeploymentType.WAR, deploymentUnit)) {
            return;
        }

        final Map<String, ComponentDescription> componentByClass = new HashMap<String, ComponentDescription>();
        final EEModuleDescription moduleDescription = deploymentUnit.getAttachment(Attachments.EE_MODULE_DESCRIPTION);
        final EEApplicationClasses applicationClassesDescription = deploymentUnit.getAttachment(Attachments.EE_APPLICATION_CLASSES_DESCRIPTION);
        final CompositeIndex compositeIndex = deploymentUnit.getAttachment(org.jboss.as.server.deployment.Attachments.COMPOSITE_ANNOTATION_INDEX);
        if (moduleDescription == null) {
            return; //not an EE deployment
        }
        for (ComponentDescription component : moduleDescription.getComponentDescriptions()) {
            componentByClass.put(component.getComponentClassName(), component);
        }

        final WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
        final TldsMetaData tldsMetaData = deploymentUnit.getAttachment(TldsMetaData.ATTACHMENT_KEY);
        final Set<String> classes = getAllComponentClasses(deploymentUnit, compositeIndex, warMetaData, tldsMetaData);
        for (String clazz : classes) {
            if (clazz == null || clazz.trim().isEmpty()) {
                continue;
            }
            ComponentDescription description = componentByClass.get(clazz);
            if (description != null) {
                //for now just make sure it has a single view
                //this will generally be a managed bean, but it could also be an EJB
                //TODO: make sure the component is a managed bean
                if (!(description.getViews().size() == 1)) {
                    throw UndertowLogger.ROOT_LOGGER.wrongComponentType(clazz);
                }
            } else {
                //we do not make the standard tags into components, as there is no need
                if (compositeIndex.getClassByName(DotName.createSimple(clazz)) == null) {
                    boolean found = false;
                    for (String pack : BUILTIN_TAGLIBS) {
                        if (clazz.startsWith(pack)) {
                            found = true;
                            break;
                        }
                    }
                    if(found) {
                        continue;
                    }
                }
                description = new WebComponentDescription(clazz, clazz, moduleDescription, deploymentUnit.getServiceName(), applicationClassesDescription);
                moduleDescription.addComponent(description);
                deploymentUnit.addToAttachmentList(WebComponentDescription.WEB_COMPONENTS, description.getStartServiceName());
            }
        }
    }

    @Override
    public void undeploy(DeploymentUnit context) {
    }

    /**
     * Gets all classes that are eligible for injection etc
     *
     * @param metaData
     * @return
     */
    private Set<String> getAllComponentClasses(DeploymentUnit deploymentUnit, CompositeIndex index, WarMetaData metaData, TldsMetaData tldsMetaData) {
        final Set<String> classes = new HashSet<String>();
        getAllComponentClasses(metaData.getMergedJBossWebMetaData(), classes);
        if (tldsMetaData == null)
            return classes;
        if (tldsMetaData.getSharedTlds(deploymentUnit) != null)
            for (TldMetaData tldMetaData : tldsMetaData.getSharedTlds(deploymentUnit)) {
                getAllComponentClasses(tldMetaData, classes);
            }
        if (tldsMetaData.getTlds() != null)
            for (Map.Entry<String, TldMetaData> tldMetaData : tldsMetaData.getTlds().entrySet()) {
                getAllComponentClasses(tldMetaData.getValue(), classes);
            }
        getAllAsyncListenerClasses(index, classes);
        return classes;
    }

    private void getAllComponentClasses(JBossWebMetaData metaData, Set<String> classes) {
        if (metaData.getServlets() != null)
            for (ServletMetaData servlet : metaData.getServlets()) {
                if (servlet.getServletClass() != null) {
                    classes.add(servlet.getServletClass());
                }
            }
        if (metaData.getFilters() != null)
            for (FilterMetaData filter : metaData.getFilters()) {
                classes.add(filter.getFilterClass());
            }
        if (metaData.getListeners() != null)
            for (ListenerMetaData listener : metaData.getListeners()) {
                classes.add(listener.getListenerClass());
            }
    }

    private void getAllComponentClasses(TldMetaData metaData, Set<String> classes) {
        if (metaData.getValidator() != null) {
            classes.add(metaData.getValidator().getValidatorClass());
        }
        if (metaData.getListeners() != null)
            for (ListenerMetaData listener : metaData.getListeners()) {
                classes.add(listener.getListenerClass());
            }
        if (metaData.getTags() != null)
            for (TagMetaData tag : metaData.getTags()) {
                classes.add(tag.getTagClass());
            }
    }

    private void getAllAsyncListenerClasses(CompositeIndex index, Set<String> classes) {
        if (index != null) {
            Set<ClassInfo> classInfos = index.getAllKnownImplementors(ASYNC_LISTENER_INTERFACE);
            for (ClassInfo classInfo : classInfos) {
                if(!Modifier.isAbstract(classInfo.flags()) && !Modifier.isInterface(classInfo.flags())) {
                    classes.add(classInfo.name().toString());
                }
            }
        }
    }
}