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

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.as.weld._private.WeldDeploymentMarker;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoader;
import org.jboss.modules.filter.PathFilters;

/**
 * Deployment processor which adds a module dependencies for modules needed for weld deployments.
 *
 * @author Stuart Douglas
 */
public class WeldDependencyProcessor implements DeploymentUnitProcessor {

    private static final ModuleIdentifier JAVAX_PERSISTENCE_API_ID = ModuleIdentifier.create("javax.persistence.api");
    private static final ModuleIdentifier JBOSS_AS_WELD_ID = ModuleIdentifier.create("org.jboss.as.weld");
    private static final ModuleIdentifier JBOSS_AS_WELD_EJB_ID = ModuleIdentifier.create("org.jboss.as.weld.ejb");
    private static final ModuleIdentifier WELD_CORE_ID = ModuleIdentifier.create("org.jboss.weld.core");
    private static final ModuleIdentifier WELD_PROBE_ID = ModuleIdentifier.create("org.jboss.weld.probe");
    private static final ModuleIdentifier WELD_API_ID = ModuleIdentifier.create("org.jboss.weld.api");
    private static final ModuleIdentifier WELD_SPI_ID = ModuleIdentifier.create("org.jboss.weld.spi");
    private static final ModuleIdentifier JAVAX_ENTERPRISE_API = ModuleIdentifier.create("javax.enterprise.api");
    private static final ModuleIdentifier JAVAX_INJECT_API = ModuleIdentifier.create("javax.inject.api");

    /**
     * Add dependencies for modules required for weld deployments, if managed weld configurations are attached to the deployment
     *
     */
    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);

        final ModuleLoader moduleLoader = Module.getBootModuleLoader();
        addDependency(moduleSpecification, moduleLoader, JAVAX_ENTERPRISE_API);
        addDependency(moduleSpecification, moduleLoader, JAVAX_INJECT_API);

        if (!WeldDeploymentMarker.isPartOfWeldDeployment(deploymentUnit)) {
            return; // Skip if there are no beans.xml files in the deployment
        }
        addDependency(moduleSpecification, moduleLoader, JAVAX_PERSISTENCE_API_ID);
        addDependency(moduleSpecification, moduleLoader, WELD_CORE_ID);
        addDependency(moduleSpecification, moduleLoader, WELD_PROBE_ID, true);
        addDependency(moduleSpecification, moduleLoader, WELD_API_ID);
        addDependency(moduleSpecification, moduleLoader, WELD_SPI_ID);

        ModuleDependency weldSubsystemDependency = new ModuleDependency(moduleLoader, JBOSS_AS_WELD_ID, false, false, false, false);
        weldSubsystemDependency.addImportFilter(PathFilters.getMetaInfFilter(), true);
        weldSubsystemDependency.addImportFilter(PathFilters.is("org/jboss/as/weld/injection"), true);
        weldSubsystemDependency.addImportFilter(PathFilters.acceptAll(), false);
        weldSubsystemDependency.addExportFilter(PathFilters.getMetaInfFilter(), true);
        moduleSpecification.addSystemDependency(weldSubsystemDependency);

        // Due to serialization of EJBs
        ModuleDependency weldEjbDependency = new ModuleDependency(moduleLoader, JBOSS_AS_WELD_EJB_ID, true, false, false, false);
        weldEjbDependency.addImportFilter(PathFilters.is("org/jboss/as/weld/ejb"), true);
        weldEjbDependency.addImportFilter(PathFilters.acceptAll(), false);
        moduleSpecification.addSystemDependency(weldEjbDependency);
    }

    private void addDependency(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader,
                               ModuleIdentifier moduleIdentifier) {
        addDependency(moduleSpecification, moduleLoader, moduleIdentifier, false);
    }

    private void addDependency(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader,
            ModuleIdentifier moduleIdentifier, boolean optional) {
        moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, moduleIdentifier, optional, false, true, false));
    }

    @Override
    public void undeploy(DeploymentUnit context) {

    }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2012, 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.jsf.deployment;

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.jsf.logging.JSFLogger;
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.DeploymentUtils;
import org.jboss.as.server.deployment.annotation.CompositeIndex;
import org.jboss.as.server.deployment.module.ResourceRoot;
import org.jboss.as.web.common.WarMetaData;
import org.jboss.as.web.common.WebComponentDescription;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.metadata.javaee.spec.ParamValueMetaData;
import org.jboss.metadata.parser.util.NoopXMLResolver;
import org.jboss.metadata.web.spec.WebMetaData;
import org.jboss.modules.Module;
import org.jboss.vfs.VirtualFile;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Sets up JSF managed beans as components using information in the annotations and
 *
 * @author Stuart Douglas
 */
public class JSFComponentProcessor implements DeploymentUnitProcessor {

    public static final DotName MANAGED_BEAN_ANNOTATION = DotName.createSimple("javax.faces.bean.ManagedBean");

    private static final String WEB_INF_FACES_CONFIG = "WEB-INF/faces-config.xml";

    private static final String MANAGED_BEAN = "managed-bean";
    private static final String MANAGED_BEAN_CLASS = "managed-bean-class";
    private static final String LIFECYCLE = "lifecycle";
    private static final String PHASE_LISTENER = "phase-listener";

    private static final String CONFIG_FILES = "javax.faces.CONFIG_FILES";

    @Override
    public void deploy(final DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        final CompositeIndex index = deploymentUnit.getAttachment(Attachments.COMPOSITE_ANNOTATION_INDEX);
        final EEModuleDescription moduleDescription = deploymentUnit.getAttachment(org.jboss.as.ee.component.Attachments.EE_MODULE_DESCRIPTION);
        final EEApplicationClasses applicationClassesDescription = deploymentUnit.getAttachment(org.jboss.as.ee.component.Attachments.EE_APPLICATION_CLASSES_DESCRIPTION);
        final Module module = deploymentUnit.getAttachment(Attachments.MODULE);
        if(JsfVersionMarker.isJsfDisabled(deploymentUnit)) {
            return;
        }
        if (index == null) {
            return;
        }
        if (module == null) {
            return;
        }
        if (!DeploymentTypeMarker.isType(DeploymentType.WAR, deploymentUnit)) {
            return;
        }
        final Set<String> managedBeanClasses = new HashSet<String>();
        handleAnnotations(index, managedBeanClasses);
        processXmlManagedBeans(deploymentUnit, managedBeanClasses);
        processPhaseListeners(deploymentUnit, managedBeanClasses);
        for (String managedBean : managedBeanClasses) {
            //try and load the class, and skip the class if it cannot be loaded
            //this is not ideal, but we are not allowed to let the deployment
            //fail due to missing managed beans
            try {
                final Class<?> componentClass = module.getClassLoader().loadClass(managedBean);
                componentClass.getConstructor();
            } catch (ClassNotFoundException e) {
                JSFLogger.ROOT_LOGGER.managedBeanLoadFail(managedBean);
                continue;
            } catch (NoSuchMethodException e) {
                JSFLogger.ROOT_LOGGER.managedBeanNoDefaultConstructor(managedBean);
                continue;
            }
            installManagedBeanComponent(managedBean, moduleDescription, deploymentUnit, applicationClassesDescription);
        }

    }

    /**
     * Parse the faces config files looking for managed bean classes. The parser is quite
     * simplistic as the only information we need is the managed-bean-class element
     */
    private void processXmlManagedBeans(final DeploymentUnit deploymentUnit, final Set<String> managedBeanClasses) {
        for (final VirtualFile facesConfig : getConfigurationFiles(deploymentUnit)) {
            InputStream is = null;
            try {
                is = facesConfig.openStream();
                final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
                inputFactory.setXMLResolver(NoopXMLResolver.create());
                XMLStreamReader parser = inputFactory.createXMLStreamReader(is);
                StringBuilder className = null;
                int indent = 0;
                boolean managedBean = false;
                boolean managedBeanClass = false;
                while (true) {
                    int event = parser.next();
                    if (event == XMLStreamConstants.END_DOCUMENT) {
                        parser.close();
                        break;
                    }
                    if (event == XMLStreamConstants.START_ELEMENT) {
                        indent++;
                        if (indent == 2) {
                            if (parser.getLocalName().equals(MANAGED_BEAN)) {
                                managedBean = true;
                            }
                        } else if (indent == 3 && managedBean) {
                            if (parser.getLocalName().equals(MANAGED_BEAN_CLASS)) {
                                managedBeanClass = true;
                                className = new StringBuilder();
                            }
                        }

                    } else if (event == XMLStreamConstants.END_ELEMENT) {
                        indent--;
                        managedBeanClass = false;
                        if (indent == 1) {
                            managedBean = false;
                        }
                        if (className != null) {
                            managedBeanClasses.add(className.toString().trim());
                            className = null;
                        }
                    } else if (managedBeanClass && event == XMLStreamConstants.CHARACTERS) {
                        className.append(parser.getText());
                    }
                }
            } catch (Exception e) {
                JSFLogger.ROOT_LOGGER.managedBeansConfigParseFailed(facesConfig);
            } finally {
                try {
                    if (is != null) {
                        is.close();
                    }
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
    }

    /**
     * WFLY-6617
     * According to  JSF 2.2 spec, it should be possible to inject beans using @EJB annotation into
     * PhaseListeners.
     */
    private void processPhaseListeners(final DeploymentUnit deploymentUnit, final Set<String> managedBeanClasses) {
        for (final VirtualFile facesConfig : getConfigurationFiles(deploymentUnit)) {
            InputStream is = null;
            try {
                is = facesConfig.openStream();
                final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
                inputFactory.setXMLResolver(NoopXMLResolver.create());
                XMLStreamReader parser = inputFactory.createXMLStreamReader(is);
                StringBuilder phaseListenerName = null;
                int indent = 0;
                boolean lifecycle = false;
                boolean phaseListener = false;
                while (true) {
                    int event = parser.next();
                    if (event == XMLStreamConstants.END_DOCUMENT) {
                        parser.close();
                        break;
                    }
                    if (event == XMLStreamConstants.START_ELEMENT) {
                        indent++;
                        if (indent == 2) {
                            if(parser.getLocalName().equals(LIFECYCLE)){
                                lifecycle = true;
                            }
                        } else if (indent == 3 && lifecycle) {
                            if(parser.getLocalName().equals(PHASE_LISTENER)){
                                phaseListener = true;
                                phaseListenerName = new StringBuilder();
                            }
                        }
                    } else if (event == XMLStreamConstants.END_ELEMENT) {
                        indent--;
                        phaseListener = false;
                        if (indent == 1) {
                            lifecycle = false;
                        }
                        if(phaseListenerName != null){
                            managedBeanClasses.add(phaseListenerName.toString().trim());
                            phaseListenerName = null;
                        }
                    } else if (phaseListener && event == XMLStreamConstants.CHARACTERS) {
                        phaseListenerName.append(parser.getText());
                    }
                }
            } catch (Exception e) {
                JSFLogger.ROOT_LOGGER.phaseListenersConfigParseFailed(facesConfig);
            } finally {
                try {
                    if (is != null) {
                        is.close();
                    }
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
    }

    public Set<VirtualFile> getConfigurationFiles(DeploymentUnit deploymentUnit) {
        final Set<VirtualFile> ret = new HashSet<VirtualFile>();
        final List<ResourceRoot> resourceRoots = DeploymentUtils.allResourceRoots(deploymentUnit);
        for (final ResourceRoot resourceRoot : resourceRoots) {
            final VirtualFile webInfFacesConfig = resourceRoot.getRoot().getChild(WEB_INF_FACES_CONFIG);
            if (webInfFacesConfig.exists()) {
                ret.add(webInfFacesConfig);
            }
            //look for files that end in .faces-config.xml
            final VirtualFile metaInf = resourceRoot.getRoot().getChild("META-INF");
            if (metaInf.exists() && metaInf.isDirectory()) {
                for (final VirtualFile file : metaInf.getChildren()) {
                    if (file.getName().equals("faces-config.xml") || file.getName().endsWith(".faces-config.xml")) {
                        ret.add(file);
                    }
                }
            }
        }
        String configFiles = null;
        //now look for files in the javax.faces.CONFIG_FILES context param
        final WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
        if (warMetaData != null) {
            final WebMetaData webMetaData = warMetaData.getWebMetaData();
            if (webMetaData != null) {
                final List<ParamValueMetaData> contextParams = webMetaData.getContextParams();
                if (contextParams != null) {
                    for (final ParamValueMetaData param : contextParams) {
                        if (param.getParamName().equals(CONFIG_FILES)) {
                            configFiles = param.getParamValue();
                            break;
                        }
                    }
                }
            }
        }
        if (configFiles != null) {
            final String[] files = configFiles.split(",");
            final ResourceRoot deploymentRoot = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_ROOT);
            if (deploymentRoot != null) {
                for (final String file : files) {
                    final VirtualFile configFile = deploymentRoot.getRoot().getChild(file);
                    if (configFile.exists()) {
                        ret.add(configFile);
                    }
                }
            }
        }
        return ret;
    }

    private void handleAnnotations(final CompositeIndex index, final Set<String> managedBeanClasses) throws DeploymentUnitProcessingException {
        final List<AnnotationInstance> annotations = index.getAnnotations(MANAGED_BEAN_ANNOTATION);
        if (annotations != null) {
            for (final AnnotationInstance annotation : annotations) {

                final AnnotationTarget target = annotation.target();
                if (target instanceof ClassInfo) {
                    final String className = ((ClassInfo) target).name().toString();
                    managedBeanClasses.add(className);
                } else {
                    throw new DeploymentUnitProcessingException(JSFLogger.ROOT_LOGGER.invalidManagedBeanAnnotation(target));
                }
            }
        }
    }

    private void installManagedBeanComponent(String className, final EEModuleDescription moduleDescription, final DeploymentUnit deploymentUnit, final EEApplicationClasses applicationClassesDescription) {
        final ComponentDescription componentDescription = new WebComponentDescription(MANAGED_BEAN.toString() + "." + className, className, moduleDescription, deploymentUnit.getServiceName(), applicationClassesDescription);
        moduleDescription.addComponent(componentDescription);
        deploymentUnit.addToAttachmentList(WebComponentDescription.WEB_COMPONENTS, componentDescription.getStartServiceName());
    }

    @Override
    public void undeploy(final DeploymentUnit context) {

    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2008, 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.wildfly.iiop.openjdk.rmi;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.WeakHashMap;

import org.omg.CORBA.Any;
import org.wildfly.iiop.openjdk.logging.IIOPLogger;

/**
 * This is a RMI/IIOP metadata conversion utility class.
 *

 * Routines here are conforming to the "Java(TM) Language to IDL Mapping
 * Specification", version 1.1 (01-06-07).
 *
 * @author Ole Husgaard
 * @version $Revision: 81018 $
 */
public class Util {

    /**
     * Return the IDL type name for the given class.
     * Here we use the mapping for parameter types and return values.
     */
    public static String getTypeIDLName(Class cls)
            throws RMIIIOPViolationException {

        if (cls.isPrimitive())
            return PrimitiveAnalysis.getPrimitiveAnalysis(cls).getIDLName();

        if (cls.isArray()) {
            // boxedRMI 1.3.6
            Class componentClass = cls;
            int sequence = 0;
            while (componentClass.isArray()) {
                componentClass = componentClass.getComponentType();
                ++sequence;
            }

            String idlName = getTypeIDLName(componentClass);
            int idx = idlName.lastIndexOf("::");
            String idlModule = idlName.substring(0, idx + 2);
            String baseName = idlName.substring(idx + 2);
            return "::org::omg::boxedRMI" + idlModule + "seq" + sequence + "_" + baseName;
        }

        // special classes
        if (cls == java.lang.String.class)
            return "::CORBA::WStringValue";
        if (cls == java.lang.Object.class)
            return "::java::lang::_Object";
        if (cls == java.lang.Class.class)
            return "::javax::rmi::CORBA::ClassDesc";
        if (cls == java.io.Serializable.class)
            return "::java::io::Serializable";
        if (cls == java.io.Externalizable.class)
            return "::java::io::Externalizable";
        if (cls == java.rmi.Remote.class)
            return "::java::rmi::Remote";
        if (cls == org.omg.CORBA.Object.class)
            return "::CORBA::Object";

        // remote interface?
        if (cls.isInterface() && java.rmi.Remote.class.isAssignableFrom(cls)) {
            InterfaceAnalysis ia = InterfaceAnalysis.getInterfaceAnalysis(cls);

            return ia.getIDLModuleName() + "::" + ia.getIDLName();
        }

        // IDL interface?
        if (cls.isInterface() &&
                org.omg.CORBA.Object.class.isAssignableFrom(cls) &&
                org.omg.CORBA.portable.IDLEntity.class.isAssignableFrom(cls)) {
            InterfaceAnalysis ia = InterfaceAnalysis.getInterfaceAnalysis(cls);

            return ia.getIDLModuleName() + "::" + ia.getIDLName();
        }

        // exception?
        if (Throwable.class.isAssignableFrom(cls)) {
            if (Exception.class.isAssignableFrom(cls) &&
                    !RuntimeException.class.isAssignableFrom(cls)) {
                ExceptionAnalysis ea = ExceptionAnalysis.getExceptionAnalysis(cls);

                return ea.getIDLModuleName() + "::" + ea.getIDLName();
            }
        }

        // got to be value
        ValueAnalysis va = ValueAnalysis.getValueAnalysis(cls);

        return va.getIDLModuleName() + "::" + va.getIDLName();
    }

    /**
     * Check if this class is valid for RMI/IIOP mapping.
     * This method will either throw an exception or return true.
     */
    public static boolean isValidRMIIIOP(Class cls)
            throws RMIIIOPViolationException {
        if (cls.isPrimitive())
            return true;

        if (cls.isArray())
            return isValidRMIIIOP(cls.getComponentType());

        // special interfaces
        if (cls == Serializable.class || cls == Externalizable.class)
            return true;

        // interface?
        if (cls.isInterface() && java.rmi.Remote.class.isAssignableFrom(cls)) {
            InterfaceAnalysis.getInterfaceAnalysis(cls);
            return true;
        }

        // exception?
        if (Throwable.class.isAssignableFrom(cls)) {
            if (Exception.class.isAssignableFrom(cls) &&
                    !RuntimeException.class.isAssignableFrom(cls)) {
                ExceptionAnalysis.getExceptionAnalysis(cls);
            }
            return true;
        }

        // special values
        if (cls == Object.class || cls == String.class || cls == Class.class)
            return true;

        // got to be value
        ValueAnalysis.getValueAnalysis(cls);
        return true;
    }

    /**
     * Insert a java primitive into an Any.
     * The primitive is assumed to be wrapped in one of the primitive
     * wrapper classes.
     */
    public static void insertAnyPrimitive(Any any, Object primitive) {
        Class type = primitive.getClass();

        if (type == Boolean.class)
            any.insert_boolean(((Boolean) primitive).booleanValue());
        else if (type == Character.class)
            any.insert_wchar(((Character) primitive).charValue());
        else if (type == Byte.class)
            any.insert_octet(((Byte) primitive).byteValue());
        else if (type == Short.class)
            any.insert_short(((Short) primitive).shortValue());
        else if (type == Integer.class)
            any.insert_long(((Integer) primitive).intValue());
        else if (type == Long.class)
            any.insert_longlong(((Long) primitive).longValue());
        else if (type == Float.class)
            any.insert_float(((Float) primitive).floatValue());
        else if (type == Double.class)
            any.insert_double(((Double) primitive).doubleValue());
        else
            throw IIOPLogger.ROOT_LOGGER.notAPrimitive(type.getName());
    }

    /**
     * Map Java name to IDL name, as per sections 1.3.2.3, 1.3.2.4 and
     * 1.3.2.2.
     * This only works for a single name component, without a qualifying
     * dot.
     */
    public static String javaToIDLName(String name) {
        if (name == null || "".equals(name) || name.indexOf('.') != -1)
            throw IIOPLogger.ROOT_LOGGER.nameCannotBeNullEmptyOrQualified();

        StringBuffer res = new StringBuffer(name.length());

        if (name.charAt(0) == '_')
            res.append('J'); // 1.3.2.3

        for (int i = 0; i < name.length(); ++i) {
            char c = name.charAt(i);

            if (isLegalIDLIdentifierChar(c))
                res.append(c);
            else // 1.3.2.4
                res.append('U').append(toHexString((int) c));
        }

        String s = res.toString();

        if (isReservedIDLKeyword(s))
            return "_" + s;
        else
            return s;
    }

    /**
     * Return the IR global ID of the given class or interface.
     * This is described in section 1.3.5.7.
     * The returned string is in the RMI hashed format, like
     * "RMI:java.util.Hashtable:C03324C0EA357270:13BB0F25214AE4B8".
     */
    public static String getIRIdentifierOfClass(Class cls) {
        if (cls.isPrimitive())
            throw IIOPLogger.ROOT_LOGGER.primitivesHaveNoIRIds();

        String result = (String) classIRIdentifierCache.get(cls);
        if (result != null)
            return result;

        String name = cls.getName();
        StringBuffer b = new StringBuffer("RMI:");

        for (int i = 0; i < name.length(); ++i) {
            char c = name.charAt(i);

            if (c < 256)
                b.append(c);
            else
                b.append("\\U").append(toHexString((int) c));
        }

        long clsHash = getClassHashCode(cls);

        b.append(':').append(toHexString(clsHash));

        ObjectStreamClass osClass = ObjectStreamClass.lookup(cls);
        if (osClass != null) {
            long serialVersionUID = osClass.getSerialVersionUID();

            if (clsHash != serialVersionUID)
                b.append(':').append(toHexString(serialVersionUID));
        }

        result = b.toString();

        classIRIdentifierCache.put(cls, result);

        return result;
    }

    // Private -------------------------------------------------------

    /**
     * A cache for calculated class hash codes.
     */
    private static Map classHashCodeCache =
            Collections.synchronizedMap(new WeakHashMap());

    /**
     * A cache for class IR identifiers.
     */
    private static Map classIRIdentifierCache =
            Collections.synchronizedMap(new WeakHashMap());

    /**
     * Reserved IDL keywords.
     *

     * Section 1.3.2.2 says that Java identifiers with these names
     * should have prepended an underscore.
     */
    private static final String[] reservedIDLKeywords = new String[]{
            "abstract",
            "any",
            "attribute",
            "boolean",
            "case",
            "char",
            "const",
            "context",
            "custom",
            "default",
            "double",
            "exception",
            "enum",
            "factory",
            "FALSE",
            "fixed",
            "float",
            "in",
            "inout",
            "interface",
            "local",
            "long",
            "module",
            "native",
            "Object",
            "octet",
            "oneway",
            "out",
            "private",
            "public",
            "raises",
            "readonly",
            "sequence",
            "short",
            "string",
            "struct",
            "supports",
            "switch",
            "TRUE",
            "truncatable",
            "typedef",
            "unsigned",
            "union",
            "ValueBase",
            "valuetype",
            "void",
            "wchar",
            "wstring"
    };

    static {
        // Initialize caches
        classHashCodeCache = Collections.synchronizedMap(new WeakHashMap());

        classIRIdentifierCache = Collections.synchronizedMap(new WeakHashMap());
    }

    /**
     * Convert an integer to a 16-digit hex string.
     */
    private static String toHexString(int i) {
        String s = Integer.toHexString(i).toUpperCase(Locale.ENGLISH);

        if (s.length() < 8)
            return "00000000".substring(8 - s.length()) + s;
        else
            return s;
    }

    /**
     * Convert a long to a 16-digit hex string.
     */
    private static String toHexString(long l) {
        String s = Long.toHexString(l).toUpperCase(Locale.ENGLISH);

        if (s.length() < 16)
            return "0000000000000000".substring(16 - s.length()) + s;
        else
            return s;
    }

    /**
     * Determine if the argument is a reserved IDL keyword.
     */
    private static boolean isReservedIDLKeyword(String s) {
        // TODO: faster lookup
        for (int i = 0; i < reservedIDLKeywords.length; ++i)
            if (reservedIDLKeywords[i].equals(s))
                return true;
        return false;
    }

    /**
     * Determine if a char is a legal IDL identifier character.
     */
    private static boolean isLegalIDLIdentifierChar(char c) {
        if (c >= 0x61 && c <= 0x7a)
            return true; // lower case letter

        if (c >= 0x30 && c <= 0x39)
            return true; // digit

        if (c >= 0x41 && c <= 0x5a)
            return true; // upper case letter

        if (c == '_')
            return true; // underscore

        return false;
    }

    /**
     * Determine if a char is legal start of an IDL identifier.
     */
    private static boolean isLegalIDLStartIdentifierChar(char c) {
        if (c >= 0x61 && c <= 0x7a)
            return true; // lower case letter

        if (c >= 0x41 && c <= 0x5a)
            return true; // upper case letter

        return false;
    }

    /**
     * Return the class hash code, as specified in "The Common Object
     * Request Broker: Architecture and Specification" (01-02-33),
     * section 10.6.2.
     */
    static long getClassHashCode(Class cls) {
        // The simple cases
        if (cls.isInterface())
            return 0;
        if (!Serializable.class.isAssignableFrom(cls))
            return 0;
        if (Externalizable.class.isAssignableFrom(cls))
            return 1;

        // Try cache
        Long l = (Long) classHashCodeCache.get(cls);
        if (l != null)
            return l.longValue();

        // Has to calculate the hash.

        ByteArrayOutputStream baos = new ByteArrayOutputStream(256);
        DataOutputStream dos = new DataOutputStream(baos);

        // Step 1
        Class superClass = cls.getSuperclass();
        if (superClass != null && superClass != Object.class) {
            try {
                dos.writeLong(getClassHashCode(superClass));
            } catch (IOException ex) {
                throw IIOPLogger.ROOT_LOGGER.unexpectedException(ex);
            }
        }

        // Step 2
        boolean hasWriteObject = false;
        try {
            Method m;
            int mods;

            m = cls.getDeclaredMethod("writeObject",
                    new Class[]{ObjectOutputStream.class});
            mods = m.getModifiers();

            if (!Modifier.isPrivate(mods) && !Modifier.isStatic(mods))
                hasWriteObject = true;
        } catch (NoSuchMethodException ex) {
            // ignore
        }
        try {
            dos.writeInt(hasWriteObject ? 2 : 1);
        } catch (IOException ex) {
            throw IIOPLogger.ROOT_LOGGER.unexpectedException(ex);
        }

        // Step 3
        Field[] fields = cls.getDeclaredFields();
        SortedSet set = new TreeSet(new FieldComparator());

        for (int i = 0; i < fields.length; ++i) {
            int mods = fields[i].getModifiers();

            if (!Modifier.isStatic(mods) && !Modifier.isTransient(mods))
                set.add(fields[i]);
        }
        Iterator iter = set.iterator();
        try {
            while (iter.hasNext()) {
                Field f = (Field) iter.next();

                dos.writeUTF(f.getName());
                dos.writeUTF(getSignature(f.getType()));
            }
        } catch (IOException ex) {
            throw IIOPLogger.ROOT_LOGGER.unexpectedException(ex);
        }

        // Convert to byte[]
        try {
            dos.flush();
        } catch (IOException ex) {
            throw IIOPLogger.ROOT_LOGGER.unexpectedException(ex);
        }
        byte[] bytes = baos.toByteArray();

        // Calculate SHA digest
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("SHA");
        } catch (NoSuchAlgorithmException ex) {
            throw IIOPLogger.ROOT_LOGGER.unavailableSHADigest(ex);
        }
        digest.update(bytes);
        byte[] sha = digest.digest();

        // Calculate hash as per section 10.6.2
        long hash = 0;
        for (int i = 0; i < Math.min(8, sha.length); i++) {
            hash += (long) (sha[i] & 255) << (i * 8);
        }

        // Save in cache
        classHashCodeCache.put(cls, new Long(hash));

        return hash;
    }

    /**
     * Calculate the signature of a class, according to the Java VM
     * specification, section 4.3.2.
     */
    private static String getSignature(Class cls) {
        if (cls.isArray())
            return "[" + cls.getComponentType();

        if (cls.isPrimitive()) {
            if (cls == Byte.TYPE)
                return "B";
            if (cls == Character.TYPE)
                return "C";
            if (cls == Double.TYPE)
                return "D";
            if (cls == Float.TYPE)
                return "F";
            if (cls == Integer.TYPE)
                return "I";
            if (cls == Long.TYPE)
                return "J";
            if (cls == Short.TYPE)
                return "S";
            if (cls == Boolean.TYPE)
                return "Z";
            throw IIOPLogger.ROOT_LOGGER.unknownPrimitiveType(cls.getName());
        }

        return "L" + cls.getName().replace('.', '/') + ";";
    }

    /**
     * Calculate the signature of a method, according to the Java VM
     * specification, section 4.3.3.
     */
    private static String getSignature(Method method) {
        StringBuffer b = new StringBuffer("(");
        Class[] parameterTypes = method.getParameterTypes();

        for (int i = 0; i < parameterTypes.length; ++i)
            b.append(getSignature(parameterTypes[i]));

        b.append(')').append(getSignature(method.getReturnType()));

        return b.toString();
    }

    /**
     * Handle mappings for primitive types, as per section 1.3.3.
     */
    static String primitiveTypeIDLName(Class type) {
        if (type == Void.TYPE)
            return "void";
        if (type == Boolean.TYPE)
            return "boolean";
        if (type == Character.TYPE)
            return "wchar";
        if (type == Byte.TYPE)
            return "octet";
        if (type == Short.TYPE)
            return "short";
        if (type == Integer.TYPE)
            return "long";
        if (type == Long.TYPE)
            return "long long";
        if (type == Float.TYPE)
            return "float";
        if (type == Double.TYPE)
            return "double";
        throw IIOPLogger.ROOT_LOGGER.notAPrimitive(type.getName());
    }

    // Inner classes -------------------------------------------------

    /**
     * A Comparator for Fields, ordering the
     * fields according to the lexicographic ordering of their Java names.
     */
    private static class FieldComparator
            implements Comparator {
        public int compare(Object o1, Object o2) {
            if (o1 == o2)
                return 0;

            String n1 = ((Field) o1).getName();
            String n2 = ((Field) o2).getName();

            return n1.compareTo(n2);
        }
    }

}


/*
 * 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.connector.deployers.ra.processors;

import static org.jboss.as.connector.logging.ConnectorLogger.ROOT_LOGGER;

import java.io.File;
import java.util.List;
import java.util.Locale;

import org.jboss.as.connector.logging.ConnectorLogger;
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.vfs.VirtualFile;
import org.jboss.vfs.VirtualFileFilter;

/**
 * Load native libraries for .rar deployments
 *
 * @author Jesper Pedersen
 */
public class RaNativeProcessor implements DeploymentUnitProcessor {

    /**
     * Construct a new instance.
     */
    public RaNativeProcessor() {
    }

    /**
     * Process a deployment for standard ra deployment files. Will parse the xml
     * file and attach a configuration discovered during processing.
     * @param phaseContext the deployment unit context
     * @throws DeploymentUnitProcessingException
     */
    @Override
    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final VirtualFile deploymentRoot = phaseContext.getDeploymentUnit().getAttachment(Attachments.DEPLOYMENT_ROOT).getRoot();

        process(deploymentRoot);
    }

    public static void process(VirtualFile deploymentRoot) throws DeploymentUnitProcessingException {
        if (deploymentRoot == null || !deploymentRoot.exists())
            return;

        final String deploymentRootName = deploymentRoot.getName().toLowerCase(Locale.ENGLISH);
        if (!deploymentRootName.endsWith(".rar")) {
            return;
        }

        try {
            List<VirtualFile> libs = deploymentRoot.getChildrenRecursively(new LibraryFilter());

            if (libs != null && libs.size() > 0) {
                for (VirtualFile vf : libs) {
                    String fileName = vf.getName().toLowerCase(Locale.ENGLISH);
                    ROOT_LOGGER.tracef("Processing library: %s", fileName);

                    try {
                        File f = vf.getPhysicalFile();
                        System.load(f.getAbsolutePath());

                        ROOT_LOGGER.debugf("Loaded library: %s", f.getAbsolutePath());

                    } catch (Throwable t) {
                        ROOT_LOGGER.debugf("Unable to load library: %s", fileName);
                    }
                }
            }
        } catch (Exception e) {
            throw ConnectorLogger.ROOT_LOGGER.failedToLoadNativeLibraries(e);
        }
    }

    public void undeploy(final DeploymentUnit context) {
    }

    private static class LibraryFilter implements VirtualFileFilter {

        public boolean accepts(VirtualFile vf) {
            if (vf == null)
                return false;

            if (vf.isFile()) {
                String fileName = vf.getName().toLowerCase(Locale.ENGLISH);
                if (fileName.endsWith(".a") || fileName.endsWith(".so") || fileName.endsWith(".dll")) {
                    return true;
                }
            }

            return false;
        }
    }
}


/*
 * Copyright 2016 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.wildfly.extension.batch.jberet.deployment;

import org.wildfly.common.Assert;
import org.wildfly.security.permission.AbstractNameSetOnlyPermission;
import org.wildfly.security.util.StringEnumeration;
import org.wildfly.security.util.StringMapping;

/**
 * A general batch permission.  The permission {@code name} must be one of the following:
 *

 *
{@code start}

 *
{@code stop}

 *
{@code restart}

 *
{@code abandon}

 *
{@code read}

 *

 * The {@code actions} are not used and should be empty or {@code null}.
 *
 * @author David M. Lloyd
 * @author James R. Perkins
 */
public final class BatchPermission extends AbstractNameSetOnlyPermission<BatchPermission> {

    private static final long serialVersionUID = 6124294238228442419L;

    private static final StringEnumeration strings = StringEnumeration.of(
            "start",
            "stop",
            "restart",
            "abandon",
            "read"
    );

    private static final StringMapping<BatchPermission> mapping = new StringMapping<>(strings, BatchPermission::new);

    private static final BatchPermission allPermission = new BatchPermission("*");

    /**
     * Construct a new instance.
     *
     * @param name the name of the permission
     */
    public BatchPermission(final String name) {
        this(name, null);
    }

    /**
     * Construct a new instance.
     *
     * @param name    the name of the permission
     * @param actions the actions (should be empty)
     */
    public BatchPermission(final String name, final String actions) {
        super(name, strings);
        requireEmptyActions(actions);
    }

    public BatchPermission withName(final String name) {
        return forName(name);
    }

    /**
     * Get the permission with the given name.
     *
     * @param name the name (must not be {@code null})
     *
     * @return the permission (not {@code null})
     *
     * @throws IllegalArgumentException if the name is not valid
     */
    public static BatchPermission forName(final String name) {
        Assert.checkNotNullParam("name", name);
        return "*".equals(name) ? allPermission : mapping.getItemByString(name);
    }
}


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

import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.transaction.TransactionSynchronizationRegistry;
import javax.transaction.UserTransaction;

import org.jboss.as.ee.component.Attachments;
import org.jboss.as.ee.component.ComponentDescription;
import org.jboss.as.ee.component.ComponentNamingMode;
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.naming.ManagedReferenceInjector;
import org.jboss.as.naming.ServiceBasedNamingStore;
import org.jboss.as.naming.deployment.ContextNames;
import org.jboss.as.naming.service.BinderService;
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.txn.service.TransactionSynchronizationRegistryService;
import org.jboss.as.txn.service.UserTransactionBindingService;
import org.jboss.as.txn.service.UserTransactionAccessControlService;
import org.jboss.as.txn.service.UserTransactionService;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceTarget;

/**
 * Processor responsible for binding transaction related resources to JNDI.
 *

 * Unlike other resource injections this binding happens for all eligible components,
 * regardless of the presence of the {@link javax.annotation.Resource} annotation.
 *
 * @author Stuart Douglas
 * @author Richard Opalka
 */
public class TransactionJndiBindingProcessor implements DeploymentUnitProcessor {
    @Override
    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        final EEModuleDescription moduleDescription = deploymentUnit.getAttachment(Attachments.EE_MODULE_DESCRIPTION);

        if(moduleDescription == null) {
            return;
        }

        final ServiceTarget serviceTarget = phaseContext.getServiceTarget();
        //if this is a war we need to bind to the modules comp namespace
        if(DeploymentTypeMarker.isType(DeploymentType.WAR,deploymentUnit)) {
            final ServiceName moduleContextServiceName = ContextNames.contextServiceNameOfModule(moduleDescription.getApplicationName(),moduleDescription.getModuleName());
            bindServices(deploymentUnit, serviceTarget, moduleContextServiceName);
        }

        for(ComponentDescription component : moduleDescription.getComponentDescriptions()) {
            if(component.getNamingMode() == ComponentNamingMode.CREATE) {
                final ServiceName compContextServiceName = ContextNames.contextServiceNameOfComponent(moduleDescription.getApplicationName(),moduleDescription.getModuleName(),component.getComponentName());
                bindServices(deploymentUnit, serviceTarget, compContextServiceName);
            }
        }

    }

    /**
     * Binds the java:comp/UserTransaction service and the java:comp/TransactionSynchronizationRegistry
     *
     * @param deploymentUnit The deployment unit
     * @param serviceTarget The service target
     * @param contextServiceName The service name of the context to bind to
     */
    private void bindServices(DeploymentUnit deploymentUnit, ServiceTarget serviceTarget, ServiceName contextServiceName) {

        final ServiceName userTransactionServiceName = contextServiceName.append("UserTransaction");
        final UserTransactionBindingService userTransactionBindingService = new UserTransactionBindingService("UserTransaction");
        serviceTarget.addService(userTransactionServiceName, userTransactionBindingService)
            .addDependency(UserTransactionAccessControlService.SERVICE_NAME, UserTransactionAccessControlService.class,userTransactionBindingService.getUserTransactionAccessControlServiceInjector())
            .addDependency(UserTransactionService.INTERNAL_SERVICE_NAME, UserTransaction.class,
                    new ManagedReferenceInjector<UserTransaction>(userTransactionBindingService.getManagedObjectInjector()))
            .addDependency(contextServiceName, ServiceBasedNamingStore.class, userTransactionBindingService.getNamingStoreInjector())
            .install();
        final Map<ServiceName, Set<ServiceName>> jndiComponentDependencies = deploymentUnit.getAttachment(org.jboss.as.server.deployment.Attachments.COMPONENT_JNDI_DEPENDENCIES);
        Set<ServiceName> jndiDependencies = jndiComponentDependencies.get(contextServiceName);
        if (jndiDependencies == null) {
            jndiComponentDependencies.put(contextServiceName, jndiDependencies = new HashSet<>());
        }
        jndiDependencies.add(userTransactionServiceName);

        final ServiceName transactionSynchronizationRegistryName = contextServiceName.append("TransactionSynchronizationRegistry");
        BinderService transactionSyncBinderService = new BinderService("TransactionSynchronizationRegistry");
        serviceTarget.addService(transactionSynchronizationRegistryName, transactionSyncBinderService)
            .addDependency(TransactionSynchronizationRegistryService.INTERNAL_SERVICE_NAME, TransactionSynchronizationRegistry.class,
                    new ManagedReferenceInjector<TransactionSynchronizationRegistry>(transactionSyncBinderService.getManagedObjectInjector()))
            .addDependency(contextServiceName, ServiceBasedNamingStore.class, transactionSyncBinderService.getNamingStoreInjector())
            .install();
        jndiDependencies.add(transactionSynchronizationRegistryName);
    }

    @Override
    public void undeploy(DeploymentUnit context) {
    }
}


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

import java.util.Collections;
import java.util.EnumSet;
import java.util.List;

import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;

import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.dmr.ModelNode;
import org.jboss.staxmapper.XMLElementReader;
import org.jboss.staxmapper.XMLExtendedStreamReader;

import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
import static org.jboss.as.controller.parsing.ParseUtils.missingRequired;
import static org.jboss.as.controller.parsing.ParseUtils.readStringAttributeElement;
import static org.jboss.as.controller.parsing.ParseUtils.requireNoAttributes;
import static org.jboss.as.controller.parsing.ParseUtils.requireNoContent;
import static org.jboss.as.controller.parsing.ParseUtils.requireNoNamespaceAttribute;
import static org.jboss.as.controller.parsing.ParseUtils.unexpectedAttribute;
import static org.jboss.as.controller.parsing.ParseUtils.unexpectedElement;
import static org.jboss.as.ejb3.subsystem.EJB3SubsystemModel.DEFAULT_DATA_STORE;
import static org.jboss.as.ejb3.subsystem.EJB3SubsystemModel.FILE_DATA_STORE;
import static org.jboss.as.ejb3.subsystem.EJB3SubsystemModel.INSTANCE_ACQUISITION_TIMEOUT;
import static org.jboss.as.ejb3.subsystem.EJB3SubsystemModel.INSTANCE_ACQUISITION_TIMEOUT_UNIT;
import static org.jboss.as.ejb3.subsystem.EJB3SubsystemModel.MAX_POOL_SIZE;
import static org.jboss.as.ejb3.subsystem.EJB3SubsystemModel.PATH;
import static org.jboss.as.ejb3.subsystem.EJB3SubsystemModel.RELATIVE_TO;
import static org.jboss.as.ejb3.subsystem.EJB3SubsystemModel.SERVICE;
import static org.jboss.as.ejb3.subsystem.EJB3SubsystemModel.STRICT_MAX_BEAN_INSTANCE_POOL;
import static org.jboss.as.ejb3.subsystem.EJB3SubsystemModel.TIMER_SERVICE;

/**
 * User: Jaikiran Pai
 */
public class EJB3Subsystem11Parser implements XMLElementReader<List<ModelNode>> {

    EJB3Subsystem11Parser() {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readElement(final XMLExtendedStreamReader reader, final List<ModelNode> operations) throws XMLStreamException {

        final ModelNode ejb3SubsystemAddOperation = new ModelNode();
        ejb3SubsystemAddOperation.get(OP).set(ADD);
        ejb3SubsystemAddOperation.get(OP_ADDR).add(SUBSYSTEM, EJB3Extension.SUBSYSTEM_NAME);

        operations.add(ejb3SubsystemAddOperation);

        // elements
        final EnumSet<EJB3SubsystemXMLElement> encountered = EnumSet.noneOf(EJB3SubsystemXMLElement.class);
        while (reader.hasNext() && reader.nextTag() != XMLStreamConstants.END_ELEMENT) {
            switch (EJB3SubsystemNamespace.forUri(reader.getNamespaceURI())) {
                case EJB3_1_1: {
                    final EJB3SubsystemXMLElement element = EJB3SubsystemXMLElement.forName(reader.getLocalName());
                    if (!encountered.add(element)) {
                        throw unexpectedElement(reader);
                    }
                    switch (element) {
                        case MDB: {
                            // read <mdb>
                            this.parseMDB(reader, operations, ejb3SubsystemAddOperation);
                            break;
                        }
                        case POOLS: {
                            // read <pools>
                            this.parsePools(reader, operations);
                            break;
                        }
                        case SESSION_BEAN: {
                            // read <session-bean>
                            this.parseSessionBean(reader, operations, ejb3SubsystemAddOperation);
                            break;
                        }
                        case TIMER_SERVICE: {
                            parseTimerService(reader, operations);
                            break;
                        }
                        default: {
                            throw unexpectedElement(reader);
                        }
                    }
                    break;
                }
                default: {
                    throw unexpectedElement(reader);
                }
            }
        }
    }

    private ModelNode parseMDB(final XMLExtendedStreamReader reader, List<ModelNode> operations, ModelNode ejb3SubsystemAddOperation) throws XMLStreamException {
        ModelNode mdbModelNode = new ModelNode();
        // no attributes expected
        requireNoAttributes(reader);

        while (reader.hasNext() && reader.nextTag() != XMLStreamConstants.END_ELEMENT) {
            switch (EJB3SubsystemXMLElement.forName(reader.getLocalName())) {
                case BEAN_INSTANCE_POOL_REF: {
                    final String poolName = readStringAttributeElement(reader, EJB3SubsystemXMLAttribute.POOL_NAME.getLocalName());
                    EJB3SubsystemRootResourceDefinition.DEFAULT_MDB_INSTANCE_POOL.parseAndSetParameter(poolName, ejb3SubsystemAddOperation, reader);
                    break;
                }
                case RESOURCE_ADAPTER_REF: {
                    final String resourceAdapterName = readStringAttributeElement(reader, EJB3SubsystemXMLAttribute.RESOURCE_ADAPTER_NAME.getLocalName());
                    EJB3SubsystemRootResourceDefinition.DEFAULT_RESOURCE_ADAPTER_NAME.parseAndSetParameter(resourceAdapterName, ejb3SubsystemAddOperation, reader);
                    break;
                }
                default: {
                    throw unexpectedElement(reader);
                }
            }
        }
        // if the resource-adapter-ref *hasn't* been explicitly specified, then default it to hornetq-ra
        if (!ejb3SubsystemAddOperation.hasDefined(EJB3SubsystemModel.DEFAULT_RESOURCE_ADAPTER_NAME)) {
            final ModelNode defaultRAName = EJB3SubsystemRootResourceDefinition.DEFAULT_RESOURCE_ADAPTER_NAME.getDefaultValue();
            if (defaultRAName != null) {
                ejb3SubsystemAddOperation.get(EJB3SubsystemModel.DEFAULT_RESOURCE_ADAPTER_NAME).set(defaultRAName);
            }
        }

        return mdbModelNode;

    }

    private void parseSessionBean(final XMLExtendedStreamReader reader, final List<ModelNode> operations, ModelNode ejb3SubsystemAddOperation) throws XMLStreamException {
        // no attributes expected
        requireNoAttributes(reader);

        while (reader.hasNext() && reader.nextTag() != XMLStreamConstants.END_ELEMENT) {
            switch (EJB3SubsystemXMLElement.forName(reader.getLocalName())) {
                case STATELESS: {
                    this.parseStatelessBean(reader, operations, ejb3SubsystemAddOperation);
                    break;
                }
                default: {
                    throw unexpectedElement(reader);
                }
            }
        }
    }

    private void parseStatelessBean(final XMLExtendedStreamReader reader, final List<ModelNode> operations, ModelNode ejb3SubsystemAddOperation) throws XMLStreamException {
        // no attributes expected
        requireNoAttributes(reader);

        while (reader.hasNext() && reader.nextTag() != XMLStreamConstants.END_ELEMENT) {
            switch (EJB3SubsystemXMLElement.forName(reader.getLocalName())) {
                case BEAN_INSTANCE_POOL_REF: {
                    final String poolName = readStringAttributeElement(reader, EJB3SubsystemXMLAttribute.POOL_NAME.getLocalName());
                    EJB3SubsystemRootResourceDefinition.DEFAULT_SLSB_INSTANCE_POOL.parseAndSetParameter(poolName, ejb3SubsystemAddOperation, reader);
                    break;
                }
                default: {
                    throw unexpectedElement(reader);
                }
            }
        }
    }

    private void parsePools(final XMLExtendedStreamReader reader, final List<ModelNode> operations) throws XMLStreamException {
        // no attributes expected
        requireNoAttributes(reader);
        while (reader.hasNext() && reader.nextTag() != XMLStreamConstants.END_ELEMENT) {
            switch (EJB3SubsystemXMLElement.forName(reader.getLocalName())) {
                case BEAN_INSTANCE_POOLS: {
                    this.parseBeanInstancePools(reader, operations);
                    break;
                }
                default: {
                    throw unexpectedElement(reader);
                }
            }
        }
    }

    private void parseBeanInstancePools(final XMLExtendedStreamReader reader, List<ModelNode> operations) throws XMLStreamException {
        // no attributes expected
        requireNoAttributes(reader);

        while (reader.hasNext() && reader.nextTag() != XMLStreamConstants.END_ELEMENT) {
            switch (EJB3SubsystemXMLElement.forName(reader.getLocalName())) {
                case STRICT_MAX_POOL: {
                    this.parseStrictMaxPool(reader, operations);
                    break;
                }
                default: {
                    throw unexpectedElement(reader);
                }
            }
        }
    }

    protected void parseStrictMaxPool(final XMLExtendedStreamReader reader, List<ModelNode> operations) throws XMLStreamException {
        final int count = reader.getAttributeCount();
        String poolName = null;
        Integer maxPoolSize = null;
        Long timeout = null;
        String unit = null;
        for (int i = 0; i < count; i++) {
            requireNoNamespaceAttribute(reader, i);
            final String value = reader.getAttributeValue(i);
            final EJB3SubsystemXMLAttribute attribute = EJB3SubsystemXMLAttribute.forName(reader.getAttributeLocalName(i));
            switch (attribute) {
                case NAME:
                    poolName = value;
                    break;
                case MAX_POOL_SIZE:
                    maxPoolSize = StrictMaxPoolResourceDefinition.MAX_POOL_SIZE.parse(value, reader).asInt();
                    break;
                case INSTANCE_ACQUISITION_TIMEOUT:
                    timeout = StrictMaxPoolResourceDefinition.INSTANCE_ACQUISITION_TIMEOUT.parse(value, reader).asLong();
                    break;
                case INSTANCE_ACQUISITION_TIMEOUT_UNIT:
                    unit = StrictMaxPoolResourceDefinition.INSTANCE_ACQUISITION_TIMEOUT_UNIT.parse(value, reader).asString();
                    break;

                default:
                    throw unexpectedAttribute(reader, i);
            }
        }
        requireNoContent(reader);
        if (poolName == null) {
            throw missingRequired(reader, Collections.singleton(EJB3SubsystemXMLAttribute.NAME.getLocalName()));
        }
        // create and add the operation
        operations.add(this.createAddStrictMaxBeanInstancePoolOperation(poolName, maxPoolSize, timeout, unit));
    }

    private void parseTimerService(final XMLExtendedStreamReader reader, List<ModelNode> operations) throws XMLStreamException {

        requireNoAttributes(reader);

        final ModelNode address = new ModelNode();
        address.add(SUBSYSTEM, EJB3Extension.SUBSYSTEM_NAME);
        address.add(SERVICE, TIMER_SERVICE);
        final ModelNode timerServiceAdd = new ModelNode();
        timerServiceAdd.get(OP).set(ADD);
        timerServiceAdd.get(OP_ADDR).set(address);

        String dataStorePath = null;
        String dataStorePathRelativeTo = null;
        while (reader.hasNext() && reader.nextTag() != XMLStreamConstants.END_ELEMENT) {
            switch (EJB3SubsystemXMLElement.forName(reader.getLocalName())) {
                case THREAD_POOL: {
                    final int count = reader.getAttributeCount();
                    for (int i = 0; i < count; i++) {
                        requireNoNamespaceAttribute(reader, i);
                        final EJB3SubsystemXMLAttribute attribute = EJB3SubsystemXMLAttribute.forName(reader.getAttributeLocalName(i));
                        switch (attribute) {
                            case CORE_THREADS:
                                //ignore, no longer supported
                                break;
                            case MAX_THREADS:
                                //ignore, no longer supported
                                break;
                            default:
                                throw unexpectedAttribute(reader, i);
                        }
                    }
                    requireNoContent(reader);
                    break;
                }
                case DATA_STORE: {
                    final int count = reader.getAttributeCount();
                    for (int i = 0; i < count; i++) {
                        requireNoNamespaceAttribute(reader, i);
                        final String value = reader.getAttributeValue(i);
                        final EJB3SubsystemXMLAttribute attribute = EJB3SubsystemXMLAttribute.forName(reader.getAttributeLocalName(i));
                        switch (attribute) {
                            case PATH:
                                if (dataStorePath != null) {
                                    throw unexpectedAttribute(reader, i);
                                }
                                dataStorePath = FileDataStoreResourceDefinition.PATH.parse(value, reader).asString();
                                break;
                            case RELATIVE_TO:
                                if (dataStorePathRelativeTo != null) {
                                    throw unexpectedAttribute(reader, i);
                                }
                                dataStorePathRelativeTo = FileDataStoreResourceDefinition.RELATIVE_TO.parse(value, reader).asString();
                                break;
                            default:
                                throw unexpectedAttribute(reader, i);
                        }
                    }
                    if (dataStorePath == null) {
                        throw missingRequired(reader, Collections.singleton(EJB3SubsystemXMLAttribute.PATH));
                    }
                    timerServiceAdd.get(DEFAULT_DATA_STORE).set("default-file-store");
                    final ModelNode fileDataStoreAdd = new ModelNode();
                    final ModelNode fileDataAddress = address.clone();
                    fileDataAddress.add(FILE_DATA_STORE, "default-file-store");
                    fileDataStoreAdd.get(OP).set(ADD);
                    fileDataStoreAdd.get(OP_ADDR).set(fileDataAddress);
                    fileDataStoreAdd.get(PATH).set(dataStorePath);
                    if (dataStorePathRelativeTo != null) {
                        fileDataStoreAdd.get(RELATIVE_TO).set(dataStorePathRelativeTo);
                    }
                    requireNoContent(reader);
                    break;
                }
                default: {
                    throw unexpectedElement(reader);
                }
            }
        }
        operations.add(timerServiceAdd);
    }

    private ModelNode createAddStrictMaxBeanInstancePoolOperation(final String name, final Integer maxPoolSize, final Long timeout, final String timeoutUnit) {
        // create /subsystem=ejb3/strict-max-bean-instance-pool=name:add(...)
        final ModelNode addStrictMaxPoolOperation = new ModelNode();
        addStrictMaxPoolOperation.get(OP).set(ADD);
        // set the address for this operation
        final PathAddress address = this.getEJB3SubsystemAddress().append(PathElement.pathElement(STRICT_MAX_BEAN_INSTANCE_POOL, name));
        addStrictMaxPoolOperation.get(OP_ADDR).set(address.toModelNode());
        // set the params for the operation
        if (maxPoolSize != null) {
            addStrictMaxPoolOperation.get(MAX_POOL_SIZE).set(maxPoolSize);
        }
        if (timeout != null) {
            addStrictMaxPoolOperation.get(INSTANCE_ACQUISITION_TIMEOUT).set(timeout);
        }
        if (timeoutUnit != null) {
            addStrictMaxPoolOperation.get(INSTANCE_ACQUISITION_TIMEOUT_UNIT).set(timeoutUnit);
        }

        return addStrictMaxPoolOperation;
    }

    private PathAddress getEJB3SubsystemAddress() {
       return EJB3Subsystem12Parser.SUBSYSTEM_PATH;
    }
}


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

import static java.security.AccessController.doPrivileged;

import javax.enterprise.inject.spi.Bean;

import org.jboss.weld.annotated.enhanced.EnhancedAnnotatedType;
import org.jboss.weld.manager.BeanManagerImpl;
import org.jboss.weld.manager.api.WeldInjectionTarget;
import org.jboss.weld.manager.api.WeldInjectionTargetBuilder;
import org.jboss.weld.resources.ClassTransformer;
import org.jboss.weld.util.Beans;
import org.wildfly.security.manager.WildFlySecurityManager;
import org.wildfly.security.manager.action.GetClassLoaderAction;

/**
 * Utility class for working with CDI InjectionTargets
 *
 * @author Jozef Hartinger
 *
 */
public class InjectionTargets {

    private InjectionTargets() {
    }

    /**
    * Creates a new InjectionTarget for a given class. If the interceptionSupport flag is set to true the resulting instance will support
    * interception (support provided by Weld). If an InjectionTarget is created for a component where interception support is implemented
    * through component's view (EJBs, managed beans) the flag must be set to false.
    *
    * @param componentClass
    * @param bean
    * @param beanManager
    * @param interceptionSupport
    * @return
    */
    public static <T> WeldInjectionTarget<T> createInjectionTarget(Class<?> componentClass, Bean<T> bean, BeanManagerImpl beanManager,
            boolean interceptionSupport) {
        final ClassTransformer transformer = beanManager.getServices().get(ClassTransformer.class);
        @SuppressWarnings("unchecked")
        final Class<T> clazz = (Class<T>) componentClass;
        EnhancedAnnotatedType<T> type = transformer.getEnhancedAnnotatedType(clazz, beanManager.getId());

        if (!type.getJavaClass().equals(componentClass)) {
            /*
             * Jasper loads a class with multiple classloaders which is not supported by Weld.
             * If this happens, use a combination of a bean archive identifier and class' classloader hashCode as the BDA ID.
             * This breaks AnnotatedType serialization but that does not matter as these are non-contextual components.
             */
            final ClassLoader classLoader = WildFlySecurityManager.isChecking() ? doPrivileged(new GetClassLoaderAction(componentClass)) : componentClass.getClassLoader();
            final String bdaId = beanManager.getId() + classLoader.hashCode();
            type = transformer.getEnhancedAnnotatedType(clazz, bdaId);
        }

        if (Beans.getBeanConstructor(type) == null) {
            /*
             * For example, AsyncListeners may be CDI-incompatible as long as the application never calls javax.servletAsyncContext#createListener(Class)
             * and only instantiates the listener itself.
             */
            return beanManager.getInjectionTargetFactory(type).createNonProducibleInjectionTarget();
        }
        WeldInjectionTargetBuilder<T> builder = beanManager.createInjectionTargetBuilder(type);
        builder.setBean(bean);
        builder.setResourceInjectionEnabled(false); // because these are all EE components where resource injection is not handled by Weld
        if (interceptionSupport) {
            return builder.build();
        } else {
            // suppress interception/decoration because this is a component for which WF provides interception support
            return builder.setInterceptionEnabled(false).setTargetClassLifecycleCallbacksEnabled(false).setDecorationEnabled(false).build();
        }
    }
}


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

import org.jboss.as.naming.ManagedReferenceFactory;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.service.ServiceBuilder;

/**
 * An injection of a fixed value.
 *
 * @author David M. Lloyd
 */
public final class FixedInjectionSource extends InjectionSource {
    private final Object value;
    private final ManagedReferenceFactory managedReferenceFactory;

    /**
     * Construct a new instance.
     *
     * @param factory The managed reference factory to inject
     * @param value the value to use for equality comparison
     */
    public FixedInjectionSource(final ManagedReferenceFactory factory, final Object value) {
        managedReferenceFactory = factory;
        this.value = value;
    }

    /**
     * {@inheritDoc}
     */
    public void getResourceValue(final ResolutionContext resolutionContext, final ServiceBuilder<?> serviceBuilder, final DeploymentPhaseContext phaseContext, final Injector<ManagedReferenceFactory> injector) {
        injector.inject(managedReferenceFactory);
    }

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof FixedInjectionSource == false) {
            return false;
        }
        FixedInjectionSource other = (FixedInjectionSource) obj;
        return this.equalTo(other);
    }

    private boolean equalTo(final FixedInjectionSource configuration) {
        return configuration != null && value.equals(configuration.value);
    }

    public int hashCode() {
        return value.hashCode();
    }
}


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

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

import java.io.IOException;
import java.io.Serializable;
import java.security.AccessController;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.SynchronizationType;
import javax.transaction.TransactionManager;
import javax.transaction.TransactionSynchronizationRegistry;

import org.jboss.as.jpa.config.Configuration;
import org.jboss.as.jpa.messages.JpaLogger;
import org.jboss.as.jpa.service.PersistenceUnitServiceImpl;
import org.jboss.as.jpa.transaction.TransactionUtil;
import org.jboss.as.jpa.util.JPAServiceNames;
import org.jboss.as.server.CurrentServiceContainer;
import org.jboss.msc.service.ServiceContainer;
import org.jboss.msc.service.ServiceController;
import org.wildfly.transaction.client.ContextTransactionManager;

/**
 * Transaction scoped entity manager will be injected into SLSB or SFSB beans.  At bean invocation time, they
 * will join the active transaction if one is present.  Otherwise, they will simply be cleared at the end of
 * the bean invocation.
 * <p/>
 * This is a proxy for the underlying persistent provider EntityManager.
 *
 * @author Scott Marlow
 */
public class TransactionScopedEntityManager extends AbstractEntityManager implements Serializable {

    private static final long serialVersionUID = 455498112L;

    private final String puScopedName;          // Scoped name of the persistent unit
    private final Map properties;
    private transient EntityManagerFactory emf;
    private final SynchronizationType synchronizationType;
    private transient TransactionSynchronizationRegistry transactionSynchronizationRegistry;
    private transient TransactionManager transactionManager;
    private transient Boolean deferDetach;

    public TransactionScopedEntityManager(String puScopedName, Map properties, EntityManagerFactory emf, SynchronizationType synchronizationType, TransactionSynchronizationRegistry transactionSynchronizationRegistry, TransactionManager transactionManager) {
        this.puScopedName = puScopedName;
        this.properties = properties;
        this.emf = emf;
        this.synchronizationType = synchronizationType;
        this.transactionSynchronizationRegistry = transactionSynchronizationRegistry;
        this.transactionManager = transactionManager;
    }

    @Override
    protected EntityManager getEntityManager() {
        EntityManager entityManager;
        boolean isInTx;

        isInTx = TransactionUtil.isInTx(transactionManager);

        if (isInTx) {
            entityManager = getOrCreateTransactionScopedEntityManager(emf, puScopedName, properties, synchronizationType);
        } else {
            entityManager = NonTxEmCloser.get(puScopedName);
            if (entityManager == null) {
                entityManager = createEntityManager(emf, properties, synchronizationType);
                NonTxEmCloser.add(puScopedName, entityManager);
            }
        }
        return entityManager;
    }

    @Override
    protected boolean isExtendedPersistenceContext() {
        return false;
    }

    @Override
    protected boolean isInTx() {
        return TransactionUtil.isInTx(transactionManager);
    }

    /**
     * Catch the application trying to close the container managed entity manager and throw an IllegalStateException
     */
    @Override
    public void close() {
        // Transaction scoped entity manager will be closed when the (owning) component invocation completes
        throw JpaLogger.ROOT_LOGGER.cannotCloseTransactionContainerEntityManger();
    }

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
        // read all non-transient fields
        in.defaultReadObject();
        final ServiceController<?> controller = currentServiceContainer().getService(JPAServiceNames.getPUServiceName(puScopedName));
        final PersistenceUnitServiceImpl persistenceUnitService = (PersistenceUnitServiceImpl) controller.getService();
        transactionManager = ContextTransactionManager.getInstance();
        transactionSynchronizationRegistry = (TransactionSynchronizationRegistry) currentServiceContainer().getService(JPAServiceNames.TRANSACTION_SYNCHRONIZATION_REGISTRY_SERVICE).getValue();

        emf = persistenceUnitService.getEntityManagerFactory();
    }

    private static ServiceContainer currentServiceContainer() {
        if(System.getSecurityManager() == null) {
            return CurrentServiceContainer.getServiceContainer();
        }
        return AccessController.doPrivileged(CurrentServiceContainer.GET_ACTION);
    }

    @Override
    public SynchronizationType getSynchronizationType() {
        return synchronizationType;
    }

    /**
     * get or create a Transactional entity manager.
     * Only call while a transaction is active in the current thread.
     *
     * @param emf
     * @param scopedPuName
     * @param properties
     * @param synchronizationType
     * @return
     */
    private EntityManager getOrCreateTransactionScopedEntityManager(
            final EntityManagerFactory emf,
            final String scopedPuName,
            final Map properties,
            final SynchronizationType synchronizationType) {
        EntityManager entityManager = TransactionUtil.getTransactionScopedEntityManager(puScopedName, transactionSynchronizationRegistry);
        if (entityManager == null) {
            entityManager = createEntityManager(emf, properties, synchronizationType);
            if (ROOT_LOGGER.isDebugEnabled()) {
                ROOT_LOGGER.debugf("%s: created entity manager session %s", TransactionUtil.getEntityManagerDetails(entityManager, scopedPuName),
                        TransactionUtil.getTransaction(transactionManager).toString());
            }
            TransactionUtil.registerSynchronization(entityManager, scopedPuName, transactionSynchronizationRegistry, transactionManager);
            TransactionUtil.putEntityManagerInTransactionRegistry(scopedPuName, entityManager, transactionSynchronizationRegistry);
        }
        else {
            testForMixedSynchronizationTypes(emf, entityManager, puScopedName, synchronizationType, properties);
            if (ROOT_LOGGER.isDebugEnabled()) {
                ROOT_LOGGER.debugf("%s: reuse entity manager session already in tx %s", TransactionUtil.getEntityManagerDetails(entityManager, scopedPuName),
                        TransactionUtil.getTransaction(transactionManager).toString());
            }
        }
        return entityManager;
    }

    private EntityManager createEntityManager(
        EntityManagerFactory emf, Map properties, final SynchronizationType synchronizationType) {
        // only JPA 2.1 applications can specify UNSYNCHRONIZED.
        // Default is SYNCHRONIZED if synchronizationType is not passed to createEntityManager
        if (SynchronizationType.UNSYNCHRONIZED.equals(synchronizationType)) {
            // properties are allowed to be be null in jpa 2.1
            return unsynchronizedEntityManagerWrapper(emf.createEntityManager(synchronizationType, properties));
        }

        if (properties != null && properties.size() > 0) {
            return emf.createEntityManager(properties);
        }
        return emf.createEntityManager();
    }

    private EntityManager unsynchronizedEntityManagerWrapper(EntityManager entityManager) {
        return new UnsynchronizedEntityManagerWrapper(entityManager);
    }

    /**
     * return true if non-tx invocations should defer detaching of entities until entity manager is closed.
     * Note that this is an extension for compatibility with JBoss application server 5.0/6.0 (see AS7-2781)
     */
    @Override
    protected boolean deferEntityDetachUntilClose() {
        if (deferDetach == null)
            deferDetach =
                    (true == Configuration.deferEntityDetachUntilClose(emf.getProperties())? Boolean.TRUE : Boolean.FALSE);
        return deferDetach.booleanValue();
    }

    /**
     * throw error if jta transaction already has an UNSYNCHRONIZED persistence context and a SYNCHRONIZED persistence context
     * is requested.  We are only fussy in this test, if the target component persistence context is SYNCHRONIZED.
     *
     * WFLY-7075 introduces two extensions, allow a (transaction) joined UNSYNCHRONIZED persistence context to be treated as SYNCHRONIZED,
     * allow the checking for mixed SynchronizationType to be skipped.
     */
    private static void testForMixedSynchronizationTypes(EntityManagerFactory emf, EntityManager entityManagerFromJTA, String scopedPuName, final SynchronizationType targetSynchronizationType, Map targetProperties) {

        boolean skipMixedSyncTypeChecking = Configuration.skipMixedSynchronizationTypeCheck(emf, targetProperties);  // extension to allow skipping of check based on properties of target entity manager
        boolean allowJoinedUnsyncPersistenceContext = Configuration.allowJoinedUnsyncPersistenceContext(emf, targetProperties); // extension to allow joined unsync persistence context to be treated as sync persistence context

        if (!skipMixedSyncTypeChecking &&
                SynchronizationType.SYNCHRONIZED.equals(targetSynchronizationType) &&
                entityManagerFromJTA instanceof SynchronizationTypeAccess &&
                SynchronizationType.UNSYNCHRONIZED.equals(((SynchronizationTypeAccess) entityManagerFromJTA).getSynchronizationType())
                && (!allowJoinedUnsyncPersistenceContext || !entityManagerFromJTA.isJoinedToTransaction())) {
            throw JpaLogger.ROOT_LOGGER.badSynchronizationTypeCombination(scopedPuName);
        }
    }
}