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

import java.util.EnumSet;
import java.util.List;
import java.util.Set;

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

import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.txn.logging.TransactionLogger;
import org.jboss.dmr.ModelNode;
import org.jboss.staxmapper.XMLElementReader;
import org.jboss.staxmapper.XMLExtendedStreamReader;

import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADD;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import static org.jboss.as.controller.parsing.ParseUtils.duplicateNamedElement;
import static org.jboss.as.controller.parsing.ParseUtils.missingOneOf;
import static org.jboss.as.controller.parsing.ParseUtils.missingRequired;
import static org.jboss.as.controller.parsing.ParseUtils.missingRequiredElement;
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 TransactionSubsystem10Parser implements XMLStreamConstants, XMLElementReader<List<ModelNode>> {

    TransactionSubsystem10Parser() {

    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void readElement(XMLExtendedStreamReader reader, List<ModelNode> list) throws XMLStreamException {
        // no attributes
        if (reader.getAttributeCount() > 0) {
            throw unexpectedAttribute(reader, 0);
        }

        final ModelNode address = new ModelNode();
        address.add(ModelDescriptionConstants.SUBSYSTEM, TransactionExtension.SUBSYSTEM_NAME);
        address.protect();

        final ModelNode subsystem = new ModelNode();
        subsystem.get(OP).set(ADD);
        subsystem.get(OP_ADDR).set(address);

        list.add(subsystem);

        // elements
        final EnumSet<Element> required = EnumSet.of(Element.RECOVERY_ENVIRONMENT, Element.CORE_ENVIRONMENT);
        final EnumSet<Element> encountered = EnumSet.noneOf(Element.class);
        while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            switch (Namespace.forUri(reader.getNamespaceURI())) {
                case TRANSACTIONS_1_0: {
                    final Element element = Element.forName(reader.getLocalName());
                    required.remove(element);
                    if (!encountered.add(element)) {
                        throw unexpectedElement(reader);
                    }
                    switch (element) {
                        case RECOVERY_ENVIRONMENT: {
                            parseRecoveryEnvironmentElement(reader, subsystem);
                            break;
                        }
                        case CORE_ENVIRONMENT: {
                            parseCoreEnvironmentElement(reader, subsystem);
                            break;
                        }
                        case COORDINATOR_ENVIRONMENT: {
                            parseCoordinatorEnvironmentElement(reader, subsystem);
                            break;
                        }
                        case OBJECT_STORE: {
                            parseObjectStoreEnvironmentElementAndEnrichOperation(reader, subsystem);
                            break;
                        }
                        default: {
                            throw unexpectedElement(reader);
                        }
                    }
                    break;
                }
                default: {
                    throw unexpectedElement(reader);
                }
            }
        }
        if (!required.isEmpty()) {
            throw missingRequiredElement(reader, required);
        }

        final ModelNode logStoreAddress = address.clone();
        final ModelNode operation = new ModelNode();
        operation.get(OP).set(ADD);
        logStoreAddress.add(LogStoreConstants.LOG_STORE, LogStoreConstants.LOG_STORE);

        logStoreAddress.protect();

        operation.get(OP_ADDR).set(logStoreAddress);
        list.add(operation);
    }

    static void parseObjectStoreEnvironmentElementAndEnrichOperation(final XMLExtendedStreamReader reader, ModelNode operation) throws XMLStreamException {

        final int count = reader.getAttributeCount();
        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 RELATIVE_TO:
                    TransactionSubsystemRootResourceDefinition.OBJECT_STORE_RELATIVE_TO.parseAndSetParameter(value, operation, reader);
                    break;
                case PATH:
                    TransactionSubsystemRootResourceDefinition.OBJECT_STORE_PATH.parseAndSetParameter(value, operation, reader);
                    break;
                default:
                    throw unexpectedAttribute(reader, i);
            }
        }
        // Handle elements
        requireNoContent(reader);

    }

    static void parseCoordinatorEnvironmentElement(final XMLExtendedStreamReader reader, final ModelNode operation) throws XMLStreamException {

        final int count = reader.getAttributeCount();
        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 ENABLE_STATISTICS:
                    TransactionSubsystemRootResourceDefinition.STATISTICS_ENABLED.parseAndSetParameter(value, operation, reader);
                    break;
                case ENABLE_TSM_STATUS:
                    TransactionSubsystemRootResourceDefinition.ENABLE_TSM_STATUS.parseAndSetParameter(value, operation, reader);
                    break;
                case DEFAULT_TIMEOUT:
                    TransactionSubsystemRootResourceDefinition.DEFAULT_TIMEOUT.parseAndSetParameter(value, operation, reader);
                    break;
                default:
                    throw unexpectedAttribute(reader, i);
            }
        }
        // Handle elements
        requireNoContent(reader);

    }

    /**
     * Handle the core-environment element and children
     *
     * @param reader
     * @return ModelNode for the core-environment
     * @throws javax.xml.stream.XMLStreamException
     *
     */
    static void parseCoreEnvironmentElement(final XMLExtendedStreamReader reader, final ModelNode operation) throws XMLStreamException {

        final int count = reader.getAttributeCount();
        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 NODE_IDENTIFIER:
                    TransactionSubsystemRootResourceDefinition.NODE_IDENTIFIER.parseAndSetParameter(value, operation, reader);
                    break;
                case PATH:
                case RELATIVE_TO:
                    throw TransactionLogger.ROOT_LOGGER.unsupportedAttribute(attribute.getLocalName(), reader.getLocation());
                default:
                    throw unexpectedAttribute(reader, i);
            }
        }
        // elements
        final EnumSet<Element> required = EnumSet.of(Element.PROCESS_ID);
        final EnumSet<Element> encountered = EnumSet.noneOf(Element.class);
        while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            final Element element = Element.forName(reader.getLocalName());
            required.remove(element);
            switch (element) {
                case PROCESS_ID: {
                    if (!encountered.add(element)) {
                        throw duplicateNamedElement(reader, reader.getLocalName());
                    }
                    parseProcessIdEnvironmentElement(reader, operation);
                    break;
                }
                default:
                    throw unexpectedElement(reader);
            }
        }
        if (!required.isEmpty()) {
            throw missingRequiredElement(reader, required);
        }
    }

    /**
     * Handle the process-id child elements
     *
     * @param reader
     * @param coreEnvironmentAdd
     * @return
     * @throws javax.xml.stream.XMLStreamException
     *
     */
    static void parseProcessIdEnvironmentElement(XMLExtendedStreamReader reader, ModelNode coreEnvironmentAdd) throws XMLStreamException {
        // no attributes
        if (reader.getAttributeCount() > 0) {
            throw unexpectedAttribute(reader, 0);
        }

        // elements
        boolean encountered = false;
        while (reader.hasNext() && reader.nextTag() != END_ELEMENT) {
            final Element element = Element.forName(reader.getLocalName());
            switch (element) {
                case UUID:
                    if (encountered) {
                        throw unexpectedElement(reader);
                    }
                    encountered = true;
                    if (reader.getAttributeCount() > 0) {
                        throw unexpectedAttribute(reader, 0);
                    }
                    coreEnvironmentAdd.get(TransactionSubsystemRootResourceDefinition.PROCESS_ID_UUID.getName()).set(true);
                    requireNoContent(reader);
                    break;
                case SOCKET: {
                    if (encountered) {
                        throw unexpectedElement(reader);
                    }
                    encountered = true;
                    parseSocketProcessIdElement(reader, coreEnvironmentAdd);
                    break;
                }
                default:
                    throw unexpectedElement(reader);
            }
        }

        if (!encountered) {
            throw missingOneOf(reader, EnumSet.of(Element.UUID, Element.SOCKET));
        }
    }

    static void parseSocketProcessIdElement(XMLExtendedStreamReader reader, ModelNode coreEnvironmentAdd) throws XMLStreamException {

        final int count = reader.getAttributeCount();
        final EnumSet<Attribute> required = EnumSet.of(Attribute.BINDING);
        for (int i = 0; i < count; i++) {
            requireNoNamespaceAttribute(reader, i);
            final String value = reader.getAttributeValue(i);
            final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
            required.remove(attribute);
            switch (attribute) {
                case BINDING:
                    TransactionSubsystemRootResourceDefinition.PROCESS_ID_SOCKET_BINDING.parseAndSetParameter(value, coreEnvironmentAdd, reader);
                    break;
                case SOCKET_PROCESS_ID_MAX_PORTS:
                    TransactionSubsystemRootResourceDefinition.PROCESS_ID_SOCKET_MAX_PORTS.parseAndSetParameter(value, coreEnvironmentAdd, reader);
                    break;
                default:
                    throw unexpectedAttribute(reader, i);
            }
        }
        if (!required.isEmpty()) {
            throw missingRequired(reader, required);
        }
        // Handle elements
        requireNoContent(reader);
    }

    static void parseRecoveryEnvironmentElement(final XMLExtendedStreamReader reader, final ModelNode operation) throws XMLStreamException {

        Set<Attribute> required = EnumSet.of(Attribute.BINDING, Attribute.STATUS_BINDING);
        final int count = reader.getAttributeCount();
        for (int i = 0; i < count; i++) {
            requireNoNamespaceAttribute(reader, i);
            final String value = reader.getAttributeValue(i);
            final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i));
            required.remove(attribute);
            switch (attribute) {
                case BINDING:
                    TransactionSubsystemRootResourceDefinition.BINDING.parseAndSetParameter(value, operation, reader);
                    break;
                case STATUS_BINDING:
                    TransactionSubsystemRootResourceDefinition.STATUS_BINDING.parseAndSetParameter(value, operation, reader);
                    break;
                case RECOVERY_LISTENER:
                    TransactionSubsystemRootResourceDefinition.RECOVERY_LISTENER.parseAndSetParameter(value, operation, reader);
                    break;
                default:
                    throw unexpectedAttribute(reader, i);
            }
        }

        if (!required.isEmpty()) {
            throw missingRequired(reader, required);
        }
        // Handle elements
        requireNoContent(reader);

    }

}


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

package org.jboss.as.jpa.service;

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

import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;

import javax.sql.DataSource;

import org.jboss.as.jpa.beanmanager.ProxyBeanManager;
import org.jboss.as.jpa.classloader.TempClassLoaderFactoryImpl;
import org.jboss.as.naming.WritableServiceBasedNamingStore;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedValue;
import org.jipijapa.plugin.spi.EntityManagerFactoryBuilder;
import org.jipijapa.plugin.spi.PersistenceProviderAdaptor;
import org.jipijapa.plugin.spi.PersistenceUnitMetadata;
import org.jipijapa.plugin.spi.TwoPhaseBootstrapCapable;
import org.wildfly.security.manager.action.GetAccessControlContextAction;
import org.wildfly.security.manager.WildFlySecurityManager;

/**
 * Handles the first phase of the EntityManagerFactoryBuilder for starting the Persistence Unit service.
 * The PersistenceUnitServiceImpl service handles the second phase and will not start until after PhaseOnePersistenceUnitServiceImpl starts.
 *
 * @author Scott Marlow
 */
public class PhaseOnePersistenceUnitServiceImpl implements Service<PhaseOnePersistenceUnitServiceImpl> {
    private final InjectedValue<Map> properties = new InjectedValue<>();
    private final InjectedValue<DataSource> jtaDataSource = new InjectedValue<>();
    private final InjectedValue<DataSource> nonJtaDataSource = new InjectedValue<>();
    private final InjectedValue<ExecutorService> executorInjector = new InjectedValue<>();

    private static final String CDI_BEAN_MANAGER = "javax.persistence.bean.manager";

    private final PersistenceProviderAdaptor persistenceProviderAdaptor;
    private final PersistenceUnitMetadata pu;
    private final ClassLoader classLoader;
    private final ServiceName deploymentUnitServiceName;
    private final ProxyBeanManager proxyBeanManager;
    private final Object wrapperBeanManagerLifeCycle;

    private volatile EntityManagerFactoryBuilder entityManagerFactoryBuilder;

    private volatile boolean secondPhaseStarted = false;

    public PhaseOnePersistenceUnitServiceImpl(
            final ClassLoader classLoader,
            final PersistenceUnitMetadata pu,
            final PersistenceProviderAdaptor persistenceProviderAdaptor,
            final ServiceName deploymentUnitServiceName,
            final ProxyBeanManager proxyBeanManager) {
        this.pu = pu;
        this.persistenceProviderAdaptor = persistenceProviderAdaptor;
        this.classLoader = classLoader;
        this.deploymentUnitServiceName = deploymentUnitServiceName;
        this.proxyBeanManager = proxyBeanManager;
        this.wrapperBeanManagerLifeCycle = proxyBeanManager != null ? persistenceProviderAdaptor.beanManagerLifeCycle(proxyBeanManager): null;
    }

    @Override
    public void start(final StartContext context) throws StartException {
        final ExecutorService executor = executorInjector.getValue();
        final AccessControlContext accessControlContext =
                AccessController.doPrivileged(GetAccessControlContextAction.getInstance());

        final Runnable task = new Runnable() {
            // run async in a background thread
            @Override
            public void run() {

                PrivilegedAction<Void> privilegedAction =
                        new PrivilegedAction<Void>() {
                            // run as security privileged action
                            @Override
                            public Void run() {
                                try {
                                    ROOT_LOGGER.startingPersistenceUnitService(1, pu.getScopedPersistenceUnitName());
                                    pu.setTempClassLoaderFactory(new TempClassLoaderFactoryImpl(classLoader));
                                    pu.setJtaDataSource(jtaDataSource.getOptionalValue());
                                    pu.setNonJtaDataSource(nonJtaDataSource.getOptionalValue());

                                    if (proxyBeanManager != null) {
                                        if (wrapperBeanManagerLifeCycle != null) {
                                          // pass the wrapper object representing the bean manager life cycle object
                                          properties.getValue().put(CDI_BEAN_MANAGER, wrapperBeanManagerLifeCycle);
                                        }
                                        else {
                                          properties.getValue().put(CDI_BEAN_MANAGER, proxyBeanManager);
                                        }
                                    }

                                    WritableServiceBasedNamingStore.pushOwner(deploymentUnitServiceName);
                                    entityManagerFactoryBuilder = createContainerEntityManagerFactoryBuilder();
                                    context.complete();
                                } catch (Throwable t) {
                                    context.failed(new StartException(t));
                                } finally {
                                    pu.setTempClassLoaderFactory(null);    // release the temp classloader factory (only needed when creating the EMF)
                                    WritableServiceBasedNamingStore.popOwner();
                                }

                                return null;
                            }
                        };
                WildFlySecurityManager.doChecked(privilegedAction, accessControlContext);
            }
        };
        try {
            executor.execute(task);
        } catch (RejectedExecutionException e) {
            task.run();
        } finally {
            context.asynchronous();
        }
    }

    @Override
    public void stop(final StopContext context) {
        final ExecutorService executor = executorInjector.getValue();
        final AccessControlContext accessControlContext =
                AccessController.doPrivileged(GetAccessControlContextAction.getInstance());

        final Runnable task = new Runnable() {
            // run async in a background thread
            @Override
            public void run() {

                PrivilegedAction<Void> privilegedAction =
                        new PrivilegedAction<Void>() {
                            // run as security privileged action
                            @Override
                            public Void run() {

                                ROOT_LOGGER.stoppingPersistenceUnitService(1, pu.getScopedPersistenceUnitName());
                                if (entityManagerFactoryBuilder != null) {
                                    WritableServiceBasedNamingStore.pushOwner(deploymentUnitServiceName);
                                    try {
                                        if (secondPhaseStarted == false) {
                                            ROOT_LOGGER.tracef("PhaseOnePersistenceUnitServiceImpl cancelling %s " +
                                                    "which didn't start (phase 2 not reached)", pu.getScopedPersistenceUnitName());
                                            entityManagerFactoryBuilder.cancel();
                                        }
                                    } catch (Throwable t) {
                                        ROOT_LOGGER.failedToStopPUService(t, pu.getScopedPersistenceUnitName());
                                    } finally {
                                        entityManagerFactoryBuilder = null;
                                        pu.setTempClassLoaderFactory(null);
                                        WritableServiceBasedNamingStore.popOwner();
                                    }
                                }
                                properties.getValue().remove(CDI_BEAN_MANAGER);
                                context.complete();

                                return null;
                            }
                        };
                WildFlySecurityManager.doChecked(privilegedAction, accessControlContext);
            }

        };
        try {
            executor.execute(task);
        } catch (RejectedExecutionException e) {
            task.run();
        } finally {
            context.asynchronous();
        }
    }

    public InjectedValue<ExecutorService> getExecutorInjector() {
        return executorInjector;
    }

    @Override
    public PhaseOnePersistenceUnitServiceImpl getValue() throws IllegalStateException, IllegalArgumentException {
        return this;
    }

    /**
     * Get the entity manager factory
     *
     * @return the entity manager factory
     */

    public EntityManagerFactoryBuilder getEntityManagerFactoryBuilder() {
        return entityManagerFactoryBuilder;
    }

    public void setSecondPhaseStarted(boolean secondPhaseStarted) {
        this.secondPhaseStarted = secondPhaseStarted;
    }

    public Injector<Map> getPropertiesInjector() {
        return properties;
    }

    public Injector<DataSource> getJtaDataSourceInjector() {
        return jtaDataSource;
    }

    public Injector<DataSource> getNonJtaDataSourceInjector() {
        return nonJtaDataSource;
    }

    public ProxyBeanManager getBeanManager() {
        return proxyBeanManager;
    }

    public Object getBeanManagerLifeCycle() {
        return wrapperBeanManagerLifeCycle;
    }

    /**
     * Create EE container entity manager factory
     *
     * @return EntityManagerFactory
     */
    private EntityManagerFactoryBuilder createContainerEntityManagerFactoryBuilder() {
        persistenceProviderAdaptor.beforeCreateContainerEntityManagerFactory(pu);
        try {
            TwoPhaseBootstrapCapable twoPhaseBootstrapCapable = (TwoPhaseBootstrapCapable)persistenceProviderAdaptor;
            return twoPhaseBootstrapCapable.getBootstrap(pu, properties.getValue());
        } finally {
            try {
                persistenceProviderAdaptor.afterCreateContainerEntityManagerFactory(pu);
            } finally {
                pu.setAnnotationIndex(null);    // close reference to Annotation Index (only needed during call to createContainerEntityManagerFactory)
            }
        }
    }
}


/*
 * JBoss, Home of Professional Open Source
 * Copyright 2013, Red Hat Middleware LLC, and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jboss.as.jpa.hibernate5;

import static org.jboss.as.jpa.hibernate5.JpaLogger.JPA_LOGGER;

import java.util.Map;
import java.util.Properties;

import javax.enterprise.inject.spi.BeanManager;
import javax.persistence.SharedCacheMode;
import javax.persistence.spi.PersistenceUnitInfo;

import org.hibernate.cfg.AvailableSettings;
import org.jboss.as.jpa.hibernate5.management.HibernateManagementAdaptor;
import org.jboss.as.jpa.hibernate5.service.WildFlyCustomJtaPlatform;
import org.jipijapa.cache.spi.Classification;
import org.jipijapa.event.impl.internal.Notification;
import org.jipijapa.plugin.spi.EntityManagerFactoryBuilder;
import org.jipijapa.plugin.spi.JtaManager;
import org.jipijapa.plugin.spi.ManagementAdaptor;
import org.jipijapa.plugin.spi.PersistenceProviderAdaptor;
import org.jipijapa.plugin.spi.PersistenceUnitMetadata;
import org.jipijapa.plugin.spi.Platform;
import org.jipijapa.plugin.spi.TwoPhaseBootstrapCapable;

/**
 * Implements the PersistenceProviderAdaptor for Hibernate
 *
 * @author Scott Marlow
 */
public class HibernatePersistenceProviderAdaptor implements PersistenceProviderAdaptor, TwoPhaseBootstrapCapable {

    public static final String NAMING_STRATEGY_JPA_COMPLIANT_IMPL = "org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl";
    private volatile Platform platform;
    private static final String SHARED_CACHE_MODE = "javax.persistence.sharedCache.mode";
    private static final String NONE = SharedCacheMode.NONE.name();
    private static final String UNSPECIFIED = SharedCacheMode.UNSPECIFIED.name();
    private static final String HIBERNATE_EXTENDED_BEANMANAGER = "org.hibernate.jpa.event.spi.jpa.ExtendedBeanManager";

    @Override
    public void injectJtaManager(JtaManager jtaManager) {
        WildFlyCustomJtaPlatform.setTransactionSynchronizationRegistry(jtaManager.getSynchronizationRegistry());
    }

    @Override
    public void injectPlatform(Platform platform) {
        if (this.platform != platform) {
            this.platform = platform;
        }
    }

    @SuppressWarnings("deprecation")
    @Override
    public void addProviderProperties(Map properties, PersistenceUnitMetadata pu) {
        putPropertyIfAbsent(pu, properties, AvailableSettings.JPAQL_STRICT_COMPLIANCE, "true"); // JIPI-24 ignore jpql aliases case
        putPropertyIfAbsent(pu, properties, AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "true");
        putPropertyIfAbsent(pu, properties, AvailableSettings.KEYWORD_AUTO_QUOTING_ENABLED,"false");
        putPropertyIfAbsent(pu, properties, AvailableSettings.IMPLICIT_NAMING_STRATEGY, NAMING_STRATEGY_JPA_COMPLIANT_IMPL);
        putPropertyIfAbsent(pu, properties, AvailableSettings.SCANNER, HibernateArchiveScanner.class);
        properties.put(AvailableSettings.APP_CLASSLOADER, pu.getClassLoader());
        putPropertyIfAbsent(pu,properties, org.hibernate.ejb.AvailableSettings.ENTITY_MANAGER_FACTORY_NAME, pu.getScopedPersistenceUnitName());
        putPropertyIfAbsent(pu, properties, AvailableSettings.SESSION_FACTORY_NAME, pu.getScopedPersistenceUnitName());
        if (!pu.getProperties().containsKey(AvailableSettings.SESSION_FACTORY_NAME)) {
            putPropertyIfAbsent(pu, properties, AvailableSettings.SESSION_FACTORY_NAME_IS_JNDI, Boolean.FALSE);
        }
        // the following properties were added to Hibernate ORM 5.3, for JPA 2.2 spec compliance.
        putPropertyIfAbsent( pu, properties, AvailableSettings.PREFER_GENERATOR_NAME_AS_DEFAULT_SEQUENCE_NAME, true );
        putPropertyIfAbsent( pu, properties, AvailableSettings.JPA_TRANSACTION_COMPLIANCE, true );
        putPropertyIfAbsent( pu, properties, AvailableSettings.JPA_CLOSED_COMPLIANCE, true );
        putPropertyIfAbsent( pu, properties, AvailableSettings.JPA_QUERY_COMPLIANCE, true );
        putPropertyIfAbsent( pu, properties, AvailableSettings.JPA_LIST_COMPLIANCE, true );
        putPropertyIfAbsent( pu, properties, AvailableSettings.JPA_CACHING_COMPLIANCE, true );
        putPropertyIfAbsent( pu, properties, AvailableSettings.JPA_PROXY_COMPLIANCE, true );
        putPropertyIfAbsent( pu, properties, AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS, false );
        putPropertyIfAbsent( pu, properties, AvailableSettings.JPA_ID_GENERATOR_GLOBAL_SCOPE_COMPLIANCE, true);

        // Search hint
        putPropertyIfAbsent( pu, properties,"hibernate.search.index_uninverting_allowed", "true" );
    }

    @Override
    public void addProviderDependencies(PersistenceUnitMetadata pu) {
        final Properties properties = pu.getProperties();
        final String sharedCacheMode = properties.getProperty(SHARED_CACHE_MODE);

        if ( Classification.NONE.equals(platform.defaultCacheClassification())) {
            JPA_LOGGER.tracef("second level cache is not supported in platform, ignoring shared cache mode");
            pu.setSharedCacheMode(SharedCacheMode.NONE);
        }
        // check if 2lc is explicitly disabled which takes precedence over other settings
        boolean sharedCacheDisabled = SharedCacheMode.NONE.equals(pu.getSharedCacheMode())
                ||
                NONE.equals(sharedCacheMode) ||
                SharedCacheMode.UNSPECIFIED.equals(pu.getSharedCacheMode()) ||
                UNSPECIFIED.equals(sharedCacheMode);
        if (!sharedCacheDisabled &&
                (null == properties.getProperty(AvailableSettings.USE_SECOND_LEVEL_CACHE) ||
                        Boolean.parseBoolean(properties.getProperty(AvailableSettings.USE_SECOND_LEVEL_CACHE))))
                {
            HibernateSecondLevelCache.addSecondLevelCacheDependencies(pu.getProperties(), pu.getScopedPersistenceUnitName());
            JPA_LOGGER.tracef("second level cache enabled for %s", pu.getScopedPersistenceUnitName());
        } else {
            JPA_LOGGER.tracef("second level cache disabled for %s, pu %s property = %s, pu.getSharedCacheMode = %s",
                    pu.getScopedPersistenceUnitName(),
                    SHARED_CACHE_MODE,
                    sharedCacheMode,
                    pu.getSharedCacheMode().toString());
            pu.setSharedCacheMode(SharedCacheMode.NONE);  // ensure that Hibernate doesn't try to use the 2lc for UNSPECIFIED
        }
    }

    private void putPropertyIfAbsent(PersistenceUnitMetadata pu, Map properties, String property, Object value) {
        if (!pu.getProperties().containsKey(property)) {
            properties.put(property, value);
        }
    }

    @Override
    public void beforeCreateContainerEntityManagerFactory(PersistenceUnitMetadata pu) {
        Notification.beforeEntityManagerFactoryCreate(Classification.INFINISPAN, pu);
    }

    @Override
    public void afterCreateContainerEntityManagerFactory(PersistenceUnitMetadata pu) {
        Notification.afterEntityManagerFactoryCreate(Classification.INFINISPAN, pu);
    }

    @Override
    public ManagementAdaptor getManagementAdaptor() {
        return HibernateManagementAdaptor.getInstance();
    }

    /**
     * determine if management console can display the second level cache entries
     *
     * @param pu
     * @return false if a custom AvailableSettings.CACHE_REGION_PREFIX property is specified.
     *         true if the scoped persistence unit name is used to prefix cache entries.
     */
    @Override
    public boolean doesScopedPersistenceUnitNameIdentifyCacheRegionName(PersistenceUnitMetadata pu) {
        String cacheRegionPrefix = pu.getProperties().getProperty(AvailableSettings.CACHE_REGION_PREFIX);

        return cacheRegionPrefix == null || cacheRegionPrefix.equals(pu.getScopedPersistenceUnitName());
    }

    @Override
    public void cleanup(PersistenceUnitMetadata pu) {

    }

    @Override
    public Object beanManagerLifeCycle(BeanManager beanManager) {

        if( isHibernateExtendedBeanManagerSupported()) {
            return new HibernateExtendedBeanManager(beanManager);
        }
        // for ORM 5.0, return null to indicate that the org.hibernate.jpa.event.spi.jpa.ExtendedBeanManager extension should not be used.
        return null;
    }

    @Override
    public void markPersistenceUnitAvailable(Object wrapperBeanManagerLifeCycle) {
        if(isHibernateExtendedBeanManagerSupported()) {
            HibernateExtendedBeanManager hibernateExtendedBeanManager = (HibernateExtendedBeanManager) wrapperBeanManagerLifeCycle;
            // notify Hibernate ORM ExtendedBeanManager extension that the entity listener(s) can now be registered.
            hibernateExtendedBeanManager.beanManagerIsAvailableForUse();
        }
    }

    /**
     * org.hibernate.jpa.event.spi.jpa.ExtendedBeanManager is added to Hibernate 5.1 as an extension for delaying registration
     * of entity listeners until the CDI AfterDeploymentValidation event is triggered.
     * This allows entity listener classes to reference the (origin) persistence unit (WFLY-2387).
     *
     * return true for Hibernate ORM 5.1+, which should contain the ExtendedBeanManager contract
     */
    private boolean isHibernateExtendedBeanManagerSupported() {
        try {
            Class.forName(HIBERNATE_EXTENDED_BEANMANAGER);
            return true;
        } catch (ClassNotFoundException ignore) {
            return false;
        } catch (NoClassDefFoundError ignore) {
            return false;
        }

    }

    /* start of TwoPhaseBootstrapCapable methods */

    public EntityManagerFactoryBuilder getBootstrap(final PersistenceUnitInfo info, final Map map) {
        return new TwoPhaseBootstrapImpl(info, map);
    }

    /* end of TwoPhaseBootstrapCapable methods */
}


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

import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jboss.as.controller.OperationDefinition;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.operations.global.ListOperations;
import org.jboss.as.controller.operations.global.MapOperations;
import org.jboss.dmr.ModelNode;

/**
 * Utility methods for creating/manipulating management operations.
 * @author Paul Ferraro
 */
public final class Operations {

    private static final String INDEX = "index";
    private static final String KEY = "key";

    /**
     * Returns the address of the specified operation
     * @param operation an operation
     * @return a path address
     */
    public static PathAddress getPathAddress(ModelNode operation) {
        return PathAddress.pathAddress(operation.require(ModelDescriptionConstants.OP_ADDR));
    }

    /**
     * Sets the address of the specified operation.
     * @param operation an operation
     * @param address a path address
     */
    public static void setPathAddress(ModelNode operation, PathAddress address) {
        operation.get(ModelDescriptionConstants.OP_ADDR).set(address.toModelNode());
    }

    /**
     * Returns the name of the specified operation
     * @param operation an operation
     * @return an operation name
     */
    public static String getName(ModelNode operation) {
        return operation.require(ModelDescriptionConstants.OP).asString();
    }

    /**
     * Returns the attribute name of the specified operation
     * @param operation an operation
     * @return an attribute name
     */
    public static String getAttributeName(ModelNode operation) {
        return operation.require(ModelDescriptionConstants.NAME).asString();
    }

    /**
     * Returns the attribute value of the specified operation
     * @param operation an operation
     * @return an attribute value
     */
    public static ModelNode getAttributeValue(ModelNode operation) {
        return operation.hasDefined(ModelDescriptionConstants.VALUE) ? operation.get(ModelDescriptionConstants.VALUE) : new ModelNode();
    }

    /**
     * Indicates whether or not this operation expects to include default values.
     * @param operation an operation
     * @return true, if default values are expected, false otherwise.
     */
    public static boolean isIncludeDefaults(ModelNode operation) {
        return operation.hasDefined(ModelDescriptionConstants.INCLUDE_DEFAULTS) ? operation.get(ModelDescriptionConstants.INCLUDE_DEFAULTS).asBoolean() : true;
    }

    /**
     * Creates a composite operation using the specified operation steps.
     * @param operations steps
     * @return a composite operation
     */
    public static ModelNode createCompositeOperation(List<ModelNode> operations) {
        ModelNode operation = Util.createOperation(ModelDescriptionConstants.COMPOSITE, PathAddress.EMPTY_ADDRESS);
        ModelNode steps = operation.get(ModelDescriptionConstants.STEPS);
        for (ModelNode step: operations) {
            steps.add(step);
        }
        return operation;
    }

    /**
     * Creates a composite operation using the specified operation steps.
     * @param operations steps
     * @return a composite operation
     */
    public static ModelNode createCompositeOperation(ModelNode... operations) {
        return createCompositeOperation(Arrays.asList(operations));
    }

    /**
     * Creates an add operation using the specified address and parameters
     * @param address a path address
     * @param parameters a map of values per attribute
     * @return an add operation
     */
    public static ModelNode createAddOperation(PathAddress address, Map<Attribute, ModelNode> parameters) {
        ModelNode operation = Util.createAddOperation(address);
        for (Map.Entry<Attribute, ModelNode> entry : parameters.entrySet()) {
            operation.get(entry.getKey().getName()).set(entry.getValue());
        }
        return operation;
    }

    /**
     * Creates an indexed add operation using the specified address and index
     * @param address a path address
     * @param index
     * @return an add operation
     */
    public static ModelNode createAddOperation(PathAddress address, int index) {
        return createAddOperation(address, index, Collections.emptyMap());
    }

    /**
     * Creates an indexed add operation using the specified address and parameters
     * @param address a path address
     * @param parameters a map of values per attribute
     * @return an add operation
     */
    public static ModelNode createAddOperation(PathAddress address, int index, Map<Attribute, ModelNode> parameters) {
        ModelNode operation = Util.createAddOperation(address);
        operation.get(ModelDescriptionConstants.ADD_INDEX).set(index);
        for (Map.Entry<Attribute, ModelNode> entry : parameters.entrySet()) {
            operation.get(entry.getKey().getName()).set(entry.getValue());
        }
        return operation;
    }

    /**
     * Creates a read-attribute operation using the specified address and name.
     * @param address a resource path
     * @param attribute an attribute
     * @return a read-attribute operation
     */
    public static ModelNode createReadAttributeOperation(PathAddress address, Attribute attribute) {
        return createAttributeOperation(ModelDescriptionConstants.READ_ATTRIBUTE_OPERATION, address, attribute);
    }

    /**
     * Creates a write-attribute operation using the specified address, name and value.
     * @param address a resource path
     * @param attribute an attribute
     * @param value an attribute value
     * @return a write-attribute operation
     */
    public static ModelNode createWriteAttributeOperation(PathAddress address, Attribute attribute, ModelNode value) {
        ModelNode operation = createAttributeOperation(ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION, address, attribute);
        operation.get(ModelDescriptionConstants.VALUE).set(value);
        return operation;
    }

    /**
     * Creates an undefine-attribute operation using the specified address and name.
     * @param address a resource path
     * @param attribute an attribute
     * @return an undefine-attribute operation
     */
    public static ModelNode createUndefineAttributeOperation(PathAddress address, Attribute attribute) {
        return createAttributeOperation(ModelDescriptionConstants.UNDEFINE_ATTRIBUTE_OPERATION, address, attribute);
    }

    private static ModelNode createAttributeOperation(String operationName, PathAddress address, Attribute attribute) {
        ModelNode operation = Util.createOperation(operationName, address);
        operation.get(ModelDescriptionConstants.NAME).set(attribute.getName());
        return operation;
    }

    /**
     * Creates a describe operation using the specified address.
     * @param address a resource path
     * @return a describe operation
     */
    public static ModelNode createDescribeOperation(PathAddress address) {
        return Util.createOperation(ModelDescriptionConstants.DESCRIBE, address);
    }

    public static ModelNode createReadResourceOperation(PathAddress address) {
        return Util.createOperation(ModelDescriptionConstants.READ_RESOURCE_OPERATION, address);
    }

    public static ModelNode createListAddOperation(PathAddress address, Attribute attribute, String value) {
        return createListElementOperation(ListOperations.LIST_ADD_DEFINITION, address, attribute, value);
    }

    public static ModelNode createListRemoveOperation(PathAddress address, Attribute attribute, String value) {
        return createListElementOperation(ListOperations.LIST_REMOVE_DEFINITION, address, attribute, value);
    }

    public static ModelNode createListRemoveOperation(PathAddress address, Attribute attribute, int index) {
        return createListElementOperation(ListOperations.LIST_REMOVE_DEFINITION, address, attribute, index);
    }

    public static ModelNode createListGetOperation(PathAddress address, Attribute attribute, int index) {
        return createListElementOperation(ListOperations.LIST_GET_DEFINITION, address, attribute, index);
    }

    private static ModelNode createListElementOperation(OperationDefinition definition, PathAddress address, Attribute attribute, String value) {
        ModelNode operation = createAttributeOperation(definition.getName(), address, attribute);
        operation.get(ModelDescriptionConstants.VALUE).set(value);
        return operation;
    }

    private static ModelNode createListElementOperation(OperationDefinition definition, PathAddress address, Attribute attribute, int index) {
        ModelNode operation = createAttributeOperation(definition.getName(), address, attribute);
        operation.get(INDEX).set(new ModelNode(index));
        return operation;
    }

    public static ModelNode createMapGetOperation(PathAddress address, Attribute attribute, String key) {
        return createMapEntryOperation(MapOperations.MAP_GET_DEFINITION, address, attribute, key);
    }

    public static ModelNode createMapPutOperation(PathAddress address, Attribute attribute, String key, String value) {
        ModelNode operation = createMapEntryOperation(MapOperations.MAP_PUT_DEFINITION, address, attribute, key);
        operation.get(ModelDescriptionConstants.VALUE).set(value);
        return operation;
    }

    public static ModelNode createMapRemoveOperation(PathAddress address, Attribute attribute, String key) {
        return createMapEntryOperation(MapOperations.MAP_REMOVE_DEFINITION, address, attribute, key);
    }

    public static ModelNode createMapClearOperation(PathAddress address, Attribute attribute) {
        ModelNode operation = Util.createOperation(MapOperations.MAP_CLEAR_DEFINITION, address);
        operation.get(ModelDescriptionConstants.NAME).set(attribute.getName());
        return operation;
    }

    private static ModelNode createMapEntryOperation(OperationDefinition definition, PathAddress address, Attribute attribute, String key) {
        ModelNode operation = createAttributeOperation(definition.getName(), address, attribute);
        operation.get(KEY).set(key);
        return operation;
    }

    /**
     * @return set of all operations that are or do result in a write
     */
    public static Set<String> getAllWriteAttributeOperationNames() {
        Set<String> writeAttributeOperations = new HashSet<>();
        writeAttributeOperations.addAll(MapOperations.MAP_OPERATION_NAMES);
        writeAttributeOperations.remove(MapOperations.MAP_GET_DEFINITION.getName());
        writeAttributeOperations.add(ModelDescriptionConstants.WRITE_ATTRIBUTE_OPERATION);
        writeAttributeOperations.add(ModelDescriptionConstants.UNDEFINE_ATTRIBUTE_OPERATION);
        return writeAttributeOperations;
    }

    private Operations() {
        // Hide
    }
}


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

package org.jboss.as.naming.deployment;

import org.jboss.as.naming.logging.NamingLogger;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.value.InjectedValue;
import org.jboss.msc.value.Value;

import javax.naming.Context;
import javax.naming.NamingException;

/**
 * Value that is looked up from a naming context.
 *
 * @author John E. Bailey
 */
public class NamingLookupValue<T> implements Value<T> {
    private final InjectedValue<Context> contextValue = new InjectedValue<Context>();
    private final String contextName;

    /**
     * Create a new instance.
     *
     * @param contextName The context name to lookup if the value is not injected
     */
    public NamingLookupValue(final String contextName) {
        this.contextName = contextName;
    }

    /**
     * Lookup the value from the naming context.
     *
     * @return the injected value if present, the value retrieved from the context if not.
     * @throws IllegalStateException The name is not found in the context when called
     */
    public T getValue() throws IllegalStateException {
        final Context context = contextValue.getValue();
        try {
            return (T)context.lookup(contextName);
        } catch (NamingException e) {
            throw NamingLogger.ROOT_LOGGER.entryNotRegistered(e, contextName, context);
        }
    }

    /**
     * Get the naming context injector.
     *
     * @return The context injector
     */
    public Injector<Context> getContextInjector() {
        return contextValue;
    }
}


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

package org.jboss.as.naming;

import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

import javax.naming.Binding;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NameClassPair;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.spi.ObjectFactory;

import org.jboss.as.naming.context.NamespaceContextSelector;
import org.jboss.as.naming.logging.NamingLogger;
import org.wildfly.naming.client.WildFlyInitialContextFactory;
import org.wildfly.security.manager.WildFlySecurityManager;

/**
 * @author Eduardo Martins
 * @author John Bailey
 */
public class InitialContext extends InitialLdapContext {

    /**
     * Map of any additional naming schemes
     */
    private static volatile Map<String, ObjectFactory> urlContextFactories = Collections.emptyMap();

    private final WildFlyInitialContextFactory delegate = new WildFlyInitialContextFactory();

    /**
     * Add an ObjectFactory to handle requests for a specific URL scheme.
     * @param scheme The URL scheme to handle.
     * @param factory The ObjectFactory that can handle the requests.
     */
    public static synchronized void addUrlContextFactory(final String scheme, ObjectFactory factory) {
        Map<String, ObjectFactory> factories = new HashMap<String, ObjectFactory>(urlContextFactories);
        factories.put(scheme, factory);
        urlContextFactories = Collections.unmodifiableMap(factories);
    }

    /**
     * Remove an ObjectFactory from the map of registered ones. To make sure that not anybody can remove an
     * ObjectFactory both the scheme as well as the actual object factory itself need to be supplied. So you
     * can only remove the factory if you have the factory object.
     * @param scheme The URL scheme for which the handler is registered.
     * @param factory The factory object associated with the scheme
     * @throws IllegalArgumentException if the requested scheme/factory combination is not registered.
     */
    public static synchronized void removeUrlContextFactory(final String scheme, ObjectFactory factory) {
        Map<String, ObjectFactory> factories = new HashMap<String, ObjectFactory>(urlContextFactories);

        ObjectFactory f = factories.get(scheme);
        if (f == factory) {
            factories.remove(scheme);
            urlContextFactories = Collections.unmodifiableMap(factories);
            return;
        } else {
            throw new IllegalArgumentException();
        }
    }

    public InitialContext(Hashtable<?,?> environment) throws NamingException {
        super(environment, null);
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void init(Hashtable<?,?> environment) throws NamingException {
        // the jdk initial context already worked out the env, no need to do it again
        myProps = (Hashtable<Object, Object>) environment;
        if (myProps != null && myProps.get(Context.INITIAL_CONTEXT_FACTORY) != null) {
            // user has specified initial context factory; try to get it
            getDefaultInitCtx();
        }
    }

    @Override
    protected Context getDefaultInitCtx() throws NamingException {
        if (!gotDefault) {
            // if there is an initial context factory prop in the env use it to create the default ctx
            final String factoryClassName = myProps != null ? (String) myProps.get(Context.INITIAL_CONTEXT_FACTORY) : null;
            if(factoryClassName == null || InitialContextFactory.class.getName().equals(factoryClassName)) {
                defaultInitCtx = new DefaultInitialContext(myProps);
            } else {
                final ClassLoader classLoader = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged();
                try {
                    final Class<?> factoryClass = Class.forName(factoryClassName, true, classLoader);
                    defaultInitCtx = ((javax.naming.spi.InitialContextFactory)factoryClass.newInstance()).getInitialContext(myProps);
                } catch (NamingException e) {
                    throw e;
                } catch (Exception e) {
                    throw NamingLogger.ROOT_LOGGER.failedToInstantiate(e, "InitialContextFactory", factoryClassName, classLoader);
                }
            }
            gotDefault = true;
        }
        return defaultInitCtx;
    }

    @Override
    protected Context getURLOrDefaultInitCtx(String name) throws NamingException {
        String scheme = getURLScheme(name);
        if (scheme != null && !scheme.equals("java")) {
            ObjectFactory factory = urlContextFactories.get(scheme);
            if (factory != null) {
                try {
                    return (Context) factory.getObjectInstance(null, null, null, myProps);
                }catch(NamingException e) {
                    throw e;
                } catch (Exception e) {
                    NamingException n = new NamingException(e.getMessage());
                    n.initCause(e);
                    throw n;
                }
            } else {
                Context ctx = delegate.getInitialContext(myProps);
                if(ctx!=null) {
                    return ctx;
                }
            }
        }
        return getDefaultInitCtx();
    }

    @Override
    protected Context getURLOrDefaultInitCtx(Name name) throws NamingException {
        if (name.size() > 0) {
            return getURLOrDefaultInitCtx(name.get(0));
        }
        return getDefaultInitCtx();
    }

    public static String getURLScheme(String str) {
        int colon_posn = str.indexOf(':');
        int slash_posn = str.indexOf('/');

        if (colon_posn > 0 && (slash_posn == -1 || colon_posn < slash_posn))
            return str.substring(0, colon_posn);
        return null;
    }

    private interface ParsedName {
        String namespace();
        Name remaining();
    }

    static class DefaultInitialContext extends NamingContext {

        DefaultInitialContext(Hashtable<Object,Object> environment) {
            super(environment);
        }

        private Context findContext(final Name name, final ParsedName parsedName) throws NamingException {
            if (parsedName.namespace() == null || parsedName.namespace().equals("")) {
                return null;
            }
            final NamespaceContextSelector selector = NamespaceContextSelector.getCurrentSelector();
            if (selector == null) {
                throw new NameNotFoundException(name.toString());
            }
            final Context namespaceContext = selector.getContext(parsedName.namespace());
            if (namespaceContext == null) {
                throw new NameNotFoundException(name.toString());
            }
            return namespaceContext;
        }

        private ParsedName parse(final Name name) throws NamingException {
            final Name remaining;
            final String namespace;
            if (name.isEmpty()) {
                namespace = null;
                remaining = name;
            } else {
                final String first = name.get(0);
                if (first.startsWith("java:")) {
                    final String theRest = first.substring(5);
                    if (theRest.startsWith("/")) {
                        namespace = null;
                        remaining = getNameParser(theRest).parse(theRest);
                    } else if (theRest.equals("jboss") && name.size() > 1 && name.get(1).equals("exported")) {
                        namespace = "jboss/exported";
                        remaining = name.getSuffix(2);
                    } else {
                        namespace = theRest;
                        remaining = name.getSuffix(1);
                    }
                } else {
                    namespace = null;
                    remaining = name;
                }
            }

            return new ParsedName() {
                public String namespace() {
                    return namespace;
                }

                public Name remaining() {
                    return remaining;
                }
            };
        }

        public Object lookup(final Name name, boolean dereference) throws NamingException {
            final ParsedName parsedName = parse(name);
            final Context namespaceContext = findContext(name, parsedName);
            if (namespaceContext == null)
                return super.lookup(parsedName.remaining(),dereference);
            else
                return namespaceContext.lookup(parsedName.remaining());
        }

        public NamingEnumeration<Binding> listBindings(final Name name) throws NamingException {
            final ParsedName parsedName = parse(name);
            final Context namespaceContext = findContext(name, parsedName);
            if (namespaceContext == null)
                return super.listBindings(parsedName.remaining());
            else
                return namespaceContext.listBindings(parsedName.remaining());
        }

        public NamingEnumeration<NameClassPair> list(final Name name) throws NamingException {
            final ParsedName parsedName = parse(name);
            final Context namespaceContext = findContext(name, parsedName);
            if (namespaceContext == null)
                return super.list(parsedName.remaining());
            else
                return namespaceContext.list(parsedName.remaining());
        }

        public void bind(Name name, Object object) throws NamingException {
            final ParsedName parsedName = parse(name);
            final Context namespaceContext = findContext(name, parsedName);
            if (namespaceContext == null)
                super.bind(parsedName.remaining(), object);
            else
                namespaceContext.bind(parsedName.remaining(), object);
        }

        public void rebind(Name name, Object object) throws NamingException {
            final ParsedName parsedName = parse(name);
            final Context namespaceContext = findContext(name, parsedName);
            if (namespaceContext == null)
                super.rebind(parsedName.remaining(), object);
            else
                namespaceContext.rebind(parsedName.remaining(), object);
        }

        public void unbind(Name name) throws NamingException {
            final ParsedName parsedName = parse(name);
            final Context namespaceContext = findContext(name, parsedName);
            if (namespaceContext == null)
                super.unbind(parsedName.remaining());
            else
                namespaceContext.unbind(parsedName.remaining());
        }

        public void destroySubcontext(Name name) throws NamingException {
            final ParsedName parsedName = parse(name);
            final Context namespaceContext = findContext(name, parsedName);
            if (namespaceContext == null)
                super.destroySubcontext(parsedName.remaining());
            else
                namespaceContext.destroySubcontext(parsedName.remaining());
        }

        public Context createSubcontext(Name name) throws NamingException {
            final ParsedName parsedName = parse(name);
            final Context namespaceContext = findContext(name, parsedName);
            if (namespaceContext == null)
                return super.createSubcontext(parsedName.remaining());
            else
                return namespaceContext.createSubcontext(parsedName.remaining());
        }
    }
}


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

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;

import javax.validation.ValidationProviderResolver;
import javax.validation.spi.ValidationProvider;

import org.wildfly.security.manager.WildFlySecurityManager;

/**
 * A {@link ValidationProviderResolver} to be used within WildFly. If several BV providers are available,
 * {@code HibernateValidator} will be the first element of the returned provider list.
 * <p/>
 * The providers are loaded via the current thread's context class loader; If no providers are found, the loader of this class
 * will be tried as fallback.
 * </p>
 * Note: This class is a copy of {@code org.jboss.as.ee.beanvalidation.WildFlyProviderResolver}.
 *
 * @author Hardy Ferentschik
 * @author Gunnar Morling
 */
public class WildFlyProviderResolver implements ValidationProviderResolver {

    /**
     * Returns a list with all {@link ValidationProvider} validation providers.
     *
     * @return a list with all {@link ValidationProvider} validation providers
     */
    @Override
    public List<ValidationProvider<?>> getValidationProviders() {
        // first try the TCCL
        List<ValidationProvider<?>> providers = loadProviders(WildFlySecurityManager.getCurrentContextClassLoaderPrivileged());

        if (providers != null && !providers.isEmpty()) {
            return providers;
        }
        // otherwise use the loader of this class
        else {
            return loadProviders(WildFlySecurityManager.getClassLoaderPrivileged(WildFlyProviderResolver.class));
        }
    }

    /**
     * Retrieves the providers from the given loader, using the service loader mechanism.
     *
     * @param classLoader the class loader to use
     * @return a list with providers retrieved via the given loader. May be empty but never {@code null}
     */
    private List<ValidationProvider<?>> loadProviders(ClassLoader classLoader) {
        @SuppressWarnings("rawtypes")
        Iterator<ValidationProvider> providerIterator = ServiceLoader.load(ValidationProvider.class, classLoader).iterator();
        LinkedList<ValidationProvider<?>> providers = new LinkedList<ValidationProvider<?>>();

        while (providerIterator.hasNext()) {
            try {
                ValidationProvider<?> provider = providerIterator.next();

                // put Hibernate Validator to the beginning of the list
                if (provider.getClass().getName().equals("org.hibernate.validator.HibernateValidator")) {
                    providers.addFirst(provider);
                } else {
                    providers.add(provider);
                }
            } catch (ServiceConfigurationError e) {
                // ignore, because it can happen when multiple
                // providers are present and some of them are not class loader
                // compatible with our API.
            }
        }

        return providers;
    }
}


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

import static org.jboss.as.security.service.SecurityBootstrapService.JACC_MODULE;

import java.security.Policy;

import javax.security.jacc.PolicyConfiguration;
import javax.security.jacc.PolicyConfigurationFactory;
import javax.security.jacc.PolicyContextException;

import org.jboss.as.security.SecurityExtension;
import org.jboss.as.security.logging.SecurityLogger;
import org.jboss.modules.ModuleLoadException;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.value.InjectedValue;
import org.wildfly.security.manager.WildFlySecurityManager;

/**
 * A service for JACC policies
 *
 * @author Marcus Moyses
 */
public abstract class JaccService<T> implements Service<PolicyConfiguration> {

    public static final ServiceName SERVICE_NAME = SecurityExtension.JBOSS_SECURITY.append("jacc");

    private final String contextId;

    private final T metaData;

    private final Boolean standalone;

    private volatile PolicyConfiguration policyConfiguration;

    private final InjectedValue<PolicyConfiguration> parentPolicy = new InjectedValue<PolicyConfiguration>();

    public JaccService(final String contextId, T metaData, Boolean standalone) {
        if (contextId == null)
            throw SecurityLogger.ROOT_LOGGER.nullArgument("JACC Context Id");
        this.contextId = contextId;
        this.metaData = metaData;
        this.standalone = standalone;
    }

    /** {@inheritDoc} */
    @Override
    public PolicyConfiguration getValue() throws IllegalStateException, IllegalArgumentException {
        return policyConfiguration;
    }

    /** {@inheritDoc} */
    @Override
    public void start(StartContext context) throws StartException {
        try {
            PolicyConfigurationFactory pcf = getPolicyConfigurationFactory();
            synchronized (pcf) { // synchronize on the factory
                policyConfiguration = pcf.getPolicyConfiguration(contextId, false);
                if (metaData != null) {
                    createPermissions(metaData, policyConfiguration);
                } else {
                    SecurityLogger.ROOT_LOGGER.debugf("Cannot create permissions with 'null' metaData for id=%s", contextId);
                }
                if (!standalone) {
                    PolicyConfiguration parent = parentPolicy.getValue();
                    if (parent != null) {
                        parent = pcf.getPolicyConfiguration(parent.getContextID(), false);
                        parent.linkConfiguration(policyConfiguration);
                        policyConfiguration.commit();
                        parent.commit();
                    } else {
                        SecurityLogger.ROOT_LOGGER.debugf("Could not retrieve parent policy for policy %s", contextId);
                    }
                } else {
                    policyConfiguration.commit();
                }
                // Allow the policy to incorporate the policy configs
                Policy.getPolicy().refresh();
            }
        } catch (Exception e) {
            throw SecurityLogger.ROOT_LOGGER.unableToStartException("JaccService", e);
        }
    }

    private PolicyConfigurationFactory getPolicyConfigurationFactory() throws ModuleLoadException, ClassNotFoundException, PolicyContextException {
        String module = WildFlySecurityManager.getPropertyPrivileged(JACC_MODULE, null);
        final ClassLoader originalClassLoader;
        final ClassLoader jaccClassLoader;
        if (module != null) {
            jaccClassLoader = SecurityActions.getModuleClassLoader(module);
            originalClassLoader = SecurityActions.setThreadContextClassLoader(jaccClassLoader);
        } else {
            jaccClassLoader = null;
            originalClassLoader = null;
        }

        try {
            return PolicyConfigurationFactory.getPolicyConfigurationFactory();
        } finally {
            if (originalClassLoader != null) {
                SecurityActions.setThreadContextClassLoader(originalClassLoader);
            }
        }
    }

    /** {@inheritDoc} */
    @Override
    public void stop(StopContext context) {
        try {
            PolicyConfigurationFactory pcf = PolicyConfigurationFactory.getPolicyConfigurationFactory();
            synchronized (pcf) { // synchronize on the factory
                policyConfiguration = pcf.getPolicyConfiguration(contextId, false);
                policyConfiguration.delete();
            }
        } catch (Exception e) {
            SecurityLogger.ROOT_LOGGER.errorDeletingJACCPolicy(e);
        }
        policyConfiguration = null;
    }

    /**
     * Target {@code Injector}
     *
     * @return target
     */
    public Injector<PolicyConfiguration> getParentPolicyInjector() {
        return parentPolicy;
    }

    /**
     * Create JACC permissions for the deployment
     *
     * @param metaData
     * @param policyConfiguration
     * @throws PolicyContextException
     */
    public abstract void createPermissions(T metaData, PolicyConfiguration policyConfiguration) throws PolicyContextException;

}


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

package org.wildfly.extension.undertow.security.jacc;

import static java.security.AccessController.doPrivileged;

import java.security.CodeSource;
import java.security.Permission;
import java.security.Policy;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.security.jacc.WebResourcePermission;
import javax.security.jacc.WebRoleRefPermission;
import javax.security.jacc.WebUserDataPermission;
import javax.servlet.http.HttpServletRequest;

import io.undertow.security.idm.Account;
import io.undertow.servlet.api.AuthorizationManager;
import io.undertow.servlet.api.Deployment;
import io.undertow.servlet.api.ServletInfo;
import io.undertow.servlet.api.SingleConstraintMatch;
import io.undertow.servlet.api.TransportGuaranteeType;
import org.wildfly.security.manager.WildFlySecurityManager;

/**
 *

 * An implementation of {@link AuthorizationManager} that uses JACC permissions to grant or deny access to web resources.
 *

 *
 * @author Stefan Guilhen
 */
public class JACCAuthorizationManager implements AuthorizationManager {

    public static final AuthorizationManager INSTANCE = new JACCAuthorizationManager();

    @Override
    public boolean isUserInRole(final String roleName, final Account account, final ServletInfo servletInfo, final HttpServletRequest request, final Deployment deployment) {
        return hasPermission(account, deployment, servletInfo, new WebRoleRefPermission(servletInfo.getName(), roleName));
    }

    @Override
    public boolean canAccessResource(List<SingleConstraintMatch> constraints, final Account account, final ServletInfo servletInfo, final HttpServletRequest request, Deployment deployment) {
        return hasPermission(account, deployment, servletInfo, new WebResourcePermission(request));
    }

    @Override
    public TransportGuaranteeType transportGuarantee(TransportGuaranteeType currentConnGuarantee, TransportGuaranteeType configuredRequiredGuarantee, final HttpServletRequest request) {
        final ProtectionDomain domain = new ProtectionDomain(null, null, null, null);
        final String[] httpMethod = new String[] {request.getMethod()};
        final String canonicalURI = getCanonicalURI(request);

        switch (currentConnGuarantee) {
            case NONE: {
                // unprotected connection - create a WebUserDataPermission without any transport guarantee.
                WebUserDataPermission permission = new WebUserDataPermission(canonicalURI, httpMethod, null);

                // if permission was implied then the unprotected connection is ok.
                if (hasPermission(domain, permission)) {
                    return TransportGuaranteeType.NONE;
                }
                else {
                    permission = new WebUserDataPermission(canonicalURI, httpMethod, TransportGuaranteeType.CONFIDENTIAL.name());
                    // permission is only granted with CONFIDENTIAL
                    if (hasPermission(domain, permission)) {
                        return TransportGuaranteeType.CONFIDENTIAL;
                    }
                    //either way we just don't have permission, let the request proceed and be rejected later
                    return TransportGuaranteeType.NONE;
                }
            }
            case INTEGRAL:
            case CONFIDENTIAL: {
                // we will try using both transport guarantees (CONFIDENTIAL and INTEGRAL) as SSL provides both.
                WebUserDataPermission permission = new WebUserDataPermission(canonicalURI, httpMethod,
                        TransportGuaranteeType.CONFIDENTIAL.name());

                if (hasPermission(domain, permission)) {
                    return TransportGuaranteeType.CONFIDENTIAL;
                }
                else {
                    // try with the INTEGRAL connection guarantee type.
                    permission = new WebUserDataPermission(canonicalURI, httpMethod, TransportGuaranteeType.INTEGRAL.name());
                    if (hasPermission(domain, permission)) {
                        return TransportGuaranteeType.INTEGRAL;
                    }
                    else {
                        return TransportGuaranteeType.REJECTED;
                    }
                }
            }
            default:
                return TransportGuaranteeType.REJECTED;
        }
    }

    /**
     *

     * Gets the canonical request URI - that is, the request URI minus the context path.
     *

     *
     * @param request the {@link HttpServletRequest} for which we want the canonical URI.
     * @return the constructed canonical URI.
     */
    private String getCanonicalURI(HttpServletRequest request) {
        String canonicalURI = request.getRequestURI().substring(request.getContextPath().length());
        if (canonicalURI == null || canonicalURI.equals("/"))
            canonicalURI = "";
        return canonicalURI;
    }

    private boolean hasPermission(Account account, Deployment deployment, ServletInfo servletInfo, Permission permission) {
        CodeSource codeSource = servletInfo.getServletClass().getProtectionDomain().getCodeSource();
        ProtectionDomain domain = new ProtectionDomain(codeSource, null, null, getGrantedRoles(account, deployment));
        return hasPermission(domain, permission);
    }

    private boolean hasPermission(ProtectionDomain domain, Permission permission) {
        Policy policy = WildFlySecurityManager.isChecking() ? doPrivileged((PrivilegedAction<Policy>) Policy::getPolicy) : Policy.getPolicy();
        return policy.implies(domain, permission);
    }

    private Principal[] getGrantedRoles(Account account, Deployment deployment) {
        if (account == null) {
            return new Principal[] {};
        }

        Set<String> roles = new HashSet<>(account.getRoles());
        Map<String, Set<String>> principalVersusRolesMap = deployment.getDeploymentInfo().getPrincipalVersusRolesMap();

        roles.addAll(principalVersusRolesMap.getOrDefault(account.getPrincipal().getName(), Collections.emptySet()));

        Principal[] principals = new Principal[roles.size()];
        int index = 0;
        for (String role : roles) {
            principals[index++] = () -> role;
        }
        return principals;
    }
}