All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pl.net.bluesoft.rnd.processtool.plugins.DataRegistryImpl Maven / Gradle / Ivy

The newest version!
package pl.net.bluesoft.rnd.processtool.plugins;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionFactoryImplementor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.stereotype.Component;
import pl.net.bluesoft.rnd.processtool.ProcessToolContext;
import pl.net.bluesoft.rnd.processtool.ProcessToolContextFactory;
import pl.net.bluesoft.rnd.processtool.auditlog.AuditLogHandler;
import pl.net.bluesoft.rnd.processtool.dao.*;
import pl.net.bluesoft.rnd.processtool.dao.impl.*;
import pl.net.bluesoft.rnd.processtool.expressions.ExpressionEvaluators;
import pl.net.bluesoft.rnd.processtool.model.IAttribute;
import pl.net.bluesoft.rnd.processtool.model.IAttributesProvider;
import pl.net.bluesoft.util.lang.FormatUtil;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;
import javax.transaction.UserTransaction;
import java.io.ByteArrayInputStream;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

import static pl.net.bluesoft.util.lang.FormatUtil.nvl;

/**
 * User: POlszewski
 * Date: 2013-10-09
 * Time: 21:46
 */
@Component
@Scope(value = "singleton")
public class DataRegistryImpl implements DataRegistry {
    private static final Logger logger = Logger.getLogger(DataRegistryImpl.class.getSimpleName());

    private final Map annotatedClasses = new HashMap();
    private final Map hibernateResources = new HashMap();
    private final Map classLoaders = new HashMap();
    private final Map> attributesMappersClasses = new LinkedHashMap>();
    private final Map> mappersClasses = new LinkedHashMap>();

    private final List auditLogHandlers = new ArrayList();
	private final ExpressionEvaluators expressionEvaluators = new ExpressionEvaluators();

    private SessionFactory sessionFactory;

    private TransactionAwareDataSourceProxy dataSourceProxy;

    @Autowired
    private ProcessToolContextFactory processToolContextFactory;

    private boolean jta;

	@Override
	public Dialect getHibernateDialect() {
        Dialect dialect = ((SessionFactoryImplementor) sessionFactory).getDialect();

        return dialect;
    }

    private class ExtClassLoader extends ClassLoader {
        private ExtClassLoader(ClassLoader parent) {
            super(parent);
        }

        @Override
        public Class loadClass(String name) throws ClassNotFoundException {
            Class aClass = annotatedClasses.get(name);
            if (aClass != null) {
                return aClass;
            }
            for (ClassLoader loader : classLoaders.values()) {
                try {
                    Class aClass1 = loader.loadClass(name);
                    if (aClass1 != null) return aClass1;
                } catch (Exception e) {
                    //do nothing
                }
            }
            return super.loadClass(name);
        }
    }

    @Override
    public synchronized void addClassLoader(String name, ClassLoader loader) {
        classLoaders.put(name, loader);
    }

    @Override
    public synchronized void removeClassLoader(String name) {
        classLoaders.remove(name);
    }

    @Override
    public synchronized ClassLoader getModelAwareClassLoader(ClassLoader parent) {
        return new ExtClassLoader(parent);
    }

    @Override
    public synchronized boolean registerModelExtension(Class... cls) {
        logger.finest("Registered model extensions: " + FormatUtil.joinClassNames(cls));
        return addAnnotatedClass(cls);
    }

    @Override
    public synchronized boolean unregisterModelExtension(Class... cls) {
        logger.finest("Unregistered model extensions: " + FormatUtil.joinClassNames(cls));
        return removeAnnotatedClass(cls);
    }

    @Override
    public synchronized void commitModelExtensions() {
        buildSessionFactory();
		buildSimpleDataSource();
    }

    @Override
    public TransactionAwareDataSourceProxy getDataSourceProxy() {
        return dataSourceProxy;
    }

    @Override
    public SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    @Override
    public ProcessToolContextFactory getProcessToolContextFactory() {
        return processToolContextFactory;
    }

    @Override
    public boolean isJta() {
        return jta;
    }

    private boolean addAnnotatedClass(Class... classes) {
        boolean needUpdate = false;
        for (Class cls : classes) {
            Class annotatedClass = annotatedClasses.get(cls.getName());
            if (annotatedClass == null || !annotatedClass.equals(cls)) {
                needUpdate = true;
                annotatedClasses.put(cls.getName(), cls);
            }
        }
        return needUpdate;
    }

    private boolean removeAnnotatedClass(Class... classes) {
        boolean needUpdate = false;
        for (Class cls : classes) {
            if (annotatedClasses.containsKey(cls.getName())) {
                needUpdate = true;
                annotatedClasses.remove(cls.getName());
            }
        }
        return needUpdate;
    }

    @Override
    public synchronized void addHibernateResource(String name, byte[] resource) {
        hibernateResources.put(name, resource);
    }

    @Override
    public synchronized void removeHibernateResource(String name) {
        hibernateResources.remove(name);
    }

    private void buildSessionFactory() {
        jta = false;
        boolean startJtaTransaction = true;
        String dataSourceName = checkForDataSource();
        UserTransaction ut = dataSourceName != null ? findUserTransaction() : null; //do not even try...

        Configuration configuration = new Configuration().configure();
        for (Class cls : annotatedClasses.values()) {
            configuration.addAnnotatedClass(cls);
        }

        for (String name : hibernateResources.keySet()) {
            byte[] b = hibernateResources.get(name);
            if (b != null && b.length > 0) {
                configuration.addInputStream(new ByteArrayInputStream(b));
            }
        }

        if (dataSourceName == null) {
            logger.severe("Aperte Workflow runs using embedded datasource. This approach is useful only for development and demoing purposes.");
                /*
                
                
                
                
                */
            configuration.setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");
            String url = "jdbc:hsqldb:" + ProcessToolContext.Util.getHomePath() + "/aperteworkflow-hsql";
            configuration.setProperty("hibernate.connection.url", url);
            configuration.setProperty("hibernate.connection.username", "sa");
            configuration.setProperty("hibernate.connection.password", "");
            logger.severe("Configured Aperte Workflow to use Hypersonic DB driver org.hsqldb.jdbcDriver, url: " + url);
        } else {
            logger.info("Configuring Aperte Workflow to use data source: " + dataSourceName);
            configuration.setProperty("hibernate.connection.datasource", dataSourceName);
        }
        String managerLookupClassName = null;
        if (ut != null) { //try to autodetect JTA settings
            logger.warning("UserTransaction found, attempting to autoconfigure Hibernate to use JTA");
            managerLookupClassName = System.getProperty("org.aperteworkflow.hibernate.transaction.manager_lookup_class");
            if (managerLookupClassName == null) {
                try {
                    Class.forName("bitronix.tm.BitronixTransactionManager").getName();
                    managerLookupClassName = "org.hibernate.transaction.BTMTransactionManagerLookup";
                    logger.warning("Found class bitronix.tm.BitronixTransactionManager, Bitronix TM detected!");
                } catch (ClassNotFoundException e) {
                    //nothing, go on.
                }
            }
            if (managerLookupClassName == null) {
                if (System.getProperty("jboss.home.dir") != null) {
                    logger.warning("Found JBoss AS environment, using JBoss Arjuna TM");
                    managerLookupClassName = "org.hibernate.transaction.JBossTransactionManagerLookup";
                    startJtaTransaction = false; //hibernate forces autocommit on transaction update, which throws exception on jboss.
                }
            }
            logger.warning("Configured hibernate.transaction.manager_lookup_class to " + managerLookupClassName);
        }
        if (managerLookupClassName != null) {
            configuration.setProperty("hibernate.transaction.factory_class",
                    nvl(System.getProperty("org.aperteworkflow.hibernate.transaction.factory_class"),
                            "org.hibernate.transaction.JTATransactionFactory")
            );
            configuration.setProperty("hibernate.transaction.manager_lookup_class", managerLookupClassName);
            configuration.setProperty("current_session_context_class", "jta");
            jta = true;
        } else {
            logger.warning("UserTransaction or factory class not found, attempting to autoconfigure Hibernate to use per-Thread session context");
            configuration.setProperty("current_session_context_class", "thread");
        }

        if (startJtaTransaction && ut != null && jta) { //needed for tomcat/bitronix
            try {
                ut.begin();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(new ExtClassLoader(cl));
            sessionFactory = configuration.buildSessionFactory();
        } finally {
            Thread.currentThread().setContextClassLoader(cl);
        }
        if (processToolContextFactory != null) {
            processToolContextFactory.updateSessionFactory(sessionFactory);
        }
        if (startJtaTransaction && ut != null && jta) { //needed for tomcat/bitronix
            try {
                ut.commit();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        if (dataSourceName == null) {
            logger.severe("Aperte Workflow runs using embedded datasource. This approach is useful only for development and demoing purposes.");
        }
    }

    /*
        
     */
    private String checkForDataSource() {
        for (String dsName : getPotentialDatasourceNames()) {
            if (dsName != null) {
                if (isValidDatasource(dsName)) {
                    return dsName;
                } else {
                    logger.info("Aperte Workflow datasource bound to name " + dsName +
                            " not found or is badly configured. Looking for another one");
                }
            }
        }
        logger.log(Level.SEVERE,
                "Aperte Workflow datasource not found or is badly configured, falling back to preconfigured HSQLDB. " +
                        " DO NOT USE THAT IN PRODUCTION ENVIRONMENT!"
        );
        return null;
    }

    private String[] getPotentialDatasourceNames() {
        return new String[]{
                System.getProperty("org.aperteworkflow.datasource"),
                "java:comp/env/jdbc/aperte-workflow-ds",
                "jdbc/aperte-workflow-ds",
        };
    }

    private boolean isValidDatasource(String dsName) {
        try {
            DataSource lookup = (DataSource) new InitialContext().lookup(dsName);
            lookup.getConnection().close();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

	private void buildSimpleDataSource()
    {
        String jndi = nvl("java:comp/env/jdbc/aperte-workflow-ds-simple");

        try {
            InitialContext ic = new InitialContext();
            ic.lookup(jndi);

            dataSourceProxy = new TransactionAwareDataSourceProxy();
            dataSourceProxy.setTargetDataSource((DataSource) ic.lookup(jndi));
        }
        catch (NamingException e) {
            logger.log(Level.SEVERE, "Problem with simple aperte data-source", e);
        }
    }


    private UserTransaction findUserTransaction() {
        UserTransaction ut = null;
        if (!"true".equalsIgnoreCase(System.getProperty("org.aperteworkflow.nojta"))) {
            try {
                ut = (UserTransaction) new InitialContext().lookup("java:comp/UserTransaction");
            } catch (Exception e) {
                logger.warning("java:comp/UserTransaction not found, looking for UserTransaction");
                try {
                    ut = (UserTransaction) new InitialContext().lookup("UserTransaction");
                } catch (Exception e1) {
                    logger.warning("UserTransaction not found in JNDI, JTA not available!");
                }
            }
        } else {
            logger.warning("User transaction lookup disabled via org.aperteworkflow.nojta setting");
        }
        return ut;
    }

    @Override
    public ProcessDictionaryDAO getProcessDictionaryDAO(Session hibernateSession) {
        return new ProcessDictionaryDAOImpl(hibernateSession);
    }

    @Override
    public ProcessInstanceDAO getProcessInstanceDAO(Session hibernateSession) {
        return new ProcessInstanceDAOImpl(hibernateSession);
    }

    @Override
    public ProcessInstanceSimpleAttributeDAO getProcessInstanceSimpleAttributeDAO(
            Session hibernateSession) {
        return new ProcessInstanceSimpleAttributeDAOImpl(hibernateSession);
    }


    @Override
    public UserSubstitutionDAO getUserSubstitutionDAO(Session hibernateSession) {
        return new UserSubstitutionDAOImpl(hibernateSession);
    }

    @Override
    public ProcessDefinitionDAO getProcessDefinitionDAO(Session hibernateSession) {
        return new ProcessDefinitionDAOImpl(hibernateSession);
    }


    @Override
    public synchronized void registerAttributesMapper(Class mapperClass) {
        if (!mapperClass.isAnnotationPresent(AttributesMapper.class))
            throw new RuntimeException("Attributes mapper class should be marked with @AttributesMapper annotation");
        this.attributesMappersClasses.put(mapperClass.getName(), mapperClass);
    }

    @Override
    public synchronized void unregisterAttributesMapper(Class mapperClass) {
        this.attributesMappersClasses.remove(mapperClass.getName());
    }


    @Override
    public List> getAttributesMappers() {
        return new ArrayList>(this.attributesMappersClasses.values());
    }

    @Override
    public List getAttributesMappersFor(final Class clazz) {
        final List mappers = new ArrayList();
        for (Class mapperClass : this.attributesMappersClasses.values()) {
            AttributesMapper annotation = mapperClass.getAnnotation(AttributesMapper.class);
            if (annotation.forAttributeClass().equals(clazz)) {
                try {
                    final IAttributesMapper mapper = mapperClass.newInstance();
                    mappers.add(mapper);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return mappers;
    }

    @Override
    public synchronized void registerMapper(final Class mapperClass) {
        if (!mapperClass.isAnnotationPresent(Mapper.class))
            throw new RuntimeException("Mapper class should be marked with @Mapper annotation");
        this.mappersClasses.put(mapperClass.getName(), mapperClass);
    }

    @Override
    public synchronized void unregisterMapper(final Class mapperClass) {
        this.mappersClasses.remove(mapperClass.getName());
    }

    @Override
    public List getMappersFor(final Class clazz, final String definitionName) {
        final List mappers = new ArrayList();
        for (Class mapperClass : this.mappersClasses.values()) {
            Mapper annotation = mapperClass.getAnnotation(Mapper.class);
            List definitionNames = Arrays.asList(annotation.forDefinitionNames());
            if (annotation.forProviderClass().equals(clazz) && (definitionNames.contains(definitionName) || definitionNames.size() == 0)) {
                try {
                    final IMapper mapper = mapperClass.newInstance();
                    mappers.add(mapper);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
		Collections.sort(mappers, BY_PRIORITY);
        return mappers;
    }

	private static final Comparator BY_PRIORITY = new Comparator() {
		@Override
		public int compare(IMapper m1, IMapper m2) {
			int p1 = m1.getClass().getAnnotation(Mapper.class).priority();
			int p2 = m2.getClass().getAnnotation(Mapper.class).priority();
			return p1 < p2 ? -1 : p1 > p2 ? 1 : 0;
		}
	};

    @Override
    public void addAuditLogHandler(AuditLogHandler handler) {
        auditLogHandlers.add(handler);
    }

    @Override
    public void removeAuditLogHandler(AuditLogHandler handler) {
        auditLogHandlers.remove(handler);
    }

    @Override
    public List getAuditLogHandlers() {
        return Collections.unmodifiableList(auditLogHandlers);
    }

	@Override
	public ExpressionEvaluators getExpressionEvaluators() {
		return expressionEvaluators;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy