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

import java.security.AccessController;
import java.util.Hashtable;
import java.util.StringTokenizer;

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
import javax.naming.directory.Attributes;
import javax.naming.spi.DirObjectFactory;
import javax.naming.spi.ObjectFactory;

import org.jboss.as.naming.ServiceAwareObjectFactory;
import org.jboss.as.naming.logging.NamingLogger;
import org.jboss.as.server.CurrentServiceContainer;
import org.jboss.modules.Module;
import org.jboss.msc.service.ServiceContainer;
import org.wildfly.security.manager.WildFlySecurityManager;

/**
 * ObjectFactoryBuilder implementation used to support custom object factories being loaded from modules. This class
 * also provides the default object factory implementation.
 *
 * @author John Bailey
 */
public class ObjectFactoryBuilder implements javax.naming.spi.ObjectFactoryBuilder, DirObjectFactory {

    public static final ObjectFactoryBuilder INSTANCE = new ObjectFactoryBuilder();

    private ObjectFactoryBuilder() {
    }

    /**
     * Create an object factory.  If the object parameter is a reference it will attempt to create an {@link javax.naming.spi.ObjectFactory}
     * from the reference.  If the parameter is not a reference, or the reference does not create an {@link javax.naming.spi.ObjectFactory}
     * it will return {@code this} as the {@link javax.naming.spi.ObjectFactory} to use.
     *
     * @param obj         The object bound in the naming context
     * @param environment The environment information
     * @return The object factory the object resolves to
     * @throws NamingException If any problems occur
     */
    public ObjectFactory createObjectFactory(final Object obj, Hashtable<?, ?> environment) throws NamingException {
        try {
            if (obj instanceof Reference) {
                return factoryFromReference((Reference) obj, environment);
            }
        } catch (Throwable ignored) {
        }
        return this;
    }

    /**
     * Create an object instance.
     *
     * @param ref         Object containing reference information
     * @param name        The name relative to nameCtx
     * @param nameCtx     The naming context
     * @param environment The environment information
     * @return The object
     * @throws Exception If any error occur
     */
    public Object getObjectInstance(final Object ref, final Name name, final Context nameCtx, final Hashtable<?, ?> environment) throws Exception {
        final ClassLoader classLoader = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged();
        if (classLoader == null) {
            return ref;
        }
        final String factoriesProp = (String) environment.get(Context.OBJECT_FACTORIES);
        if (factoriesProp != null) {
            final String[] classes = factoriesProp.split(":");
            for (String className : classes) {
                try {
                    final Class<?> factoryClass = classLoader.loadClass(className);
                    final ObjectFactory objectFactory = ObjectFactory.class.cast(factoryClass.newInstance());
                    final Object result = objectFactory.getObjectInstance(ref, name, nameCtx, environment);
                    if (result != null) {
                        return result;
                    }
                } catch (Throwable ignored) {
                }
            }
        }
        return ref;
    }

    /**
     * Create an object instance.
     *
     * @param ref         Object containing reference information
     * @param name        The name relative to nameCtx
     * @param nameCtx     The naming context
     * @param environment The environment information
     * @param attributes  The directory attributes
     * @return The object
     * @throws Exception If any error occur
     */
    public Object getObjectInstance(final Object ref, final Name name, final Context nameCtx, final Hashtable<?, ?> environment, final Attributes attributes) throws Exception {
        final ClassLoader classLoader = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged();
        if (classLoader == null) {
            return ref;
        }
        final String factoriesProp = (String) environment.get(Context.OBJECT_FACTORIES);
        if (factoriesProp != null) {
            final String[] classes = factoriesProp.split(":");
            for (String className : classes) {
                try {
                    final Class<?> factoryClass = classLoader.loadClass(className);
                    final ObjectFactory objectFactory = ObjectFactory.class.cast(factoryClass.newInstance());
                    final Object result;
                    if (objectFactory instanceof DirObjectFactory) {
                        result = DirObjectFactory.class.cast(objectFactory).getObjectInstance(ref, name, nameCtx, environment, attributes);
                    } else {
                        result = objectFactory.getObjectInstance(ref, name, nameCtx, environment);
                    }
                    if (result != null) {
                        return result;
                    }
                } catch (Throwable ignored) {
                }
            }
        }
        return ref;
    }

    private ObjectFactory factoryFromReference(final Reference reference, final Hashtable<?, ?> environment) throws Exception {
        if (reference.getFactoryClassName() == null) {
            return lookForURLs(reference, environment);
        }
        if (reference instanceof ModularReference) {
            return factoryFromModularReference(ModularReference.class.cast(reference), environment);
        }
        return factoryFromReference(reference, WildFlySecurityManager.getCurrentContextClassLoaderPrivileged(), environment);
    }

    private ObjectFactory factoryFromModularReference(ModularReference modularReference, final Hashtable<?, ?> environment) throws Exception {
        final Module module = Module.getCallerModuleLoader().loadModule(modularReference.getModuleIdentifier());
        final ClassLoader classLoader = module.getClassLoader();
        return factoryFromReference(modularReference, classLoader, environment);
    }

    private ObjectFactory factoryFromReference(final Reference reference, final ClassLoader classLoader, final Hashtable<?, ?> environment) throws Exception {
        try {
            final Class<?> factoryClass = classLoader.loadClass(reference.getFactoryClassName());
            ObjectFactory factory = ObjectFactory.class.cast(factoryClass.newInstance());
            if (factory instanceof ServiceAwareObjectFactory) {
                ((ServiceAwareObjectFactory) factory).injectServiceRegistry(currentServiceContainer());
            }
            return factory;
        } catch (Throwable t) {
            throw NamingLogger.ROOT_LOGGER.objectFactoryCreationFailure(t);
        }
    }

    static ObjectFactory lookForURLs(Reference ref, Hashtable environment)
            throws NamingException {

        for (int i = 0; i < ref.size(); i++) {
            RefAddr addr = ref.get(i);
            if (addr instanceof StringRefAddr &&
                    addr.getType().equalsIgnoreCase("URL")) {

                String url = (String) addr.getContent();
                ObjectFactory answer = processURL(url, environment);
                if (answer != null) {
                    return answer;
                }
            }
        }
        return null;
    }

    private static ObjectFactory processURL(Object refInfo, Hashtable environment) throws NamingException {

        if (refInfo instanceof String) {
            String url = (String) refInfo;
            String scheme = getURLScheme(url);
            if (scheme != null) {
                ObjectFactory answer = getURLObjectFactory(scheme, url, environment);
                if (answer != null) {
                    return answer;
                }
            }
        }

        if (refInfo instanceof String[]) {
            String[] urls = (String[]) refInfo;
            for (int i = 0; i < urls.length; i++) {
                String scheme = getURLScheme(urls[i]);
                if (scheme != null) {
                    ObjectFactory answer = getURLObjectFactory(scheme, urls[i], environment);
                    if (answer != null) {
                        return answer;
                    }
                }
            }
        }
        return null;
    }

    private static ObjectFactory getURLObjectFactory(String scheme, String url, Hashtable environment) throws NamingException {

        String facProp = (String) environment.get(Context.URL_PKG_PREFIXES);
        if (facProp != null) {
            facProp += ":" + "com.sun.jndi.url";
        } else {
            facProp = "com.sun.jndi.url";
        }

        ClassLoader loader = WildFlySecurityManager.getCurrentContextClassLoaderPrivileged();

        String suffix = "." + scheme + "." + scheme + "URLContextFactory";

        // Not cached; find first factory and cache
        StringTokenizer parser = new StringTokenizer(facProp, ":");
        String className;
        ObjectFactory factory = null;
        while (parser.hasMoreTokens()) {
            className = parser.nextToken() + suffix;
            try {
                Class<?> clazz;
                if (loader == null) {
                    clazz = Class.forName(className);
                } else {
                    clazz = Class.forName(className, true, loader);
                }
                return new ReferenceUrlContextFactoryWrapper((ObjectFactory) clazz.newInstance(), url);
            } catch (InstantiationException | IllegalAccessException e) {
                NamingException ne = new NamingException(className);
                ne.setRootCause(e);
                throw ne;
            } catch (Exception e) {
            }
        }

        return factory;
    }

    private static String getURLScheme(String str) {
        int colon = str.indexOf(':');
        int slash = str.indexOf('/');

        if (colon > 0 && (slash == -1 || colon + 1 == slash))
            return str.substring(0, colon);
        return null;
    }

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

    private static final class ReferenceUrlContextFactoryWrapper implements ObjectFactory {

        private final ObjectFactory factory;
        private final String url;

        private ReferenceUrlContextFactoryWrapper(final ObjectFactory factory, final String url) {
            this.factory = factory;
            this.url = url;
        }

        @Override
        public Object getObjectInstance(final Object obj, final Name name, final Context nameCtx, final Hashtable<?, ?> environment) throws Exception {
            return factory.getObjectInstance(url, name, nameCtx, environment);
        }
    }
}


/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2018 Red Hat, Inc., and individual contributors
 * as indicated by the @author tags.
 *
 * 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.hibernate;

import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.wildfly.security.manager.WildFlySecurityManager;

/**
 * MethodAdapter
 *
 * @deprecated
 */
class MethodAdapter extends MethodVisitor {

    private static final boolean disableAmbiguousChanges = Boolean.parseBoolean(
            WildFlySecurityManager.getPropertyPrivileged("Hibernate51CompatibilityTransformer.disableAmbiguousChanges", "false"));
    public static final BasicLogger logger = Logger.getLogger("org.jboss.as.hibernate.transformer");
    private final boolean rewriteSessionImplementor;
    private final TransformedState transformedState;
    private final MethodVisitor mv;
    private final String moduleName;
    private final String className;

    MethodAdapter(boolean rewriteSessionImplementor, int api, MethodVisitor mv,
                  final String moduleName, final String className, TransformedState transformedState) {
        super(api, mv);
        this.rewriteSessionImplementor = rewriteSessionImplementor;
        this.mv = mv;
        this.moduleName = moduleName;
        this.className = className;
        this.transformedState = transformedState;
    }

    // Change call to org.hibernate.BasicQueryContract.getFlushMode() to instead call BasicQueryContract.getHibernateFlushMode().
    // Change call to org.hibernate.Session.getFlushMode, to instead call Session.getHibernateFlushMode()
    // Calls to Hibernate ORM 5.3 getFlushMode(), will not be changed as the desc will not match (desc == "()Ljavax.persistence.FlushModeType;")
    @Override
    public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
        if (rewriteSessionImplementor &&
                hasSessionImplementor(desc) &&
                (opcode == Opcodes.INVOKESPECIAL || opcode == Opcodes.INVOKEVIRTUAL)) {
            // if we have a user type calling a method from org.hibernate, we rewrite it to use SharedSessionContractImplementor
            logger.debugf("Deprecated Hibernate51CompatibilityTransformer transformed application classes in '%s', " +
                            "class '%s' is calling method %s.%s, which must be changed to use SharedSessionContractImplementor as parameter.",
                    moduleName, className, owner, name);
            mv.visitMethodInsn(opcode, owner, name, replaceSessionImplementor(desc), itf);
            transformedState.setClassTransformed(true);
        } else if (opcode == Opcodes.INVOKEINTERFACE &&
                (owner.equals("org/hibernate/Session") || owner.equals("org/hibernate/BasicQueryContract"))
                && name.equals("getFlushMode") && desc.equals("()Lorg/hibernate/FlushMode;")) {
            logger.debugf("Deprecated Hibernate51CompatibilityTransformer transformed application classes in '%s', " +
                            "class '%s' is calling %s.getFlushMode, which must be changed to call getHibernateFlushMode().",
                    moduleName, className, owner);
            name = "getHibernateFlushMode";
            mv.visitMethodInsn(opcode, owner, name, desc, itf);
            transformedState.setClassTransformed(true);
        } else if (opcode == Opcodes.INVOKEINTERFACE &&
                owner.equals("org/hibernate/Query") &&
                name.equals("getFirstResult") &&
                desc.equals("()Ljava/lang/Integer;")) {
            logger.debugf("Deprecated Hibernate51CompatibilityTransformer transformed application classes in '%s', " +
                            "class '%s', is calling org.hibernate.Query.getFirstResult, which must be changed to call getHibernateFirstResult() " +
                            "so null can be returned when the value is uninitialized. Please note that if a negative value was set using " +
                            "org.hibernate.Query.setFirstResult, then getHibernateFirstResult() will return 0.",
                    moduleName, className);
            name = "getHibernateFirstResult";
            mv.visitMethodInsn(opcode, owner, name, desc, itf);
            transformedState.setClassTransformed(true);
        } else if (opcode == Opcodes.INVOKEINTERFACE &&
                owner.equals("org/hibernate/Query") &&
                name.equals("getMaxResults") &&
                desc.equals("()Ljava/lang/Integer;")) {
            logger.debugf("Deprecated Hibernate51CompatibilityTransformer transformed application classes in '%s', " +
                            "class '%s', is calling org.hibernate.Query.getMaxResults, which must be changed to call getHibernateMaxResults() " +
                            "so that null will be returned when the value is uninitialized or ORM 5.1 org.hibernate.Query#setMaxResults was " +
                            "used to set a value <= 0"
                    , moduleName, className);
            name = "getHibernateMaxResults";
            mv.visitMethodInsn(opcode, owner, name, desc, itf);
            transformedState.setClassTransformed(true);
        } else if (!disableAmbiguousChanges && opcode == Opcodes.INVOKEINTERFACE &&
                owner.equals("org/hibernate/Query") &&
                name.equals("setFirstResult") &&
                desc.equals("(I)Lorg/hibernate/Query;")) {
            logger.debugf("Deprecated Hibernate51CompatibilityTransformer transformed application classes in '%s', " +
                            "class '%s', is calling org.hibernate.Query.setFirstResult, which must be changed to call setHibernateFirstResult() " +
                            "so setting a value < 0 results in pagination starting with the 0th row as was done in Hibernate ORM 5.1 " +
                            "(instead of throwing IllegalArgumentException as specified by JPA)."
                    , moduleName, className);
            name = "setHibernateFirstResult";
            mv.visitMethodInsn(opcode, owner, name, desc, itf);
            transformedState.setClassTransformed(true);
        } else if (!disableAmbiguousChanges && opcode == Opcodes.INVOKEINTERFACE &&
                owner.equals("org/hibernate/Query") &&
                name.equals("setMaxResults") &&
                desc.equals("(I)Lorg/hibernate/Query;")) {
            logger.debugf("Deprecated Hibernate51CompatibilityTransformer transformed application classes in '%s', " +
                            "class '%s', is calling org.hibernate.Query.setMaxResults, which must be changed to call setHibernateMaxResults() " +
                            "so that values <= 0 are treated the same as uninitialized.  Review Hibernate ORM migration doc "
                    , moduleName, className);
            name = "setHibernateMaxResults";
            mv.visitMethodInsn(opcode, owner, name, desc, itf);
            transformedState.setClassTransformed(true);
        } else {
            mv.visitMethodInsn(opcode, owner, name, desc, itf);
        }

    }

    @Override
    public void visitFieldInsn(int opcode, String owner, String name, String desc) {
        // App References to Enum org.hibernate.FlushMode.NEVER (0) should be transformed to reference FlushMode.MANUAL (0) instead.
        if (opcode == Opcodes.GETSTATIC &&
                owner.equals("org/hibernate/FlushMode") &&
                name.equals("NEVER") &&
                desc.equals("Lorg/hibernate/FlushMode;")) {
            logger.debugf("Deprecated Hibernate51CompatibilityTransformer transformed application classes in '%s', " +
                            "class '%s' is using org.hibernate.FlushMode.NEVER, change to org.hibernate.FlushMode.MANUAL."
                    , moduleName, className);
            mv.visitFieldInsn(opcode, owner, "MANUAL", desc);
            transformedState.setClassTransformed(true);
        } else {
            mv.visitFieldInsn(opcode, owner, name, desc);
        }
    }

    private static String replaceSessionImplementor(String desc) {
        return desc.replace("Lorg/hibernate/engine/spi/SessionImplementor;",
                "Lorg/hibernate/engine/spi/SharedSessionContractImplementor;");
    }

    private boolean hasSessionImplementor(String desc) {
        return desc.contains("Lorg/hibernate/engine/spi/SessionImplementor;");
    }

}


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

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.ejb.ConcurrencyManagementType;
import javax.ejb.LockType;
import javax.ejb.TransactionManagementType;

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.ViewConfiguration;
import org.jboss.as.ee.component.ViewConfigurator;
import org.jboss.as.ee.component.ViewDescription;
import org.jboss.as.ee.component.interceptors.InterceptorOrder;
import org.jboss.as.ejb3.logging.EjbLogger;
import org.jboss.as.ejb3.component.EJBComponentDescription;
import org.jboss.as.ejb3.component.EJBViewDescription;
import org.jboss.as.ejb3.component.MethodIntf;
import org.jboss.as.ejb3.component.concurrent.EJBContextHandleFactory;
import org.jboss.as.ejb3.component.interceptors.CurrentInvocationContextInterceptor;
import org.jboss.as.ejb3.concurrency.AccessTimeoutDetails;
import org.jboss.as.ejb3.deployment.EjbJarDescription;
import org.jboss.as.ejb3.tx.CMTTxInterceptor;
import org.jboss.as.server.deployment.Attachments;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.reflect.ClassReflectionIndexUtil;
import org.jboss.as.server.deployment.reflect.DeploymentReflectionIndex;
import org.jboss.invocation.ImmediateInterceptorFactory;
import org.jboss.invocation.proxy.MethodIdentifier;
import org.jboss.metadata.ejb.spec.SessionBeanMetaData;
import org.jboss.msc.service.ServiceName;

/**
 * @author Jaikiran Pai
 * @author Richard Opalka
 */
public abstract class SessionBeanComponentDescription extends EJBComponentDescription {

    /**
     * Flag marking the presence/absence of a no-interface view on the session bean
     */
    private boolean noInterfaceViewPresent;

    /**
     * The {@link javax.ejb.ConcurrencyManagementType} for this bean
     */
    private ConcurrencyManagementType concurrencyManagementType;

    /**
     * Map of class name to default {@link LockType} for this bean.
     */
    private final Map<String, LockType> beanLevelLockType = new HashMap<String, LockType>();

    /**
     * Map of class name to default {@link AccessTimeoutDetails} for this component.
     */
    private final Map<String, AccessTimeoutDetails> beanLevelAccessTimeout = new HashMap<String, AccessTimeoutDetails>();

    /**
     * The {@link LockType} applicable for a specific bean methods.
     */
    private final Map<MethodIdentifier, LockType> methodLockTypes = new HashMap<MethodIdentifier, LockType>();

    /**
     * The {@link javax.ejb.AccessTimeout} applicable for a specific bean methods.
     */
    private final Map<MethodIdentifier, AccessTimeoutDetails> methodAccessTimeouts = new HashMap<MethodIdentifier, AccessTimeoutDetails>();

    /**
     * Methods on the component marked as @Asynchronous
     */
    private final Set<MethodIdentifier> asynchronousMethods = new HashSet<MethodIdentifier>();

    /**
     * Classes the component marked as @Asynchronous
     */
    private final Set<String> asynchronousClasses = new HashSet<String>();

    /**
     * mapped-name of the session bean
     */
    private String mappedName;

    public enum SessionBeanType {
        STATELESS,
        STATEFUL,
        SINGLETON
    }

    /**
     * Construct a new instance.
     *
     * @param componentName      the component name
     * @param componentClassName the component instance class name
     * @param ejbJarDescription  the module description
     */
    public SessionBeanComponentDescription(final String componentName, final String componentClassName,
                                           final EjbJarDescription ejbJarDescription, final ServiceName deploymentUnitServiceName,
                                           final SessionBeanMetaData descriptorData) {
        super(componentName, componentClassName, ejbJarDescription, deploymentUnitServiceName, descriptorData);
    }

    public void addLocalBusinessInterfaceViews(final Collection<String> classNames) {
        for (final String viewClassName : classNames) {
            assertNoRemoteView(viewClassName);
            registerView(viewClassName, MethodIntf.LOCAL);
        }
    }

    public void addLocalBusinessInterfaceViews(final String... classNames) {
        addLocalBusinessInterfaceViews(Arrays.asList(classNames));
    }

    public void addNoInterfaceView() {
        noInterfaceViewPresent = true;
        final ViewDescription viewDescription = registerView(getEJBClassName(), MethodIntf.LOCAL);
        //set up interceptor for non-business methods
        viewDescription.getConfigurators().add(new ViewConfigurator() {
            @Override
            public void configure(final DeploymentPhaseContext context, final ComponentConfiguration componentConfiguration, final ViewDescription description, final ViewConfiguration configuration) throws DeploymentUnitProcessingException {
                DeploymentReflectionIndex index = context.getDeploymentUnit().getAttachment(Attachments.REFLECTION_INDEX);
                for (final Method method : configuration.getProxyFactory().getCachedMethods()) {
                    if (!Modifier.isPublic(method.getModifiers()) && isNotOverriden(method, componentConfiguration.getComponentClass(), index)) {
                        configuration.addClientInterceptor(method, new ImmediateInterceptorFactory(new NotBusinessMethodInterceptor(method)), InterceptorOrder.Client.NOT_BUSINESS_METHOD_EXCEPTION);
                    }
                }
            }
        });
    }

    public EJBViewDescription addWebserviceEndpointView() {
        return registerView(getEJBClassName(), MethodIntf.SERVICE_ENDPOINT);
    }

    public void addRemoteBusinessInterfaceViews(final Collection<String> classNames) {
        for (final String viewClassName : classNames) {
            assertNoLocalView(viewClassName);
            registerView(viewClassName, MethodIntf.REMOTE);
        }
    }

    private void assertNoRemoteView(final String viewClassName) {
        EJBViewDescription ejbView = null;
        for (final ViewDescription view : getViews()) {
            ejbView = (EJBViewDescription) view;
            if (viewClassName.equals(ejbView.getViewClassName()) && ejbView.getMethodIntf() == MethodIntf.REMOTE) {
                throw EjbLogger.ROOT_LOGGER.failToAddClassToLocalView(viewClassName, getEJBName());
            }
        }
    }

    private void assertNoLocalView(final String viewClassName) {
        EJBViewDescription ejbView = null;
        for (final ViewDescription view : getViews()) {
            ejbView = (EJBViewDescription) view;
            if (viewClassName.equals(ejbView.getViewClassName()) && ejbView.getMethodIntf() == MethodIntf.LOCAL) {
                throw EjbLogger.ROOT_LOGGER.failToAddClassToLocalView(viewClassName, getEJBName());
            }
        }
    }

    public boolean hasNoInterfaceView() {
        return this.noInterfaceViewPresent;
    }

    /**
     * Sets the {@link javax.ejb.LockType} applicable for the bean.
     *
     * @param className The class that has the annotation
     * @param locktype  The lock type applicable for the bean
     */
    public void setBeanLevelLockType(String className, LockType locktype) {
        this.beanLevelLockType.put(className, locktype);
    }

    /**
     * Returns the {@link LockType} applicable for the bean.
     *
     * @return
     */
    public Map<String, LockType> getBeanLevelLockType() {
        return this.beanLevelLockType;
    }

    /**
     * Sets the {@link LockType} for the specific bean method
     *
     * @param lockType The applicable lock type for the method
     * @param method   The method
     */
    public void setLockType(LockType lockType, MethodIdentifier method) {
        this.methodLockTypes.put(method, lockType);
    }

    public Map<MethodIdentifier, LockType> getMethodApplicableLockTypes() {
        return this.methodLockTypes;
    }

    /**
     * Returns the {@link javax.ejb.AccessTimeout} applicable for the bean.
     *
     * @return
     */
    public Map<String, AccessTimeoutDetails> getBeanLevelAccessTimeout() {
        return this.beanLevelAccessTimeout;
    }

    /**
     * Sets the {@link javax.ejb.AccessTimeout} applicable for the bean.
     *
     * @param accessTimeout The access timeout applicable for the class
     */
    public void setBeanLevelAccessTimeout(String className, AccessTimeoutDetails accessTimeout) {
        this.beanLevelAccessTimeout.put(className, accessTimeout);
    }

    /**
     * Sets the {@link javax.ejb.AccessTimeout} for the specific bean method
     *
     * @param accessTimeout The applicable access timeout for the method
     * @param method        The method
     */
    public void setAccessTimeout(AccessTimeoutDetails accessTimeout, MethodIdentifier method) {
        this.methodAccessTimeouts.put(method, accessTimeout);
    }

    public Map<MethodIdentifier, AccessTimeoutDetails> getMethodApplicableAccessTimeouts() {
        return this.methodAccessTimeouts;
    }

    /**
     * Returns the concurrency management type for this bean.
     *

     * This method returns null if the concurrency management type hasn't explicitly been set on this
     * {@link SessionBeanComponentDescription}
     *
     * @return
     */
    public ConcurrencyManagementType getConcurrencyManagementType() {
        return this.concurrencyManagementType;
    }

    public void setConcurrencyManagementType(final ConcurrencyManagementType concurrencyManagementType) {
        this.concurrencyManagementType = concurrencyManagementType;
    }

    /**
     * Returns the mapped-name of this bean
     *
     * @return
     */
    public String getMappedName() {
        return this.mappedName;
    }

    /**
     * Sets the mapped-name for this bean
     *
     * @param mappedName
     */
    public void setMappedName(String mappedName) {
        this.mappedName = mappedName;
    }

    /**
     * Add an asynchronous method.
     *
     * @param methodIdentifier The identifier for an async method
     */
    public void addAsynchronousMethod(final MethodIdentifier methodIdentifier) {
        asynchronousMethods.add(methodIdentifier);
    }

    /**
     * @return The identifier of all async methods
     */
    public Set<MethodIdentifier> getAsynchronousMethods() {
        return asynchronousMethods;
    }

    /**
     * Add a bean class or superclass that has been marked asynchronous
     *
     * @param viewName The view name
     */
    public void addAsynchronousClass(final String viewName) {
        asynchronousClasses.add(viewName);
    }

    /**
     * @return The class name of all asynchronous classes
     */
    public Set<String> getAsynchronousClasses() {
        return asynchronousClasses;
    }

    /**
     * Returns the type of the session bean
     *
     * @return
     */
    public abstract SessionBeanType getSessionBeanType();

    @Override
    protected void setupViewInterceptors(EJBViewDescription view) {
        // let super do it's job first
        super.setupViewInterceptors(view);

        // tx management interceptor(s)
        addTxManagementInterceptorForView(view);

        if(view.isEjb2xView()) {
            view.getConfigurators().add(getSessionBeanObjectViewConfigurator());
        }

    }

    protected abstract ViewConfigurator getSessionBeanObjectViewConfigurator();

    /**
     * Sets up the transaction management interceptor for all methods of the passed view.
     *
     * @param view The EJB bean view
     */
    protected static void addTxManagementInterceptorForView(ViewDescription view) {
        // add a Tx configurator
        view.getConfigurators().add(new ViewConfigurator() {
            @Override
            public void configure(DeploymentPhaseContext context, ComponentConfiguration componentConfiguration, ViewDescription description, ViewConfiguration configuration) throws DeploymentUnitProcessingException {
                EJBComponentDescription ejbComponentDescription = (EJBComponentDescription) componentConfiguration.getComponentDescription();
                // Add CMT interceptor factory
                if (TransactionManagementType.CONTAINER.equals(ejbComponentDescription.getTransactionManagementType())) {
                    configuration.addViewInterceptor(CMTTxInterceptor.FACTORY, InterceptorOrder.View.CMT_TRANSACTION_INTERCEPTOR);
                }
            }
        });
    }

    @Override
    protected void addCurrentInvocationContextFactory() {
        // add the current invocation context interceptor at the beginning of the component instance post construct chain
        this.getConfigurators().add(new ComponentConfigurator() {
            @Override
            public void configure(DeploymentPhaseContext context, ComponentDescription description, ComponentConfiguration configuration) throws DeploymentUnitProcessingException {
                configuration.addPostConstructInterceptor(CurrentInvocationContextInterceptor.FACTORY, InterceptorOrder.ComponentPostConstruct.EJB_SESSION_CONTEXT_INTERCEPTOR);
                configuration.addPreDestroyInterceptor(CurrentInvocationContextInterceptor.FACTORY, InterceptorOrder.ComponentPreDestroy.EJB_SESSION_CONTEXT_INTERCEPTOR);
                if(description.isPassivationApplicable()) {
                    configuration.addPrePassivateInterceptor(CurrentInvocationContextInterceptor.FACTORY, InterceptorOrder.ComponentPassivation.EJB_SESSION_CONTEXT_INTERCEPTOR);
                    configuration.addPostActivateInterceptor(CurrentInvocationContextInterceptor.FACTORY, InterceptorOrder.ComponentPassivation.EJB_SESSION_CONTEXT_INTERCEPTOR);
                }
                configuration.getConcurrentContext().addFactory(EJBContextHandleFactory.INSTANCE);
            }
        });
    }

    @Override
    protected void addCurrentInvocationContextFactory(ViewDescription view) {
        view.getConfigurators().add(new ViewConfigurator() {
            @Override
            public void configure(DeploymentPhaseContext context, ComponentConfiguration componentConfiguration, ViewDescription description, ViewConfiguration configuration) throws DeploymentUnitProcessingException {
                configuration.addViewInterceptor(CurrentInvocationContextInterceptor.FACTORY, InterceptorOrder.View.INVOCATION_CONTEXT_INTERCEPTOR);
            }
        });

    }

    @Override
    public boolean isSession() {
        return true;
    }

    @Override
    public boolean isSingleton() {
        return getSessionBeanType() == SessionBeanType.SINGLETON;
    }

    @Override
    public boolean isStateful() {
        return getSessionBeanType() == SessionBeanType.STATEFUL;
    }

    @Override
    public boolean isStateless() {
        return getSessionBeanType() == SessionBeanType.STATELESS;
    }

    @Override
    public SessionBeanMetaData getDescriptorData() {
        return (SessionBeanMetaData) super.getDescriptorData();
    }

    private boolean isNotOverriden(final Method method, final Class<?> actualClass, final DeploymentReflectionIndex deploymentReflectionIndex) throws DeploymentUnitProcessingException {
        return Modifier.isPrivate(method.getModifiers()) || ClassReflectionIndexUtil.findRequiredMethod(deploymentReflectionIndex, actualClass, method).getDeclaringClass() == method.getDeclaringClass();
    }
}


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

/**
 * PrimitiveClassLoaderUtil
 *

 * Util for centralizing the logic for handling primitive types
 * during classloading. Use the {@link #loadClass(String, ClassLoader)}
 * to centralize the logic of checking for primitive types to ensure that
 * the {@link ClassLoader#loadClass(String)} method is not invoked for primitives.
 *
 * @author Jaikiran Pai
 * @version $Revision: $
 */
public class PrimitiveClassLoaderUtil {

    /**
     * First checks if name is a primitive type. If yes, then returns
     * the corresponding {@link Class} for that primitive. If it's not a primitive
     * then the {@link Class#forName(String, boolean, ClassLoader)} method is invoked, passing
     * it the name, false and the cl classloader
     *
     * @param name The class that has to be loaded
     * @param cl   The {@link ClassLoader} to use, if name is *not* a primitive
     * @return Returns the {@link Class} corresponding to name
     * @throws ClassNotFoundException If the class for name could not be found
     * @see ClassLoader#loadClass(String)
     */
    public static Class<?> loadClass(String name, ClassLoader cl) throws ClassNotFoundException {
        /*
        * Handle Primitives
        */
        if (name.equals(void.class.getName())) {
            return void.class;
        }
        if (name.equals(byte.class.getName())) {
            return byte.class;
        }
        if (name.equals(short.class.getName())) {
            return short.class;
        }
        if (name.equals(int.class.getName())) {
            return int.class;
        }
        if (name.equals(long.class.getName())) {
            return long.class;
        }
        if (name.equals(char.class.getName())) {
            return char.class;
        }
        if (name.equals(boolean.class.getName())) {
            return boolean.class;
        }
        if (name.equals(float.class.getName())) {
            return float.class;
        }
        if (name.equals(double.class.getName())) {
            return double.class;
        }
        // Now that we know its not a primitive, lets just allow
        // the passed classloader to handle the request.
        // Note that we are intentionally using Class.forName(name,boolean,cl)
        // to handle issues with loading array types in Java 6 http://bugs.sun.com/view_bug.do?bug_id=6434149
        return Class.forName(name, false, cl);

    }
}


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

/**
 * Represents the Hibernate query name which is passed in as a parameter.  The displayQuery can be obtained which
 * has spaces and other symbols replaced with a textual description (which shouldn't be changed or localized.
 * The localization rule is so that one set of admin scripts will work against any back end system.  If it becomes
 * more important to localize the textual descriptions, care should be taken to avoid duplicate values when doing so.
 *
 * @author Scott Marlow
 */
public class QueryName {

    // query name as returned from hibernate Statistics.getQueries()
    private final String hibernateQuery;

    // query name transformed for display use (allowed to be ugly but should be unique)
    private final String displayQuery;

    // HQL symbol or operators
    private static final String SQL_NE = "<>";
    private static final String NE_BANG = "!=";
    private static final String NE_HAT = "^=";
    private static final String LE = "<=";
    private static final String GE = ">=";
    private static final String CONCAT = "||";
    private static final String LT = "<";
    private static final String EQ = "=";
    private static final String GT = ">";
    private static final String OPEN = "(";
    private static final String CLOSE = ")";
    private static final String OPEN_BRACKET = "[";
    private static final String CLOSE_BRACKET = "]";
    private static final String PLUS = "+";
    private static final String MINUS = "-";
    private static final String STAR = "*";
    private static final String DIV = "/";
    private static final String MOD = "%";
    private static final String COLON = ":";
    private static final String PARAM = "?";
    private static final String COMMA = ",";
    private static final String SPACE = " ";
    private static final String TAB = "\t";
    private static final String NEWLINE = "\n";
    private static final String LINEFEED = "\r";
    private static final String QUOTE = "'";
    private static final String DQUOTE = "\"";
    private static final String TICK = "`";
    private static final String OPEN_BRACE = "{";
    private static final String CLOSE_BRACE = "}";
    private static final String HAT = "^";
    private static final String AMPERSAND = "&";

    // textual representation (not to be localized as we don't won't duplication between any of the values)
    private static final String NOT_EQUAL__ = "_not_equal_";
    private static final String BANG_NOT_EQUAL__ = "_bang_not_equal_";
    private static final String HAT_NOT_EQUAL__ = "_hat_not_equal_";
    private static final String LESS_THAN_EQUAL__ = "_less_than_equal_";
    private static final String GREATER_THAN_EQUAL__ = "_greater_than_equal_";
    private static final String CONCAT__ = "_concat_";
    private static final String LESS_THAN__ = "_less_than_";
    private static final String EQUAL__ = "_equal_";
    private static final String GREATER__ = "_greater_";
    private static final String LEFT_PAREN__ = "_left_paren_";
    private static final String RIGHT_PAREN__ = "_right_paren_";
    private static final String LEFT_BRACKET__ = "_left_bracket_";
    private static final String RIGHT_BRACKET__ = "_right_bracket_";
    private static final String PLUS__ = "_plus_";
    private static final String MINUS__ = "_minus_";
    private static final String STAR__ = "_star_";
    private static final String DIVIDE__ = "_divide_";
    private static final String MODULUS__ = "_modulus_";
    private static final String COLON__ = "_colon_";
    private static final String PARAM__ = "_param_";
    private static final String COMMA__ = "_comma_";
    private static final String SPACE__ = "_space_";
    private static final String TAB__ = "_tab_";
    private static final String NEWLINE__ = "_newline_";
    private static final String LINEFEED__ = "_linefeed_";
    private static final String QUOTE__ = "_quote_";
    private static final String DQUOTE__ = "_double_quote_";
    private static final String TICK__ = "_tick_";
    private static final String OPEN_BRACE__ = "_left_brace_";
    private static final String CLOSE_BRACE__ = "_right_brace_";
    private static final String HAT__ = "_hat_";
    private static final String AMPERSAND__ = "_ampersand_";

    public static QueryName queryName(String query) {
        return new QueryName(query);
    }

    /**
     * Construct
     *
     * @param query
     */
    public QueryName(String query) {
        hibernateQuery = query;
        displayQuery = displayable(query);

    }

    public String getDisplayName() {
        return displayQuery;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        QueryName queryName = (QueryName) o;

        if (displayQuery != null ? !displayQuery.equals(queryName.displayQuery) : queryName.displayQuery != null)
            return false;
        if (hibernateQuery != null ? !hibernateQuery.equals(queryName.hibernateQuery) : queryName.hibernateQuery != null)
            return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = hibernateQuery != null ? hibernateQuery.hashCode() : 0;
        result = 31 * result + (displayQuery != null ? displayQuery.hashCode() : 0);
        return result;
    }

    /**
     * transform a Hibernate HQL query into something that can be displayed/used for management operations
     *
     * @param query
     * @return
     */
    private String displayable(String query) {
        if (query == null ||
            query.length() == 0) {
            return query;
        }

        StringBuilder buff = new StringBuilder(query);

        // handle two character transforms first
        subst(buff, SQL_NE, NOT_EQUAL__);
        subst(buff, NE_BANG, BANG_NOT_EQUAL__);
        subst(buff, NE_HAT, HAT_NOT_EQUAL__);
        subst(buff, LE, LESS_THAN_EQUAL__);
        subst(buff, GE, GREATER_THAN_EQUAL__);
        subst(buff, CONCAT, CONCAT__);
        subst(buff, LT, LESS_THAN__);
        subst(buff, EQ, EQUAL__);
        subst(buff, GT, GREATER__);
        subst(buff, OPEN, LEFT_PAREN__);
        subst(buff, CLOSE, RIGHT_PAREN__);
        subst(buff, OPEN_BRACKET, LEFT_BRACKET__);
        subst(buff, CLOSE_BRACKET, RIGHT_BRACKET__);
        subst(buff, PLUS, PLUS__);
        subst(buff, MINUS, MINUS__);
        subst(buff, STAR, STAR__);
        subst(buff, DIV, DIVIDE__);
        subst(buff, MOD, MODULUS__);
        subst(buff, COLON, COLON__);
        subst(buff, PARAM, PARAM__);
        subst(buff, COMMA, COMMA__);
        subst(buff, SPACE, SPACE__);
        subst(buff, TAB, TAB__);
        subst(buff, NEWLINE, NEWLINE__);
        subst(buff, LINEFEED, LINEFEED__);
        subst(buff, QUOTE, QUOTE__);
        subst(buff, DQUOTE, DQUOTE__);
        subst(buff, TICK, TICK__);
        subst(buff, OPEN_BRACE, OPEN_BRACE__);
        subst(buff, CLOSE_BRACE, CLOSE_BRACE__);
        subst(buff, HAT, HAT__);
        subst(buff, AMPERSAND, AMPERSAND__);
        return buff.toString();
    }

    /**
     * Substitute sub-strings inside of a string.
     *
     * @param stringBuilder String buffer to use for substitution (buffer is not reset)
     * @param from String to substitute from
     * @param to   String to substitute to
     */
    private static void subst(final StringBuilder stringBuilder, final String from, final String to) {
        int begin = 0, end = 0;

        while ((end = stringBuilder.indexOf(from, end)) != -1) {
            stringBuilder.delete(end, end + from.length());
            stringBuilder.insert(end, to);

            // update positions
            begin = end + to.length();
            end = begin;
        }
    }

}


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

import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.controller.transform.ResourceTransformationContext;
import org.jboss.as.controller.transform.ResourceTransformer;

/**
 * @author Paul Ferraro
 */
public class SimpleResourceTransformer implements ResourceTransformer {

    private final PathAddressTransformer addressTransformer;

    public SimpleResourceTransformer(PathAddressTransformer converter) {
        this.addressTransformer = converter;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void transformResource(ResourceTransformationContext context, PathAddress address, Resource resource) throws OperationFailedException {
        context.addTransformedResourceFromRoot(this.addressTransformer.transform(address), resource).processChildren(resource);
    }
}


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

import static java.lang.Thread.currentThread;
import static java.security.AccessController.doPrivileged;
import static org.jboss.as.connector.logging.ConnectorLogger.DS_DEPLOYER_LOGGER;

import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.sql.Driver;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.RejectedExecutionException;

import javax.naming.Reference;
import javax.resource.spi.ManagedConnectionFactory;
import javax.security.auth.Subject;
import javax.sql.DataSource;
import javax.sql.XADataSource;

import org.jboss.as.connector.logging.ConnectorLogger;
import org.jboss.as.connector.metadata.api.common.Credential;
import org.jboss.as.connector.security.ElytronSubjectFactory;
import org.jboss.as.connector.services.driver.InstalledDriver;
import org.jboss.as.connector.services.driver.registry.DriverRegistry;
import org.jboss.as.connector.util.Injection;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.core.security.ServerSecurityManager;
import org.jboss.as.naming.deployment.ContextNames;
import org.jboss.jca.adapters.jdbc.BaseWrapperManagedConnectionFactory;
import org.jboss.jca.adapters.jdbc.JDBCResourceAdapter;
import org.jboss.jca.adapters.jdbc.local.LocalManagedConnectionFactory;
import org.jboss.jca.adapters.jdbc.spi.ClassLoaderPlugin;
import org.jboss.jca.adapters.jdbc.xa.XAManagedConnectionFactory;
import org.jboss.jca.common.api.metadata.common.Extension;
import org.jboss.jca.common.api.metadata.ds.CommonDataSource;
import org.jboss.jca.common.api.metadata.ds.DataSources;
import org.jboss.jca.common.api.metadata.ds.DsSecurity;
import org.jboss.jca.common.api.metadata.ds.Statement;
import org.jboss.jca.common.api.metadata.ds.TimeOut;
import org.jboss.jca.common.api.metadata.ds.Validation;
import org.jboss.jca.common.api.metadata.ds.XaDataSource;
import org.jboss.jca.common.api.metadata.spec.ConfigProperty;
import org.jboss.jca.common.api.metadata.spec.XsdString;
import org.jboss.jca.common.api.validator.ValidateException;
import org.jboss.jca.common.metadata.ds.DatasourcesImpl;
import org.jboss.jca.common.metadata.ds.DriverImpl;
import org.jboss.jca.core.api.connectionmanager.ccm.CachedConnectionManager;
import org.jboss.jca.core.api.management.ManagementRepository;
import org.jboss.jca.core.bootstrapcontext.BootstrapContextCoordinator;
import org.jboss.jca.core.connectionmanager.ConnectionManager;
import org.jboss.jca.core.security.picketbox.PicketBoxSubjectFactory;
import org.jboss.jca.core.spi.mdr.MetadataRepository;
import org.jboss.jca.core.spi.mdr.NotFoundException;
import org.jboss.jca.core.spi.rar.ResourceAdapterRepository;
import org.jboss.jca.core.spi.transaction.TransactionIntegration;
import org.jboss.jca.deployers.DeployersLogger;
import org.jboss.jca.deployers.common.AbstractDsDeployer;
import org.jboss.jca.deployers.common.CommonDeployment;
import org.jboss.jca.deployers.common.DeployException;
import org.jboss.logging.Logger;
import org.jboss.msc.inject.Injector;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceContainer;
import org.jboss.msc.service.ServiceController;
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.jboss.security.SubjectFactory;
import org.wildfly.common.function.ExceptionSupplier;
import org.wildfly.security.auth.client.AuthenticationContext;
import org.wildfly.security.credential.source.CredentialSource;
import org.wildfly.security.manager.WildFlySecurityManager;
import org.wildfly.security.manager.action.ClearContextClassLoaderAction;
import org.wildfly.security.manager.action.GetClassLoaderAction;
import org.wildfly.security.manager.action.SetContextClassLoaderFromClassAction;

/**
 * Base service for managing a data-source.
 * @author John Bailey
 * @author maeste
 */
public abstract class AbstractDataSourceService implements Service<DataSource> {

    /**
     * Consumers outside of the data-source subsystem should use the capability {@code org.wildfly.data-source} where
     * the dynamic name is the resource name in the model.
     */
    public static final ServiceName SERVICE_NAME_BASE = ServiceName.JBOSS.append("data-source");

    public static ServiceName getServiceName(ContextNames.BindInfo bindInfo) {
        return SERVICE_NAME_BASE.append(bindInfo.getBinderServiceName().getCanonicalName());
    }

    private static final DeployersLogger DEPLOYERS_LOGGER = Logger.getMessageLogger(DeployersLogger.class, AS7DataSourceDeployer.class.getName());
    protected final InjectedValue<TransactionIntegration> transactionIntegrationValue = new InjectedValue<TransactionIntegration>();
    private final InjectedValue<Driver> driverValue = new InjectedValue<Driver>();
    private final InjectedValue<ManagementRepository> managementRepositoryValue = new InjectedValue<ManagementRepository>();
    private final InjectedValue<SubjectFactory> subjectFactory = new InjectedValue<SubjectFactory>();
    private final InjectedValue<DriverRegistry> driverRegistry = new InjectedValue<DriverRegistry>();
    private final InjectedValue<CachedConnectionManager> ccmValue = new InjectedValue<CachedConnectionManager>();
    private final InjectedValue<ExecutorService> executor = new InjectedValue<ExecutorService>();
    private final InjectedValue<MetadataRepository> mdr = new InjectedValue<MetadataRepository>();
    private final InjectedValue<ServerSecurityManager> secManager = new InjectedValue<ServerSecurityManager>();
    private final InjectedValue<ResourceAdapterRepository> raRepository = new InjectedValue<ResourceAdapterRepository>();
    private final InjectedValue<AuthenticationContext> authenticationContext = new InjectedValue<>();
    private final InjectedValue<AuthenticationContext> recoveryAuthenticationContext = new InjectedValue<>();
    private final InjectedValue<ExceptionSupplier<CredentialSource, Exception>> credentialSourceSupplier = new InjectedValue<>();
    private final InjectedValue<ExceptionSupplier<CredentialSource, Exception>> recoveryCredentialSourceSupplier = new InjectedValue<>();

    private final String dsName;
    private final ContextNames.BindInfo jndiName;

    protected CommonDeployment deploymentMD;
    private WildFlyDataSource sqlDataSource;

    /**
     * The class loader to use. If null the Driver class loader will be used instead.
     */
    private final ClassLoader classLoader;

    protected AbstractDataSourceService(final String dsName, final ContextNames.BindInfo jndiName, final ClassLoader classLoader ) {
        this.dsName = dsName;
        this.classLoader = classLoader;
        this.jndiName = jndiName;
    }

    public synchronized void start(StartContext startContext) throws StartException {
        try {
            final ServiceContainer container = startContext.getController().getServiceContainer();

            deploymentMD = getDeployer().deploy(container);
            if (deploymentMD.getCfs().length != 1) {
                throw ConnectorLogger.ROOT_LOGGER.cannotStartDs();
            }
            sqlDataSource = new WildFlyDataSource((javax.sql.DataSource) deploymentMD.getCfs()[0], jndiName.getAbsoluteJndiName());
            DS_DEPLOYER_LOGGER.debugf("Adding datasource: %s", deploymentMD.getCfJndiNames()[0]);
            CommonDeploymentService cdService = new CommonDeploymentService(deploymentMD);
            final ServiceName cdServiceName = CommonDeploymentService.getServiceName(jndiName);
            final ServiceBuilder cdServiceSB = startContext.getChildTarget().addService(cdServiceName, cdService);
                    // The dependency added must be the JNDI name which for subsystem resources is an alias. This service
                    // is also used in deployments where the capability service name is not registered for the service.
            cdServiceSB.requires(getServiceName(jndiName));
            cdServiceSB.setInitialMode(ServiceController.Mode.ACTIVE).install();
        } catch (Throwable t) {
            throw ConnectorLogger.ROOT_LOGGER.deploymentError(t, dsName);
        }
    }

    protected abstract AS7DataSourceDeployer getDeployer() throws ValidateException ;

    public void stop(final StopContext stopContext) {
        ExecutorService executorService = executor.getValue();
        Runnable r = new Runnable() {
            @Override
            public void run() {
                try {
                    stopService();
                } finally {
                    stopContext.complete();
                }
            }
        };
        try {
            executorService.execute(r);
        } catch (RejectedExecutionException e) {
            r.run();
        } finally {
            stopContext.asynchronous();
        }
    }

    /**
     * Performs the actual work of stopping the service. Should be called by {@link #stop(org.jboss.msc.service.StopContext)}
     * asynchronously from the MSC thread that invoked stop.
     */
    protected synchronized void stopService() {
        if (deploymentMD != null) {

            if (deploymentMD.getResourceAdapterKey() != null) {
                try {
                    raRepository.getValue().unregisterResourceAdapter(deploymentMD.getResourceAdapterKey());
                } catch (org.jboss.jca.core.spi.rar.NotFoundException nfe) {
                    ConnectorLogger.ROOT_LOGGER.exceptionDuringUnregistering(nfe);
                }
            }

            if (deploymentMD.getResourceAdapter() != null) {
                deploymentMD.getResourceAdapter().stop();

                if (BootstrapContextCoordinator.getInstance() != null && deploymentMD.getBootstrapContextIdentifier() != null) {
                    BootstrapContextCoordinator.getInstance().removeBootstrapContext(deploymentMD.getBootstrapContextIdentifier());
                }
            }

            if (deploymentMD.getDataSources() != null && managementRepositoryValue.getValue() != null) {
                for (org.jboss.jca.core.api.management.DataSource mgtDs : deploymentMD.getDataSources()) {
                    managementRepositoryValue.getValue().getDataSources().remove(mgtDs);
                }
            }

            if (deploymentMD.getConnectionManagers() != null) {
                for (ConnectionManager cm : deploymentMD.getConnectionManagers()) {
                    cm.shutdown();
                }
            }
        }

        sqlDataSource = null;

    }

    public CommonDeployment getDeploymentMD() {
        return deploymentMD;
    }

    public synchronized DataSource getValue() throws IllegalStateException, IllegalArgumentException {
        return sqlDataSource;
    }

    public Injector<TransactionIntegration> getTransactionIntegrationInjector() {
        return transactionIntegrationValue;
    }

    public Injector<Driver> getDriverInjector() {
        return driverValue;
    }

    public Injector<ManagementRepository> getManagementRepositoryInjector() {
        return managementRepositoryValue;
    }

    public Injector<DriverRegistry> getDriverRegistryInjector() {
        return driverRegistry;
    }

    public Injector<SubjectFactory> getSubjectFactoryInjector() {
        return subjectFactory;
    }

    public Injector<CachedConnectionManager> getCcmInjector() {
        return ccmValue;
    }

    public Injector<ExecutorService> getExecutorServiceInjector() {
        return executor;
    }

    public Injector<MetadataRepository> getMdrInjector() {
            return mdr;
        }

    public Injector<ResourceAdapterRepository> getRaRepositoryInjector() {
            return raRepository;
        }

    public Injector<ServerSecurityManager> getServerSecurityManager() {
        return secManager;
    }

    Injector<AuthenticationContext> getAuthenticationContext() {
        return authenticationContext;
    }

    Injector<AuthenticationContext> getRecoveryAuthenticationContext() {
        return recoveryAuthenticationContext;
    }

    public InjectedValue<ExceptionSupplier<CredentialSource, Exception>> getCredentialSourceSupplierInjector() {
        return credentialSourceSupplier;
    }

    public InjectedValue<ExceptionSupplier<CredentialSource, Exception>> getRecoveryCredentialSourceSupplierInjector() {
        return recoveryCredentialSourceSupplier;
    }

    protected String buildConfigPropsString(Map<String, String> configProps) {
        final StringBuffer valueBuf = new StringBuffer();
        for (Map.Entry<String, String> connProperty : configProps.entrySet()) {
            valueBuf.append(connProperty.getKey());
            valueBuf.append("=");
            valueBuf.append(connProperty.getValue());
            valueBuf.append(";");
        }
        return valueBuf.toString();
    }

    protected TransactionIntegration getTransactionIntegration() {
        if (! WildFlySecurityManager.isChecking()) {
            currentThread().setContextClassLoader(TransactionIntegration.class.getClassLoader());
        } else {
            doPrivileged(new SetContextClassLoaderFromClassAction(TransactionIntegration.class));
        }
        try {
            return transactionIntegrationValue.getValue();
        } finally {
            doPrivileged(ClearContextClassLoaderAction.getInstance());
        }
    }

    private ClassLoader driverClassLoader() {
        if(classLoader != null) {
            return classLoader;
        }
        final Class<? extends Driver> clazz = driverValue.getValue().getClass();
        return ! WildFlySecurityManager.isChecking() ? clazz.getClassLoader() : doPrivileged(new GetClassLoaderAction(clazz));
    }

    protected class AS7DataSourceDeployer extends AbstractDsDeployer {

        private final org.jboss.jca.common.api.metadata.ds.DataSource dataSourceConfig;
        private final XaDataSource xaDataSourceConfig;

        public AS7DataSourceDeployer(XaDataSource xaDataSourceConfig) {
            super();
            this.xaDataSourceConfig = xaDataSourceConfig;
            this.dataSourceConfig = null;

        }

        public AS7DataSourceDeployer(org.jboss.jca.common.api.metadata.ds.DataSource dataSourceConfig) {
            super();
            this.dataSourceConfig = dataSourceConfig;
            this.xaDataSourceConfig = null;

        }

        public CommonDeployment deploy(ServiceContainer serviceContainer) throws DeployException {
            try {
                if (serviceContainer == null) {
                    throw new DeployException(ConnectorLogger.ROOT_LOGGER.nullVar("ServiceContainer"));
                }

                HashMap<String, org.jboss.jca.common.api.metadata.ds.Driver> drivers = new HashMap<String, org.jboss.jca.common.api.metadata.ds.Driver>(
                        1);

                DataSources dataSources = null;
                if (dataSourceConfig != null) {
                    String dsClsName = dataSourceConfig.getDataSourceClass();
                    if (dsClsName != null) {
                        try {
                            Class<? extends DataSource> dsCls = driverClassLoader().loadClass(dsClsName).asSubclass(DataSource.class);
                            JdbcDriverAdd.checkDSCls(dsCls, DataSource.class);
                        } catch (OperationFailedException e) {
                            throw ConnectorLogger.ROOT_LOGGER.cannotDeploy(e);
                        } catch (ClassCastException e) {
                            throw ConnectorLogger.ROOT_LOGGER.cannotDeploy(ConnectorLogger.ROOT_LOGGER.notAValidDataSourceClass(dsClsName, DataSource.class.getName()));
                        } catch (ClassNotFoundException e) {
                            throw ConnectorLogger.ROOT_LOGGER.cannotDeploy(ConnectorLogger.ROOT_LOGGER.failedToLoadDataSourceClass(dsClsName, e));
                        }
                    }

                    String driverName = dataSourceConfig.getDriver();
                    InstalledDriver installedDriver = driverRegistry.getValue().getInstalledDriver(driverName);
                    if (installedDriver != null) {
                        String moduleName = installedDriver.getModuleName() != null ? installedDriver.getModuleName().getName()
                                : null;
                        org.jboss.jca.common.api.metadata.ds.Driver driver = new DriverImpl(installedDriver.getDriverName(),
                                installedDriver.getMajorVersion(), installedDriver.getMinorVersion(),
                                moduleName, installedDriver.getDriverClassName(),
                                installedDriver.getDataSourceClassName(), installedDriver.getXaDataSourceClassName());
                        drivers.put(driverName, driver);
                    }
                    dataSources = new DatasourcesImpl(Arrays.asList(dataSourceConfig), null, drivers);
                } else if (xaDataSourceConfig != null) {
                    String xaDSClsName = xaDataSourceConfig.getXaDataSourceClass();
                    if (xaDSClsName != null) {
                        try {
                            Class<? extends XADataSource> xaDsCls = driverClassLoader().loadClass(xaDSClsName).asSubclass(XADataSource.class);
                            JdbcDriverAdd.checkDSCls(xaDsCls, XADataSource.class);
                        } catch (OperationFailedException e) {
                            throw ConnectorLogger.ROOT_LOGGER.cannotDeploy(e);
                        } catch (ClassCastException e) {
                            throw ConnectorLogger.ROOT_LOGGER.cannotDeploy(ConnectorLogger.ROOT_LOGGER.notAValidDataSourceClass(xaDSClsName, XADataSource.class.getName()));
                        } catch (ClassNotFoundException e) {
                            throw ConnectorLogger.ROOT_LOGGER.cannotDeploy(ConnectorLogger.ROOT_LOGGER.failedToLoadDataSourceClass(xaDSClsName, e));
                        }
                    }

                    String driverName = xaDataSourceConfig.getDriver();
                    InstalledDriver installedDriver = driverRegistry.getValue().getInstalledDriver(driverName);
                    if (installedDriver != null) {
                        String moduleName = installedDriver.getModuleName() != null ? installedDriver.getModuleName().getName()
                                : null;
                        org.jboss.jca.common.api.metadata.ds.Driver driver = new DriverImpl(installedDriver.getDriverName(),
                                installedDriver.getMajorVersion(), installedDriver.getMinorVersion(), moduleName,
                                installedDriver.getDriverClassName(),
                                installedDriver.getDataSourceClassName(), installedDriver.getXaDataSourceClassName());
                        drivers.put(driverName, driver);
                    }
                    dataSources = new DatasourcesImpl(null, Arrays.asList(xaDataSourceConfig), drivers);
                }

                CommonDeployment c = createObjectsAndInjectValue(new URL("file://DataSourceDeployment"), dsName,
                        "uniqueJdbcLocalId", "uniqueJdbcXAId", dataSources, AbstractDataSourceService.class.getClassLoader());
                return c;
            } catch (MalformedURLException e) {
                throw ConnectorLogger.ROOT_LOGGER.cannotDeploy(e);
            } catch (ValidateException e) {
                throw ConnectorLogger.ROOT_LOGGER.cannotDeployAndValidate(e);
            }

        }

        @Override
        protected ClassLoader getDeploymentClassLoader(String uniqueId) {
            return driverClassLoader();
        }

        @Override
        protected String[] bindConnectionFactory(String deployment, final String jndi, Object cf) throws Throwable {
            // AS7-2222: Just hack it
            if (cf instanceof javax.resource.Referenceable) {
                ((javax.resource.Referenceable)cf).setReference(new Reference(jndi));
            }

            // don't register because it's one during add operation
            return new String[] { jndi };
        }

        @Override
        protected Object initAndInject(String className, List<? extends ConfigProperty> configs, ClassLoader cl)
                throws DeployException {
            try {
                Class clz = Class.forName(className, true, cl);
                Object o = clz.newInstance();

                if (configs != null) {
                    Injection injector = new Injection();
                    for (ConfigProperty cpmd : configs) {
                        if (cpmd.isValueSet()) {

                            if (XsdString.isNull(cpmd.getConfigPropertyType())) {
                                injector.inject(o,
                                        cpmd.getConfigPropertyName().getValue(),
                                        cpmd.getConfigPropertyValue().getValue());
                            } else {
                                injector.inject(o,
                                        cpmd.getConfigPropertyName().getValue(),
                                        cpmd.getConfigPropertyValue().getValue(),
                                        cpmd.getConfigPropertyType().getValue());
                            }

                        }
                    }
                }

                return o;
            } catch (Throwable t) {
                throw ConnectorLogger.ROOT_LOGGER.deploymentFailed(t, className);
            }
        }

        @Override
        protected org.jboss.jca.core.spi.security.SubjectFactory getSubjectFactory(
                org.jboss.jca.common.api.metadata.common.Credential credential, final String jndiName) throws DeployException {
            if (credential == null)
                return null;
            // safe assertion because all parsers create Credential
            assert credential instanceof Credential;
            final String securityDomain = credential.getSecurityDomain();
            if (((Credential) credential).isElytronEnabled()) {
                try {
                    return new ElytronSubjectFactory(authenticationContext.getOptionalValue(), new java.net.URI(jndiName));
                } catch (URISyntaxException e) {
                    throw ConnectorLogger.ROOT_LOGGER.cannotDeploy(e);
                }
            } else if (securityDomain == null || securityDomain.trim().equals("") || subjectFactory.getOptionalValue() == null) {
                return null;
            } else {
                return new PicketBoxSubjectFactory(subjectFactory.getValue()){
                    @Override
                    public Subject createSubject(final String sd) {
                        ServerSecurityManager sm = secManager.getOptionalValue();
                        if (sm != null) {
                            sm.push(sd);
                        }
                        try {
                            return super.createSubject(sd);
                        } finally {
                            if (sm != null) {
                                sm.pop();
                            }
                        }
                    }
                };
            }
        }

        @Override
        public CachedConnectionManager getCachedConnectionManager() {
            return ccmValue.getOptionalValue();
        }

        @Override
        public ManagementRepository getManagementRepository() {
            return managementRepositoryValue.getValue();
        }

        @Override
        protected void initAndInjectClassLoaderPlugin(ManagedConnectionFactory mcf, CommonDataSource dsMetadata)
                throws DeployException {
            ((BaseWrapperManagedConnectionFactory) mcf).setClassLoaderPlugin(new ClassLoaderPlugin() {

                @Override
                public ClassLoader getClassLoader() {
                    return driverClassLoader();
                }
            });
        }

        @Override
        public TransactionIntegration getTransactionIntegration() {
            if (! WildFlySecurityManager.isChecking()) {
                currentThread().setContextClassLoader(TransactionIntegration.class.getClassLoader());
            } else {
                doPrivileged(new SetContextClassLoaderFromClassAction(TransactionIntegration.class));
            }
            try {
                return transactionIntegrationValue.getValue();
            } finally {
                if (! WildFlySecurityManager.isChecking()) {
                    currentThread().setContextClassLoader(null);
                } else {
                    doPrivileged(ClearContextClassLoaderAction.getInstance());
                }
            }
        }

        @Override
        protected ManagedConnectionFactory createMcf(XaDataSource arg0, String arg1, ClassLoader arg2)
                throws NotFoundException, DeployException {
            final XAManagedConnectionFactory xaManagedConnectionFactory = new XAManagedConnectionFactory(xaDataSourceConfig.getXaDataSourceProperty());

            if (xaDataSourceConfig.getUrlDelimiter() != null) {
                xaManagedConnectionFactory.setURLDelimiter(xaDataSourceConfig.getUrlDelimiter());
            }
            if (xaDataSourceConfig.getXaDataSourceClass() != null) {
                xaManagedConnectionFactory.setXADataSourceClass(xaDataSourceConfig.getXaDataSourceClass());
            }
            if (xaDataSourceConfig.getUrlSelectorStrategyClassName() != null) {
                xaManagedConnectionFactory
                        .setUrlSelectorStrategyClassName(xaDataSourceConfig.getUrlSelectorStrategyClassName());
            }
            if (xaDataSourceConfig.getXaPool() != null && xaDataSourceConfig.getXaPool().isSameRmOverride() != null) {
                xaManagedConnectionFactory.setIsSameRMOverrideValue(xaDataSourceConfig.getXaPool().isSameRmOverride());
            }

            if (xaDataSourceConfig.getNewConnectionSql() != null) {
                xaManagedConnectionFactory.setNewConnectionSQL(xaDataSourceConfig.getNewConnectionSql());
            }

            if (xaDataSourceConfig.getUrlSelectorStrategyClassName() != null) {
                xaManagedConnectionFactory
                        .setUrlSelectorStrategyClassName(xaDataSourceConfig.getUrlSelectorStrategyClassName());
            }

            setMcfProperties(xaManagedConnectionFactory, xaDataSourceConfig, xaDataSourceConfig.getStatement());
            return xaManagedConnectionFactory;

        }

        @Override
        protected ManagedConnectionFactory createMcf(org.jboss.jca.common.api.metadata.ds.DataSource arg0, String arg1,
                ClassLoader arg2) throws NotFoundException, DeployException {
            final LocalManagedConnectionFactory managedConnectionFactory = new LocalManagedConnectionFactory();
            managedConnectionFactory.setDriverClass(dataSourceConfig.getDriverClass());

            if (dataSourceConfig.getUrlDelimiter() != null) {
                try {
                    managedConnectionFactory.setURLDelimiter(dataSourceConfig.getUrlDelimiter());
                } catch (Exception e) {
                    throw ConnectorLogger.ROOT_LOGGER.failedToGetUrlDelimiter(e);
                }
            }

            if (dataSourceConfig.getDataSourceClass() != null) {
                managedConnectionFactory.setDataSourceClass(dataSourceConfig.getDataSourceClass());
            }

            if (dataSourceConfig.getConnectionProperties() != null) {
                managedConnectionFactory.setConnectionProperties(buildConfigPropsString(dataSourceConfig
                        .getConnectionProperties()));
            }
            if (dataSourceConfig.getConnectionUrl() != null) {
                managedConnectionFactory.setConnectionURL(dataSourceConfig.getConnectionUrl());
            }

            if (dataSourceConfig.getNewConnectionSql() != null) {
                managedConnectionFactory.setNewConnectionSQL(dataSourceConfig.getNewConnectionSql());
            }

            if (dataSourceConfig.getUrlSelectorStrategyClassName() != null) {
                managedConnectionFactory.setUrlSelectorStrategyClassName(dataSourceConfig.getUrlSelectorStrategyClassName());
            }
            setMcfProperties(managedConnectionFactory, dataSourceConfig, dataSourceConfig.getStatement());

            return managedConnectionFactory;
        }

        private void setMcfProperties(final BaseWrapperManagedConnectionFactory managedConnectionFactory,
                CommonDataSource dataSourceConfig, final Statement statement) {

            if (dataSourceConfig.getTransactionIsolation() != null) {
                managedConnectionFactory.setTransactionIsolation(dataSourceConfig.getTransactionIsolation().name());
            }

            final DsSecurity security = dataSourceConfig.getSecurity();
            if (security != null) {
                if (security.getUserName() != null) {
                    managedConnectionFactory.setUserName(security.getUserName());
                }
                if (security.getPassword() != null) {
                    managedConnectionFactory.setPassword(security.getPassword());
                }
            }

            final TimeOut timeOut = dataSourceConfig.getTimeOut();
            if (timeOut != null) {
                if (timeOut.getUseTryLock() != null) {
                    managedConnectionFactory.setUseTryLock(timeOut.getUseTryLock().intValue());
                }
                if (timeOut.getQueryTimeout() != null) {
                    managedConnectionFactory.setQueryTimeout(timeOut.getQueryTimeout().intValue());
                }
                if (timeOut.isSetTxQueryTimeout()) {
                    managedConnectionFactory.setTransactionQueryTimeout(true);
                }
            }

            if (statement != null) {
                if (statement.getTrackStatements() != null) {
                    managedConnectionFactory.setTrackStatements(statement.getTrackStatements().name());
                }
                if (statement.isSharePreparedStatements() != null) {
                    managedConnectionFactory.setSharePreparedStatements(statement.isSharePreparedStatements());
                }
                if (statement.getPreparedStatementsCacheSize() != null) {
                    managedConnectionFactory.setPreparedStatementCacheSize(statement.getPreparedStatementsCacheSize()
                            .intValue());
                }
            }

            final Validation validation = dataSourceConfig.getValidation();
            if (validation != null) {
                if (validation.getCheckValidConnectionSql() != null) {
                    managedConnectionFactory.setCheckValidConnectionSQL(validation.getCheckValidConnectionSql());
                }
                final Extension validConnectionChecker = validation.getValidConnectionChecker();
                if (validConnectionChecker != null) {
                    if (validConnectionChecker.getClassName() != null) {
                        managedConnectionFactory.setValidConnectionCheckerClassName(validConnectionChecker.getClassName());
                    }
                    if (validConnectionChecker.getConfigPropertiesMap() != null) {
                        managedConnectionFactory
                                .setValidConnectionCheckerProperties(buildConfigPropsString(validConnectionChecker
                                        .getConfigPropertiesMap()));
                    }
                }
                final Extension exceptionSorter = validation.getExceptionSorter();
                if (exceptionSorter != null) {
                    if (exceptionSorter.getClassName() != null) {
                        managedConnectionFactory.setExceptionSorterClassName(exceptionSorter.getClassName());
                    }
                    if (exceptionSorter.getConfigPropertiesMap() != null) {
                        managedConnectionFactory.setExceptionSorterProperties(buildConfigPropsString(exceptionSorter
                                .getConfigPropertiesMap()));
                    }
                }
                final Extension staleConnectionChecker = validation.getStaleConnectionChecker();
                if (staleConnectionChecker != null) {
                    if (staleConnectionChecker.getClassName() != null) {
                        managedConnectionFactory.setStaleConnectionCheckerClassName(staleConnectionChecker.getClassName());
                    }
                    if (staleConnectionChecker.getConfigPropertiesMap() != null) {
                        managedConnectionFactory
                                .setStaleConnectionCheckerProperties(buildConfigPropsString(staleConnectionChecker
                                        .getConfigPropertiesMap()));
                    }
                }
            }
        }

        // Override this method to change how dsName is build in AS7
        @Override
        protected String buildJndiName(String rawJndiName, Boolean javaContext) {
            final String jndiName;
            if (!rawJndiName.startsWith("java:") && javaContext) {
                if (rawJndiName.startsWith("jboss/")) {
                    // Bind to java:jboss/ namespace
                    jndiName = "java:" + rawJndiName;
                } else {
                    // Bind to java:/ namespace
                    jndiName= "java:/" + rawJndiName;
                }
            } else {
                jndiName = rawJndiName;
            }
            return jndiName;
        }

        @Override
        protected DeployersLogger getLogger() {
            return DEPLOYERS_LOGGER;
        }

        @Override
        protected javax.resource.spi.ResourceAdapter createRa(String uniqueId, ClassLoader cl) throws NotFoundException, DeployException {

            List<? extends ConfigProperty> l = new ArrayList<ConfigProperty>();

            javax.resource.spi.ResourceAdapter rar =
                    (javax.resource.spi.ResourceAdapter) initAndInject(JDBCResourceAdapter.class.getName(), l, cl);

            return rar;
        }

        @Override
        protected String registerResourceAdapterToResourceAdapterRepository(javax.resource.spi.ResourceAdapter instance) {
            return raRepository.getValue().registerResourceAdapter(instance);
        }

    }

}


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

import static org.jboss.as.controller.PathAddress.EMPTY_ADDRESS;
import static org.jboss.as.controller.RunningMode.NORMAL;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.apache.activemq.artemis.api.core.TransportConfiguration;
import org.apache.activemq.artemis.api.core.client.ActiveMQClient;
import org.apache.activemq.artemis.api.core.client.ClientSession;
import org.apache.activemq.artemis.api.core.client.ClientSessionFactory;
import org.apache.activemq.artemis.api.core.client.ServerLocator;
import org.apache.activemq.artemis.cli.commands.tools.xml.XmlDataImporter;
import org.apache.activemq.artemis.core.remoting.impl.invm.InVMConnectorFactory;
import org.jboss.as.controller.AbstractRuntimeOnlyHandler;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
import org.jboss.as.controller.descriptions.ResourceDescriptionResolver;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.registry.Resource;
import org.jboss.as.controller.services.path.PathResourceDefinition;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
import org.wildfly.extension.messaging.activemq.logging.MessagingLogger;

import static org.wildfly.extension.messaging.activemq.ExportJournalOperation.checkAllowedOnJournal;

/**
 * Import a dump of Artemis journal in a running Artemis server.
 * WildFly must be running in NORMAL mode to perform this operation.
 *
 * The dump file MUST be on WildFly host. It is not attached to the operation stream.
 *
 * @author Jeff Mesnil (c) 2015 Red Hat inc.
 */
public class ImportJournalOperation extends AbstractRuntimeOnlyHandler {

    private static AttributeDefinition FILE = SimpleAttributeDefinitionBuilder.create("file", PathResourceDefinition.PATH)
            .setAllowExpression(false)
            .setRequired(true)
            .build();

    private static AttributeDefinition LEGACY_PREFIXES = SimpleAttributeDefinitionBuilder.create("legacy-prefixes", ModelType.BOOLEAN)
            // import with legacy prefix by default for backwards compatibility
            .setDefaultValue(new ModelNode(true))
            .setAllowExpression(false)
            .setRequired(false)
            .build();

    private static final String OPERATION_NAME = "import-journal";

    static final ImportJournalOperation INSTANCE = new ImportJournalOperation();

    private ImportJournalOperation() {

    }

    static void registerOperation(final ManagementResourceRegistration registry, final ResourceDescriptionResolver resourceDescriptionResolver) {
        registry.registerOperationHandler(new SimpleOperationDefinitionBuilder(OPERATION_NAME, resourceDescriptionResolver)
                        .addParameter(FILE)
                        .addParameter(LEGACY_PREFIXES)
                        .setRuntimeOnly()
                        .setReplyValueType(ModelType.BOOLEAN)
                        .build(),
                INSTANCE);
    }

    @Override
    protected void executeRuntimeStep(OperationContext context, ModelNode operation) throws OperationFailedException {
        if (context.getRunningMode() != NORMAL) {
            throw MessagingLogger.ROOT_LOGGER.managementOperationAllowedOnlyInRunningMode(OPERATION_NAME, NORMAL);
        }
        checkAllowedOnJournal(context, OPERATION_NAME);

        String file = FILE.resolveModelAttribute(context, operation).asString();
        boolean legacyPrefixes = LEGACY_PREFIXES.resolveModelAttribute(context, operation).asBoolean();

        final XmlDataImporter importer = new XmlDataImporter();
        importer.legacyPrefixes = legacyPrefixes;

        TransportConfiguration transportConfiguration = createInVMTransportConfiguration(context);
        try (
                InputStream is = new FileInputStream(new File(file));
                ServerLocator serverLocator = ActiveMQClient.createServerLocator(false, transportConfiguration);
                ClientSessionFactory sf = serverLocator.createSessionFactory()
        ) {
            ClientSession session = sf.createSession();
            importer.process(is, session);
        } catch (Exception e) {
            throw new OperationFailedException(e);
        }
    }

    /**
     * The XmlDataImporter requires a connector to connect to the artemis broker.
     *
     * We require to use a in-vm one so that importing a journal is not subject to any network connection problem.
     */
    private TransportConfiguration createInVMTransportConfiguration(OperationContext context) throws OperationFailedException {
        final Resource serverResource = context.readResource(EMPTY_ADDRESS, false);
        Set<Resource.ResourceEntry> invmConnectors = serverResource.getChildren(CommonAttributes.IN_VM_CONNECTOR);
        if (invmConnectors.isEmpty()) {
            throw MessagingLogger.ROOT_LOGGER.noInVMConnector();
        }
        Resource.ResourceEntry connectorEntry = invmConnectors.iterator().next();

        Resource connectorResource = context.readResource(PathAddress.pathAddress(connectorEntry.getPathElement()), false);
        ModelNode model = connectorResource.getModel();

        Map<String, Object> params = new HashMap<>(CommonAttributes.PARAMS.unwrap(context, model));
        params.put(InVMTransportDefinition.SERVER_ID.getName(), InVMTransportDefinition.SERVER_ID.resolveModelAttribute(context, model).asInt());
        TransportConfiguration transportConfiguration = new TransportConfiguration(InVMConnectorFactory.class.getName(), params);
        return transportConfiguration;
    }
}