Package org.wildfly.extension.picketlink.idm.service

Source Code of org.wildfly.extension.picketlink.idm.service.JPAIdentityStoreService$EntityManagerInvocationHandler

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

import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.transaction.jta.platform.internal.JBossAppServerJtaPlatform;
import org.jboss.modules.Module;
import org.jboss.msc.service.Service;
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.picketlink.idm.jpa.internal.JPAIdentityStore;
import org.picketlink.idm.spi.ContextInitializer;
import org.picketlink.idm.spi.IdentityContext;
import org.picketlink.idm.spi.IdentityStore;
import org.wildfly.extension.picketlink.idm.config.JPAStoreSubsystemConfiguration;
import org.wildfly.extension.picketlink.idm.config.JPAStoreSubsystemConfigurationBuilder;
import org.wildfly.extension.picketlink.idm.jpa.transaction.TransactionalEntityManagerHelper;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.metamodel.EntityType;
import javax.transaction.Status;
import javax.transaction.TransactionManager;
import javax.transaction.TransactionSynchronizationRegistry;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import static java.lang.reflect.Modifier.isAbstract;
import static org.picketlink.common.util.StringUtil.isNullOrEmpty;
import static org.wildfly.extension.picketlink.logging.PicketLinkLogger.ROOT_LOGGER;

/**
* @author Pedro Igor
*/
public class JPAIdentityStoreService implements Service<JPAIdentityStoreService> {

    private static final String JPA_ANNOTATION_PACKAGE = "org.picketlink.idm.jpa.annotations";
    public static final String DEFAULT_PERSISTENCE_UNIT_NAME = "identity";

    private final JPAStoreSubsystemConfigurationBuilder configurationBuilder;
    private JPAStoreSubsystemConfiguration storeConfig;
    private EntityManagerFactory emf;
    private final InjectedValue<TransactionManager> transactionManager = new InjectedValue<TransactionManager>();
    private InjectedValue<TransactionSynchronizationRegistry> transactionSynchronizationRegistry = new InjectedValue<TransactionSynchronizationRegistry>();
    private TransactionalEntityManagerHelper transactionalEntityManagerHelper;

    public JPAIdentityStoreService(JPAStoreSubsystemConfigurationBuilder configurationBuilder) {
        this.configurationBuilder = configurationBuilder;
    }

    @Override
    public void start(StartContext startContext) throws StartException {
        this.storeConfig = this.configurationBuilder.create();
        this.transactionalEntityManagerHelper = new TransactionalEntityManagerHelper(
            this.transactionSynchronizationRegistry.getValue(),
            this.transactionManager.getValue());

        try {
            configureEntityManagerFactory();
            configureEntities();
        } catch (Exception e) {
            throw ROOT_LOGGER.idmJpaStartFailed(e);
        }

        this.configurationBuilder.addContextInitializer(new ContextInitializer() {
            @Override
            public void initContextForStore(IdentityContext context, IdentityStore<?> store) {
                if (store instanceof JPAIdentityStore) {
                    EntityManager entityManager = context.getParameter(JPAIdentityStore.INVOCATION_CTX_ENTITY_MANAGER);

                    if (entityManager == null || !entityManager.isOpen()) {
                        context.setParameter(JPAIdentityStore.INVOCATION_CTX_ENTITY_MANAGER, getEntityManager(getTransactionManager().getValue()));
                    }
                }
            }
        });
    }

    @Override
    public void stop(StopContext stopContext) {
        if (this.storeConfig.getEntityManagerFactoryJndiName() == null) {
            this.emf.close();
        }
    }

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

    public InjectedValue<TransactionManager> getTransactionManager() {
        return this.transactionManager;
    }

    public InjectedValue<TransactionSynchronizationRegistry> getTransactionSynchronizationRegistry() {
        return this.transactionSynchronizationRegistry;
    }

    private void configureEntityManagerFactory() {
        if (this.storeConfig.getEntityManagerFactoryJndiName() != null) {
            this.emf = lookupEntityManagerFactory();
        } else {
            this.emf = createEmbeddedEntityManagerFactory();
        }
    }

    private EntityManagerFactory lookupEntityManagerFactory() {
        ROOT_LOGGER.debugf("Looking up EntityManagerFactory from [%s]", this.storeConfig.getEntityManagerFactoryJndiName());
        try {
            return (EntityManagerFactory) new InitialContext().lookup(this.storeConfig.getEntityManagerFactoryJndiName());
        } catch (NamingException e) {
            throw ROOT_LOGGER.idmJpaEMFLookupFailed(this.storeConfig.getEntityManagerFactoryJndiName());
        }
    }

    private EntityManagerFactory createEmbeddedEntityManagerFactory() {
        ROOT_LOGGER.debugf("Creating embedded EntityManagerFactory.");
        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();

        try {
            Map<Object, Object> properties = new HashMap<Object, Object>();
            String dataSourceJndiUrl = this.storeConfig.getDataSourceJndiUrl();

            if (!isNullOrEmpty(dataSourceJndiUrl)) {
                ROOT_LOGGER.debugf("Using datasource [%s] for embedded EntityManagerFactory.", dataSourceJndiUrl);
                properties.put("javax.persistence.jtaDataSource", dataSourceJndiUrl);
            }

            properties.put(AvailableSettings.JTA_PLATFORM, new JBossAppServerJtaPlatform());

            Module entityModule = this.storeConfig.getEntityModule();

            if (entityModule != null) {
                Thread.currentThread().setContextClassLoader(entityModule.getClassLoader());
            }

            return Persistence.createEntityManagerFactory(this.storeConfig.getEntityModuleUnitName(), properties);
        } finally {
            Thread.currentThread().setContextClassLoader(originalClassLoader);
        }
    }

    private void configureEntities() {
        Set<EntityType<?>> mappedEntities = this.emf.getMetamodel().getEntities();

        for (EntityType<?> entity : mappedEntities) {
            Class<?> javaType = entity.getJavaType();
            if (!isAbstract(javaType.getModifiers()) && isIdentityEntity(javaType)) {
                ROOT_LOGGER.debugf("Mapping entity [%s] to JPA Identity Store.", javaType.getName());
                this.configurationBuilder.mappedEntity(javaType);
            }
        }
    }

    private boolean isIdentityEntity(Class<?> cls) {
        Class<?> checkClass = cls;

        while (!checkClass.equals(Object.class)) {
            for (Annotation a : checkClass.getAnnotations()) {
                if (a.annotationType().getName().startsWith(JPA_ANNOTATION_PACKAGE)) {
                    return true;
                }
            }

            // No class annotation was found, check the fields
            for (Field f : checkClass.getDeclaredFields()) {
                for (Annotation a : f.getAnnotations()) {
                    if (a.annotationType().getName().startsWith(JPA_ANNOTATION_PACKAGE)) {
                        return true;
                    }
                }
            }

            // Check the superclass
            checkClass = checkClass.getSuperclass();
        }

        return false;
    }

    private EntityManager getEntityManager(TransactionManager transactionManager) {
        EntityManager entityManager = getOrCreateTransactionalEntityManager(transactionManager);

        if (entityManager == null || !entityManager.isOpen()) {
            entityManager = createEntityManager(transactionManager);
        }

        return entityManager;
    }

    /**
     * <p>Returns an {@link javax.persistence.EntityManager} associated with the actual {@link javax.transaction.Transaction}, if present.</p>
     *
     * <p>If {@link javax.transaction.Transaction} is {@link Status#STATUS_ACTIVE}, this method tries to return an entity manager
     * already associated with it. If there is no entity manager a new one is created.</p>
     *
     * <p>This method is specially useful when IDM is being called from an EJB or any other component that have already started
     * a transaction. In this case, the client code is responsible to commit or rollback the transaction accordingly.</p>
     *
     * <p>The returned {@link EntityManager} is always closed before the transaction completes.</p>
     *
     * @param transactionManager The transaction manager.
     *
     * @return The {@link EntityManager} associated with the actual transaction or null if there is no active transactions.
     */
    private EntityManager getOrCreateTransactionalEntityManager(TransactionManager transactionManager) {
        try {
            if (transactionManager.getStatus() == Status.STATUS_ACTIVE) {
                EntityManager entityManager = this.transactionalEntityManagerHelper.getTransactionScopedEntityManager(getPersistenceUnitName());

                if (entityManager == null) {
                    entityManager = createEntityManager(transactionManager);
                    this.transactionalEntityManagerHelper.putEntityManagerInTransactionRegistry(getPersistenceUnitName(), entityManager);
                }

                return entityManager;
            }
        } catch (Exception e) {
            throw ROOT_LOGGER.idmJpaFailedCreateTransactionEntityManager(e);
        }

        return null;
    }

    private String getPersistenceUnitName() {
        String persistenceUnitName = this.storeConfig.getEntityModuleUnitName();

        if (persistenceUnitName == null) {
            persistenceUnitName = DEFAULT_PERSISTENCE_UNIT_NAME;
        }

        return persistenceUnitName;
    }

    private EntityManager createEntityManager(TransactionManager transactionManager) {
        return (EntityManager) Proxy.newProxyInstance(Thread.currentThread()
            .getContextClassLoader(), new Class<?>[]{EntityManager.class}, new EntityManagerInvocationHandler(this.emf.createEntityManager(),
            this.storeConfig.getEntityModule(), transactionManager));
    }

    private class EntityManagerInvocationHandler implements InvocationHandler {

        private final Module entityModule;
        private final TransactionManager transactionManager;
        private final EntityManager entityManager;

        public EntityManagerInvocationHandler(EntityManager entityManager, Module entitiesModule, TransactionManager transactionManager) {
            this.entityManager = entityManager;
            this.entityModule = entitiesModule;
            this.transactionManager = transactionManager;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            boolean isManagedTransaction = false;

            if (isTxRequired(method)) {
                if (this.transactionManager.getStatus() == Status.STATUS_NO_TRANSACTION) {
                    this.transactionManager.begin();
                    isManagedTransaction = true;
                }

                this.entityManager.joinTransaction();
            }

            ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();

            try {
                if (this.entityModule != null) {
                    Thread.currentThread().setContextClassLoader(this.entityModule.getClassLoader());
                }

                return method.invoke(this.entityManager, args);
            } finally {
                Thread.currentThread().setContextClassLoader(originalClassLoader);

                if (isManagedTransaction) {
                    if (this.transactionManager.getStatus() == Status.STATUS_ACTIVE) {
                        this.transactionManager.commit();
                        this.transactionManager.suspend();
                    }
                }
            }
        }

        private boolean isTxRequired(Method method) {
            String n = method.getName();
            return "flush".equals(n) || "getLockMode".equals(n) || "lock".equals(n) || "merge".equals(n) || "persist".equals(n) || "refresh".equals(n) || "remove".equals(n);
        }
    }
}
TOP

Related Classes of org.wildfly.extension.picketlink.idm.service.JPAIdentityStoreService$EntityManagerInvocationHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.