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

package org.wildfly.extension.undertow.deployment;

import java.io.Closeable;
import java.io.File;
import java.io.FilePermission;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import io.undertow.util.FileUtils;
import org.jboss.as.controller.services.path.PathManager;
import org.jboss.as.ee.structure.DeploymentType;
import org.jboss.as.ee.structure.DeploymentTypeMarker;
import org.jboss.as.server.deployment.Attachments;
import org.jboss.as.server.deployment.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.MountedDeploymentOverlay;
import org.jboss.as.server.deployment.PrivateSubDeploymentMarker;
import org.jboss.as.server.deployment.module.FilterSpecification;
import org.jboss.as.server.deployment.module.ModuleRootMarker;
import org.jboss.as.server.deployment.module.ModuleSpecification;
import org.jboss.as.server.deployment.module.MountHandle;
import org.jboss.as.server.deployment.module.ResourceRoot;
import org.jboss.as.server.deployment.module.TempFileProviderService;
import org.jboss.as.web.common.SharedTldsMetaDataBuilder;
import org.jboss.as.web.common.WarMetaData;
import org.jboss.modules.filter.PathFilters;
import org.jboss.modules.security.ImmediatePermissionFactory;
import org.jboss.vfs.VFS;
import org.jboss.vfs.VirtualFile;
import org.jboss.vfs.VirtualFileFilter;
import org.jboss.vfs.VisitorAttributes;
import org.jboss.vfs.util.SuffixMatchFilter;
import org.wildfly.extension.undertow.logging.UndertowLogger;

/**
 * Create and mount classpath entries in the .war deployment.
 *
 * @author Emanuel Muckenhuber
 * @author Thomas.Diesler@jboss.com
 */
public class WarStructureDeploymentProcessor implements DeploymentUnitProcessor {

    private static final String TEMP_DIR = "jboss.server.temp.dir";

    public static final String WEB_INF_LIB = "WEB-INF/lib";
    public static final String WEB_INF_CLASSES = "WEB-INF/classes";
    public static final String META_INF = "META-INF";

    private static final String WEB_INF_EXTERNAL_MOUNTS = "WEB-INF/undertow-external-mounts.conf";

    public static final VirtualFileFilter DEFAULT_WEB_INF_LIB_FILTER = new SuffixMatchFilter(".jar", VisitorAttributes.DEFAULT);

    private final SharedTldsMetaDataBuilder sharedTldsMetaData;

    public WarStructureDeploymentProcessor(final SharedTldsMetaDataBuilder sharedTldsMetaData) {
        this.sharedTldsMetaData = sharedTldsMetaData;
    }

    @Override
    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        if (!DeploymentTypeMarker.isType(DeploymentType.WAR, deploymentUnit)) {
            return; // Skip non web deployments
        }

        final ResourceRoot deploymentResourceRoot = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_ROOT);

        final VirtualFile deploymentRoot = deploymentResourceRoot.getRoot();
        if (deploymentRoot == null) {
            return;
        }

        // set the child first behaviour
        final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
        if (moduleSpecification == null) {
            return;
        }
        moduleSpecification.setPrivateModule(true);

        // other sub deployments should not have access to classes in the war module
        PrivateSubDeploymentMarker.mark(deploymentUnit);

        // we do not want to index the resource root, only WEB-INF/classes and WEB-INF/lib
        deploymentResourceRoot.putAttachment(Attachments.INDEX_RESOURCE_ROOT, false);

        // Make sure the root does not end up in the module, only META-INF
        deploymentResourceRoot.getExportFilters().add(new FilterSpecification(PathFilters.getMetaInfFilter(), true));
        deploymentResourceRoot.getExportFilters().add(new FilterSpecification(PathFilters.getMetaInfSubdirectoriesFilter(), true));
        deploymentResourceRoot.getExportFilters().add(new FilterSpecification(PathFilters.acceptAll(), false));
        ModuleRootMarker.mark(deploymentResourceRoot, true);

        // TODO: This needs to be ported to add additional resource roots the standard way
        final MountHandle mountHandle = deploymentResourceRoot.getMountHandle();
        try {
            // add standard resource roots, this should eventually replace ClassPathEntry
            final List<ResourceRoot> resourceRoots = createResourceRoots(deploymentRoot, deploymentUnit);
            for (ResourceRoot root : resourceRoots) {
                deploymentUnit.addToAttachmentList(Attachments.RESOURCE_ROOTS, root);
            }
        } catch (Exception e) {
            throw new DeploymentUnitProcessingException(e);
        }
        // Add the war metadata
        final WarMetaData warMetaData = new WarMetaData();
        deploymentUnit.putAttachment(WarMetaData.ATTACHMENT_KEY, warMetaData);

        String deploymentName;
        if(deploymentUnit.getParent() == null) {
            deploymentName = deploymentUnit.getName();
        } else {
            deploymentName = deploymentUnit.getParent().getName() + "." + deploymentUnit.getName();
        }

        PathManager pathManager = deploymentUnit.getAttachment(Attachments.PATH_MANAGER);

        File tempDir = new File(pathManager.getPathEntry(TEMP_DIR).resolvePath(), deploymentName);
        tempDir.mkdirs();
        warMetaData.setTempDir(tempDir);

        moduleSpecification.addPermissionFactory(new ImmediatePermissionFactory(new FilePermission(tempDir.getAbsolutePath() + File.separatorChar + "-", "read,write,delete")));

        // Add the shared TLDs metadata
        final TldsMetaData tldsMetaData = new TldsMetaData();
        tldsMetaData.setSharedTlds(sharedTldsMetaData);
        deploymentUnit.putAttachment(TldsMetaData.ATTACHMENT_KEY, tldsMetaData);

        processExternalMounts(deploymentUnit, deploymentRoot);
    }

    private void processExternalMounts(DeploymentUnit deploymentUnit, VirtualFile deploymentRoot) throws DeploymentUnitProcessingException {
        VirtualFile mounts = deploymentRoot.getChild(WEB_INF_EXTERNAL_MOUNTS);
        if(!mounts.exists()) {
            return;
        }
        try (InputStream data = mounts.openStream()) {
            String contents = FileUtils.readFile(data);
            String[] lines = contents.split("\n");
            for(String line : lines) {
                String trimmed = line;
                int commentIndex = trimmed.indexOf("#");
                if(commentIndex > -1) {
                    trimmed = trimmed.substring(0, commentIndex);
                }
                trimmed = trimmed.trim();
                if(trimmed.isEmpty()) {
                    continue;
                }
                File path = new File(trimmed);
                if(path.exists()) {
                    deploymentUnit.addToAttachmentList(UndertowAttachments.EXTERNAL_RESOURCES, path);
                } else {
                    throw UndertowLogger.ROOT_LOGGER.couldNotFindExternalPath(path);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

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

    /**
     * Create the resource roots for a .war deployment
     *
     *
     * @param deploymentRoot the deployment root
     * @return the resource roots
     * @throws java.io.IOException for any error
     */
    private List<ResourceRoot> createResourceRoots(final VirtualFile deploymentRoot, final DeploymentUnit deploymentUnit) throws IOException, DeploymentUnitProcessingException {
        final List<ResourceRoot> entries = new ArrayList<ResourceRoot>();
        // WEB-INF classes
        final VirtualFile webinfClasses = deploymentRoot.getChild(WEB_INF_CLASSES);
        if (webinfClasses.exists()) {
            final ResourceRoot webInfClassesRoot = new ResourceRoot(webinfClasses.getName(), webinfClasses, null);
            ModuleRootMarker.mark(webInfClassesRoot);
            entries.add(webInfClassesRoot);
        }
        // WEB-INF lib
        Map<String, MountedDeploymentOverlay> overlays = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_OVERLAY_LOCATIONS);
        final VirtualFile webinfLib = deploymentRoot.getChild(WEB_INF_LIB);
        if (webinfLib.exists()) {
            final List<VirtualFile> archives = webinfLib.getChildren(DEFAULT_WEB_INF_LIB_FILTER);
            for (final VirtualFile archive : archives) {
                try {

                    String relativeName = archive.getPathNameRelativeTo(deploymentRoot);
                    MountedDeploymentOverlay overlay = overlays.get(relativeName);
                    Closeable closable = null;
                    if(overlay != null) {
                        overlay.remountAsZip(false);
                    } else if (archive.isFile()) {
                        closable = VFS.mountZip(archive, archive, TempFileProviderService.provider());
                    } else {
                        closable = null;
                    }
                    final ResourceRoot webInfArchiveRoot = new ResourceRoot(archive.getName(), archive, new MountHandle(closable));
                    ModuleRootMarker.mark(webInfArchiveRoot);
                    entries.add(webInfArchiveRoot);
                } catch (IOException e) {
                    throw new DeploymentUnitProcessingException(UndertowLogger.ROOT_LOGGER.failToProcessWebInfLib(archive), e);
                }
            }
        }
        return entries;
    }
}


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

import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;

import org.jboss.as.controller.Extension;
import org.jboss.as.controller.ExtensionContext;
import org.jboss.as.controller.ModelVersion;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ProcessType;
import org.jboss.as.controller.SubsystemRegistration;
import org.jboss.as.controller.access.constraint.ApplicationTypeConfig;
import org.jboss.as.controller.access.constraint.SensitivityClassification;
import org.jboss.as.controller.access.management.ApplicationTypeAccessConstraintDefinition;
import org.jboss.as.controller.access.management.SensitiveTargetAccessConstraintDefinition;
import org.jboss.as.controller.descriptions.ResourceDescriptionResolver;
import org.jboss.as.controller.descriptions.StandardResourceDescriptionResolver;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.parsing.ExtensionParsingContext;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.naming.management.JndiViewOperation;

/**
 * Domain extension used to initialize the naming subsystem element handlers.
 *
 * @author John E. Bailey
 * @author Emanuel Muckenhuber
 */
public class NamingExtension implements Extension {

    public static final String SUBSYSTEM_NAME = "naming";
    private static final String NAMESPACE_1_0 = "urn:jboss:domain:naming:1.0";
    private static final String NAMESPACE_1_1 = "urn:jboss:domain:naming:1.1";
    private static final String NAMESPACE_1_2 = "urn:jboss:domain:naming:1.2";
    private static final String NAMESPACE_1_3 = "urn:jboss:domain:naming:1.3";
    private static final String NAMESPACE_1_4 = "urn:jboss:domain:naming:1.4";
    static final String NAMESPACE_2_0 = "urn:jboss:domain:naming:2.0";
    //2.1 introduced in WildFly 10.1
    private static final ModelVersion CURRENT_MODEL_VERSION = ModelVersion.create(2, 1, 0);

    static final String RESOURCE_NAME = NamingExtension.class.getPackage().getName() + ".LocalDescriptions";
    static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, NamingExtension.SUBSYSTEM_NAME);

    static final SensitiveTargetAccessConstraintDefinition JNDI_VIEW_CONSTRAINT = new SensitiveTargetAccessConstraintDefinition(
            new SensitivityClassification(SUBSYSTEM_NAME, "jndi-view", false, true, true));

    static final SensitiveTargetAccessConstraintDefinition NAMING_BINDING_SENSITIVITY_CONSTRAINT = new SensitiveTargetAccessConstraintDefinition(
            new SensitivityClassification(SUBSYSTEM_NAME, "naming-binding", false, false, false));

    static final ApplicationTypeAccessConstraintDefinition NAMING_BINDING_APPLICATION_CONSTRAINT = new ApplicationTypeAccessConstraintDefinition(
            new ApplicationTypeConfig(NamingExtension.SUBSYSTEM_NAME, NamingSubsystemModel.BINDING));

    public static ResourceDescriptionResolver getResourceDescriptionResolver(final String keyPrefix) {
        return new StandardResourceDescriptionResolver(keyPrefix, RESOURCE_NAME, NamingExtension.class.getClassLoader(), true, true);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void initialize(ExtensionContext context) {
        final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, CURRENT_MODEL_VERSION);

        final ManagementResourceRegistration registration = subsystem.registerSubsystemModel(new NamingSubsystemRootResourceDefinition());

        registration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);

        registration.registerSubModel(NamingBindingResourceDefinition.INSTANCE);
        registration.registerSubModel(RemoteNamingResourceDefinition.INSTANCE);

        if (context.isRuntimeOnlyRegistrationValid()) {
            registration.registerOperationHandler(NamingSubsystemRootResourceDefinition.JNDI_VIEW, JndiViewOperation.INSTANCE, false);
        }

        subsystem.registerXMLElementWriter(NamingSubsystemXMLPersister.INSTANCE);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void initializeParsers(ExtensionParsingContext context) {
        context.setSubsystemXmlMapping(SUBSYSTEM_NAME, NAMESPACE_1_0, () -> new NamingSubsystem10Parser(context.getProcessType() == ProcessType.APPLICATION_CLIENT));
        context.setSubsystemXmlMapping(SUBSYSTEM_NAME, NAMESPACE_1_1, NamingSubsystem11Parser::new);
        context.setSubsystemXmlMapping(SUBSYSTEM_NAME, NAMESPACE_1_2, NamingSubsystem12Parser::new);
        context.setSubsystemXmlMapping(SUBSYSTEM_NAME, NAMESPACE_1_3, NamingSubsystem13Parser::new);
        context.setSubsystemXmlMapping(SUBSYSTEM_NAME, NAMESPACE_1_4, NamingSubsystem14Parser::new);
        context.setSubsystemXmlMapping(SUBSYSTEM_NAME, NAMESPACE_2_0, NamingSubsystem20Parser::new);
    }

}


/*
 * 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.util.List;
import java.util.Map;

import javax.persistence.EntityGraph;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.FlushModeType;
import javax.persistence.LockModeType;
import javax.persistence.Query;
import javax.persistence.StoredProcedureQuery;
import javax.persistence.SynchronizationType;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.metamodel.Metamodel;
import org.jboss.as.jpa.messages.JpaLogger;

/**
 * Abstract entity manager used by all container managed entity managers.
 *
 * @author Scott Marlow (forked from jboss-jpa)
 */
public abstract class AbstractEntityManager implements EntityManager {

    // constants for TRACE lock mode logging
    public static final String NULL_LOCK_MODE = "(null)";
    public static final String OPTIMISTIC_LOCK_MODE = "optimistic";
    public static final String OPTIMISTIC_FORCE_INCREMENT_LOCK_MODE = "optimistic_force_increment";
    public static final String READ_LOCK_MODE = "read";
    public static final String WRITE_LOCK_MODE = "write";
    public static final String PESSIMISTIC_READ_LOCK_MODE = "pessimistic_read";
    public static final String PESSIMISTIC_FORCE_INCREMENT_LOCK_MODE = "pessimistic_force_increment";
    public static final String PESSIMISTIC_WRITE_LOCK_MODE = "pessimistic_write";
    public static final String NONE_LOCK_MODE = "none";

    private final transient boolean isTraceEnabled = ROOT_LOGGER.isTraceEnabled();

    protected abstract EntityManager getEntityManager();

    /**
     * @return true if an extended persistence context is in use
     *

     *         Precondition: getEntityManager() must be called previous to calling isExtendedPersistenceContext
     */
    protected abstract boolean isExtendedPersistenceContext();

    /**
     * @return true if a JTA transaction is active
     *

     *         Precondition: getEntityManager() must be called previous to calling isInTx
     */
    protected abstract boolean isInTx();

    public abstract SynchronizationType getSynchronizationType();

    protected abstract boolean deferEntityDetachUntilClose();

    public <T> T unwrap(Class<T> cls) {
        return getEntityManager().unwrap(cls);
    }

    public <T> TypedQuery<T> createNamedQuery(String name, Class<T> resultClass) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            // invoke underlying entity manager method and if not running in a tx
            // return a Query wrapper around the result.
            EntityManager entityManager = getEntityManager();
            return detachTypedQueryNonTxInvocation(entityManager,entityManager.createNamedQuery(name, resultClass));
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createNamedQuery name '%s', resultClass '%s' took %dms", name, resultClass.getName(), elapsed);
            }
        }
    }

    public <T> TypedQuery<T> createQuery(CriteriaQuery<T> criteriaQuery) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            // invoke underlying entity manager method and if not running in a tx
            // return a Query wrapper around the result.
            EntityManager entityManager = getEntityManager();
            return detachTypedQueryNonTxInvocation(entityManager,entityManager.createQuery(criteriaQuery));
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createQuery took %dms", elapsed);
            }
        }
    }

    public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            // invoke underlying entity manager method and if not running in a tx
            // return a Query wrapper around the result.
            EntityManager entityManager = getEntityManager();
            return detachTypedQueryNonTxInvocation(entityManager,entityManager.createQuery(qlString, resultClass));
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createQuery resultClass '%s' took %dms", resultClass.getName(), elapsed);
            }
        }
    }

    public void detach(Object entity) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            getEntityManager().detach(entity);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("detach entityClass '%s' took %dms", entity.getClass().getName(), elapsed);
            }
        }
    }

    public <T> T find(Class<T> entityClass, Object primaryKey, Map<String, Object> properties) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            final EntityManager underlyingEntityManager = getEntityManager();
            T result = underlyingEntityManager.find(entityClass, primaryKey, properties);
            detachNonTxInvocation(underlyingEntityManager);
            return result;
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("find entityClass '%s' took %dms", entityClass.getName(), elapsed);
            }
        }
    }

    public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            final EntityManager underlyingEntityManager = getEntityManager();
            T result = underlyingEntityManager.find(entityClass, primaryKey, lockMode);
            detachNonTxInvocation(underlyingEntityManager);
            return result;
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("find entityClass '%s', lockMode '%s' took %dms", entityClass.getName(), getLockModeAsString(lockMode), elapsed);
            }
        }
    }

    public <T> T find(Class<T> entityClass, Object primaryKey, LockModeType lockMode, Map<String, Object> properties) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            final EntityManager underlyingEntityManager = getEntityManager();
            T result = underlyingEntityManager.find(entityClass, primaryKey, lockMode, properties);
            detachNonTxInvocation(underlyingEntityManager);
            return result;
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("find entityClass '%s', lockMode '%s' took %dms", entityClass.getName(), getLockModeAsString(lockMode), elapsed);
            }
        }
    }

    public <T> T find(Class<T> entityClass, Object primaryKey) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            final EntityManager underlyingEntityManager = getEntityManager();
            T result = getEntityManager().find(entityClass, primaryKey);
            detachNonTxInvocation(underlyingEntityManager);
            return result;
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("find entityClass '%s' took %dms", entityClass.getName(), elapsed);
            }
        }
    }

    public CriteriaBuilder getCriteriaBuilder() {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().getCriteriaBuilder();
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("getCriteriaBuilder took %dms", elapsed);
            }
        }
    }

    public EntityManagerFactory getEntityManagerFactory() {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().getEntityManagerFactory();
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("getEntityManagerFactory took %dms", elapsed);
            }
        }
    }

    public LockModeType getLockMode(Object entity) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        LockModeType result = null;
        try {
            result = getEntityManager().getLockMode(entity);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("getLockMode entityClass '%s', lockMode '%s'  took %dms", entity.getClass().getName(), getLockModeAsString(result), elapsed);
            }
        }
        return result;
    }

    public Metamodel getMetamodel() {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().getMetamodel();
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("getMetamodel took %dms", elapsed);
            }
        }
    }

    public Map<String, Object> getProperties() {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().getProperties();
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("getProperties took %dms", elapsed);
            }
        }
    }

    public void lock(Object entity, LockModeType lockMode, Map<String, Object> properties) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            getEntityManager().lock(entity, lockMode, properties);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("lock entityClass '%s', lockMode '%s'  took %dms", entity.getClass().getName(), getLockModeAsString(lockMode), elapsed);
            }
        }
    }

    public void setProperty(String propertyName, Object value) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            getEntityManager().setProperty(propertyName, value);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("setProperty took %dms", elapsed);
            }
        }
    }

    public void clear() {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            getEntityManager().clear();
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("clear took %dms", elapsed);
            }
        }
    }

    public void close() {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            getEntityManager().close();
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("close took %dms", elapsed);
            }
        }
    }

    public boolean contains(Object entity) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().contains(entity);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("contains '%s' took %dms", entity.getClass().getName(), elapsed);
            }
        }
    }

    public Query createNamedQuery(String name) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            // invoke underlying entity manager method and if not running in a tx
            // return a Query wrapper around the result.
            EntityManager entityManager = getEntityManager();
            return detachQueryNonTxInvocation(entityManager, entityManager.createNamedQuery(name));
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createNamedQuery name '%s' took %dms", name, elapsed);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public Query createNativeQuery(String sqlString, Class resultClass) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            // invoke underlying entity manager method and if not running in a tx
            // return a Query wrapper around the result.
            EntityManager entityManager = getEntityManager();
            return detachQueryNonTxInvocation(entityManager, entityManager.createNativeQuery(sqlString, resultClass));
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createNativeQuery resultClass '%s' took %dms", resultClass.getName(), elapsed);
            }
        }
    }

    public Query createNativeQuery(String sqlString, String resultSetMapping) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            // invoke underlying entity manager method and if not running in a tx
            // return a Query wrapper around the result.
            EntityManager entityManager = getEntityManager();
            return detachQueryNonTxInvocation(entityManager, entityManager.createNativeQuery(sqlString, resultSetMapping));
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createNativeQuery took %dms", elapsed);
            }
        }
    }

    public Query createNativeQuery(String sqlString) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            // invoke underlying entity manager method and if not running in a tx
            // return a Query wrapper around the result.
            EntityManager entityManager = getEntityManager();
            return detachQueryNonTxInvocation(entityManager, entityManager.createNativeQuery(sqlString));
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createNativeQuery took %dms", elapsed);
            }
        }
    }

    public Query createQuery(String ejbqlString) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            // invoke underlying entity manager method and if not running in a tx
            // return a Query wrapper around the result.
            EntityManager entityManager = getEntityManager();
            return detachQueryNonTxInvocation(entityManager, entityManager.createQuery(ejbqlString));
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createQuery took %dms", elapsed);
            }
        }
    }

    public void flush() {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            getEntityManager().flush();
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("flush took %dms", elapsed);
            }
        }
    }

    public Object getDelegate() {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().getDelegate();
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("getDelegate took %dms", elapsed);
            }
        }
    }

    public FlushModeType getFlushMode() {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().getFlushMode();
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("getFlushMode took %dms", elapsed);
            }
        }
    }

    public <T> T getReference(Class<T> entityClass, Object primaryKey) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            final EntityManager underlyingEntityManager = getEntityManager();
            T result = getEntityManager().getReference(entityClass, primaryKey);
            detachNonTxInvocation(underlyingEntityManager);
            return result;
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("getReference entityClass '%s' took %dms", entityClass.getName(), elapsed);
            }
        }
    }

    public EntityTransaction getTransaction() {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().getTransaction();
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("getTransaction took %dms", elapsed);
            }
        }
    }

    public boolean isOpen() {
        return getEntityManager().isOpen();
    }

    public void joinTransaction() {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            getEntityManager().joinTransaction();
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("joinTransaction took %dms", elapsed);
            }
        }
    }

    public void lock(Object entity, LockModeType lockMode) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            getEntityManager().lock(entity, lockMode);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("lock entityClass '%s', lockMode '%s' took %dms", entity.getClass().getName(), getLockModeAsString(lockMode), elapsed);
            }
        }
    }

    public <T> T merge(T entity) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            transactionIsRequired();
            return getEntityManager().merge(entity);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("merge entityClass '%s' took %dms", entity.getClass().getName(), elapsed);
            }
        }
    }

    public void persist(Object entity) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            transactionIsRequired();
            getEntityManager().persist(entity);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("persist entityClass '%s' took %dms", entity.getClass().getName(), elapsed);
            }
        }
    }

    public void refresh(Object entity) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            transactionIsRequired();
            getEntityManager().refresh(entity);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("refresh entityClass '%s' took %dms", entity.getClass().getName(), elapsed);
            }
        }
    }

    public void refresh(Object entity, Map<String, Object> properties) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            transactionIsRequired();
            getEntityManager().refresh(entity, properties);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("refresh entityClass '%s' took %dms", entity.getClass().getName(), elapsed);
            }
        }
    }

    public void refresh(Object entity, LockModeType lockMode) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            transactionIsRequired();
            getEntityManager().refresh(entity, lockMode);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("refresh entityClass '%s', lockMode '%s' took %dms", entity.getClass().getName(), getLockModeAsString(lockMode), elapsed);
            }
        }
    }

    public void refresh(Object entity, LockModeType lockMode, Map<String, Object> properties) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            transactionIsRequired();
            getEntityManager().refresh(entity, lockMode, properties);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("refresh entityClass '%s', lockMode '%s' took %dms", entity.getClass().getName(), getLockModeAsString(lockMode), elapsed);
            }
        }
    }

    public void remove(Object entity) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            transactionIsRequired();
            getEntityManager().remove(entity);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("remove entityClass '%s' took %dms", entity.getClass().getName(), elapsed);
            }
        }
    }

    public void setFlushMode(FlushModeType flushMode) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            getEntityManager().setFlushMode(flushMode);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("setFlushMode took %dms", elapsed);
            }
        }
    }

    public Query createQuery(CriteriaUpdate criteriaUpdate) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().createQuery(criteriaUpdate);

        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createQuery(CriteriaUpdate) took %dms", elapsed);
            }
        }
    }

    public Query createQuery(CriteriaDelete criteriaDelete) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().createQuery(criteriaDelete);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createQuery(criteriaDelete) took %dms", elapsed);
            }
        }
    }

    public StoredProcedureQuery createNamedStoredProcedureQuery(String name) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            EntityManager entityManager = getEntityManager();
            return detachStoredProcedureQueryNonTxInvocation(entityManager, entityManager.createNamedStoredProcedureQuery(name));
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createNamedStoredProcedureQuery %s took %dms", name, elapsed);
            }
        }
    }

    public StoredProcedureQuery createStoredProcedureQuery(String procedureName) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            EntityManager entityManager = getEntityManager();
            return detachStoredProcedureQueryNonTxInvocation(entityManager, entityManager.createStoredProcedureQuery(procedureName));
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createStoredProcedureQuery %s took %dms", procedureName, elapsed);
            }
        }
    }

    public StoredProcedureQuery createStoredProcedureQuery(String procedureName, Class... resultClasses) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            EntityManager entityManager = getEntityManager();
            return detachStoredProcedureQueryNonTxInvocation(entityManager, entityManager.createStoredProcedureQuery(procedureName, resultClasses));
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createStoredProcedureQuery %s, resultClasses... took %dms", procedureName, elapsed);
            }
        }
    }

    public StoredProcedureQuery createStoredProcedureQuery(String procedureName, String... resultSetMappings) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            EntityManager entityManager = getEntityManager();
            return detachStoredProcedureQueryNonTxInvocation(entityManager, entityManager.createStoredProcedureQuery(procedureName, resultSetMappings));
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createStoredProcedureQuery %s, resultSetMappings... took %dms", procedureName, elapsed);
            }
        }
    }

    public <T> EntityGraph<T> createEntityGraph(Class<T> tClass) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().createEntityGraph(tClass);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createEntityGraph %s took %dms", tClass.getName(), elapsed);
            }
        }

    }

    public EntityGraph<?> createEntityGraph(String s) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().createEntityGraph(s);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("createEntityGraph %s took %dms", s, elapsed);
            }
        }
    }

    public EntityGraph<?> getEntityGraph(String s) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().getEntityGraph(s);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("getEntityGraph %s took %dms", s, elapsed);
            }
        }
    }

    public <T> List<EntityGraph<? super T>> getEntityGraphs(Class<T> tClass) {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().getEntityGraphs(tClass);
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("getEntityGraphs %s took %dms", tClass.getName(), elapsed);
            }
        }
    }

    public boolean isJoinedToTransaction() {
        long start = 0;
        if (isTraceEnabled)
            start = System.currentTimeMillis();
        try {
            return getEntityManager().isJoinedToTransaction();
        } finally {
            if (isTraceEnabled) {
                long elapsed = System.currentTimeMillis() - start;
                ROOT_LOGGER.tracef("isJoinedToTransaction() took %dms", elapsed);
            }
        }
    }

    // used by TransactionScopedEntityManager to auto detach loaded entities
    // after each non-jta invocation
    protected void detachNonTxInvocation(EntityManager underlyingEntityManager) {
        if (!this.isExtendedPersistenceContext() && !this.isInTx() && !deferEntityDetachUntilClose()) {
            underlyingEntityManager.clear();
        }
    }

    // for JPA 2.0 section 3.8.6
    // used by TransactionScopedEntityManager to detach entities loaded by a query in a non-jta invocation.
    protected Query detachQueryNonTxInvocation(EntityManager underlyingEntityManager, Query underLyingQuery) {
        if (!this.isExtendedPersistenceContext() && !this.isInTx()) {
            return new QueryNonTxInvocationDetacher(underlyingEntityManager, underLyingQuery);
        }
        return underLyingQuery;
    }

    // for JPA 2.0 section 3.8.6
    // used by TransactionScopedEntityManager to detach entities loaded by a query in a non-jta invocation.
    protected <T> TypedQuery<T> detachTypedQueryNonTxInvocation(EntityManager underlyingEntityManager, TypedQuery<T> underLyingQuery) {
        if (!this.isExtendedPersistenceContext() && !this.isInTx()) {
            return new TypedQueryNonTxInvocationDetacher<>(underlyingEntityManager, underLyingQuery);
        }
        return underLyingQuery;
    }

    private StoredProcedureQuery detachStoredProcedureQueryNonTxInvocation(EntityManager underlyingEntityManager, StoredProcedureQuery underlyingStoredProcedureQuery) {
        if (!this.isExtendedPersistenceContext() && !this.isInTx()) {
            return new StoredProcedureQueryNonTxInvocationDetacher(underlyingEntityManager, underlyingStoredProcedureQuery);
        }
        return underlyingStoredProcedureQuery;
    }

    // JPA 7.9.1 if invoked without a JTA transaction and a transaction scoped persistence context is used,
    // will throw TransactionRequiredException for any calls to entity manager remove/merge/persist/refresh.
    private void transactionIsRequired() {
        if (!this.isExtendedPersistenceContext() && !this.isInTx()) {
            throw JpaLogger.ROOT_LOGGER.transactionRequired();
        }
    }

    private static String getLockModeAsString(LockModeType lockMode) {
        if (lockMode == null)
            return NULL_LOCK_MODE;
        switch (lockMode) {
            case OPTIMISTIC:
                return OPTIMISTIC_LOCK_MODE;
            case OPTIMISTIC_FORCE_INCREMENT:
                return OPTIMISTIC_FORCE_INCREMENT_LOCK_MODE;
            case READ:
                return READ_LOCK_MODE;
            case WRITE:
                return WRITE_LOCK_MODE;
            case PESSIMISTIC_READ:
                return PESSIMISTIC_READ_LOCK_MODE;
            case PESSIMISTIC_FORCE_INCREMENT:
                return PESSIMISTIC_FORCE_INCREMENT_LOCK_MODE;
            case PESSIMISTIC_WRITE:
                return PESSIMISTIC_WRITE_LOCK_MODE;
            default:
            case NONE:
                return NONE_LOCK_MODE;
        }
    }

}


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

import org.omg.CORBA.TypeCode;
import org.omg.CORBA.TCKind;
import org.omg.CORBA.IRObject;
import org.omg.CORBA.DefinitionKind;
import org.omg.CORBA.PrimitiveKind;
import org.omg.CORBA.PrimitiveDef;
import org.omg.CORBA.PrimitiveDefOperations;
import org.omg.CORBA.PrimitiveDefPOATie;

import java.util.Map;
import java.util.HashMap;

/**
 * PrimitiveDef IR object.
 *
 * @author Ole Husgaard
 * @version $Revision: 81018 $
 */
class PrimitiveDefImpl
        extends IDLTypeImpl
        implements PrimitiveDefOperations, LocalIDLType {
    // Constants -----------------------------------------------------

    // Attributes ----------------------------------------------------

    // Static --------------------------------------------------------

    // Constructors --------------------------------------------------

    PrimitiveDefImpl(TypeCode typeCode,
                     RepositoryImpl repository) {
        super(typeCode, DefinitionKind.dk_Primitive, repository);
    }

    // Public --------------------------------------------------------

    // LocalIRObject implementation ---------------------------------

    public IRObject getReference() {
        if (ref == null) {
            ref = org.omg.CORBA.PrimitiveDefHelper.narrow(
                    servantToReference(new PrimitiveDefPOATie(this)));
        }
        return ref;
    }

    // PrimitiveDefOperations implementation ----------------------------

    public PrimitiveKind kind() {
        return (PrimitiveKind) primitiveTCKindMap.get(type().kind());
    }

    // Package protected ---------------------------------------------

    static boolean isPrimitiveTCKind(TCKind tcKind) {
        return primitiveTCKindMap.containsKey(tcKind);
    }

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

    /**
     * My CORBA reference.
     */
    private PrimitiveDef ref = null;

    /**
     * Maps TCKind to PrimitiveKind.
     */
    private static Map primitiveTCKindMap;

    static {
        // Create and initialize the map
        primitiveTCKindMap = new HashMap();
        primitiveTCKindMap.put(TCKind.tk_null, PrimitiveKind.pk_null);
        primitiveTCKindMap.put(TCKind.tk_void, PrimitiveKind.pk_void);
        primitiveTCKindMap.put(TCKind.tk_short, PrimitiveKind.pk_short);
        primitiveTCKindMap.put(TCKind.tk_long, PrimitiveKind.pk_long);
        primitiveTCKindMap.put(TCKind.tk_ushort, PrimitiveKind.pk_ushort);
        primitiveTCKindMap.put(TCKind.tk_ulong, PrimitiveKind.pk_ulong);
        primitiveTCKindMap.put(TCKind.tk_float, PrimitiveKind.pk_float);
        primitiveTCKindMap.put(TCKind.tk_double, PrimitiveKind.pk_double);
        primitiveTCKindMap.put(TCKind.tk_boolean, PrimitiveKind.pk_boolean);
        primitiveTCKindMap.put(TCKind.tk_char, PrimitiveKind.pk_char);
        primitiveTCKindMap.put(TCKind.tk_octet, PrimitiveKind.pk_octet);
        primitiveTCKindMap.put(TCKind.tk_any, PrimitiveKind.pk_any);
        primitiveTCKindMap.put(TCKind.tk_TypeCode, PrimitiveKind.pk_TypeCode);
        primitiveTCKindMap.put(TCKind.tk_Principal, PrimitiveKind.pk_Principal);
        primitiveTCKindMap.put(TCKind.tk_objref, PrimitiveKind.pk_objref);
        primitiveTCKindMap.put(TCKind.tk_string, PrimitiveKind.pk_string);
        primitiveTCKindMap.put(TCKind.tk_longlong, PrimitiveKind.pk_longlong);
        primitiveTCKindMap.put(TCKind.tk_ulonglong, PrimitiveKind.pk_ulonglong);
        primitiveTCKindMap.put(TCKind.tk_longdouble, PrimitiveKind.pk_longdouble);
        primitiveTCKindMap.put(TCKind.tk_wchar, PrimitiveKind.pk_wchar);
        primitiveTCKindMap.put(TCKind.tk_wstring, PrimitiveKind.pk_wstring);
    }

}


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

import static org.jboss.as.controller.parsing.ParseUtils.requireNoAttributes;
import static org.jboss.as.controller.parsing.ParseUtils.requireNoContent;
import static org.jboss.as.jaxrs.logging.JaxrsLogger.JAXRS_LOGGER;

import java.util.List;

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

import org.jboss.as.controller.Extension;
import org.jboss.as.controller.ExtensionContext;
import org.jboss.as.controller.ModelVersion;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.SubsystemRegistration;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.descriptions.ResourceDescriptionResolver;
import org.jboss.as.controller.descriptions.StandardResourceDescriptionResolver;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.parsing.ExtensionParsingContext;
import org.jboss.as.controller.persistence.SubsystemMarshallingContext;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.dmr.ModelNode;
import org.jboss.staxmapper.XMLElementReader;
import org.jboss.staxmapper.XMLElementWriter;
import org.jboss.staxmapper.XMLExtendedStreamReader;
import org.jboss.staxmapper.XMLExtendedStreamWriter;

/**
 * Domain extension used to initialize the jaxrs subsystem.
 *
 * @author Stuart Douglas
 * @author Emanuel Muckenhuber
 */
public class JaxrsExtension implements Extension {

    public static final String SUBSYSTEM_NAME = "jaxrs";
    public static final String NAMESPACE = "urn:jboss:domain:jaxrs:1.0";

    public static final ModelVersion MODEL_VERSION_1_0_0 = ModelVersion.create(1, 0, 0);
    public static final ModelVersion MODEL_VERSION_2_0_0 = ModelVersion.create(2, 0, 0);

    private static final ModelVersion CURRENT_MODEL_VERSION = MODEL_VERSION_2_0_0;

    private static final JaxrsSubsystemParser parser = new JaxrsSubsystemParser();

    private static final String RESOURCE_NAME = JaxrsExtension.class.getPackage().getName() + ".LocalDescriptions";
    static PathElement SUBSYSTEM_PATH = PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, SUBSYSTEM_NAME);

    static ResourceDescriptionResolver getResolver(final String... keyPrefix) {
        StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
        for (String kp : keyPrefix) {
            prefix.append('.').append(kp);
        }
        return new StandardResourceDescriptionResolver(prefix.toString(), RESOURCE_NAME, JaxrsExtension.class.getClassLoader(), true, false);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void initialize(final ExtensionContext context) {
        JAXRS_LOGGER.debug("Activating JAX-RS Extension");
        final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, CURRENT_MODEL_VERSION);
        final ManagementResourceRegistration registration = subsystem.registerSubsystemModel(JaxrsSubsystemDefinition.INSTANCE);
        registration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
        ManagementResourceRegistration jaxrsResReg = subsystem.registerDeploymentModel(JaxrsDeploymentDefinition.INSTANCE);
        jaxrsResReg.registerSubModel(DeploymentRestResourcesDefintion.INSTANCE);
        subsystem.registerXMLElementWriter(parser);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void initializeParsers(final ExtensionParsingContext context) {
        context.setSubsystemXmlMapping(SUBSYSTEM_NAME, JaxrsExtension.NAMESPACE, () -> parser);
    }

    static class JaxrsSubsystemParser implements XMLStreamConstants, XMLElementReader<List<ModelNode>>, XMLElementWriter<SubsystemMarshallingContext> {

        /**
         * {@inheritDoc}
         */
        @Override
        public void readElement(final XMLExtendedStreamReader reader, final List<ModelNode> list) throws XMLStreamException {
            // Require no attributes or content
            requireNoAttributes(reader);
            requireNoContent(reader);
            list.add(Util.createAddOperation(PathAddress.pathAddress(SUBSYSTEM_PATH)));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void writeContent(final XMLExtendedStreamWriter streamWriter, final SubsystemMarshallingContext context) throws XMLStreamException {
            context.startSubsystemElement(JaxrsExtension.NAMESPACE, true);
        }

    }
}


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

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.ejb.Local;
import javax.ejb.LocalBean;
import javax.ejb.Remote;

import org.jboss.as.ee.component.ComponentDescription;
import org.jboss.as.ee.component.EEModuleDescription;
import org.jboss.as.ee.metadata.MetadataCompleteMarker;
import org.jboss.as.ejb3.logging.EjbLogger;
import org.jboss.as.ejb3.component.session.SessionBeanComponentDescription;
import org.jboss.as.ejb3.deployment.EjbDeploymentAttachmentKeys;
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.metadata.ejb.spec.EjbJarMetaData;
import org.jboss.metadata.ejb.spec.EjbJarVersion;
import org.jboss.modules.Module;

import static org.jboss.as.ee.component.Attachments.EE_MODULE_DESCRIPTION;
import static org.jboss.as.ejb3.deployment.processors.ViewInterfaces.getPotentialViewInterfaces;

/**
 * Processes {@link Local @Local} and {@link @Remote} annotation of a session bean and sets up the {@link SessionBeanComponentDescription}
 * out of it.
 *

 *
 * @author Jaikiran Pai
 */
public class BusinessViewAnnotationProcessor implements DeploymentUnitProcessor {

    private final boolean appclient;

    public BusinessViewAnnotationProcessor(final boolean appclient) {
        this.appclient = appclient;
    }

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

        if (MetadataCompleteMarker.isMetadataComplete(deploymentUnit)) {
            return;
        }

        final EEModuleDescription eeModuleDescription = deploymentUnit.getAttachment(EE_MODULE_DESCRIPTION);
        final Collection<ComponentDescription> componentDescriptions = eeModuleDescription.getComponentDescriptions();
        final Module module = deploymentUnit.getAttachment(org.jboss.as.server.deployment.Attachments.MODULE);
        if(module == null) {
            return;
        }
        final ClassLoader moduleClassLoader = module.getClassLoader();
        if (componentDescriptions != null) {
            for (ComponentDescription componentDescription : componentDescriptions) {
                if (componentDescription instanceof SessionBeanComponentDescription == false) {
                    continue;
                }
                final Class<?> ejbClass = this.getEjbClass(componentDescription.getComponentClassName(), moduleClassLoader);
                try {
                    this.processViewAnnotations(deploymentUnit, ejbClass, (SessionBeanComponentDescription) componentDescription);
                } catch (Exception e) {
                    throw EjbLogger.ROOT_LOGGER.failedToProcessBusinessInterfaces(ejbClass, e);
                }
            }
        }
        if (appclient) {
            for (ComponentDescription componentDescription : deploymentUnit.getAttachmentList(org.jboss.as.ee.component.Attachments.ADDITIONAL_RESOLVABLE_COMPONENTS)) {
                if (componentDescription instanceof SessionBeanComponentDescription == false) {
                    continue;
                }
                final Class<?> ejbClass = this.getEjbClass(componentDescription.getComponentClassName(), moduleClassLoader);
                try {
                    this.processViewAnnotations(deploymentUnit, ejbClass, (SessionBeanComponentDescription) componentDescription);
                } catch (Exception e) {
                    throw EjbLogger.ROOT_LOGGER.failedToProcessBusinessInterfaces(ejbClass, e);
                }
            }
        }
    }

    /**
     * Processes the session bean for remote and local views and updates the {@link SessionBeanComponentDescription}
     * accordingly
     *
     * @param deploymentUnit The deployment unit
     * @param sessionBeanClass The bean class
     * @param sessionBeanComponentDescription
     *                         The component description
     * @throws DeploymentUnitProcessingException
     *
     */
    private void processViewAnnotations(final DeploymentUnit deploymentUnit, final Class<?> sessionBeanClass, final SessionBeanComponentDescription sessionBeanComponentDescription) throws DeploymentUnitProcessingException {
        final Collection<Class<?>> remoteBusinessInterfaces = this.getRemoteBusinessInterfaces(deploymentUnit, sessionBeanClass);
        if (remoteBusinessInterfaces != null && !remoteBusinessInterfaces.isEmpty()) {
            sessionBeanComponentDescription.addRemoteBusinessInterfaceViews(this.toString(remoteBusinessInterfaces));
        }

        // fetch the local business interfaces of the bean
        Collection<Class<?>> localBusinessInterfaces = this.getLocalBusinessInterfaces(deploymentUnit, sessionBeanClass);
        if (localBusinessInterfaces != null && !localBusinessInterfaces.isEmpty()) {
            sessionBeanComponentDescription.addLocalBusinessInterfaceViews(this.toString(localBusinessInterfaces));
        }

        if (hasNoInterfaceView(sessionBeanClass)) {
            sessionBeanComponentDescription.addNoInterfaceView();
        }

        // EJB 3.1 FR 4.9.7 & 4.9.8, if the bean exposes no views
        if (hasNoViews(sessionBeanComponentDescription)) {
            final Set<Class<?>> potentialBusinessInterfaces = getPotentialBusinessInterfaces(sessionBeanClass);
            if (potentialBusinessInterfaces.isEmpty()) {
                sessionBeanComponentDescription.addNoInterfaceView();
            } else if (potentialBusinessInterfaces.size() == 1) {
                sessionBeanComponentDescription.addLocalBusinessInterfaceViews(potentialBusinessInterfaces.iterator().next().getName());
            } else if (isEjbVersionGreaterThanOrEqualTo32(deploymentUnit)) {
                // EJB 3.2 spec states (section 4.9.7):
                // ... or if the bean class is annotated with neither the Local nor the Remote annotation, all implemented interfaces (excluding the interfaces listed above)
                // are assumed to be local business interfaces of the bean
                sessionBeanComponentDescription.addLocalBusinessInterfaceViews(toString(potentialBusinessInterfaces));
            }
        }
    }

    private Collection<Class<?>> getRemoteBusinessInterfaces(final DeploymentUnit deploymentUnit, final Class<?> sessionBeanClass) throws DeploymentUnitProcessingException {
        final Remote remoteViewAnnotation = sessionBeanClass.getAnnotation(Remote.class);
        if (remoteViewAnnotation == null) {
            Collection<Class<?>> interfaces = getBusinessInterfacesFromInterfaceAnnotations(sessionBeanClass, Remote.class);
            if (!interfaces.isEmpty()) {
                return interfaces;
            }
            return Collections.emptySet();
        }
        Class<?>[] remoteViews = remoteViewAnnotation.value();
        if (remoteViews == null || remoteViews.length == 0) {
            Set<Class<?>> interfaces = getPotentialBusinessInterfaces(sessionBeanClass);
            // For version < 3.2, the EJB spec didn't allow more than one implementing interfaces to be considered as remote when the bean class had the @Remote annotation without any explicit value.
            // EJB 3.2 allows it (core spec, section 4.9.7)
            if (interfaces.size() != 1 && !isEjbVersionGreaterThanOrEqualTo32(deploymentUnit)) {
                throw EjbLogger.ROOT_LOGGER.beanWithRemoteAnnotationImplementsMoreThanOneInterface(sessionBeanClass);
            }
            return interfaces;
        }
        return Arrays.asList(remoteViews);
    }

    private Collection<Class<?>> getLocalBusinessInterfaces(final DeploymentUnit deploymentUnit, final Class<?> sessionBeanClass) throws DeploymentUnitProcessingException {
        final Local localViewAnnotation = sessionBeanClass.getAnnotation(Local.class);
        if (localViewAnnotation == null) {
            Collection<Class<?>> interfaces = getBusinessInterfacesFromInterfaceAnnotations(sessionBeanClass, Local.class);
            if (!interfaces.isEmpty()) {
                return interfaces;
            }
            return Collections.emptySet();
        }
        Class<?>[] localViews = localViewAnnotation.value();
        if (localViews == null || localViews.length == 0) {
            Set<Class<?>> interfaces = getPotentialBusinessInterfaces(sessionBeanClass);
            // For version < 3.2, the EJB spec didn't allow more than one implementing interfaces to be considered as local when the bean class had the @Local annotation without any explicit value.
            // EJB 3.2 allows it (core spec, section 4.9.7)
            if (interfaces.size() != 1 && !isEjbVersionGreaterThanOrEqualTo32(deploymentUnit)) {
                throw EjbLogger.ROOT_LOGGER.beanWithLocalAnnotationImplementsMoreThanOneInterface(sessionBeanClass);
            }
            return interfaces;
        }

        return Arrays.asList(localViews);
    }

    private static Collection<Class<?>> getBusinessInterfacesFromInterfaceAnnotations(Class<?> sessionBeanClass, Class<? extends Annotation> annotation) {
        final Set<Class<?>> potentialBusinessInterfaces = getPotentialBusinessInterfaces(sessionBeanClass);
        final Set<Class<?>> businessInterfaces = new HashSet<Class<?>>();
        for (Class<?> iface : potentialBusinessInterfaces) {
            if (iface.getAnnotation(annotation) != null) {
                businessInterfaces.add(iface);
            }
        }
        return businessInterfaces;
    }

    /**
     * Returns all interfaces implemented by a bean that are eligible to be business interfaces
     *
     * @param sessionBeanClass The bean class
     * @return A collection of all potential business interfaces
     */
    private static Set<Class<?>> getPotentialBusinessInterfaces(Class<?> sessionBeanClass) {
        return getPotentialViewInterfaces(sessionBeanClass);
    }

    /**
     * Returns true if the sessionBeanClass has a {@link LocalBean no-interface view annotation}.
     * Else returns false.
     *
     * @param sessionBeanClass The session bean {@link Class class}
     * @return
     */
    private static boolean hasNoInterfaceView(Class<?> sessionBeanClass) {
        return sessionBeanClass.getAnnotation(LocalBean.class) != null;
    }

    private static boolean hasNoViews(SessionBeanComponentDescription sessionBeanComponentDescription) {
        return sessionBeanComponentDescription.getViews() == null || sessionBeanComponentDescription.getViews().isEmpty();
    }

    private Class<?> getEjbClass(String className, ClassLoader cl) throws DeploymentUnitProcessingException {
        try {
            return cl.loadClass(className);
        } catch (ClassNotFoundException e) {
            throw new DeploymentUnitProcessingException(e);
        }
    }

    private Collection<String> toString(Collection<Class<?>> classes) {
        final Collection<String> classNames = new ArrayList<String>(classes.size());
        for (Class<?> klass : classes) {
            classNames.add(klass.getName());
        }
        return classNames;
    }

    private boolean isEjbVersionGreaterThanOrEqualTo32(final DeploymentUnit deploymentUnit) {
        if (deploymentUnit == null) {
            return false;
        }
        final EjbJarMetaData ejbJarMetaData = deploymentUnit.getAttachment(EjbDeploymentAttachmentKeys.EJB_JAR_METADATA);
        // if there's no EjbJarMetadata then it means that there's no ejb-jar.xml. That effectively means that the version of this EJB deployment is the "latest"
        // which in this case (i.e. starting WildFly 8 version) is "greater than or equal to 3.2". Hence return true.
        if (ejbJarMetaData == null) {
            return true;
        }
        // let the ejb jar metadata tell us what the version is
        return ejbJarMetaData.isVersionGreaterThanOrEqual(EjbJarVersion.EJB_3_2);
    }

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


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

import org.jboss.as.ee.component.Attachments;
import org.jboss.as.ee.component.ComponentDescription;
import org.jboss.as.ee.component.EEModuleDescription;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.as.server.deployment.annotation.CompositeIndex;

import java.util.Collection;

/**
 * Abstract deployment unit processors used to process {@link org.jboss.as.ee.component.ComponentDescription} instances.
 *
 * @author John Bailey
 */
public abstract class AbstractComponentConfigProcessor implements DeploymentUnitProcessor {

    /**
     * {@inheritDoc} *
     */
    public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        final EEModuleDescription eeModuleDescription = deploymentUnit.getAttachment(Attachments.EE_MODULE_DESCRIPTION);
        final Collection<ComponentDescription> componentConfigurations = eeModuleDescription.getComponentDescriptions();
        if (componentConfigurations == null || componentConfigurations.isEmpty()) {
            return;
        }

        for (ComponentDescription componentConfiguration : componentConfigurations) {
            final CompositeIndex index = deploymentUnit.getAttachment(org.jboss.as.server.deployment.Attachments.COMPOSITE_ANNOTATION_INDEX);
            if (index != null) {
                processComponentConfig(deploymentUnit, phaseContext, index, componentConfiguration);
            }
        }
    }

    /**
     * Process the component configuration instance.
     *
     * @param deploymentUnit         The deployment unit
     * @param phaseContext           The phase context
     * @param index                  The annotation index
     * @param componentDescription The component configuration
     * @throws DeploymentUnitProcessingException if any problems occur
     */
    protected abstract void processComponentConfig(final DeploymentUnit deploymentUnit, final DeploymentPhaseContext phaseContext, final CompositeIndex index, final ComponentDescription componentDescription) throws DeploymentUnitProcessingException;

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


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

package org.jboss.as.jpa.processor;

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.persistence.spi.PersistenceProvider;

import org.jboss.as.jpa.config.PersistenceProviderDeploymentHolder;
import org.jboss.as.jpa.messages.JpaLogger;
import org.jboss.as.jpa.persistenceprovider.PersistenceProviderResolverImpl;
import org.jboss.as.jpa.transaction.JtaManagerImpl;
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.DeploymentUtils;
import org.jboss.as.server.deployment.ServicesAttachment;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleClassLoader;
import org.jipijapa.plugin.spi.PersistenceProviderAdaptor;
import org.jipijapa.plugin.spi.Platform;

/**
 * Deploy JPA Persistence providers that are found in the application deployment.
 *
 * @author Scott Marlow
 */
public class PersistenceProviderHandler {

    private static final String PERSISTENCE_PROVIDER_CLASSNAME = PersistenceProvider.class.getName();

    public static void deploy(final DeploymentPhaseContext phaseContext, final Platform platform) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        final Module module = deploymentUnit.getAttachment(Attachments.MODULE);

        final ServicesAttachment servicesAttachment = deploymentUnit.getAttachment(Attachments.SERVICES);
        if (module != null && servicesAttachment != null) {
            final ModuleClassLoader deploymentModuleClassLoader = module.getClassLoader();
            PersistenceProvider provider;

            // collect list of persistence providers packaged with the application
            final List<String> providerNames = servicesAttachment.getServiceImplementations(PERSISTENCE_PROVIDER_CLASSNAME);
            List<PersistenceProvider> providerList = new ArrayList<PersistenceProvider>();

            for (String providerName : providerNames) {
                try {
                    final Class<? extends PersistenceProvider> providerClass = deploymentModuleClassLoader.loadClass(providerName).asSubclass(PersistenceProvider.class);
                    final Constructor<? extends PersistenceProvider> constructor = providerClass.getConstructor();
                    provider = constructor.newInstance();
                    providerList.add(provider);
                    JpaLogger.ROOT_LOGGER.tracef("deployment %s is using its own copy of %s", deploymentUnit.getName(), providerName);

                } catch (Exception e) {
                    throw JpaLogger.ROOT_LOGGER.cannotDeployApp(e, providerName);
                }
            }

            if (providerList.size() > 0) {
                final String adapterClass = deploymentUnit.getAttachment(JpaAttachments.ADAPTOR_CLASS_NAME);
                PersistenceProviderAdaptor adaptor;
                if (adapterClass != null) {
                    try {
                        adaptor = (PersistenceProviderAdaptor) deploymentModuleClassLoader.loadClass(adapterClass).newInstance();
                        adaptor.injectJtaManager(new JtaManagerImpl(deploymentUnit.getAttachment(JpaAttachments.TRANSACTION_SYNCHRONIZATION_REGISTRY)));
                        adaptor.injectPlatform(platform);
                        ArrayList<PersistenceProviderAdaptor> adaptorList = new ArrayList<>();
                        adaptorList.add(adaptor);
                        PersistenceProviderDeploymentHolder.savePersistenceProviderInDeploymentUnit(deploymentUnit, providerList, adaptorList);
                    } catch (InstantiationException e) {
                        throw JpaLogger.ROOT_LOGGER.cannotCreateAdapter(e, adapterClass);
                    } catch (IllegalAccessException e) {
                        throw JpaLogger.ROOT_LOGGER.cannotCreateAdapter(e, adapterClass);
                    } catch (ClassNotFoundException e) {
                        throw JpaLogger.ROOT_LOGGER.cannotCreateAdapter(e, adapterClass);
                    }
                } else {
                    // register the provider (no adapter specified)
                    PersistenceProviderDeploymentHolder.savePersistenceProviderInDeploymentUnit(deploymentUnit, providerList, null);
                }
            }
        }
    }

    public static void finishDeploy(final DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        PersistenceProviderDeploymentHolder persistenceProviderDeploymentHolder  = PersistenceProviderDeploymentHolder.getPersistenceProviderDeploymentHolder(deploymentUnit);
        Map<String, PersistenceProvider> providerMap = persistenceProviderDeploymentHolder != null ?
                persistenceProviderDeploymentHolder.getProviders() : null;

        if (providerMap != null) {
            Set<ClassLoader> deploymentClassLoaders = allDeploymentModuleClassLoaders(deploymentUnit);
            synchronized (providerMap){
                for(Map.Entry<String, PersistenceProvider> kv: providerMap.entrySet()){
                    PersistenceProviderResolverImpl.getInstance().addDeploymentSpecificPersistenceProvider(kv.getValue(), deploymentClassLoaders);
                }
            }
        }
    }

    public static void undeploy(final DeploymentUnit deploymentUnit) {
        Set<ClassLoader> deploymentClassLoaders = allDeploymentModuleClassLoaders(deploymentUnit);
        PersistenceProviderResolverImpl.getInstance().clearCachedDeploymentSpecificProviders(deploymentClassLoaders);
    }

    /**
     * returns the toplevel deployment module classloader and all subdeployment classloaders
     *
     * @param deploymentUnit
     * @return
     */
    private static Set<ClassLoader> allDeploymentModuleClassLoaders(DeploymentUnit deploymentUnit) {
        Set<ClassLoader> deploymentClassLoaders = new HashSet<ClassLoader>();
        final DeploymentUnit topDeploymentUnit = DeploymentUtils.getTopDeploymentUnit(deploymentUnit);
        final Module toplevelModule = topDeploymentUnit.getAttachment(Attachments.MODULE);
        if (toplevelModule != null) {
            deploymentClassLoaders.add(toplevelModule.getClassLoader());
            final List<DeploymentUnit> subDeployments = topDeploymentUnit.getAttachmentList(Attachments.SUB_DEPLOYMENTS);
            for (DeploymentUnit subDeploymentUnit: subDeployments) {
                final Module subDeploymentModule = subDeploymentUnit.getAttachment(Attachments.MODULE);
                if (subDeploymentModule != null) {
                    deploymentClassLoaders.add(subDeploymentModule.getClassLoader());
                }
            }
        }
        return deploymentClassLoaders;
    }
}