/*
 * 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.ee.component.deployers;

import org.jboss.as.ee.logging.EeLogger;
import org.jboss.as.ee.component.Attachments;
import org.jboss.as.ee.component.BindingConfiguration;
import org.jboss.as.ee.component.ComponentDescription;
import org.jboss.as.ee.component.DeploymentDescriptorEnvironment;
import org.jboss.as.ee.component.EEApplicationClasses;
import org.jboss.as.ee.component.EnvEntryInjectionSource;
import org.jboss.as.ee.component.FixedInjectionSource;
import org.jboss.as.ee.component.InjectionSource;
import org.jboss.as.ee.component.LookupInjectionSource;
import org.jboss.as.ee.component.ResourceInjectionTarget;
import org.jboss.as.naming.ImmediateManagedReference;
import org.jboss.as.naming.ManagedReference;
import org.jboss.as.naming.ManagedReferenceFactory;
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.metadata.javaee.spec.EnvironmentEntriesMetaData;
import org.jboss.metadata.javaee.spec.EnvironmentEntryMetaData;
import org.jboss.metadata.javaee.spec.MessageDestinationReferenceMetaData;
import org.jboss.metadata.javaee.spec.MessageDestinationReferencesMetaData;
import org.jboss.metadata.javaee.spec.ResourceEnvironmentReferenceMetaData;
import org.jboss.metadata.javaee.spec.ResourceEnvironmentReferencesMetaData;
import org.jboss.metadata.javaee.spec.ResourceReferenceMetaData;
import org.jboss.metadata.javaee.spec.ResourceReferencesMetaData;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import static org.jboss.as.ee.logging.EeLogger.ROOT_LOGGER;

/**
 * Deployment processor that sets up env-entry, resource-ref and resource-env-ref bindings
 *
 * @author Stuart Douglas
 * @author Eduardo Martins
 */
public class ResourceReferenceProcessor extends AbstractDeploymentDescriptorBindingsProcessor {

    private static final String JAVAX_NAMING_CONTEXT = "javax.naming.Context";

    @Override
    protected List<BindingConfiguration> processDescriptorEntries(DeploymentUnit deploymentUnit, DeploymentDescriptorEnvironment environment, ResourceInjectionTarget resourceInjectionTarget, final ComponentDescription componentDescription, ClassLoader classLoader, DeploymentReflectionIndex deploymentReflectionIndex, final EEApplicationClasses applicationClasses) throws DeploymentUnitProcessingException {
        List<BindingConfiguration> bindings = new ArrayList<BindingConfiguration>();
        bindings.addAll(getEnvironmentEntries(environment, classLoader, deploymentReflectionIndex, resourceInjectionTarget));
        bindings.addAll(getResourceEnvRefEntries(deploymentUnit, environment, classLoader, deploymentReflectionIndex, resourceInjectionTarget));
        bindings.addAll(getResourceRefEntries(deploymentUnit, environment, classLoader, deploymentReflectionIndex, resourceInjectionTarget));
        bindings.addAll(getMessageDestinationRefs(environment, classLoader, deploymentReflectionIndex, resourceInjectionTarget, deploymentUnit));
        return bindings;
    }

    private List<BindingConfiguration> getResourceEnvRefEntries(final DeploymentUnit deploymentUnit, final DeploymentDescriptorEnvironment environment, ClassLoader classLoader, DeploymentReflectionIndex deploymentReflectionIndex, ResourceInjectionTarget resourceInjectionTarget) throws DeploymentUnitProcessingException {
        List<BindingConfiguration> bindings = new ArrayList<BindingConfiguration>();
        final ResourceEnvironmentReferencesMetaData resourceEnvRefs = environment.getEnvironment().getResourceEnvironmentReferences();
        final EEResourceReferenceProcessorRegistry registry = deploymentUnit.getAttachment(Attachments.RESOURCE_REFERENCE_PROCESSOR_REGISTRY);
        if (resourceEnvRefs == null) {
            return bindings;
        }
        for (ResourceEnvironmentReferenceMetaData resourceEnvRef : resourceEnvRefs) {
            if(resourceEnvRef.isDependencyIgnored()) {
                continue;
            }
            final String name;
            if (resourceEnvRef.getName().startsWith("java:")) {
                name = resourceEnvRef.getName();
            } else {
                name = environment.getDefaultContext() + resourceEnvRef.getName();
            }
            Class<?> classType = null;
            if (resourceEnvRef.getType() != null) {
                try {
                    classType = classLoader.loadClass(resourceEnvRef.getType());
                } catch (ClassNotFoundException e) {
                    throw EeLogger.ROOT_LOGGER.cannotLoad(e, resourceEnvRef.getType());
                }
            }
            // our injection (source) comes from the local (ENC) lookup, no matter what.
            InjectionSource injectionSource = new LookupInjectionSource(name);
            classType = processInjectionTargets(resourceInjectionTarget, injectionSource, classLoader, deploymentReflectionIndex, resourceEnvRef, classType);
            if (!isEmpty(resourceEnvRef.getLookupName())) {
                injectionSource = new LookupInjectionSource(resourceEnvRef.getLookupName(), classType != null && JAVAX_NAMING_CONTEXT.equals(classType.getName()));
            } else {
                if (classType == null) {
                    throw EeLogger.ROOT_LOGGER.cannotDetermineType(name);
                }
                //check if it is a well known type
                final String lookup = ResourceInjectionAnnotationParsingProcessor.FIXED_LOCATIONS.get(classType.getName());
                if (lookup != null) {
                    injectionSource = new LookupInjectionSource(lookup);
                } else {
                    final EEResourceReferenceProcessor resourceReferenceProcessor = registry.getResourceReferenceProcessor(classType.getName());
                    if (resourceReferenceProcessor != null) {
                        injectionSource = resourceReferenceProcessor.getResourceReferenceBindingSource();
                    } else {
                        //TODO: how are we going to handle these? Previously they would have been handled by jboss-*.xml
                        if (resourceEnvRef.getResourceEnvRefName().startsWith("java:")) {
                            ROOT_LOGGER.cannotResolve("resource-env-ref", name);
                            continue;
                        } else {
                            injectionSource = new LookupInjectionSource("java:jboss/resources/" + resourceEnvRef.getResourceEnvRefName());
                        }
                    }
                }
            }
            bindings.add(new BindingConfiguration(name, injectionSource));
        }
        return bindings;
    }

    private List<BindingConfiguration> getResourceRefEntries(final DeploymentUnit deploymentUnit, DeploymentDescriptorEnvironment environment, ClassLoader classLoader, DeploymentReflectionIndex deploymentReflectionIndex, ResourceInjectionTarget resourceInjectionTarget) throws DeploymentUnitProcessingException {
        List<BindingConfiguration> bindings = new ArrayList<BindingConfiguration>();
        final EEResourceReferenceProcessorRegistry registry = deploymentUnit.getAttachment(Attachments.RESOURCE_REFERENCE_PROCESSOR_REGISTRY);
        final ResourceReferencesMetaData resourceRefs = environment.getEnvironment().getResourceReferences();
        if (resourceRefs == null) {
            return bindings;
        }
        for (final ResourceReferenceMetaData resourceRef : resourceRefs) {
            if(resourceRef.isDependencyIgnored()) {
                continue;
            }
            final String name;
            if (resourceRef.getName().startsWith("java:")) {
                name = resourceRef.getName();
            } else {
                name = environment.getDefaultContext() + resourceRef.getName();
            }
            Class<?> classType = null;
            if (resourceRef.getType() != null) {
                try {
                    classType = classLoader.loadClass(resourceRef.getType());
                } catch (ClassNotFoundException e) {
                    throw EeLogger.ROOT_LOGGER.cannotLoad(e, resourceRef.getType());
                }
            }
            // our injection (source) comes from the local (ENC) lookup, no matter what.
            InjectionSource injectionSource = new LookupInjectionSource(name);
            classType = processInjectionTargets(resourceInjectionTarget, injectionSource, classLoader, deploymentReflectionIndex, resourceRef, classType);
            if (!isEmpty(resourceRef.getLookupName())) {
                injectionSource = new LookupInjectionSource(resourceRef.getLookupName(), classType != null && JAVAX_NAMING_CONTEXT.equals(classType.getName()));
            } else if (!isEmpty(resourceRef.getResUrl())) {
                final String url = resourceRef.getResUrl();
                if (classType != null && classType.equals(URI.class)) {
                    try {
                        //we need a newURI every time
                        injectionSource = new FixedInjectionSource(new ManagedReferenceFactory() {
                            @Override
                            public ManagedReference getReference() {
                                try {
                                    return new ImmediateManagedReference(new URI(url));
                                } catch (URISyntaxException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                        }, new URI(url));
                    } catch (URISyntaxException e) {
                        throw EeLogger.ROOT_LOGGER.cannotParseResourceRefUri(e, resourceRef.getResUrl());
                    }
                } else {
                    try {
                        injectionSource = new FixedInjectionSource(new ManagedReferenceFactory() {
                            @Override
                            public ManagedReference getReference() {
                                try {
                                    return new ImmediateManagedReference(new URL(url));
                                } catch (MalformedURLException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                        }, new URL(url));
                    } catch (MalformedURLException e) {
                        throw EeLogger.ROOT_LOGGER.cannotParseResourceRefUri(e, resourceRef.getResUrl());
                    }
                }
            } else {
                if (classType == null) {
                    throw EeLogger.ROOT_LOGGER.cannotDetermineType(name);
                }
                //check if it is a well known type
                final String lookup = ResourceInjectionAnnotationParsingProcessor.FIXED_LOCATIONS.get(classType.getName());
                if (lookup != null) {
                    injectionSource = new LookupInjectionSource(lookup);
                } else {
                    final EEResourceReferenceProcessor resourceReferenceProcessor = registry.getResourceReferenceProcessor(classType.getName());
                    if (resourceReferenceProcessor != null) {
                        injectionSource = resourceReferenceProcessor.getResourceReferenceBindingSource();
                    } else if (!resourceRef.getResourceRefName().startsWith("java:")) {
                        injectionSource = new LookupInjectionSource("java:jboss/resources/" + resourceRef.getResourceRefName());
                    } else {
                        //if we cannot resolve it just log
                        ROOT_LOGGER.cannotResolve("resource-env-ref", name);
                        continue;
                    }
                }
            }
            bindings.add(new BindingConfiguration(name, injectionSource));
        }
        return bindings;
    }

    private List<BindingConfiguration> getEnvironmentEntries(final DeploymentDescriptorEnvironment environment, final ClassLoader classLoader, DeploymentReflectionIndex deploymentReflectionIndex, ResourceInjectionTarget resourceInjectionTarget) throws DeploymentUnitProcessingException {
        final List<BindingConfiguration> bindings = new ArrayList<BindingConfiguration>();
        final EnvironmentEntriesMetaData envEntries = environment.getEnvironment().getEnvironmentEntries();
        if (envEntries == null) {
            return bindings;
        }
        for (final EnvironmentEntryMetaData envEntry : envEntries) {
            if(envEntry.isDependencyIgnored()) {
                continue;
            }
            final String name;
            if (envEntry.getName().startsWith("java:")) {
                name = envEntry.getName();
            } else {
                name = environment.getDefaultContext() + envEntry.getEnvEntryName();
            }

            Class<?> classType = null;
            if (envEntry.getType() != null) {
                try {
                    classType = this.loadClass(envEntry.getType(), classLoader);
                } catch (ClassNotFoundException e) {
                    throw EeLogger.ROOT_LOGGER.cannotLoad(e, envEntry.getType());
                }
            }
            final String value = envEntry.getValue();
            final String lookup = envEntry.getLookupName();
            if (!isEmpty(value) && !isEmpty(lookup)) {
                throw EeLogger.ROOT_LOGGER.cannotSpecifyBoth("<env-entry-value>", "<lookup-name>");
            } else if (isEmpty(lookup) && isEmpty(value)) {
                //if no value is provided then it is not an error
                //this reference should simply be ignored
                // (Java ee platform spec 6.0 fr pg 80)
                continue;
            }

            // our injection (source) comes from the local (ENC) lookup, no matter what.
            LookupInjectionSource injectionSource = new LookupInjectionSource(name);
            classType = processInjectionTargets(resourceInjectionTarget, injectionSource, classLoader, deploymentReflectionIndex, envEntry, classType);
            if (classType == null) {
                throw EeLogger.ROOT_LOGGER.cannotDetermineType("<env-entry>", name, "<env-entry-type>");
            }

            final String type = classType.getName();
            final BindingConfiguration bindingConfiguration;
            if (!isEmpty(lookup)) {
                bindingConfiguration = new BindingConfiguration(name, new LookupInjectionSource(lookup));
            } else if (type.equals(String.class.getName())) {
                bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(value));
            } else if (type.equals(Integer.class.getName()) || type.equals("int")) {
                bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Integer.valueOf(value)));
            } else if (type.equals(Short.class.getName()) || type.equals("short")) {
                bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Short.valueOf(value)));
            } else if (type.equals(Long.class.getName()) || type.equals("long")) {
                bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Long.valueOf(value)));
            } else if (type.equals(Byte.class.getName()) || type.equals("byte")) {
                bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Byte.valueOf(value)));
            } else if (type.equals(Double.class.getName()) || type.equals("double")) {
                bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Double.valueOf(value)));
            } else if (type.equals(Float.class.getName()) || type.equals("float")) {
                bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Float.valueOf(value)));
            } else if (type.equals(Boolean.class.getName()) || type.equals("boolean")) {
                bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Boolean.valueOf(value)));
            } else if (type.equals(Character.class.getName()) || type.equals("char")) {
                if (value.length() != 1) {
                    throw EeLogger.ROOT_LOGGER.invalidCharacterLength("env-entry", value);
                }
                bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(value.charAt(0)));
            } else if (type.equals(Class.class.getName())) {
                try {
                    bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(classLoader.loadClass(value)));
                } catch (ClassNotFoundException e) {
                    throw EeLogger.ROOT_LOGGER.cannotLoad(value);
                }
            } else if (classType.isEnum() || (classType.getEnclosingClass() != null && classType.getEnclosingClass().isEnum())) {
                bindingConfiguration = new BindingConfiguration(name, new EnvEntryInjectionSource(Enum.valueOf((Class) classType, value)));
            } else {
                throw EeLogger.ROOT_LOGGER.unknownElementType("env-entry", type);
            }
            bindings.add(bindingConfiguration);
        }
        return bindings;
    }

    /**
     * TODO: should this be part of the messaging subsystem
     */
    private List<BindingConfiguration> getMessageDestinationRefs(final DeploymentDescriptorEnvironment environment, final ClassLoader classLoader, final DeploymentReflectionIndex deploymentReflectionIndex, final ResourceInjectionTarget resourceInjectionTarget, final DeploymentUnit deploymentUnit) throws DeploymentUnitProcessingException {
        final List<BindingConfiguration> bindings = new ArrayList<BindingConfiguration>();
        final MessageDestinationReferencesMetaData messageDestinationReferences = environment.getEnvironment().getMessageDestinationReferences();
        if (messageDestinationReferences == null) {
            return bindings;
        }
        for (final MessageDestinationReferenceMetaData messageRef : messageDestinationReferences) {
            if(messageRef.isDependencyIgnored()) {
                continue;
            }
            final String name;
            if (messageRef.getName().startsWith("java:")) {
                name = messageRef.getName();
            } else {
                name = environment.getDefaultContext() + messageRef.getName();
            }
            Class<?> classType = null;
            if (messageRef.getType() != null) {
                try {
                    classType = classLoader.loadClass(messageRef.getType());
                } catch (ClassNotFoundException e) {
                    throw EeLogger.ROOT_LOGGER.cannotLoad(e, messageRef.getType());
                }
            }
            // our injection (source) comes from the local (ENC) lookup, no matter what.
            final LookupInjectionSource injectionSource = new LookupInjectionSource(name);

            classType = processInjectionTargets(resourceInjectionTarget, injectionSource, classLoader, deploymentReflectionIndex, messageRef, classType);
            final BindingConfiguration bindingConfiguration;
            if (!isEmpty(messageRef.getLookupName())) {
                bindingConfiguration = new BindingConfiguration(name, new LookupInjectionSource(messageRef.getLookupName()));
                bindings.add(bindingConfiguration);
            } else if (!isEmpty(messageRef.getMappedName())) {
                bindingConfiguration = new BindingConfiguration(name, new LookupInjectionSource(messageRef.getMappedName()));
                bindings.add(bindingConfiguration);
            } else if (!isEmpty(messageRef.getLink())) {
                final MessageDestinationInjectionSource messageDestinationInjectionSource = new MessageDestinationInjectionSource(messageRef.getLink(), name);
                bindingConfiguration = new BindingConfiguration(name, messageDestinationInjectionSource);
                deploymentUnit.addToAttachmentList(Attachments.MESSAGE_DESTINATIONS, messageDestinationInjectionSource);
                bindings.add(bindingConfiguration);
            } else {
                ROOT_LOGGER.cannotResolve("message-destination-ref", name);
            }
        }
        return bindings;
    }

    private boolean isEmpty(final String string) {
        return string == null || string.isEmpty();
    }

    @Override
    public void undeploy(DeploymentUnit context) {

    }

    private Class<?> loadClass(String className, ClassLoader cl) throws ClassNotFoundException {
        if (className == null || className.trim().isEmpty()) {
            throw EeLogger.ROOT_LOGGER.cannotBeNullOrEmpty("Classname", className);
        }
        if (className.equals(void.class.getName())) {
            return void.class;
        }
        if (className.equals(byte.class.getName())) {
            return byte.class;
        }
        if (className.equals(short.class.getName())) {
            return short.class;
        }
        if (className.equals(int.class.getName())) {
            return int.class;
        }
        if (className.equals(long.class.getName())) {
            return long.class;
        }
        if (className.equals(char.class.getName())) {
            return char.class;
        }
        if (className.equals(boolean.class.getName())) {
            return boolean.class;
        }
        if (className.equals(float.class.getName())) {
            return float.class;
        }
        if (className.equals(double.class.getName())) {
            return double.class;
        }
        // Now that we know its not a primitive, lets just allow
        // the passed classloader to handle the request.
        return Class.forName(className, false, cl);
    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2014, Red Hat Middleware LLC, 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.tomcat;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.jboss.as.webservices.logging.WSLogger;
import org.jboss.as.webservices.util.ASHelper;
import org.jboss.as.webservices.util.WebMetaDataHelper;
import org.jboss.metadata.javaee.spec.ParamValueMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.metadata.web.spec.ServletMetaData;
import org.jboss.ws.common.integration.WSConstants;
import org.jboss.ws.common.integration.WSHelper;
import org.jboss.wsf.spi.deployment.Deployment;
import org.jboss.wsf.spi.deployment.Endpoint;
import org.jboss.wsf.spi.deployment.WSFServlet;

/**
 * The modifier of jboss web meta data. It configures WS transport for every webservice endpoint plus propagates WS stack
 * specific context parameters if required.
 *
 * @author Richard Opalka
 * @author Thomas Diesler
 * @author Alessio Soldano
 */
final class WebMetaDataModifier {

    WebMetaDataModifier() {
        super();
    }

    /**
     * Modifies web meta data to configure webservice stack transport and properties.
     *
     * @param dep webservice deployment
     */
    void modify(final Deployment dep) {
        final JBossWebMetaData jbossWebMD = WSHelper.getOptionalAttachment(dep, JBossWebMetaData.class);

        if (jbossWebMD != null) {
            this.configureEndpoints(dep, jbossWebMD);
            this.modifyContextRoot(dep, jbossWebMD);
        }
    }

    /**
     * Configures transport servlet class for every found webservice endpoint.
     *
     * @param dep webservice deployment
     * @param jbossWebMD web meta data
     */
    private void configureEndpoints(final Deployment dep, final JBossWebMetaData jbossWebMD) {
        final String transportClassName = this.getTransportClassName(dep);
        WSLogger.ROOT_LOGGER.trace("Modifying servlets");

        // get a list of the endpoint bean class names
        final Set<String> epNames = new HashSet<String>();
        for (Endpoint ep : dep.getService().getEndpoints()) {
            epNames.add(ep.getTargetBeanName());
        }

        // fix servlet class names for endpoints
        for (final ServletMetaData servletMD : jbossWebMD.getServlets()) {
            final String endpointClassName = ASHelper.getEndpointClassName(servletMD);
            if (endpointClassName != null && endpointClassName.length() > 0) { // exclude JSP
                if (epNames.contains(endpointClassName)) {
                    // set transport servlet
                    servletMD.setServletClass(WSFServlet.class.getName());
                    WSLogger.ROOT_LOGGER.tracef("Setting transport class: %s for endpoint: %s", transportClassName, endpointClassName);
                    final List<ParamValueMetaData> initParams = WebMetaDataHelper.getServletInitParams(servletMD);
                    // configure transport class name
                    WebMetaDataHelper.newParamValue(WSFServlet.STACK_SERVLET_DELEGATE_CLASS, transportClassName, initParams);
                    // configure webservice endpoint
                    WebMetaDataHelper.newParamValue(Endpoint.SEPID_DOMAIN_ENDPOINT, endpointClassName, initParams);
                } else if (endpointClassName.startsWith("org.apache.cxf")) {
                    throw WSLogger.ROOT_LOGGER.invalidWSServlet(endpointClassName);
                }
            }
        }
    }

    /**
     * Modifies context root.
     *
     * @param dep webservice deployment
     * @param jbossWebMD web meta data
     */
    private void modifyContextRoot(final Deployment dep, final JBossWebMetaData jbossWebMD) {
        final String contextRoot = dep.getService().getContextRoot();
        if (WSLogger.ROOT_LOGGER.isTraceEnabled()) {
            WSLogger.ROOT_LOGGER.tracef("Setting context root: %s for deployment: %s", contextRoot, dep.getSimpleName());
        }
        jbossWebMD.setContextRoot(contextRoot);
    }

    /**
     * Returns stack specific transport class name.
     *
     * @param dep webservice deployment
     * @return stack specific transport class name
     * @throws IllegalStateException if transport class name is not found in deployment properties map
     */
    private String getTransportClassName(final Deployment dep) {
        String transportClassName = (String) dep.getProperty(WSConstants.STACK_TRANSPORT_CLASS);
        if (transportClassName == null) throw WSLogger.ROOT_LOGGER.missingDeploymentProperty(WSConstants.STACK_TRANSPORT_CLASS);
        return transportClassName;
    }

}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2013, 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.clustering.marshalling.jboss;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Arrays;

import org.jboss.marshalling.Marshaller;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.SimpleDataInput;
import org.jboss.marshalling.SimpleDataOutput;
import org.jboss.marshalling.Unmarshaller;
import org.wildfly.clustering.marshalling.spi.IndexSerializer;
import org.wildfly.clustering.marshalling.spi.MarshalledValue;
import org.wildfly.security.manager.WildFlySecurityManager;

/**
 * A marshalled value that is lazily serialized and deserialized on demand.
 * This implementation does not preserve the hash code of its object in serialized form.
 * @author Paul Ferraro
 */
public class SimpleMarshalledValue<T> implements MarshalledValue<T, MarshallingContext> {
    private static final long serialVersionUID = -8852566958387608376L;

    private transient volatile MarshallingContext context;
    private transient volatile T object;
    private transient volatile byte[] bytes;

    public SimpleMarshalledValue(T object, MarshallingContext context) {
        this.context = context;
        this.object = object;
    }

    SimpleMarshalledValue(byte[] bytes) {
        this.bytes = bytes;
    }

    // Used for testing purposes only
    T peek() {
        return this.object;
    }

    byte[] getBytes() throws IOException {
        byte[] bytes = this.bytes;
        if (bytes != null) return bytes;
        if (this.object == null) return null;
        int version = this.context.getCurrentVersion();
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        ClassLoader loader = setThreadContextClassLoader(this.context.getClassLoader());
        try (SimpleDataOutput data = new SimpleDataOutput(Marshalling.createByteOutput(output))) {
            IndexSerializer.VARIABLE.writeInt(data, version);
            try (Marshaller marshaller = this.context.createMarshaller(version)) {
                marshaller.start(data);
                marshaller.writeObject(this.object);
                marshaller.finish();
                return output.toByteArray();
            }
        } finally {
            setThreadContextClassLoader(loader);
        }
    }

    /**
     * {@inheritDoc}
     * @see org.wildfly.clustering.marshalling.spi.MarshalledValue#get(java.lang.Object)
     */
    @SuppressWarnings("unchecked")
    @Override
    public synchronized T get(MarshallingContext context) throws IOException, ClassNotFoundException {
        if (this.object == null) {
            this.context = context;
            if (this.bytes != null) {
                ByteArrayInputStream input = new ByteArrayInputStream(this.bytes);
                ClassLoader loader = setThreadContextClassLoader(this.context.getClassLoader());
                try (SimpleDataInput data = new SimpleDataInput(Marshalling.createByteInput(input))) {
                    int version = IndexSerializer.VARIABLE.readInt(data);
                    try (Unmarshaller unmarshaller = context.createUnmarshaller(version)) {
                        unmarshaller.start(data);
                        this.object = (T) unmarshaller.readObject();
                        unmarshaller.finish();
                        this.bytes = null; // Free up memory
                    }
                } finally {
                    setThreadContextClassLoader(loader);
                }
            }
        }
        return this.object;
    }

    /**
     * {@inheritDoc}
     * N.B. Calls to hashCode will return 0 if this marshalled value was deserialized but its object not yet rehydrated.
     * If consistent hashCode is required, use {@link HashableMarshalledValue instead}.
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
        return (this.object != null) ? this.object.hashCode() : 0;
    }

    @Override
    public boolean equals(Object object) {
        if ((object == null) || !(object instanceof SimpleMarshalledValue)) return false;
        @SuppressWarnings("unchecked")
        SimpleMarshalledValue<T> value = (SimpleMarshalledValue<T>) object;
        if ((this.object != null) && (value.object != null)) {
            return this.object.equals(value.object);
        }
        try {
            byte[] us = this.getBytes();
            byte[] them = value.getBytes();
            return ((us != null) && (them != null)) ? Arrays.equals(us, them) : (us == them);
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }

    @Override
    public String toString() {
        if (this.object != null) return this.object.toString();
        byte[] bytes = this.bytes;
        return (bytes != null) ? bytes.toString() : null;
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        byte[] bytes = this.getBytes();
        if (bytes != null) {
            out.writeInt(bytes.length);
            out.write(bytes);
        } else {
            out.writeInt(0);
        }
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        int size = in.readInt();
        byte[] bytes = null;
        if (size > 0) {
            bytes = new byte[size];
            in.readFully(bytes);
        }
        this.bytes = bytes;
    }

    private static ClassLoader setThreadContextClassLoader(ClassLoader loader) {
        return (loader != null) ? WildFlySecurityManager.setCurrentContextClassLoaderPrivileged(loader) : null;
    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2009, Red Hat Middleware LLC, 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.timerservice.schedule.util;

import java.util.Calendar;
import java.util.GregorianCalendar;

/**
 * CalendarUtil
 *
 * @author Jaikiran Pai
 * @version $Revision: $
 */
public class CalendarUtil {

    /**
     * Returns the last date of the month represented by the passed cal
     *
     * @param calendar The {@link java.util.Calendar} whose {@link java.util.Calendar#MONTH} field will be used
     *                 as the current month
     * @return
     */
    public static int getLastDateOfMonth(Calendar calendar) {
        Calendar tmpCal = new GregorianCalendar(calendar.getTimeZone());
        tmpCal.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
        tmpCal.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
        tmpCal.set(Calendar.DAY_OF_MONTH, 1);
        return tmpCal.getActualMaximum(Calendar.DAY_OF_MONTH);
    }

    public static Integer getNthDayOfMonth(Calendar cal, int n, int dayOfWeek) {
        int dateOfFirstXXXDay = getFirstDateInMonthForDayOfWeek(cal, dayOfWeek);

        final int FIRST_WEEK = 1;
        final int NUM_DAYS_IN_WEEK = 7;

        int weekDiff = n - FIRST_WEEK;

        int dateOfNthXXXDayInMonth = dateOfFirstXXXDay + (weekDiff * NUM_DAYS_IN_WEEK);
        int maxDateInCurrentMonth = CalendarUtil.getLastDateOfMonth(cal);
        if (dateOfNthXXXDayInMonth > maxDateInCurrentMonth) {
            return null;
        }

        return dateOfNthXXXDayInMonth;
    }

    public static int getDateOfLastDayOfWeekInMonth(Calendar calendar, int dayOfWeek) {
        int lastDateOfMonth = getLastDateOfMonth(calendar);
        Calendar tmpCal = new GregorianCalendar(calendar.getTimeZone());
        tmpCal.set(Calendar.YEAR, calendar.get(Calendar.YEAR));
        tmpCal.set(Calendar.MONTH, calendar.get(Calendar.MONTH));
        tmpCal.set(Calendar.DATE, lastDateOfMonth);

        int day = tmpCal.get(Calendar.DAY_OF_WEEK);
        if (day == dayOfWeek) {
            return tmpCal.get(Calendar.DATE);
        }
        while (day != dayOfWeek) {
            tmpCal.add(Calendar.DATE, -1);
            day = tmpCal.get(Calendar.DAY_OF_WEEK);
        }
        return tmpCal.get(Calendar.DATE);
    }

    private static int getFirstDateInMonthForDayOfWeek(Calendar cal, final int dayOfWeek) {
        Calendar tmpCal = new GregorianCalendar(cal.getTimeZone());
        tmpCal.set(Calendar.YEAR, cal.get(Calendar.YEAR));
        tmpCal.set(Calendar.MONTH, cal.get(Calendar.MONTH));
        tmpCal.set(Calendar.DATE, 1);

        int day = tmpCal.get(Calendar.DAY_OF_WEEK);
        if (day == dayOfWeek) {
            return tmpCal.get(Calendar.DATE);
        }
        while (day != dayOfWeek) {
            tmpCal.add(Calendar.DATE, 1);
            day = tmpCal.get(Calendar.DAY_OF_WEEK);
        }
        return tmpCal.get(Calendar.DATE);
    }

}


/*
 * 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.ejb3.component.messagedriven;

import java.beans.IntrospectionException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;
import java.util.List;
import java.util.Properties;

import javax.resource.ResourceException;
import javax.resource.spi.ActivationSpec;
import javax.resource.spi.ResourceAdapter;

import org.jboss.as.connector.util.ConnectorServices;
import org.jboss.as.ee.component.BasicComponent;
import org.jboss.as.ee.component.ComponentConfiguration;
import org.jboss.as.ejb3.logging.EjbLogger;
import org.jboss.as.ejb3.component.EJBComponentCreateService;
import org.jboss.as.ejb3.component.pool.PoolConfig;
import org.jboss.as.ejb3.deployment.ApplicationExceptions;
import org.jboss.as.server.suspend.SuspendController;
import org.jboss.common.beans.property.BeanUtils;
import org.jboss.jca.core.spi.rar.Activation;
import org.jboss.jca.core.spi.rar.Endpoint;
import org.jboss.jca.core.spi.rar.MessageListener;
import org.jboss.jca.core.spi.rar.NotFoundException;
import org.jboss.jca.core.spi.rar.ResourceAdapterRepository;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.value.InjectedValue;

/**
 * @author Stuart Douglas
 * @author Richard Opalka
 */
public class MessageDrivenComponentCreateService extends EJBComponentCreateService {

    private final Class<?> messageListenerInterface;
    private final Properties activationProps;
    private final String resourceAdapterName;
    private final boolean deliveryActive;
    private final ServiceName deliveryControllerName;
    private final InjectedValue<ResourceAdapterRepository> resourceAdapterRepositoryInjectedValue = new InjectedValue<ResourceAdapterRepository>();
    private final InjectedValue<ResourceAdapter> resourceAdapterInjectedValue = new InjectedValue<ResourceAdapter>();
    private final InjectedValue<PoolConfig> poolConfig = new InjectedValue<PoolConfig>();
    private final InjectedValue<SuspendController> suspendControllerInjectedValue = new InjectedValue<>();
    private final ClassLoader moduleClassLoader;

    /**
     * Construct a new instance.
     *
     * @param componentConfiguration the component configuration
     */
    public MessageDrivenComponentCreateService(final ComponentConfiguration componentConfiguration, final ApplicationExceptions ejbJarConfiguration, final Class<?> messageListenerInterface) {
        super(componentConfiguration, ejbJarConfiguration);

        final MessageDrivenComponentDescription componentDescription = (MessageDrivenComponentDescription) componentConfiguration.getComponentDescription();
        this.resourceAdapterName = componentDescription.getResourceAdapterName();
        this.deliveryControllerName = componentDescription.isDeliveryControlled()? componentDescription.getDeliveryControllerName(): null;
        this.deliveryActive = !componentDescription.isDeliveryControlled() && componentDescription.isDeliveryActive();
        // see MessageDrivenComponentDescription.<init>
        this.messageListenerInterface = messageListenerInterface;

        this.activationProps = componentDescription.getActivationProps();
        this.moduleClassLoader = componentConfiguration.getModuleClassLoader();
    }

    @Override
    public void start(StartContext context) throws StartException {
        super.start(context);
        // AS7-3073 just log a message about the MDB being started
        EjbLogger.ROOT_LOGGER.logMDBStart(this.getComponentName(), this.resourceAdapterName);
    }

    @Override
    protected BasicComponent createComponent() {
        String configuredResourceAdapterName = resourceAdapterName;

        // Match configured value to the actual RA names
        final String activeResourceAdapterName = searchActiveResourceAdapterName(configuredResourceAdapterName);

        final ActivationSpec activationSpec = createActivationSpecs(activeResourceAdapterName, messageListenerInterface, activationProps, getDeploymentClassLoader());
        final MessageDrivenComponent component = new MessageDrivenComponent(this, messageListenerInterface, activationSpec, deliveryActive, deliveryControllerName, activeResourceAdapterName);
        // set the endpoint
        final Endpoint endpoint = getEndpoint(activeResourceAdapterName);

        component.setEndpoint(endpoint);

        return component;
    }

    private ActivationSpec createActivationSpecs(final String resourceAdapterName, final Class<?> messageListenerInterface,
                                                final Properties activationConfigProperties, final ClassLoader classLoader) {
        try {
            // first get the ra "identifier" (with which it is registered in the resource adapter repository) for the
            // ra name
            final String raIdentifier = ConnectorServices.getRegisteredResourceAdapterIdentifier(resourceAdapterName);
            if (raIdentifier == null) {
                throw EjbLogger.ROOT_LOGGER.unknownResourceAdapter(resourceAdapterName);
            }
            final ResourceAdapterRepository resourceAdapterRepository = resourceAdapterRepositoryInjectedValue.getValue();
            if (resourceAdapterRepository == null) {
                throw EjbLogger.ROOT_LOGGER.resourceAdapterRepositoryUnAvailable();
            }
            // now get the message listeners for this specific ra identifier
            final List<MessageListener> messageListeners = resourceAdapterRepository.getMessageListeners(raIdentifier);
            if (messageListeners == null || messageListeners.isEmpty()) {
                throw EjbLogger.ROOT_LOGGER.unknownMessageListenerType(messageListenerInterface.getName(), resourceAdapterName);
            }
            MessageListener requiredMessageListener = null;
            // now find the expected message listener from the list of message listeners for this resource adapter
            for (final MessageListener messageListener : messageListeners) {
                if (messageListenerInterface.equals(messageListener.getType())) {
                    requiredMessageListener = messageListener;
                    break;
                }
            }
            if (requiredMessageListener == null) {
                throw EjbLogger.ROOT_LOGGER.unknownMessageListenerType(messageListenerInterface.getName(), resourceAdapterName);
            }
            // found the message listener, now finally create the activation spec
            final Activation activation = requiredMessageListener.getActivation();
            // filter out the activation config properties, specified on the MDB, which aren't accepted by the resource
            // adaptor
            final Properties validActivationConfigProps = this.filterUnknownActivationConfigProperties(resourceAdapterName, activation, activationConfigProperties);
            // now set the activation config properties on the ActivationSpec
            final ActivationSpec activationSpec = activation.createInstance();
            BeanUtils.mapJavaBeanProperties(activationSpec, validActivationConfigProps);

            return activationSpec;

        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (ResourceException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (NotFoundException e) {
            throw new RuntimeException(e);
        } catch (IntrospectionException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * Removes activation config properties which aren't recognized by the resource adapter activation, from the
     * passed activationConfigProps and returns only those Properties which are valid.
     *
     * @param resourceAdapterName   The resource adapter name
     * @param activation            {@link Activation}
     * @param activationConfigProps Activation config properties which will be checked for validity
     * @return
     */
    private Properties filterUnknownActivationConfigProperties(final String resourceAdapterName, final Activation activation, final Properties activationConfigProps) {
        if (activationConfigProps == null) {
            return null;
        }
        final Map<String, Class<?>> raActivationConfigProps = activation.getConfigProperties();
        final Set<String> raRequiredConfigProps = activation.getRequiredConfigProperties();
        final Enumeration<?> propNames = activationConfigProps.propertyNames();
        final Properties validActivationConfigProps = new Properties();
        // initialize to all the activation config properties that have been set on the MDB
        validActivationConfigProps.putAll(activationConfigProps);
        while (propNames.hasMoreElements()) {
            final Object propName = propNames.nextElement();
            if (raActivationConfigProps.containsKey(propName) == false && raRequiredConfigProps.contains(propName) == false) {
                // not a valid activation config property, so log a WARN and filter it out from the valid activation config properties
                validActivationConfigProps.remove(propName);
                EjbLogger.ROOT_LOGGER.activationConfigPropertyIgnored(propName, resourceAdapterName);
            }
        }
        return validActivationConfigProps;
    }

    /**
     * Returns the {@link org.jboss.jca.core.spi.rar.Endpoint} corresponding to the passed resourceAdapterName
     *
     * @param resourceAdapterName The resource adapter name
     * @return
     */
    private Endpoint getEndpoint(final String resourceAdapterName) {
        // first get the ra "identifier" (with which it is registered in the resource adapter repository) for the
        // ra name
        final String raIdentifier = ConnectorServices.getRegisteredResourceAdapterIdentifier(resourceAdapterName);
        if (raIdentifier == null) {
            throw EjbLogger.ROOT_LOGGER.unknownResourceAdapter(resourceAdapterName);
        }
        final ResourceAdapterRepository resourceAdapterRepository = resourceAdapterRepositoryInjectedValue.getValue();
        if (resourceAdapterRepository == null) {
            throw EjbLogger.ROOT_LOGGER.resourceAdapterRepositoryUnAvailable();
        }
        try {
            return resourceAdapterRepository.getEndpoint(raIdentifier);
        } catch (NotFoundException nfe) {
            throw EjbLogger.ROOT_LOGGER.noSuchEndpointException(resourceAdapterName, nfe);
        }
    }

    private String searchActiveResourceAdapterName(String configuredResourceAdapterName) {
        // Use the configured value unless it doesn't match and some variant of it does
        String result = configuredResourceAdapterName;
        if (ConnectorServices.getRegisteredResourceAdapterIdentifier(configuredResourceAdapterName) == null) {
            // No direct match. See if we have a match with .rar removed or appended
            String amended = stripDotRarSuffix(configuredResourceAdapterName);
            if (configuredResourceAdapterName.equals(amended)) {
                // There was no .rar to strip; try appending
                amended = configuredResourceAdapterName + ".rar";
            }
            if (ConnectorServices.getRegisteredResourceAdapterIdentifier(amended) != null) {
                result = amended;
            }
        }

        return result;
    }

    PoolConfig getPoolConfig() {
        return this.poolConfig.getOptionalValue();
    }

    public InjectedValue<PoolConfig> getPoolConfigInjector() {
        return this.poolConfig;
    }

    private ClassLoader getDeploymentClassLoader() {
        return getComponentClass().getClassLoader();
    }

    public InjectedValue<ResourceAdapterRepository> getResourceAdapterRepositoryInjector() {
        return this.resourceAdapterRepositoryInjectedValue;
    }

    public InjectedValue<ResourceAdapter> getResourceAdapterInjector() {
        return this.resourceAdapterInjectedValue;
    }

    public ClassLoader getModuleClassLoader() {
        return moduleClassLoader;
    }

    public InjectedValue<SuspendController> getSuspendControllerInjectedValue() {
        return suspendControllerInjectedValue;
    }

    private String stripDotRarSuffix(final String raName) {
        if (raName == null) {
            return null;
        }
        // See RaDeploymentParsingProcessor
        if (raName.endsWith(".rar")) {
            return raName.substring(0, raName.indexOf(".rar"));
        }
        return raName;
    }
}


/*
 * 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.security;

import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.Set;

import org.jboss.security.RunAs;
import org.jboss.security.SecurityContext;
import org.jboss.security.SecurityContextAssociation;
import org.jboss.security.SecurityContextFactory;
import org.jboss.security.SecurityRolesAssociation;
import org.wildfly.extension.undertow.logging.UndertowLogger;
import org.wildfly.security.manager.WildFlySecurityManager;

import javax.security.auth.Subject;

import static java.security.AccessController.doPrivileged;

/**
 * Privileged Actions
 *
 * @author Anil.Saldhana@redhat.com
 * @since Jan 12, 2011
 */
class SecurityActions {

    public static final String AUTH_EXCEPTION_KEY = "org.jboss.security.exception";

    /**
     * Create a JBoss Security Context with the given security domain name
     *
     * @param domain the security domain name (such as "other" )
     * @return an instanceof {@code SecurityContext}
     */
    static SecurityContext createSecurityContext(final String domain) {
        if (WildFlySecurityManager.isChecking()) {
            return WildFlySecurityManager.doUnchecked(new PrivilegedAction<SecurityContext>() {
                @Override
                public SecurityContext run() {
                    try {
                        return SecurityContextFactory.createSecurityContext(domain);
                    } catch (Exception e) {
                        throw UndertowLogger.ROOT_LOGGER.failToCreateSecurityContext(e);
                    }
                }
            });
        } else {
            try {
                return SecurityContextFactory.createSecurityContext(domain);
            } catch (Exception e) {
                throw UndertowLogger.ROOT_LOGGER.failToCreateSecurityContext(e);
            }
        }
    }

    /**
     * Set the {@code SecurityContext} on the {@code SecurityContextAssociation}
     *
     * @param sc the security context
     */
    static void setSecurityContextOnAssociation(final SecurityContext sc) {
        if (WildFlySecurityManager.isChecking()) {
            WildFlySecurityManager.doUnchecked(new PrivilegedAction<Void>() {
                @Override
                public Void run() {
                    SecurityContextAssociation.setSecurityContext(sc);
                    return null;
                }
            });
        } else {
            SecurityContextAssociation.setSecurityContext(sc);
        }
    }

    /**
     * Get the current {@code SecurityContext}
     *
     * @return an instance of {@code SecurityContext}
     */
    static SecurityContext getSecurityContext() {
        if (WildFlySecurityManager.isChecking()) {
            return WildFlySecurityManager.doUnchecked(new PrivilegedAction<SecurityContext>() {
                public SecurityContext run() {
                    return SecurityContextAssociation.getSecurityContext();
                }
            });
        } else {
            return SecurityContextAssociation.getSecurityContext();
        }
    }

    /**
     * Clears current {@code SecurityContext}
     */
    static void clearSecurityContext() {
        if (WildFlySecurityManager.isChecking()) {
            WildFlySecurityManager.doUnchecked(new PrivilegedAction<Void>() {
                public Void run() {
                    SecurityContextAssociation.clearSecurityContext();
                    return null;
                }
            });
        } else {
            SecurityContextAssociation.clearSecurityContext();
        }
    }

    static void setSecurityRoles(final Map<String, Set<String>> roles) {
        if(WildFlySecurityManager.isChecking()) {

            WildFlySecurityManager.doUnchecked(new PrivilegedAction<Void>() {
                public Void run() {
                    SecurityRolesAssociation.setSecurityRoles(roles);
                    return null;
                }
            });
        } else {
            SecurityRolesAssociation.setSecurityRoles(roles);
        }
    }

    /**
     * Sets the run as identity
     *
     * @param principal the identity
     */
    static RunAs setRunAsIdentity(final RunAs principal, final SecurityContext sc) {
        if (WildFlySecurityManager.isChecking()) {
            return WildFlySecurityManager.doUnchecked(new PrivilegedAction<RunAs>() {

                @Override
                public RunAs run() {
                    if (sc == null) {
                        throw UndertowLogger.ROOT_LOGGER.noSecurityContext();
                    }
                    RunAs old = sc.getOutgoingRunAs();
                    sc.setOutgoingRunAs(principal);
                    return old;
                }
            });
        } else {
            if (sc == null) {
                throw UndertowLogger.ROOT_LOGGER.noSecurityContext();
            }
            RunAs old = sc.getOutgoingRunAs();
            sc.setOutgoingRunAs(principal);
            return old;
        }
    }

    /**
     * Removes the run as identity
     *
     * @return the identity removed
     */
    static RunAs popRunAsIdentity(final SecurityContext sc) {
        if (WildFlySecurityManager.isChecking()) {
            return AccessController.doPrivileged(new PrivilegedAction<RunAs>() {
                @Override
                public RunAs run() {
                    if (sc == null) {
                        throw UndertowLogger.ROOT_LOGGER.noSecurityContext();
                    }
                    RunAs principal = sc.getOutgoingRunAs();
                    sc.setOutgoingRunAs(null);
                    return principal;
                }
            });
        } else {
            if (sc == null) {
                throw UndertowLogger.ROOT_LOGGER.noSecurityContext();
            }
            RunAs principal = sc.getOutgoingRunAs();
            sc.setOutgoingRunAs(null);
            return principal;
        }
    }

    public static RunAs getRunAsIdentity(final SecurityContext sc) {
        if (WildFlySecurityManager.isChecking()) {
            return AccessController.doPrivileged(new PrivilegedAction<RunAs>() {
                @Override
                public RunAs run() {
                    if (sc == null) {
                        throw UndertowLogger.ROOT_LOGGER.noSecurityContext();
                    }
                    return sc.getOutgoingRunAs();
                }
            });
        } else {
            if (sc == null) {
                throw UndertowLogger.ROOT_LOGGER.noSecurityContext();
            }
            return sc.getOutgoingRunAs();
        }
    }
    static Subject getSubject() {
        if (WildFlySecurityManager.isChecking()) {
            return doPrivileged(new PrivilegedAction<Subject>() {
                public Subject run() {
                    Subject subject = null;
                    SecurityContext sc = getSecurityContext();
                    if (sc != null) {
                        subject = sc.getUtil().getSubject();
                    }
                    return subject;
                }
            });
        } else {
            Subject subject = null;
            SecurityContext sc = getSecurityContext();
            if (sc != null) {
                subject = sc.getUtil().getSubject();
            }
            return subject;
        }
    }
}