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

import static org.jboss.as.connector.logging.ConnectorLogger.SUBSYSTEM_DATASOURCES_LOGGER;
import static org.jboss.as.connector.subsystems.jca.Constants.DEFAULT_NAME;

import java.sql.Driver;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.jboss.as.connector.logging.ConnectorLogger;
import org.jboss.as.connector.metadata.api.common.SecurityMetadata;
import org.jboss.as.connector.metadata.api.ds.DsSecurity;
import org.jboss.as.connector.services.datasources.statistics.DataSourceStatisticsService;
import org.jboss.as.connector.services.driver.registry.DriverRegistry;
import org.jboss.as.connector.subsystems.datasources.AbstractDataSourceService;
import org.jboss.as.connector.subsystems.datasources.CommonDeploymentService;
import org.jboss.as.connector.subsystems.datasources.Constants;
import org.jboss.as.connector.subsystems.datasources.DataSourceReferenceFactoryService;
import org.jboss.as.connector.subsystems.datasources.DataSourcesExtension;
import org.jboss.as.connector.subsystems.datasources.DataSourcesSubsystemProviders;
import org.jboss.as.connector.subsystems.datasources.LocalDataSourceService;
import org.jboss.as.connector.subsystems.datasources.ModifiableDataSource;
import org.jboss.as.connector.subsystems.datasources.ModifiableXaDataSource;
import org.jboss.as.connector.subsystems.datasources.Util;
import org.jboss.as.connector.subsystems.datasources.XMLDataSourceRuntimeHandler;
import org.jboss.as.connector.subsystems.datasources.XMLXaDataSourceRuntimeHandler;
import org.jboss.as.connector.subsystems.datasources.XaDataSourceService;
import org.jboss.as.connector.util.ConnectorServices;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.capability.CapabilityServiceSupport;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.core.security.ServerSecurityManager;
import org.jboss.as.naming.ManagedReferenceFactory;
import org.jboss.as.naming.ServiceBasedNamingStore;
import org.jboss.as.naming.deployment.ContextNames;
import org.jboss.as.naming.service.BinderService;
import org.jboss.as.naming.service.NamingService;
import org.jboss.as.security.deployment.SecurityAttachments;
import org.jboss.as.security.service.SimpleSecurityManagerService;
import org.jboss.as.security.service.SubjectFactoryService;
import org.jboss.as.server.Services;
import org.jboss.as.server.deployment.Attachments;
import org.jboss.as.server.deployment.DeploymentModelUtils;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentResourceSupport;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.jca.common.api.metadata.Defaults;
import org.jboss.jca.common.api.metadata.common.Credential;
import org.jboss.jca.common.api.metadata.ds.DataSource;
import org.jboss.jca.common.api.metadata.ds.DataSources;
import org.jboss.jca.common.api.metadata.ds.DsXaPool;
import org.jboss.jca.common.api.metadata.ds.XaDataSource;
import org.jboss.jca.common.metadata.ds.DsXaPoolImpl;
import org.jboss.jca.core.api.connectionmanager.ccm.CachedConnectionManager;
import org.jboss.jca.core.api.management.ManagementRepository;
import org.jboss.jca.core.spi.mdr.MetadataRepository;
import org.jboss.jca.core.spi.rar.ResourceAdapterRepository;
import org.jboss.jca.core.spi.transaction.TransactionIntegration;
import org.jboss.jca.deployers.common.CommonDeployment;
import org.jboss.msc.service.LifecycleEvent;
import org.jboss.msc.service.LifecycleListener;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceTarget;
import org.jboss.security.SubjectFactory;

/**
 * Picks up -ds.xml deployments
 *
 * @author Jesper Pedersen
 */
public class DsXmlDeploymentInstallProcessor implements DeploymentUnitProcessor {

    private static final String DATA_SOURCE = "data-source";
    private static final String XA_DATA_SOURCE = "xa-data-source";
    private static final String CONNECTION_PROPERTIES = "connection-properties";
    private static final String XA_CONNECTION_PROPERTIES = "xa-datasource-properties";

    private static final PathAddress SUBSYSTEM_ADDRESS = PathAddress.pathAddress(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, DataSourcesExtension.SUBSYSTEM_NAME));
    private static final PathAddress DATASOURCE_ADDRESS;
    private static final PathAddress XA_DATASOURCE_ADDRESS;

    static {
        XA_DATASOURCE_ADDRESS = SUBSYSTEM_ADDRESS.append(PathElement.pathElement(XA_DATA_SOURCE));
        DATASOURCE_ADDRESS = SUBSYSTEM_ADDRESS.append(PathElement.pathElement(DATA_SOURCE));
    }

    /**
     * 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 DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        final CapabilityServiceSupport support = deploymentUnit.getAttachment(Attachments.CAPABILITY_SERVICE_SUPPORT);

        final List<DataSources> dataSourcesList = deploymentUnit.getAttachmentList(DsXmlDeploymentParsingProcessor.DATA_SOURCES_ATTACHMENT_KEY);

        final boolean legacySecurityPresent = phaseContext.getDeploymentUnit().hasAttachment(SecurityAttachments.SECURITY_ENABLED);

        for(DataSources dataSources : dataSourcesList) {
            if (dataSources.getDrivers() != null && dataSources.getDrivers().size() > 0) {
                ConnectorLogger.DS_DEPLOYER_LOGGER.driversElementNotSupported(deploymentUnit.getName());
            }

            ServiceTarget serviceTarget = phaseContext.getServiceTarget();

            if (dataSources.getDataSource() != null && dataSources.getDataSource().size() > 0) {
                for (int i = 0; i < dataSources.getDataSource().size(); i++) {
                    DataSource ds = (DataSource)dataSources.getDataSource().get(i);
                    if (ds.isEnabled() && ds.getDriver() != null) {
                        try {
                            final String jndiName = Util.cleanJndiName(ds.getJndiName(), ds.isUseJavaContext());
                            LocalDataSourceService lds = new LocalDataSourceService(jndiName, ContextNames.bindInfoFor(jndiName));
                            lds.getDataSourceConfigInjector().inject(buildDataSource(ds));
                            final String dsName = ds.getJndiName();
                            final PathAddress addr = getDataSourceAddress(dsName, deploymentUnit, false);
                            installManagementModel(ds, deploymentUnit, addr);
                            // TODO why have we been ignoring a configured legacy security domain but no legacy security present?
                            boolean useLegacySecurity = legacySecurityPresent && isLegacySecurityRequired(ds.getSecurity());
                            startDataSource(lds, jndiName, ds.getDriver(), serviceTarget,
                                    getRegistration(false, deploymentUnit), getResource(dsName, false, deploymentUnit), dsName, useLegacySecurity, ds.isJTA(), support);
                        } catch (Exception e) {
                            throw ConnectorLogger.ROOT_LOGGER.exceptionDeployingDatasource(e, ds.getJndiName());
                        }
                    } else {
                        ConnectorLogger.DS_DEPLOYER_LOGGER.debugf("Ignoring: %s", ds.getJndiName());
                    }
                }
            }

            if (dataSources.getXaDataSource() != null && dataSources.getXaDataSource().size() > 0) {
               for (int i = 0; i < dataSources.getXaDataSource().size(); i++) {
                    XaDataSource xads = (XaDataSource)dataSources.getXaDataSource().get(i);
                    if (xads.isEnabled() && xads.getDriver() != null) {
                        try {
                            String jndiName = Util.cleanJndiName(xads.getJndiName(), xads.isUseJavaContext());
                            XaDataSourceService xds = new XaDataSourceService(jndiName, ContextNames.bindInfoFor(jndiName));
                            xds.getDataSourceConfigInjector().inject(buildXaDataSource(xads));
                            final String dsName = xads.getJndiName();
                            final PathAddress addr = getDataSourceAddress(dsName, deploymentUnit, true);
                            installManagementModel(xads, deploymentUnit, addr);
                            final Credential credential = xads.getRecovery() == null? null: xads.getRecovery().getCredential();
                            // TODO why have we been ignoring a configured legacy security domain but no legacy security present?
                            boolean useLegacySecurity = legacySecurityPresent && (isLegacySecurityRequired(xads.getSecurity())
                                                        || isLegacySecurityRequired(credential));
                            startDataSource(xds, jndiName, xads.getDriver(), serviceTarget,
                                    getRegistration(true, deploymentUnit), getResource(dsName, true, deploymentUnit), dsName, useLegacySecurity, true, support);

                        } catch (Exception e) {
                            throw ConnectorLogger.ROOT_LOGGER.exceptionDeployingDatasource(e, xads.getJndiName());
                        }
                    } else {
                        ConnectorLogger.DS_DEPLOYER_LOGGER.debugf("Ignoring %s", xads.getJndiName());
                    }
                }
            }
        }

    }

    private void installManagementModel(final DataSource ds, final DeploymentUnit deploymentUnit, final PathAddress addr) {
        XMLDataSourceRuntimeHandler.INSTANCE.registerDataSource(addr, ds);
        final DeploymentResourceSupport deploymentResourceSupport = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_RESOURCE_SUPPORT);
        deploymentResourceSupport.getDeploymentSubModel(DataSourcesExtension.SUBSYSTEM_NAME, addr.getLastElement());
        if (ds.getConnectionProperties() != null) {
            for (final Map.Entry<String, String> prop : ds.getConnectionProperties().entrySet()) {
                PathAddress registration = PathAddress.pathAddress(addr.getLastElement(), PathElement.pathElement(CONNECTION_PROPERTIES, prop.getKey()));
                deploymentResourceSupport.getDeploymentSubModel(DataSourcesExtension.SUBSYSTEM_NAME, registration);
            }
        }
    }

    private void installManagementModel(final XaDataSource ds, final DeploymentUnit deploymentUnit, final PathAddress addr) {
        XMLXaDataSourceRuntimeHandler.INSTANCE.registerDataSource(addr, ds);
        final DeploymentResourceSupport deploymentResourceSupport = deploymentUnit.getAttachment(Attachments.DEPLOYMENT_RESOURCE_SUPPORT);
        deploymentResourceSupport.getDeploymentSubModel(DataSourcesExtension.SUBSYSTEM_NAME, addr.getLastElement());
        if (ds.getXaDataSourceProperty() != null) {
            for (final Map.Entry<String, String> prop : ds.getXaDataSourceProperty().entrySet()) {
                PathAddress registration = PathAddress.pathAddress(addr.getLastElement(), PathElement.pathElement(XA_CONNECTION_PROPERTIES, prop.getKey()));
                deploymentResourceSupport.getDeploymentSubModel(DataSourcesExtension.SUBSYSTEM_NAME, registration);
            }
        }
    }

    private void undeployDataSource(final DataSource ds, final DeploymentUnit deploymentUnit) {
        final PathAddress addr = getDataSourceAddress(ds.getJndiName(), deploymentUnit, false);
        XMLDataSourceRuntimeHandler.INSTANCE.unregisterDataSource(addr);
    }

    private void undeployXaDataSource(final XaDataSource ds, final DeploymentUnit deploymentUnit) {
        final PathAddress addr = getDataSourceAddress(ds.getJndiName(), deploymentUnit, true);
        XMLXaDataSourceRuntimeHandler.INSTANCE.unregisterDataSource(addr);
    }

    @Override
    public void undeploy(final DeploymentUnit deploymentUnit) {
        final List<DataSources> dataSourcesList = deploymentUnit.getAttachmentList(DsXmlDeploymentParsingProcessor.DATA_SOURCES_ATTACHMENT_KEY);

        for (final DataSources dataSources : dataSourcesList) {
            if (dataSources.getDataSource() != null) {
                for (int i = 0; i < dataSources.getDataSource().size(); i++) {
                    DataSource ds = dataSources.getDataSource().get(i);
                    undeployDataSource(ds, deploymentUnit);
                }
            }
            if (dataSources.getXaDataSource() != null) {
                for (int i = 0; i < dataSources.getXaDataSource().size(); i++) {
                    XaDataSource xads = dataSources.getXaDataSource().get(i);
                    undeployXaDataSource(xads, deploymentUnit);
                }
            }
        }

        deploymentUnit.removeAttachment(DsXmlDeploymentParsingProcessor.DATA_SOURCES_ATTACHMENT_KEY);
    }

    private ModifiableDataSource buildDataSource(DataSource ds) throws org.jboss.jca.common.api.validator.ValidateException {
        assert ds.getSecurity() == null || ds.getSecurity() instanceof DsSecurity;
        return new ModifiableDataSource(ds.getConnectionUrl(),
                ds.getDriverClass(), ds.getDataSourceClass(), ds.getDriver(),
                ds.getTransactionIsolation(), ds.getConnectionProperties(), ds.getTimeOut(),
                (DsSecurity) ds.getSecurity(), ds.getStatement(), ds.getValidation(),
                ds.getUrlDelimiter(), ds.getUrlSelectorStrategyClassName(), ds.getNewConnectionSql(),
                ds.isUseJavaContext(), ds.getPoolName(), ds.isEnabled(), ds.getJndiName(),
                ds.isSpy(), ds.isUseCcm(), ds.isJTA(), ds.isConnectable(), ds.isTracking(), ds.getMcp(), ds.isEnlistmentTrace(), ds.getPool());
    }

    private ModifiableXaDataSource buildXaDataSource(XaDataSource xads) throws org.jboss.jca.common.api.validator.ValidateException {
        final DsXaPool xaPool;
        if (xads.getXaPool() == null) {
            xaPool = new DsXaPoolImpl(Defaults.MIN_POOL_SIZE, Defaults.INITIAL_POOL_SIZE, Defaults.MAX_POOL_SIZE, Defaults.PREFILL, Defaults.USE_STRICT_MIN, Defaults.FLUSH_STRATEGY,
                                      Defaults.IS_SAME_RM_OVERRIDE, Defaults.INTERLEAVING, Defaults.PAD_XID, Defaults.WRAP_XA_RESOURCE, Defaults.NO_TX_SEPARATE_POOL, Defaults.ALLOW_MULTIPLE_USERS, null, Defaults.FAIR, null);
        } else {
            final DsXaPool p = xads.getXaPool();
            xaPool = new DsXaPoolImpl(getDef(p.getMinPoolSize(), Defaults.MIN_POOL_SIZE), getDef(p.getInitialPoolSize(), Defaults.INITIAL_POOL_SIZE), getDef(p.getMaxPoolSize(), Defaults.MAX_POOL_SIZE), getDef(p.isPrefill(), Defaults.PREFILL),
                    getDef(p.isUseStrictMin(), Defaults.USE_STRICT_MIN), getDef(p.getFlushStrategy(), Defaults.FLUSH_STRATEGY), getDef(p.isSameRmOverride(),
                    Defaults.IS_SAME_RM_OVERRIDE), getDef(p.isInterleaving(), Defaults.INTERLEAVING), getDef(p.isPadXid(), Defaults.PAD_XID)
                    , getDef(p.isWrapXaResource(), Defaults.WRAP_XA_RESOURCE), getDef(p.isNoTxSeparatePool(), Defaults.NO_TX_SEPARATE_POOL), getDef(p.isAllowMultipleUsers(), Defaults.ALLOW_MULTIPLE_USERS),
                    p.getCapacity(), getDef(p.isFair(), Defaults.FAIR), p.getConnectionListener());
        }

        return new ModifiableXaDataSource(xads.getTransactionIsolation(),
                xads.getTimeOut(), xads.getSecurity(),
                xads.getStatement(), xads.getValidation(),
                xads.getUrlDelimiter(), xads.getUrlProperty(), xads.getUrlSelectorStrategyClassName(),
                xads.isUseJavaContext(), xads.getPoolName(), xads.isEnabled(), xads.getJndiName(),
                xads.isSpy(), xads.isUseCcm(), xads.isConnectable(), xads.isTracking(),
                xads.getMcp(), xads.isEnlistmentTrace(),
                xads.getXaDataSourceProperty(), xads.getXaDataSourceClass(), xads.getDriver(),
                xads.getNewConnectionSql(), xaPool, xads.getRecovery());
    }

    private <T> T getDef(T value, T def) {
        return value != null ? value : def;
    }

    private void startDataSource(final AbstractDataSourceService dataSourceService,
                                 final String jndiName,
                                 final String driverName,
                                 final ServiceTarget serviceTarget,
                                 final ManagementResourceRegistration registration,
                                 final Resource resource,
                                 final String managementName,
                                 boolean requireLegacySecurity,
                                 final boolean isTransactional,
                                 final CapabilityServiceSupport support) {

        final ContextNames.BindInfo bindInfo = ContextNames.bindInfoFor(jndiName);

        final ServiceName dataSourceServiceName = AbstractDataSourceService.getServiceName(bindInfo);
        final ServiceBuilder<?> dataSourceServiceBuilder =
                Services.addServerExecutorDependency(
                        serviceTarget.addService(dataSourceServiceName, dataSourceService),
                        dataSourceService.getExecutorServiceInjector())
                .addDependency(ConnectorServices.IRONJACAMAR_MDR, MetadataRepository.class, dataSourceService.getMdrInjector())
                .addDependency(ConnectorServices.RA_REPOSITORY_SERVICE, ResourceAdapterRepository.class, dataSourceService.getRaRepositoryInjector())
                .addDependency(support.getCapabilityServiceName(ConnectorServices.TRANSACTION_INTEGRATION_CAPABILITY_NAME), TransactionIntegration.class,
                        dataSourceService.getTransactionIntegrationInjector())
                .addDependency(ConnectorServices.MANAGEMENT_REPOSITORY_SERVICE, ManagementRepository.class,
                        dataSourceService.getManagementRepositoryInjector())
                .addDependency(ConnectorServices.CCM_SERVICE, CachedConnectionManager.class, dataSourceService.getCcmInjector())
                .addDependency(ConnectorServices.JDBC_DRIVER_REGISTRY_SERVICE, DriverRegistry.class,
                        dataSourceService.getDriverRegistryInjector());
        dataSourceServiceBuilder.requires(ConnectorServices.BOOTSTRAP_CONTEXT_SERVICE.append(DEFAULT_NAME));
        dataSourceServiceBuilder.requires(support.getCapabilityServiceName(NamingService.CAPABILITY_NAME));

        if (requireLegacySecurity) {
            dataSourceServiceBuilder.addDependency(SimpleSecurityManagerService.SERVICE_NAME, ServerSecurityManager.class,
                    dataSourceService.getServerSecurityManager());
            dataSourceServiceBuilder.addDependency(SubjectFactoryService.SERVICE_NAME, SubjectFactory.class,
                    dataSourceService.getSubjectFactoryInjector());
        }

        //Register an empty override model regardless of we're enabled or not - the statistics listener will add the relevant childresources
        if (registration.isAllowsOverride()) {
            ManagementResourceRegistration overrideRegistration = registration.getOverrideModel(managementName);
            if (overrideRegistration == null || overrideRegistration.isAllowsOverride()) {
                overrideRegistration = registration.registerOverrideModel(managementName, DataSourcesSubsystemProviders.OVERRIDE_DS_DESC);
            }
            DataSourceStatisticsService statsService = new DataSourceStatisticsService(registration, false );
            final ServiceBuilder statsServiceSB =
                            serviceTarget.addService(dataSourceServiceName.append(Constants.STATISTICS), statsService);
            statsServiceSB.requires(dataSourceServiceName);
            statsServiceSB.addDependency(CommonDeploymentService.getServiceName(bindInfo), CommonDeployment.class, statsService.getCommonDeploymentInjector());
            statsServiceSB.setInitialMode(ServiceController.Mode.PASSIVE);
            statsServiceSB.install();
            DataSourceStatisticsService.registerStatisticsResources(resource);
        } // else should probably throw an ISE or something

        final ServiceName driverServiceName = ServiceName.JBOSS.append("jdbc-driver", driverName.replaceAll("\\.", "_"));
        if (driverServiceName != null) {
            dataSourceServiceBuilder.addDependency(driverServiceName, Driver.class,
                    dataSourceService.getDriverInjector());
        }

        final DataSourceReferenceFactoryService referenceFactoryService = new DataSourceReferenceFactoryService();
        final ServiceName referenceFactoryServiceName = DataSourceReferenceFactoryService.SERVICE_NAME_BASE
                .append(jndiName);
        final ServiceBuilder<?> referenceBuilder = serviceTarget.addService(referenceFactoryServiceName,
                referenceFactoryService).addDependency(dataSourceServiceName, javax.sql.DataSource.class,
                referenceFactoryService.getDataSourceInjector());

        final BinderService binderService = new BinderService(bindInfo.getBindName());
        final ServiceBuilder<?> binderBuilder = serviceTarget
                .addService(bindInfo.getBinderServiceName(), binderService)
                .addDependency(referenceFactoryServiceName, ManagedReferenceFactory.class, binderService.getManagedObjectInjector())
                .addDependency(bindInfo.getParentContextServiceName(), ServiceBasedNamingStore.class, binderService.getNamingStoreInjector()).addListener(new LifecycleListener() {
                    private volatile boolean bound;
                    public void handleEvent(final ServiceController<?> controller, final LifecycleEvent event) {
                        switch (event) {
                            case UP: {
                                if (isTransactional) {
                                    SUBSYSTEM_DATASOURCES_LOGGER.boundDataSource(jndiName);
                                } else {
                                    SUBSYSTEM_DATASOURCES_LOGGER.boundNonJTADataSource(jndiName);
                                }
                                bound = true;
                                break;
                            }
                            case DOWN: {
                                if (bound) {
                                    if (isTransactional) {
                                        SUBSYSTEM_DATASOURCES_LOGGER.unboundDataSource(jndiName);
                                    } else {
                                        SUBSYSTEM_DATASOURCES_LOGGER.unBoundNonJTADataSource(jndiName);
                                    }
                                }
                                break;
                            }
                            case REMOVED: {
                                SUBSYSTEM_DATASOURCES_LOGGER.debugf("Removed JDBC Data-source [%s]", jndiName);
                                break;
                            }
                        }
                    }
                });

        dataSourceServiceBuilder.setInitialMode(ServiceController.Mode.ACTIVE).install();
        referenceBuilder.setInitialMode(ServiceController.Mode.ACTIVE).install();
        binderBuilder.setInitialMode(ServiceController.Mode.ACTIVE).install();
    }

    private static PathAddress getDataSourceAddress(final String jndiName, DeploymentUnit deploymentUnit, boolean xa) {
        List<PathElement> elements = new ArrayList<PathElement>();
        if (deploymentUnit.getParent() == null) {
            elements.add(PathElement.pathElement(ModelDescriptionConstants.DEPLOYMENT, deploymentUnit.getName()));
        } else {
            elements.add(PathElement.pathElement(ModelDescriptionConstants.DEPLOYMENT, deploymentUnit.getParent().getName()));
            elements.add(PathElement.pathElement(ModelDescriptionConstants.SUBDEPLOYMENT, deploymentUnit.getName()));
        }
        elements.add(PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, DataSourcesExtension.SUBSYSTEM_NAME));
        if (xa) {
            elements.add(PathElement.pathElement(XA_DATA_SOURCE, jndiName));
        } else {
            elements.add(PathElement.pathElement(DATA_SOURCE, jndiName));
        }
        return PathAddress.pathAddress(elements);
    }

    static Resource getOrCreate(final Resource parent, final PathAddress address) {
        Resource current = parent;
        for (final PathElement element : address) {
            synchronized (current) {
                if (current.hasChild(element)) {
                    current = current.requireChild(element);
                } else {
                    final Resource resource = Resource.Factory.create();
                    current.registerChild(element, resource);
                    current = resource;
                }
            }
        }
        return current;
    }

    private Resource getResource(final String dsName, final boolean xa, final DeploymentUnit unit) {
        final Resource root = unit.getAttachment(DeploymentModelUtils.DEPLOYMENT_RESOURCE);
        final String key = xa ? XA_DATA_SOURCE : DATA_SOURCE;
        final PathAddress address = PathAddress.pathAddress(PathElement.pathElement(key, dsName));
        synchronized (root) {
            final Resource subsystem = getOrCreate(root, SUBSYSTEM_ADDRESS);
            return getOrCreate(subsystem, address);
        }
    }

    private ManagementResourceRegistration getRegistration(final boolean xa, final DeploymentUnit unit) {
        final Resource root = unit.getAttachment(DeploymentModelUtils.DEPLOYMENT_RESOURCE);
        synchronized (root) {
            ManagementResourceRegistration registration = unit.getAttachment(DeploymentModelUtils.MUTABLE_REGISTRATION_ATTACHMENT);
            final PathAddress address = xa ? XA_DATASOURCE_ADDRESS : DATASOURCE_ADDRESS;
            ManagementResourceRegistration subModel = registration.getSubModel(address);
            if (subModel == null) {
                throw new IllegalStateException(address.toString());
            }
            return subModel;
        }
    }

    private static boolean isLegacySecurityRequired(org.jboss.jca.common.api.metadata.common.SecurityMetadata config) {
        boolean result = config != null && config instanceof SecurityMetadata && !((SecurityMetadata) config).isElytronEnabled();
        if (result) {
            String domain = config.resolveSecurityDomain();
            result = domain != null && domain.trim().length() > 0;
        }
        return result;
    }
}


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

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

import java.lang.reflect.Constructor;
import java.sql.Driver;
import java.util.List;

import org.jboss.as.connector.services.driver.DriverService;
import org.jboss.as.connector.services.driver.InstalledDriver;
import org.jboss.as.connector.services.driver.registry.DriverRegistry;
import org.jboss.as.connector.util.ConnectorServices;
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.ServicesAttachment;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleClassLoader;
import org.jboss.msc.service.ServiceController.Mode;
import org.jboss.msc.service.ServiceName;

/**
 * Deploy any JDBC drivers in a deployment unit.
 * @author David M. Lloyd
 */
public final class DriverProcessor implements DeploymentUnitProcessor {

    /** {@inheritDoc} */
    @Override
    public void deploy(final DeploymentPhaseContext phaseContext) 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 classLoader = module.getClassLoader();
            final List<String> driverNames = servicesAttachment.getServiceImplementations(Driver.class.getName());
            int idx = 0;
            for (String driverClassName : driverNames) {
                try {
                    final Class<? extends Driver> driverClass = classLoader.loadClass(driverClassName).asSubclass(Driver.class);
                    final Constructor<? extends Driver> constructor = driverClass.getConstructor();
                    final Driver driver = constructor.newInstance();
                    final int majorVersion = driver.getMajorVersion();
                    final int minorVersion = driver.getMinorVersion();
                    final boolean compliant = driver.jdbcCompliant();
                    if (compliant) {
                        DEPLOYER_JDBC_LOGGER.deployingCompliantJdbcDriver(driverClass, Integer.valueOf(majorVersion),
                                Integer.valueOf(minorVersion));
                    } else {
                        DEPLOYER_JDBC_LOGGER.deployingNonCompliantJdbcDriver(driverClass, Integer.valueOf(majorVersion),
                                Integer.valueOf(minorVersion));
                    }
                    String driverName = deploymentUnit.getName();
                    if ((driverName.contains(".") && ! driverName.endsWith(".jar")) || driverNames.size() != 1) {
                        driverName += "_" + driverClassName + "_" + majorVersion + "_" + minorVersion;
                    }
                    InstalledDriver driverMetadata = new InstalledDriver(driverName, driverClass.getName(), null, null, majorVersion,
                            minorVersion, compliant);
                    DriverService driverService = new DriverService(driverMetadata, driver);
                    phaseContext
                            .getServiceTarget()
                            .addService(ServiceName.JBOSS.append("jdbc-driver", driverName.replaceAll("\\.", "_")), driverService)
                            .addDependency(ConnectorServices.JDBC_DRIVER_REGISTRY_SERVICE, DriverRegistry.class,
                                    driverService.getDriverRegistryServiceInjector()).setInitialMode(Mode.ACTIVE).install();

                    if (idx == 0 && driverNames.size() != 1) {
                        // create short name driver service
                        driverName = deploymentUnit.getName(); // reset driverName to the deployment unit name
                        driverMetadata = new InstalledDriver(driverName, driverClass.getName(), null,
                                null, majorVersion, minorVersion, compliant);
                        driverService = new DriverService(driverMetadata, driver);
                        phaseContext.getServiceTarget()
                                .addService(ServiceName.JBOSS.append("jdbc-driver", driverName.replaceAll("\\.", "_")), driverService)
                                .addDependency(ConnectorServices.JDBC_DRIVER_REGISTRY_SERVICE, DriverRegistry.class, driverService.getDriverRegistryServiceInjector())
                                .setInitialMode(Mode.ACTIVE).install();
                    }
                    idx++;
                } catch (Throwable e) {
                    DEPLOYER_JDBC_LOGGER.cannotInstantiateDriverClass(driverClassName, e);
                }

            }
        }
    }

    /** {@inheritDoc} */
    @Override
    public void undeploy(final 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.ejb3.interceptor;

import org.jboss.metadata.ejb.parser.spec.AbstractMetaDataParser;
import org.jboss.metadata.ejb.parser.spec.InterceptorBindingMetaDataParser;
import org.jboss.metadata.ejb.spec.InterceptorBindingMetaData;
import org.jboss.metadata.property.PropertyReplacer;

import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

/**
 * Responsible for parsing the container-interceptors in jboss-ejb3.xml
 *
 * @author Jaikiran Pai
 */
public class ContainerInterceptorsParser extends AbstractMetaDataParser<ContainerInterceptorsMetaData> {

    public static final ContainerInterceptorsParser INSTANCE = new ContainerInterceptorsParser();
    public static final String NAMESPACE_URI_1_0 = "urn:container-interceptors:1.0";

    private static final String ELEMENT_CONTAINER_INTERCEPTORS = "container-interceptors";
    private static final String ELEMENT_INTERCEPTOR_BINDING = "interceptor-binding";

    @Override
    public ContainerInterceptorsMetaData parse(XMLStreamReader reader, PropertyReplacer propertyReplacer) throws XMLStreamException {
        // make sure it's the right namespace
        if (!reader.getNamespaceURI().equals(NAMESPACE_URI_1_0)) {
            throw unexpectedElement(reader);
        }
        // only process relevant elements
        if (!reader.getLocalName().equals(ELEMENT_CONTAINER_INTERCEPTORS)) {
            throw unexpectedElement(reader);
        }

        final ContainerInterceptorsMetaData metaData = new ContainerInterceptorsMetaData();
        processElements(metaData, reader, propertyReplacer);
        return metaData;
    }

    @Override
    protected void processElement(final ContainerInterceptorsMetaData containerInterceptorsMetadata, final XMLStreamReader reader, final PropertyReplacer propertyReplacer) throws XMLStreamException {
        final String localName = reader.getLocalName();
        if (!localName.equals(ELEMENT_INTERCEPTOR_BINDING)) {
            throw unexpectedElement(reader);
        }
        // parse the interceptor-binding
        final InterceptorBindingMetaData interceptorBinding = this.readInterceptorBinding(reader, propertyReplacer);
        // add the interceptor binding to the container interceptor metadata
        containerInterceptorsMetadata.addInterceptorBinding(interceptorBinding);
    }

    /**
     * Parses the interceptor-binding element and returns the corresponding {@link InterceptorBindingMetaData}
     *
     * @param reader
     * @param propertyReplacer
     * @return
     * @throws XMLStreamException
     */
    private InterceptorBindingMetaData readInterceptorBinding(final XMLStreamReader reader, final PropertyReplacer propertyReplacer) throws XMLStreamException {
        return InterceptorBindingMetaDataParser.INSTANCE.parse(reader, propertyReplacer);
    }
}


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

import org.jboss.as.ee.component.Attachments;
import org.jboss.as.ee.component.ComponentConfiguration;
import org.jboss.as.ee.component.ComponentConfigurator;
import org.jboss.as.ee.component.ComponentDescription;
import org.jboss.as.ee.component.EEModuleDescription;
import org.jboss.as.ee.component.interceptors.InterceptorOrder;
import org.jboss.as.ejb3.component.session.SessionBeanComponentDescription;
import org.jboss.as.jpa.interceptor.SBInvocationInterceptor;
import org.jboss.as.jpa.interceptor.SFSBCreateInterceptor;
import org.jboss.as.jpa.interceptor.SFSBDestroyInterceptor;
import org.jboss.as.jpa.interceptor.SFSBInvocationInterceptor;
import org.jboss.as.jpa.interceptor.SFSBPreCreateInterceptor;
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 static org.jboss.as.jpa.messages.JpaLogger.ROOT_LOGGER;

/**
 * @author Stuart Douglas
 */
public class JPAInterceptorProcessor implements DeploymentUnitProcessor {

    @Override
    public void deploy(final DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
        final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
        final EEModuleDescription moduleDescription = deploymentUnit.getAttachment(Attachments.EE_MODULE_DESCRIPTION);
        for (ComponentDescription component : moduleDescription.getComponentDescriptions()) {
            if (component instanceof SessionBeanComponentDescription) {
                ROOT_LOGGER.tracef("registering session bean interceptors for component '%s' in '%s'", component.getComponentName(), deploymentUnit.getName());
                registerSessionBeanInterceptors((SessionBeanComponentDescription) component, deploymentUnit);
            }
        }
    }

    // Register our listeners on SFSB that will be created
    private void registerSessionBeanInterceptors(SessionBeanComponentDescription componentDescription, final DeploymentUnit deploymentUnit) {
        // if it's a SFSB then setup appropriate interceptors
        if (componentDescription.isStateful()) {

            // first setup the post construct and pre destroy component interceptors
            componentDescription.getConfigurators().addFirst(new ComponentConfigurator() {
                @Override
                public void configure(DeploymentPhaseContext context, ComponentDescription description, ComponentConfiguration configuration) throws
                    DeploymentUnitProcessingException {
                    configuration.addPostConstructInterceptor(SFSBPreCreateInterceptor.FACTORY, InterceptorOrder.ComponentPostConstruct.JPA_SFSB_PRE_CREATE);
                    configuration.addPostConstructInterceptor(SFSBCreateInterceptor.FACTORY, InterceptorOrder.ComponentPostConstruct.JPA_SFSB_CREATE);
                    configuration.addPreDestroyInterceptor(SFSBDestroyInterceptor.FACTORY, InterceptorOrder.ComponentPreDestroy.JPA_SFSB_DESTROY);
                    configuration.addComponentInterceptor(SFSBInvocationInterceptor.FACTORY, InterceptorOrder.Component.JPA_SFSB_INTERCEPTOR, false);

                    //we need to serialized the entity manager state
                    configuration.getInterceptorContextKeys().add(SFSBInvocationInterceptor.CONTEXT_KEY);
                }
            });
        }
        // register interceptor on stateful/stateless SB with transactional entity manager.
        if ((componentDescription.isStateful() || componentDescription.isStateless())) {
            componentDescription.getConfigurators().add(new ComponentConfigurator() {
                @Override
                public void configure(DeploymentPhaseContext context, ComponentDescription description, ComponentConfiguration configuration) throws
                    DeploymentUnitProcessingException {
                    configuration.addComponentInterceptor(SBInvocationInterceptor.FACTORY, InterceptorOrder.Component.JPA_SESSION_BEAN_INTERCEPTOR, false);
                }
            });
        }
    }

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


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

import java.nio.charset.StandardCharsets;

import org.ietf.jgss.GSSException;
import org.ietf.jgss.Oid;
import org.jboss.metadata.ejb.jboss.IORASContextMetaData;
import org.jboss.metadata.ejb.jboss.IORSASContextMetaData;
import org.jboss.metadata.ejb.jboss.IORSecurityConfigMetaData;
import org.jboss.metadata.ejb.jboss.IORTransportConfigMetaData;
import org.omg.CORBA.Any;
import org.omg.CORBA.BAD_PARAM;
import org.omg.CORBA.ORB;
import org.omg.CSI.ITTAnonymous;
import org.omg.CSI.ITTDistinguishedName;
import org.omg.CSI.ITTPrincipalName;
import org.omg.CSI.ITTX509CertChain;
import org.omg.CSIIOP.AS_ContextSec;
import org.omg.CSIIOP.CompoundSecMech;
import org.omg.CSIIOP.CompoundSecMechList;
import org.omg.CSIIOP.CompoundSecMechListHelper;
import org.omg.CSIIOP.Confidentiality;
import org.omg.CSIIOP.DetectMisordering;
import org.omg.CSIIOP.DetectReplay;
import org.omg.CSIIOP.EstablishTrustInClient;
import org.omg.CSIIOP.EstablishTrustInTarget;
import org.omg.CSIIOP.IdentityAssertion;
import org.omg.CSIIOP.Integrity;
import org.omg.CSIIOP.SAS_ContextSec;
import org.omg.CSIIOP.ServiceConfiguration;
import org.omg.CSIIOP.TAG_TLS_SEC_TRANS;
import org.omg.CSIIOP.TLS_SEC_TRANS;
import org.omg.CSIIOP.TLS_SEC_TRANSHelper;
import org.omg.CSIIOP.TransportAddress;
import org.omg.GSSUP.GSSUPMechOID;
import org.omg.GSSUP.InitialContextToken;
import org.omg.GSSUP.InitialContextTokenHelper;
import org.omg.IOP.Codec;
import org.omg.IOP.TAG_CSI_SEC_MECH_LIST;
import org.omg.IOP.TAG_NULL_TAG;
import org.omg.IOP.TaggedComponent;
import org.omg.IOP.CodecPackage.InvalidTypeForEncoding;
import org.omg.PortableInterceptor.ClientRequestInfo;
import org.omg.SSLIOP.SSL;
import org.omg.SSLIOP.SSLHelper;
import org.omg.SSLIOP.TAG_SSL_SEC_TRANS;
import org.wildfly.iiop.openjdk.Constants;
import org.wildfly.iiop.openjdk.logging.IIOPLogger;
import org.wildfly.iiop.openjdk.service.CorbaORBService;

/**
 *

 * This class defines utility methods for creating, comparing, encoding and decoding CSIv2 components.
 *

 *
 * @author Francisco Reverbel
 * @author Stefan Guilhen
 */
public final class CSIv2Util {

    /**
     * DER-encoded ASN.1 representation of the GSSUP mechanism OID.
     */
    private static final byte[] gssUpMechOidArray = createGSSUPMechOID();

    /**
     *

     * Private constructor to implement the singleton pattern.
     *

     */
    private CSIv2Util() {
    }

    /**
     *

     * Make a deep copy of an {@code IOP:TaggedComponent}.
     *

     *
     * @param tc the {@code TaggedComponent} to be copied.
     * @return a reference to the created copy.
     */
    public static TaggedComponent createCopy(TaggedComponent tc) {
        TaggedComponent copy = null;

        if (tc != null) {
            byte[] buf = new byte[tc.component_data.length];
            System.arraycopy(tc.component_data, 0, buf, 0, tc.component_data.length);
            copy = new TaggedComponent(tc.tag, buf);
        }
        return copy;
    }

    /**
     *

     * Return a top-level {@code IOP::TaggedComponent} to be stuffed into an IOR, containing a structure
     * {@code SSLIOP::SSL}, tagged as {@code TAG_SSL_SEC_TRANS}.
     *

     *

     * Should be called with non-null metadata, in which case we probably don't want to include security info in the IOR.
     *

     *
     * @param metadata the metadata object that contains the SSL configuration info.
     * @param codec    the {@code Codec} used to encode the SSL component.
     * @param sslPort  an {@code int} representing the SSL port.
     * @param orb      a reference to the running {@code ORB}.
     * @return a {@code TaggedComponent} representing the encoded SSL component.
     */
    public static TaggedComponent createSSLTaggedComponent(IORSecurityConfigMetaData metadata, Codec codec, int sslPort,
                                                           ORB orb) {
        if (metadata == null) {
            IIOPLogger.ROOT_LOGGER.debug("Method createSSLTaggedComponent() called with null metadata");
            return null;
        }

        if (sslPort == 0) {
            // no support for transport security.
            return null;
        }

        TaggedComponent tc;
        try {
            int supports = createTargetSupports(metadata.getTransportConfig());
            int requires = createTargetRequires(metadata.getTransportConfig());
            SSL ssl = new SSL((short) supports, (short) requires, (short) sslPort);
            Any any = orb.create_any();
            SSLHelper.insert(any, ssl);
            byte[] componentData = codec.encode_value(any);
            tc = new TaggedComponent(TAG_SSL_SEC_TRANS.value, componentData);
        } catch (InvalidTypeForEncoding e) {
            throw IIOPLogger.ROOT_LOGGER.unexpectedException(e);
        }
        return tc;
    }

    /**
     *

     * Return a top-level {@code IOP:TaggedComponent} to be stuffed into an IOR, containing a {@code org.omg.CSIIOP}.
     * {@code CompoundSecMechList}, tagged as {@code TAG_CSI_SEC_MECH_LIST}. Only one such component can exist inside
     * an IOR.
     *

     *

     * Should be called with non-null metadata, in which case we probably don't want to include security info in the IOR.
     *

     *
     * @param metadata the metadata object that contains the CSIv2 security configuration info.
     * @param codec    the {@code Codec} used to encode the CSIv2 security component.
     * @param sslPort  an {@code int} representing the SSL port.
     * @param orb      a reference to the running {@code ORB}.
     * @return a {@code TaggedComponent} representing the encoded CSIv2 security component.
     */
    public static TaggedComponent createSecurityTaggedComponent(IORSecurityConfigMetaData metadata, Codec codec,
                                                                int sslPort, ORB orb) {
        if (metadata == null) {
            IIOPLogger.ROOT_LOGGER.debug("Method createSecurityTaggedComponent() called with null metadata");
            return null;
        }

        TaggedComponent tc;

        // get the the supported security mechanisms.
        CompoundSecMech[] mechList = createCompoundSecMechanisms(metadata, codec, sslPort, orb);

        // the above is wrapped into a org.omg.CSIIOP.CompoundSecMechList structure, which is NOT a CompoundSecMech[].
        // we don't support stateful/reusable security contexts (false).
        CompoundSecMechList csmList = new CompoundSecMechList(false, mechList);
        // finally, the CompoundSecMechList must be encoded as a TaggedComponent
        try {
            Any any = orb.create_any();
            CompoundSecMechListHelper.insert(any, csmList);
            byte[] b = codec.encode_value(any);
            tc = new TaggedComponent(TAG_CSI_SEC_MECH_LIST.value, b);
        } catch (InvalidTypeForEncoding e) {
            throw IIOPLogger.ROOT_LOGGER.unexpectedException(e);
        }
        return tc;
    }

    /**
     *

     * Create a {@code org.omg.CSIIOP.CompoundSecMechanisms} which is a sequence of {@code CompoundSecMech}. Here we only
     * support one security mechanism.
     *

     *
     * @param metadata the metadata object that contains the CSIv2 security configuration info.
     * @param codec    the {@code Codec} used to encode the CSIv2 security component.
     * @param sslPort  an {@code int} representing the SSL port.
     * @param orb      a reference to the running {@code ORB}.
     * @return the constructed {@code CompoundSecMech} array.
     */
    public static CompoundSecMech[] createCompoundSecMechanisms(IORSecurityConfigMetaData metadata, Codec codec,
                                                                int sslPort, ORB orb) {
        // support just 1 security mechanism for now (and ever).
        CompoundSecMech[] csmList = new CompoundSecMech[1];

        // a CompoundSecMech contains: target_requires, transport_mech, as_context_mech, sas_context_mech.
        TaggedComponent transport_mech = createTransportMech(metadata.getTransportConfig(), codec, sslPort, orb);

        // create AS Context.
        AS_ContextSec asContext = createAuthenticationServiceContext(metadata);

        // create SAS Context.
        SAS_ContextSec sasContext = createSecureAttributeServiceContext(metadata);

        // create target_requires bit field (AssociationOption) can't read directly the transport_mech TaggedComponent.
        int target_requires = createTargetRequires(metadata.getTransportConfig()) | asContext.target_requires |
                sasContext.target_requires;

        CompoundSecMech csm = new CompoundSecMech((short) target_requires, transport_mech, asContext, sasContext);
        csmList[0] = csm;

        return csmList;
    }

    /**
     *

     * Create the Secure Attribute Service (SAS) context included in a {@code CompoundSecMech} definition.
     *

     *
     * @param metadata the metadata object that contains the CSIv2 security configuration info.
     * @return the constructed {@code SAS_ContextSec} instance.
     */
    public static SAS_ContextSec createSecureAttributeServiceContext(IORSecurityConfigMetaData metadata) {
        SAS_ContextSec context;

        // context contains target_supports, target_requires, privilige_authorities, supported_naming_mechanisms, supported_identity_types.
        int support = 0;
        int require = 0;
        ServiceConfiguration[] privilAuth = new ServiceConfiguration[0];
        byte[][] supNamMechs = {};
        int supIdenTypes = 0; // 0 means ITTAbsent

        // the the SasContext metadata.
        IORSASContextMetaData sasMeta = metadata.getSasContext();

        // if no SAS context metadata, or caller propagation is not supported, we return with a more or less empty sas context.
        if (sasMeta == null || sasMeta.getCallerPropagation().equals(IORSASContextMetaData.CALLER_PROPAGATION_NONE)) {
            context = new SAS_ContextSec((short) support, (short) require, privilAuth, supNamMechs, supIdenTypes);
        } else {
            support = IdentityAssertion.value;

            // supporting GSSUP (username/password) naming mechanism.
            byte[] upMech = createGSSUPMechOID();
            supNamMechs = new byte[1][upMech.length];
            System.arraycopy(upMech, 0, supNamMechs[0], 0, upMech.length);

            // since we support IdentityAssertion we need to specify supported identity types. CTS says we need them all
            supIdenTypes = ITTAnonymous.value | ITTPrincipalName.value | ITTX509CertChain.value | ITTDistinguishedName.value;
            context = new SAS_ContextSec((short) support, (short) require, privilAuth, supNamMechs, supIdenTypes);
        }

        return context;
    }

    /**
     *

     * Create the client Authentication Service (AS) context included in a {@code CompoundSecMech} definition.
     *

     *
     * @param metadata the metadata object that contains the CSIv2 security configuration info.
     * @return the constructed {@code AS_ContextSec} instance.
     */
    public static AS_ContextSec createAuthenticationServiceContext(IORSecurityConfigMetaData metadata) {

        AS_ContextSec context;

        // the content of the context.
        int support = 0;
        int require = 0;
        byte[] clientAuthMech = {};
        byte[] targetName = {};

        IORASContextMetaData asMeta = metadata.getAsContext();

        // if no AS context metatada exists, or authentication method "none" is specified, we can produce an empty AS context.
        if (asMeta == null || asMeta.getAuthMethod().equals(IORASContextMetaData.AUTH_METHOD_NONE)) {
            context = new AS_ContextSec((short) support, (short) require, clientAuthMech, targetName);
        } else {
            // we do support.
            support = EstablishTrustInClient.value;

            // required depends on the metadata.
            if (asMeta.isRequired()) {
                require = EstablishTrustInClient.value;
            }

            // we only support GSSUP authentication method.
            clientAuthMech = createGSSUPMechOID();

            // finally, encode the "realm" name as a CSI.GSS_NT_ExportedName.
            // clientAuthMech should contain the DER encoded GSSUPMechOID at this point.
            String realm = asMeta.getRealm();
            targetName = createGSSExportedName(clientAuthMech, realm.getBytes(StandardCharsets.UTF_8));

            context = new AS_ContextSec((short) support, (short) require, clientAuthMech, targetName);
        }

        return context;
    }

    /**
     *

     * Create a transport mechanism {@code TaggedComponent} to be stuffed into a {@code CompoundSecMech}.
     *

     *

     * If no {@code TransportConfig} metadata is specified, or ssl port is negative, or the specified metadata indicates
     * that transport config is not supported, then a {@code TAG_NULL_TAG} (empty) {@code TaggedComponent} will be returned.
     *

     *

     * Otherwise a {@code org.omg.CSIIOP.TLS_SEC_TRANS}, tagged as {@code TAG_TLS_SEC_TRANS} will be returned, indicating support
     * for TLS/SSL as a CSIv2 transport mechanism.
     *

     *

     * Multiple {@code TransportAddress} may be included in the SSL info (host/port pairs), but we only include one.
     *

     *
     * @param tconfig the transport configuration metadata.
     * @param codec   the {@code Codec} used to encode the transport configuration.
     * @param sslPort an {@code int} representing the SSL port.
     * @param orb     a reference to the running {@code ORB}.
     * @return the constructed {@code TaggedComponent}.
     */
    public static TaggedComponent createTransportMech(IORTransportConfigMetaData tconfig, Codec codec, int sslPort, ORB orb) {

        TaggedComponent tc;

        // what we support and require as a target.
        int support = 0;
        int require = 0;

        if (tconfig != null) {
            require = createTargetRequires(tconfig);
            support = createTargetSupports(tconfig);
        }

        if (tconfig == null || support == 0 || sslPort == 0) {
            // no support for transport security.
            tc = new TaggedComponent(TAG_NULL_TAG.value, new byte[0]);
        } else {
            // my ip address.
            String host = CorbaORBService.getORBProperty(Constants.ORB_ADDRESS);

            // this will create only one transport address.
            TransportAddress[] taList = createTransportAddress(host, sslPort);
            TLS_SEC_TRANS tst = new TLS_SEC_TRANS((short) support, (short) require, taList);

            // The tricky part, we must encode TLS_SEC_TRANS into an octet sequence.
            try {
                Any any = orb.create_any();
                TLS_SEC_TRANSHelper.insert(any, tst);
                byte[] b = codec.encode_value(any);
                tc = new TaggedComponent(TAG_TLS_SEC_TRANS.value, b);
            } catch (InvalidTypeForEncoding e) {
                throw IIOPLogger.ROOT_LOGGER.unexpectedException(e);
            }
        }

        return tc;
    }

    /**
     *

     * Create a {@code TransportAddress[]} with a single {@code TransportAddress}.
     *

     *
     * @param host a {@code String} representing the address host.
     * @param port an {@code int} representing the address port.
     * @return the constructed {@code TransportAddress} array.
     */
    public static TransportAddress[] createTransportAddress(String host, int port) {
        // idl type is unsigned sort, so we need this trick
        short short_port = (port > 32767) ? (short) (port - 65536) : (short) port;

        TransportAddress ta = new TransportAddress(host, short_port);
        TransportAddress[] taList = new TransportAddress[1];
        taList[0] = ta;

        return taList;
    }

    /**
     *

     * Create the bitmask of what the target requires.
     *

     *
     * @param tc the transport configuration metadata.
     * @return an {@code int} representing the transport mechanism required by the target.
     */
    public static int createTargetRequires(IORTransportConfigMetaData tc) {
        int requires = 0;

        if (tc != null) {
            if (tc.getIntegrity().equals(IORTransportConfigMetaData.INTEGRITY_REQUIRED)) {
                requires = requires | Integrity.value;
            }
            if (tc.getConfidentiality().equals(IORTransportConfigMetaData.CONFIDENTIALITY_REQUIRED)) {
                requires = requires | Confidentiality.value;
            }
            if (tc.getDetectMisordering().equalsIgnoreCase(IORTransportConfigMetaData.DETECT_MISORDERING_REQUIRED)) {
                requires = requires | DetectMisordering.value;
            }
            if (tc.getDetectReplay().equalsIgnoreCase(IORTransportConfigMetaData.DETECT_REPLAY_REQUIRED)) {
                requires = requires | DetectReplay.value;
            }
            // no EstablishTrustInTarget required - client decides
            if (tc.getEstablishTrustInClient().equals(IORTransportConfigMetaData.ESTABLISH_TRUST_IN_CLIENT_REQUIRED)) {
                requires = requires | EstablishTrustInClient.value;
            }
        }

        return requires;
    }

    /**
     *

     * Create the bitmask of what the target supports.
     *

     *
     * @param tc the transport configuration metadata.
     * @return an {@code int} representing the transport mechanisms supported by the target.
     */
    public static int createTargetSupports(IORTransportConfigMetaData tc) {
        int supports = 0;

        if (tc != null) {
            if (!tc.getIntegrity().equals(IORTransportConfigMetaData.INTEGRITY_NONE)) {
                supports = supports | Integrity.value;
            }
            if (!tc.getConfidentiality().equals(IORTransportConfigMetaData.CONFIDENTIALITY_NONE)) {
                supports = supports | Confidentiality.value;
            }
            if (!tc.getDetectMisordering().equalsIgnoreCase(IORTransportConfigMetaData.DETECT_MISORDERING_NONE)) {
                supports = supports | DetectMisordering.value;
            }
            if (!tc.getDetectReplay().equalsIgnoreCase(IORTransportConfigMetaData.DETECT_REPLAY_NONE)) {
                supports = supports | DetectReplay.value;
            }
            if (!tc.getEstablishTrustInTarget().equals(IORTransportConfigMetaData.ESTABLISH_TRUST_IN_TARGET_NONE)) {
                supports = supports | EstablishTrustInTarget.value;
            }
            if (!tc.getEstablishTrustInClient().equals(IORTransportConfigMetaData.ESTABLISH_TRUST_IN_CLIENT_NONE)) {
                supports = supports | EstablishTrustInClient.value;
            }
        }

        return supports;
    }

    /**
     *

     * Create an ASN.1, DER encoded representation for the GSSUP OID mechanism.
     *

     *
     * @return the DER encoded representation of the GSSUP OID.
     */
    public static byte[] createGSSUPMechOID() {
        // kudos to org.ietf.jgss.Oid for the Oid utility need to strip the "oid:" part of the GSSUPMechOID first.

        byte[] retval = {};
        try {
            Oid oid = new Oid(GSSUPMechOID.value.substring(4));
            retval = oid.getDER();
        } catch (GSSException e) {
            IIOPLogger.ROOT_LOGGER.caughtExceptionEncodingGSSUPMechOID(e);
        }
        return retval;
    }

    /**
     *

     * Generate an exported name as specified in [RFC 2743], section 3.2 copied below:
     *

     * 3.2: Mechanism-Independent Exported Name Object Format
     *

     * This section specifies a mechanism-independent level of encapsulating representation for names exported via the
     * GSS_Export_name() call, including an object identifier representing the exporting mechanism. The format of names
     * encapsulated via this representation shall be defined within individual mechanism drafts.  The Object Identifier
     * value to indicate names of this type is defined in Section 4.7 of this document.
     *

     * No name type OID is included in this mechanism-independent level of format definition, since (depending on
     * individual mechanism specifications) the enclosed name may be implicitly typed or may be explicitly typed using
     * a means other than OID encoding.
     *

     * The bytes within MECH_OID_LEN and NAME_LEN elements are represented most significant byte first (equivalently,
     * in IP network byte order).
     *

     * Length          Name            Description
     *

     * 2               TOK_ID          Token Identifier
     * For exported name objects, this must be hex 04 01.
     * 2               MECH_OID_LEN    Length of the Mechanism OID
     * MECH_OID_LEN    MECH_OID        Mechanism OID, in DER
     * 4               NAME_LEN        Length of name
     * NAME_LEN        NAME            Exported name; format defined in applicable mechanism draft.
     *

     * A concrete example of the contents of an exported name object, derived from the Kerberos Version 5 mechanism, is
     * as follows:
     *

     * 04 01 00 0B 06 09 2A 86 48 86 F7 12 01 02 02 hx xx xx xl pp qq ... zz
     *

     * ...
     *
     * @param oid  the DER encoded OID.
     * @param name the name to be converted to {@code GSSExportedName}.
     * @return a {@code byte[]} representing the exported name.
     */
    public static byte[] createGSSExportedName(byte[] oid, byte[] name) {
        int olen = oid.length;
        int nlen = name.length;

        // size according to spec.
        int size = 2 + 2 + olen + 4 + nlen;

        // allocate space for the exported name.
        byte[] buf = new byte[size];
        // index.
        int i = 0;

        // standard header.
        buf[i++] = 0x04;
        buf[i++] = 0x01;

        // encode oid length.
        buf[i++] = (byte) (olen & 0xFF00);
        buf[i++] = (byte) (olen & 0x00FF);

        // copy the oid in the exported name buffer.
        System.arraycopy(oid, 0, buf, i, olen);
        i += olen;

        // encode the name length in the exported buffer.
        buf[i++] = (byte) (nlen & 0xFF000000);
        buf[i++] = (byte) (nlen & 0x00FF0000);
        buf[i++] = (byte) (nlen & 0x0000FF00);
        buf[i++] = (byte) (nlen & 0x000000FF);

        // finally, copy the name bytes.
        System.arraycopy(name, 0, buf, i, nlen);

        return buf;
    }

    /**
     *

     * ASN.1-encode an {@code InitialContextToken} as defined in RFC 2743, Section 3.1, "Mechanism-Independent Token
     * Format", pp. 81-82. The encoded token contains the ASN.1 tag 0x60, followed by a token length (which is itself
     * stored in a variable-length format and takes 1 to 5 bytes), the GSSUP mechanism identifier, and a mechanism-specific
     * token, which in this case is a CDR encapsulation of the GSSUP {@code InitialContextToken} in the {@code authToken}
     * parameter.
     *

     *
     * @param authToken the {@code InitialContextToken} to be encoded.
     * @param codec     the {@code Codec} used to encode the token.
     * @return a {@code byte[]} representing the encoded token.
     */
    public static byte[] encodeInitialContextToken(InitialContextToken authToken, Codec codec) {
        byte[] out;
        Any any = ORB.init().create_any();
        InitialContextTokenHelper.insert(any, authToken);
        try {
            out = codec.encode_value(any);
        } catch (Exception e) {
            return new byte[0];
        }

        int length = out.length + gssUpMechOidArray.length;
        int n;

        if (length < (1 << 7)) {
            n = 0;
        } else if (length < (1 << 8)) {
            n = 1;
        } else if (length < (1 << 16)) {
            n = 2;
        } else if (length < (1 << 24)) {
            n = 3;
        } else {// if (length < (1 << 32))
            n = 4;
        }

        byte[] encodedToken = new byte[2 + n + length];
        encodedToken[0] = 0x60;

        if (n == 0) {
            encodedToken[1] = (byte) length;
        } else {
            encodedToken[1] = (byte) (n | 0x80);
            switch (n) {
                case 1:
                    encodedToken[2] = (byte) length;
                    break;
                case 2:
                    encodedToken[2] = (byte) (length >> 8);
                    encodedToken[3] = (byte) length;
                    break;
                case 3:
                    encodedToken[2] = (byte) (length >> 16);
                    encodedToken[3] = (byte) (length >> 8);
                    encodedToken[4] = (byte) length;
                    break;
                default: // case 4:
                    encodedToken[2] = (byte) (length >> 24);
                    encodedToken[3] = (byte) (length >> 16);
                    encodedToken[4] = (byte) (length >> 8);
                    encodedToken[5] = (byte) length;
            }
        }
        System.arraycopy(gssUpMechOidArray, 0, encodedToken, 2 + n, gssUpMechOidArray.length);
        System.arraycopy(out, 0, encodedToken, 2 + n + gssUpMechOidArray.length, out.length);

        return encodedToken;
    }

    /**
     *

     * Decodes an ASN.1-encoded {@code InitialContextToken}. See {@code encodeInitialContextToken} for a description of
     * the encoded token format.
     *

     *
     * @param encodedToken the encoded token.
     * @param codec        the {@code Codec} used to decode the token.
     * @return the decoded {@code InitialContextToken} instance.
     * @see #encodeInitialContextToken(org.omg.GSSUP.InitialContextToken, org.omg.IOP.Codec)
     */
    public static InitialContextToken decodeInitialContextToken(byte[] encodedToken, Codec codec) {
        if (encodedToken[0] != 0x60)
            return null;

        int encodedLength = 0;
        int n = 0;

        if (encodedToken[1] >= 0)
            encodedLength = encodedToken[1];
        else {
            n = encodedToken[1] & 0x7F;
            for (int i = 1; i <= n; i++) {
                encodedLength += (encodedToken[1 + i] & 0xFF) << (n - i) * 8;
            }
        }

        int length = encodedLength - gssUpMechOidArray.length;
        byte[] encodedInitialContextToken = new byte[length];

        System.arraycopy(encodedToken, 2 + n + gssUpMechOidArray.length,
                encodedInitialContextToken, 0,
                length);
        Any any;
        try {
            any = codec.decode_value(encodedInitialContextToken, InitialContextTokenHelper.type());
        } catch (Exception e) {
            return null;
        }

        return InitialContextTokenHelper.extract(any);
    }

    /**
     *

     * ASN.1-encodes a GSS exported name with the GSSUP mechanism OID. See {@code createGSSExportedName} for a
     * description of the encoding format.
     *

     *
     * @param name the exported name to be encoded.
     * @return a {@code byte[]} representing the encoded exported name.
     * @see #createGSSExportedName(byte[], byte[])
     */
    public static byte[] encodeGssExportedName(byte[] name) {
        return createGSSExportedName(gssUpMechOidArray, name);
    }

    /**
     *

     * Decodes a GSS exported name that has been encoded with the GSSUP mechanism OID. See {@code createGSSExportedName}
     * for a description of the encoding format.
     *

     *
     * @param encodedName the encoded exported name.
     * @return a {@code byte[]} representing the decoded exported name.
     * @see #createGSSExportedName(byte[], byte[])
     */
    public static byte[] decodeGssExportedName(byte[] encodedName) {
        if (encodedName[0] != 0x04 || encodedName[1] != 0x01)
            return null;

        int mechOidLength = (encodedName[2] & 0xFF) << 8; //MECH_OID_LEN
        mechOidLength += (encodedName[3] & 0xFF);      // MECH_OID_LEN

        byte[] oidArray = new byte[mechOidLength];
        System.arraycopy(encodedName, 4, oidArray, 0, mechOidLength);

        for (int i = 0; i < mechOidLength; i++) {
            if (gssUpMechOidArray[i] != oidArray[i]) {
                return null;
            }
        }

        int offset = 4 + mechOidLength;
        int nameLength = (encodedName[offset] & 0xFF) << 24;
        nameLength += (encodedName[++offset] & 0xFF) << 16;
        nameLength += (encodedName[++offset] & 0xFF) << 8;
        nameLength += (encodedName[++offset] & 0xFF);

        byte[] name = new byte[nameLength];
        System.arraycopy(encodedName, ++offset, name, 0, nameLength);

        return name;
    }

    /**
     *

     * Helper method to be called from a client request interceptor. The {@code ri} parameter refers to the current
     * request. This method returns the first {@code CompoundSecMech} found in the target IOR such that
     *

     *
all {@code CompoundSecMech} requirements are satisfied by the options in the {@code clientSupports}
     * parameter, and

     *
every requirement in the {@code clientRequires} parameter is satisfied by the {@code CompoundSecMech}.
     *

     *

     * The method returns null if the target IOR contains no {@code CompoundSecMech}s or if no matching
     * {@code CompoundSecMech} is found.
     *

     *

     * Since this method is intended to be called from a client request interceptor, it converts unexpected exceptions
     * into {@code MARSHAL} exceptions.
     *

     *
     * @param ri             a reference to the current {@code ClientRequestInfo}.
     * @param codec          the {@code Codec} used to decode the CSIv2 components.
     * @param clientSupports the client supported transport options that must be satisfied by the {@code CompoundSecMech}.
     * @param clientRequires the client required transport options that must be satisfied by the {@code CompoundSecMech}.
     * @return the {@code CompoundSecMech} instance that satisfies all client options, or {@code null} if no such object
     *         can be found.
     */
    public static CompoundSecMech getMatchingSecurityMech(ClientRequestInfo ri, Codec codec, short clientSupports,
                                                          short clientRequires) {
        CompoundSecMechList csmList;
        try {
            TaggedComponent tc = ri.get_effective_component(org.omg.IOP.TAG_CSI_SEC_MECH_LIST.value);

            Any any = codec.decode_value(tc.component_data, CompoundSecMechListHelper.type());
            csmList = CompoundSecMechListHelper.extract(any);

            // look for the first matching security mech.
            for (int i = 0; i < csmList.mechanism_list.length; i++) {
                CompoundSecMech securityMech = csmList.mechanism_list[i];
                AS_ContextSec authConfig = securityMech.as_context_mech;

                if ((EstablishTrustInTarget.value & (clientRequires ^ authConfig.target_supports)
                        & ~authConfig.target_supports) != 0) {
                    // client requires EstablishTrustInTarget, but target does not support it: skip this securityMech.
                    continue;
                }

                if ((EstablishTrustInClient.value & (authConfig.target_requires ^ clientSupports)
                        & ~clientSupports) != 0) {
                    // target requires EstablishTrustInClient, but client does not support it: skip this securityMech.
                    continue;
                }

                SAS_ContextSec identityConfig = securityMech.sas_context_mech;

                if ((IdentityAssertion.value & (identityConfig.target_requires ^ clientSupports)
                        & ~clientSupports) != 0) {
                    // target requires IdentityAssertion, but client does not support it: skip this securityMech
                    continue;
                }

                // found matching securityMech.
                return securityMech;
            }
            // no matching securityMech was found.
            return null;
        } catch (BAD_PARAM e) {
            // no component with TAG_CSI_SEC_MECH_LIST was found.
            return null;
        } catch (org.omg.IOP.CodecPackage.TypeMismatch e) {
            // unexpected exception in codec
            throw IIOPLogger.ROOT_LOGGER.unexpectedException(e);
        } catch (org.omg.IOP.CodecPackage.FormatMismatch e) {
            // unexpected exception in codec
            throw IIOPLogger.ROOT_LOGGER.unexpectedException(e);
        }
    }

    /**
     *

     * Generate a string representation of the {@code CompoundSecMech}.
     *

     *
     * @param securityMech the {@code CompoundSecMech} to create the string for.
     * @param builder      the buffer to write to.
     */
    public static void toString(CompoundSecMech securityMech, StringBuilder builder) {
        AS_ContextSec asMech = securityMech != null ? securityMech.as_context_mech : null;
        SAS_ContextSec sasMech = securityMech != null ? securityMech.sas_context_mech : null;
        if (securityMech != null) {
            builder.append("CompoundSecMech[");
            builder.append("target_requires: ");
            builder.append(securityMech.target_requires);
            if (asMech != null) {
                builder.append("AS_ContextSec[");

                builder.append("client_authentication_mech: ");
                builder.append(new String(asMech.client_authentication_mech, StandardCharsets.UTF_8));
                builder.append(", target_name: ");
                builder.append(new String(asMech.target_name, StandardCharsets.UTF_8));
                builder.append(", target_requires: ");
                builder.append(asMech.target_requires);
                builder.append(", target_supports: ");
                builder.append(asMech.target_supports);
                builder.append("]");
            }
            if (sasMech != null) {
                builder.append("SAS_ContextSec[");
                builder.append("supported_identity_types: ");
                builder.append(sasMech.supported_identity_types);
                builder.append(", target_requires: ");
                builder.append(sasMech.target_requires);
                builder.append(", target_supports: ");
                builder.append(sasMech.target_supports);
                builder.append("]");
            }
            builder.append("]");
        }
    }
}


/*
 * 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.ee.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.operations.common.Util;
import org.jboss.as.ee.logging.EeLogger;
import org.jboss.dmr.ModelNode;
import org.jboss.staxmapper.XMLElementReader;
import org.jboss.staxmapper.XMLExtendedStreamReader;

import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.NAME;
import static org.jboss.as.controller.parsing.ParseUtils.missingRequired;
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;

/**
*/
class EESubsystemParser10 implements XMLStreamConstants, XMLElementReader<List<ModelNode>> {

    EESubsystemParser10() {

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readElement(XMLExtendedStreamReader reader, List<ModelNode> list) throws XMLStreamException {
        // EE subsystem doesn't have any attributes, so make sure that the xml doesn't have any
        requireNoAttributes(reader);

        final ModelNode eeSubSystem = Util.createAddOperation(PathAddress.pathAddress(EeExtension.PATH_SUBSYSTEM));
        // add the subsystem to the ModelNode(s)
        list.add(eeSubSystem);

        // elements
        final EnumSet<Element> encountered = EnumSet.noneOf(Element.class);
        while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            switch (Namespace.forUri(reader.getNamespaceURI())) {
                case EE_1_0: {
                    final Element element = Element.forName(reader.getLocalName());
                    if (!encountered.add(element)) {
                        throw unexpectedElement(reader);
                    }
                    switch (element) {
                        case GLOBAL_MODULES: {
                            final ModelNode model = parseGlobalModules(reader);
                            eeSubSystem.get(GlobalModulesDefinition.GLOBAL_MODULES).set(model);
                            break;
                        }
                        case EAR_SUBDEPLOYMENTS_ISOLATED: {
                            final String earSubDeploymentsIsolated = parseEarSubDeploymentsIsolatedElement(reader);
                            // set the ear subdeployment isolation on the subsystem operation
                            EeSubsystemRootResource.EAR_SUBDEPLOYMENTS_ISOLATED.parseAndSetParameter(earSubDeploymentsIsolated, eeSubSystem, reader);
                            break;
                        }
                        default: {
                            throw unexpectedElement(reader);
                        }
                    }
                    break;
                }
                default: {
                    throw unexpectedElement(reader);
                }
            }
        }
    }

    static ModelNode parseGlobalModules(XMLExtendedStreamReader reader) throws XMLStreamException {

        ModelNode globalModules = new ModelNode();

        requireNoAttributes(reader);

        while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            switch (Element.forName(reader.getLocalName())) {
                case MODULE: {
                    final ModelNode module = new ModelNode();
                    final int count = reader.getAttributeCount();
                    String name = null;
                    String slot = null;
                    for (int i = 0; i < count; i++) {
                        requireNoNamespaceAttribute(reader, i);
                        final String value = reader.getAttributeValue(i);
                        final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
                        switch (attribute) {
                            case NAME:
                                if (name != null) {
                                    throw unexpectedAttribute(reader, i);
                                }
                                name = value;
                                GlobalModulesDefinition.NAME_AD.parseAndSetParameter(name, module, reader);
                                break;
                            case SLOT:
                                if (slot != null) {
                                    throw unexpectedAttribute(reader, i);
                                }
                                slot = value;
                                GlobalModulesDefinition.SLOT_AD.parseAndSetParameter(slot, module, reader);
                                break;
                            default:
                                throw unexpectedAttribute(reader, i);
                        }
                    }
                    if (name == null) {
                        throw missingRequired(reader, Collections.singleton(NAME));
                    }

                    globalModules.add(module);

                    requireNoContent(reader);
                    break;
                }
                default: {
                    throw unexpectedElement(reader);
                }
            }
        }
        return globalModules;
    }

    static String parseEarSubDeploymentsIsolatedElement(XMLExtendedStreamReader reader) throws XMLStreamException {

        // we don't expect any attributes for this element.
        requireNoAttributes(reader);

        final String value = reader.getElementText();
        if (value == null || value.trim().isEmpty()) {
            throw EeLogger.ROOT_LOGGER.invalidValue(value, Element.EAR_SUBDEPLOYMENTS_ISOLATED.getLocalName(), reader.getLocation());
        }
        return value.trim();
    }
}


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

import org.jboss.as.ee.component.BindingConfiguration;
import org.jboss.as.ee.component.ComponentDescription;
import org.jboss.as.ee.component.DeploymentDescriptorEnvironment;
import org.jboss.as.ee.component.EEApplicationClasses;
import org.jboss.as.ee.component.LookupInjectionSource;
import org.jboss.as.ee.component.ResourceInjectionTarget;
import org.jboss.as.ee.component.deployers.AbstractDeploymentDescriptorBindingsProcessor;
import org.jboss.as.ee.utils.ClassLoadingUtils;
import org.jboss.as.ejb3.logging.EjbLogger;
import org.jboss.as.ejb3.deployment.EjbDeploymentAttachmentKeys;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.reflect.DeploymentReflectionIndex;
import org.jboss.metadata.javaee.spec.EJBLocalReferenceMetaData;
import org.jboss.metadata.javaee.spec.EJBLocalReferencesMetaData;
import org.jboss.metadata.javaee.spec.EJBReferenceMetaData;
import org.jboss.metadata.javaee.spec.EJBReferencesMetaData;
import org.jboss.metadata.javaee.spec.Environment;
import org.jboss.metadata.javaee.spec.RemoteEnvironment;

/**
 * Deployment processor responsible for processing ejb references from deployment descriptors
 *
 * @author Stuart Douglas
 */
public class EjbRefProcessor extends AbstractDeploymentDescriptorBindingsProcessor {

    private final boolean appclient;

    public EjbRefProcessor(boolean appclient) {
        this.appclient = appclient;
    }

    /**
     * Resolves ejb-ref and ejb-local-ref elements
     *
     *
     * @param deploymentUnit
     * @param environment               The environment to resolve the elements for
     * @param componentDescription
     *@param classLoader               The deployment class loader
     * @param deploymentReflectionIndex The reflection index
     * @param applicationClasses    @return The bindings for the environment entries
     */
    protected List<BindingConfiguration> processDescriptorEntries(DeploymentUnit deploymentUnit, DeploymentDescriptorEnvironment environment, ResourceInjectionTarget resourceInjectionTarget, final ComponentDescription componentDescription, ClassLoader classLoader, DeploymentReflectionIndex deploymentReflectionIndex, final EEApplicationClasses applicationClasses) throws DeploymentUnitProcessingException {
        final RemoteEnvironment remoteEnvironment = environment.getEnvironment();
        List<BindingConfiguration> bindingDescriptions = new ArrayList<BindingConfiguration>();

        EJBReferencesMetaData ejbRefs = remoteEnvironment.getEjbReferences();
        if (ejbRefs != null) {
            for (EJBReferenceMetaData ejbRef : ejbRefs) {
                String name = ejbRef.getEjbRefName();
                String ejbName = ejbRef.getLink();
                String lookup = ejbRef.getLookupName() != null ? ejbRef.getLookupName() : ejbRef.getMappedName();
                String remoteInterface = ejbRef.getRemote();
                String home = ejbRef.getHome();
                Class<?> remoteInterfaceType = null;

                //if a home is specified this is the type that is bound
                if (!isEmpty(home)) {
                    try {
                        remoteInterfaceType = ClassLoadingUtils.loadClass(home, deploymentUnit);
                    } catch (ClassNotFoundException e) {
                        throw EjbLogger.ROOT_LOGGER.failedToLoadViewClass(e, home);
                    }
                } else if (!isEmpty(remoteInterface)) {
                    try {
                        remoteInterfaceType = ClassLoadingUtils.loadClass(remoteInterface, deploymentUnit);
                    } catch (ClassNotFoundException e) {
                        throw EjbLogger.ROOT_LOGGER.failedToLoadViewClass(e, remoteInterface);
                    }
                }

                if (!name.startsWith("java:")) {
                    name = environment.getDefaultContext() + name;
                }

                // our injection (source) comes from the local (ENC) lookup, no matter what.
                LookupInjectionSource injectionSource = new LookupInjectionSource(name);

                //add any injection targets
                remoteInterfaceType = processInjectionTargets(resourceInjectionTarget, injectionSource, classLoader, deploymentReflectionIndex, ejbRef, remoteInterfaceType);

                final BindingConfiguration bindingConfiguration;
                EjbInjectionSource ejbInjectionSource = null;

                if (!isEmpty(lookup)) {
                    if (!lookup.startsWith("java:")) {
                        bindingConfiguration = new BindingConfiguration(name, new EjbLookupInjectionSource(lookup, remoteInterfaceType));
                    } else {
                        bindingConfiguration = new BindingConfiguration(name, new LookupInjectionSource(lookup));
                    }
                } else {

                    if (remoteInterfaceType == null) {
                        throw EjbLogger.ROOT_LOGGER.couldNotDetermineEjbRefForInjectionTarget(name, resourceInjectionTarget);
                    }
                    if (!isEmpty(ejbName)) {
                        bindingConfiguration = new BindingConfiguration(name, ejbInjectionSource = new EjbInjectionSource(ejbName, remoteInterfaceType.getName(), name, deploymentUnit, appclient));
                    } else {
                        bindingConfiguration = new BindingConfiguration(name, ejbInjectionSource = new EjbInjectionSource(remoteInterfaceType.getName(), name, deploymentUnit, appclient));
                    }
                }

                if (ejbInjectionSource != null) {
                    deploymentUnit.addToAttachmentList(EjbDeploymentAttachmentKeys.EJB_INJECTIONS, ejbInjectionSource);
                }
                bindingDescriptions.add(bindingConfiguration);
            }
        }

        if (remoteEnvironment instanceof Environment && !appclient) {
            EJBLocalReferencesMetaData ejbLocalRefs = ((Environment) remoteEnvironment).getEjbLocalReferences();
            if (ejbLocalRefs != null) {
                for (EJBLocalReferenceMetaData ejbRef : ejbLocalRefs) {
                    String name = ejbRef.getEjbRefName();
                    String ejbName = ejbRef.getLink();
                    String lookup = ejbRef.getLookupName() != null ? ejbRef.getLookupName() : ejbRef.getMappedName();
                    String localInterface = ejbRef.getLocal();
                    String localHome = ejbRef.getLocalHome();
                    Class<?> localInterfaceType = null;

                    //if a home is specified this is the type that is bound
                    if (!isEmpty(localHome)) {
                        try {
                            localInterfaceType = ClassLoadingUtils.loadClass(localHome, deploymentUnit);
                        } catch (ClassNotFoundException e) {
                            throw EjbLogger.ROOT_LOGGER.failedToLoadViewClass(e, localHome);
                        }
                    } else if (!isEmpty(localInterface)) {
                        try {
                            localInterfaceType = ClassLoadingUtils.loadClass(localInterface, deploymentUnit);
                        } catch (ClassNotFoundException e) {
                            throw EjbLogger.ROOT_LOGGER.failedToLoadViewClass(e, localInterface);
                        }
                    }

                    if (!name.startsWith("java:")) {
                        name = environment.getDefaultContext() + name;
                    }

                    // our injection (source) comes from the local (ENC) lookup, no matter what.
                    LookupInjectionSource injectionSource = new LookupInjectionSource(name);

                    //add any injection targets
                    localInterfaceType = processInjectionTargets(resourceInjectionTarget, injectionSource, classLoader, deploymentReflectionIndex, ejbRef, localInterfaceType);

                    if (localInterfaceType == null) {
                        throw EjbLogger.ROOT_LOGGER.couldNotDetermineEjbLocalRefForInjectionTarget(name, resourceInjectionTarget);
                    }
                    final BindingConfiguration bindingConfiguration;
                    EjbInjectionSource ejbInjectionSource = null;

                    if (!isEmpty(lookup)) {
                        if (!lookup.startsWith("java:")) {
                            bindingConfiguration = new BindingConfiguration(name, new EjbLookupInjectionSource(lookup, localInterfaceType));
                        } else {
                            bindingConfiguration = new BindingConfiguration(name, new LookupInjectionSource(lookup));
                        }
                    } else if (!isEmpty(ejbName)) {
                        bindingConfiguration = new BindingConfiguration(name, ejbInjectionSource = new EjbInjectionSource(ejbName, localInterfaceType.getName(), name, deploymentUnit, appclient));
                    } else {
                        bindingConfiguration = new BindingConfiguration(name, ejbInjectionSource = new EjbInjectionSource(localInterfaceType.getName(), name, deploymentUnit, appclient));
                    }
                    if (ejbInjectionSource != null) {
                        deploymentUnit.addToAttachmentList(EjbDeploymentAttachmentKeys.EJB_INJECTIONS, ejbInjectionSource);
                    }
                    bindingDescriptions.add(bindingConfiguration);
                }
            }
        }
        return bindingDescriptions;
    }

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

}