com.clustercontrol.commons.util.JpaTransactionManager.java Source code

Java tutorial

Introduction

Here is the source code for com.clustercontrol.commons.util.JpaTransactionManager.java

Source

/*
    
Copyright (C) 2012 NTT DATA Corporation
    
This program is free software; you can redistribute it and/or
Modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, version 2.
    
This program 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 General Public License for more details.
    
 */

package com.clustercontrol.commons.util;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityExistsException;
import javax.persistence.EntityTransaction;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.clustercontrol.accesscontrol.bean.PrivilegeConstant.ObjectPrivilegeMode;
import com.clustercontrol.accesscontrol.util.ObjectPrivilegeCallback;
import com.clustercontrol.fault.HinemosUnknown;

/*
 * JPA?
 */
public class JpaTransactionManager {

    private static Log m_log = LogFactory.getLog(JpaTransactionManager.class);

    public final static String EM = "entityManager";
    public final static String CALLBACKS = "callbackImpl";
    public final static String IS_CALLBACKED = "isCallbacked";

    // HinemosEntityManager
    private HinemosEntityManager em = null;
    // HinemosEntityManager??????
    private boolean nestedEm = false;
    // EntityTransaction
    private EntityTransaction tx = null;
    // EntityTransaction??????
    private boolean nestedTx = false;

    /**
     * 
     */
    public JpaTransactionManager() {
        // EntityManager?
        em = (HinemosEntityManager) HinemosSessionContext.instance().getProperty(EM);
        if (em == null) {
            em = new HinemosEntityManager();
            em.setEntityManager(JpaPersistenceConfig.getHinemosEMFactory().createEntityManager());
            HinemosSessionContext.instance().setProperty(EM, em);

            clearCallbacks();
            unsetCallbacked();
        } else {
            nestedEm = true;
        }
    }

    /**
     * 
     * 
     * @param abortIfTxBegined true????????Exception
     */
    public void begin(boolean abortIfTxBegined) throws HinemosUnknown {
        // EntityTransaction
        tx = em.getTransaction();
        nestedTx = tx.isActive();

        if (!nestedTx) {
            addCallback(new ObjectPrivilegeCallback());

            if (!isCallbacked()) {
                List<JpaTransactionCallback> callbacks = getCallbacks();
                for (JpaTransactionCallback callback : callbacks) {
                    if (m_log.isDebugEnabled()) {
                        m_log.debug("executing callback preBegin : " + callback.getClass().getName());
                    }
                    try {
                        setCallbacked();

                        callback.preBegin();
                        callback.getClass().getMethod("preBegin").invoke(callback);
                    } catch (Throwable t) {
                        m_log.warn("callback execution failure : " + callback.getClass().getName(), t);
                    } finally {
                        unsetCallbacked();
                    }
                }
            }

            tx.begin();

            if (!isCallbacked()) {
                List<JpaTransactionCallback> callbacks = getCallbacks();
                for (JpaTransactionCallback callback : callbacks) {
                    if (m_log.isDebugEnabled()) {
                        m_log.debug("executing callback postBegin : " + callback.getClass().getName());
                    }
                    try {
                        setCallbacked();

                        callback.postBegin();
                    } catch (Throwable t) {
                        m_log.warn("callback execution failure : " + callback.getClass().getName(), t);
                    } finally {
                        unsetCallbacked();
                    }
                }
            }
        } else {
            if (abortIfTxBegined) {
                HinemosUnknown e = new HinemosUnknown("transaction has already started.");
                m_log.info("begin() : " + e.getClass().getSimpleName() + ", " + e.getMessage());
                throw e;
            }
        }
    }

    /**
     * 
     * (????)
     */
    public void begin() {
        try {
            begin(false);
        } catch (HinemosUnknown e) {
            m_log.debug(e.getMessage(), e);
        }
    }

    /**
     * ?
     * 
     * @param isGetTransaction trueEntityManager??
     */
    public void commit(boolean isGetTransaction) {
        if (isGetTransaction) {
            tx = em.getTransaction();
        }
        if (!nestedTx) {
            if (!isCallbacked()) {
                List<JpaTransactionCallback> callbacks = getCallbacks();
                for (JpaTransactionCallback callback : callbacks) {
                    if (m_log.isDebugEnabled()) {
                        m_log.debug("executing callback preCommit : " + callback.getClass().getName());
                    }
                    try {
                        setCallbacked();

                        callback.preCommit();
                    } catch (Throwable t) {
                        m_log.warn("callback execution failure : " + callback.getClass().getName());
                        throw t;
                    } finally {
                        unsetCallbacked();
                    }
                }
            }

            tx.commit();

            if (!isCallbacked()) {
                List<JpaTransactionCallback> callbacks = getCallbacks();
                for (JpaTransactionCallback callback : callbacks) {
                    if (m_log.isDebugEnabled()) {
                        m_log.debug("executing callback postCommit : " + callback.getClass().getName());
                    }
                    try {
                        setCallbacked();

                        callback.postCommit();
                    } catch (Throwable t) {
                        m_log.warn("callback execution failure : " + callback.getClass().getName());
                        throw t;
                    } finally {
                        unsetCallbacked();
                    }
                }
            }
        }
    }

    /**
     * ?
     * begin()?????
     */
    public void commit() {
        commit(false);
    }

    /**
     * ?
     * (JPA????????????INSERT -> DELETE????????
     * INSERT(persist) -> flush -> DELETE(remove)????????
     * flush?????DELETE -> INSERT???????SQLException?????
     */
    public void flush() {
        if (!isCallbacked()) {
            List<JpaTransactionCallback> callbacks = getCallbacks();
            for (JpaTransactionCallback callback : callbacks) {
                if (m_log.isDebugEnabled()) {
                    m_log.debug("executing callback preFlush : " + callback.getClass().getName());
                }
                try {
                    setCallbacked();

                    callback.preFlush();
                } catch (Throwable t) {
                    m_log.warn("callback execution failure : " + callback.getClass().getName(), t);
                } finally {
                    unsetCallbacked();
                }
            }
        }

        em.flush();

        if (!isCallbacked()) {
            List<JpaTransactionCallback> callbacks = getCallbacks();
            for (JpaTransactionCallback callback : callbacks) {
                if (m_log.isDebugEnabled()) {
                    m_log.debug("executing callback postFlush : " + callback.getClass().getName());
                }
                try {
                    setCallbacked();

                    callback.postFlush();
                } catch (Throwable t) {
                    m_log.warn("callback execution failure : " + callback.getClass().getName(), t);
                } finally {
                    unsetCallbacked();
                }
            }
        }
    }

    /**
     * ??
     */
    public void rollback() {
        if (!nestedTx) {
            if (tx != null && tx.isActive()) {
                m_log.debug("session is rollback.");

                if (!isCallbacked()) {
                    List<JpaTransactionCallback> callbacks = getCallbacks();
                    for (JpaTransactionCallback callback : callbacks) {
                        if (m_log.isDebugEnabled()) {
                            m_log.debug("executing callback preRollback : " + callback.getClass().getName());
                        }
                        try {
                            setCallbacked();

                            callback.preRollback();
                        } catch (Throwable t) {
                            m_log.warn("callback execution failure : " + callback.getClass().getName(), t);
                        } finally {
                            unsetCallbacked();
                        }
                    }
                }

                tx.rollback();

                if (!isCallbacked()) {
                    List<JpaTransactionCallback> callbacks = getCallbacks();
                    for (JpaTransactionCallback callback : callbacks) {
                        if (m_log.isDebugEnabled()) {
                            m_log.debug("executing callback postRollback : " + callback.getClass().getName());
                        }
                        try {
                            setCallbacked();

                            callback.postRollback();
                        } catch (Throwable t) {
                            m_log.warn("callback execution failure : " + callback.getClass().getName(), t);
                        } finally {
                            unsetCallbacked();
                        }
                    }
                }
            }
        }
    }

    /**
     * ?
     */
    public void close() {
        if (!nestedEm && em != null) {
            if (em.isOpen()) {
                try {
                    List<JpaTransactionCallback> callbacks = getCallbacks();

                    if (!isCallbacked()) {
                        for (JpaTransactionCallback callback : callbacks) {
                            if (m_log.isDebugEnabled()) {
                                m_log.debug("executing callback preClose : " + callback.getClass().getName());
                            }
                            try {
                                setCallbacked();

                                callback.preClose();
                            } catch (Throwable t) {
                                m_log.warn("callback execution failure : " + callback.getClass().getName(), t);
                            } finally {
                                unsetCallbacked();
                            }
                        }
                    }

                    // commit or rollback???close?????????(rollback)?
                    // ???connection????????
                    EntityTransaction tx = em.getTransaction();
                    if (tx.isActive()) {
                        if (m_log.isDebugEnabled()) {
                            StackTraceElement[] eList = Thread.currentThread().getStackTrace();
                            StringBuilder trace = new StringBuilder();
                            for (StackTraceElement e : eList) {
                                if (trace.length() > 0) {
                                    trace.append("\n");
                                }
                                trace.append(String.format("%s.%s(%s:%d)", e.getClassName(), e.getMethodName(),
                                        e.getFileName(), e.getLineNumber()));
                            }
                            m_log.debug(
                                    "closing uncompleted transaction. this transaction will be rollbacked before closing : "
                                            + trace);
                        }
                        tx.rollback();
                    }

                    em.close();
                    HinemosSessionContext.instance().setProperty(JpaTransactionManager.EM, null);

                    // postClose???innerTransaction????????callback???
                    for (JpaTransactionCallback callback : callbacks) {
                        if (m_log.isDebugEnabled()) {
                            m_log.debug("executing callback postClose : " + callback.getClass().getName());
                        }
                        try {
                            callback.postClose();
                        } catch (Throwable t) {
                            m_log.warn("callback execution failure : " + callback.getClass().getName(), t);
                        }
                    }
                } finally {
                    HinemosSessionContext.instance().setProperty(JpaTransactionManager.EM, null);
                }
            }
            HinemosSessionContext.instance().setProperty(EM, null);
        }
    }

    /**
     * EntityManager?
     */
    public HinemosEntityManager getEntityManager() {
        return em;
    }

    /**
     * ??????
     */
    public boolean isNestedEm() {
        return nestedEm;
    }

    /**
     * ??
     *   ????EntityExistsException
     * 
     *  Eclipselink(2.4.1?)???persist()?EntityExistsException?
     *  ??????????
     * 
     *  Eclipselink(2.4.1?)???In-memory?Cascade.remove???
     *  DB??????????
     *
     * @param clazz ?Entity
     * @param primaryKey ?PrimaryKey
     * @throws EntityExistsException
     */
    public <T> void checkEntityExists(Class<T> clazz, Object primaryKey) throws EntityExistsException {
        String strPk = "";
        if (primaryKey instanceof String) {
            strPk = "primaryKey = " + primaryKey;
        } else {
            strPk = primaryKey.toString();
        }
        Object obj = em.find(clazz, primaryKey, JpaPersistenceConfig.JPA_EXISTS_CHECK_HINT_MAP,
                ObjectPrivilegeMode.NONE);
        if (obj != null) {
            // ?
            EntityExistsException e = new EntityExistsException(clazz.getSimpleName() + ", " + strPk);
            throw e;
        }
    }

    /**
     * API??????callback??<br />
     * @return callback?
     */
    @SuppressWarnings("unchecked")
    private List<JpaTransactionCallback> getCallbacks() {
        if (em.getProperties().containsKey(CALLBACKS)) {
            return new ArrayList<JpaTransactionCallback>(
                    (List<JpaTransactionCallback>) em.getProperties().get(CALLBACKS));
        } else {
            return new ArrayList<JpaTransactionCallback>();
        }
    }

    /**
     * EntityManager?callback??<br/>
     */
    private void clearCallbacks() {
        em.setProperty(IS_CALLBACKED, new ArrayList<JpaTransactionCallback>());
    }

    /**
     * ?????callback???????<br/>
     * callback??DB??callback???????<br/>
     */
    private void setCallbacked() {
        em.setProperty(IS_CALLBACKED, Boolean.TRUE);
    }

    /**
     * ?????callback?????<br/>
     */
    private void unsetCallbacked() {
        em.setProperty(IS_CALLBACKED, Boolean.FALSE);
    }

    /**
     * ??????callback???????<br/>
     * @return callback????true, ???false
     */
    private boolean isCallbacked() {
        if (em.getProperties().containsKey(IS_CALLBACKED)) {
            return (Boolean) em.getProperties().get(IS_CALLBACKED);
        } else {
            return false;
        }
    }

    /**
     * API??????callback?<br />
     * ?????callback??<br />
     * ???????????callback????????callback????<br />
     * 
     * @param callback ?callback
     */
    public void addCallback(JpaTransactionCallback callback) {
        if (callback == null) {
            m_log.warn("skipped callback addition : null");
            return;
        }
        for (JpaTransactionCallback obj : getCallbacks()) {
            if (callback.equals(obj)) {
                if (m_log.isDebugEnabled()) {
                    m_log.debug("skipped callback addition : " + callback.getClass().getName());
                }
                return;
            }
        }

        List<JpaTransactionCallback> callbacks = getCallbacks();
        callbacks.add(callback);
        if (m_log.isDebugEnabled()) {
            m_log.debug("adding callback : " + callback.getClass().getName());
        }
        em.setProperty(CALLBACKS, callbacks);
    }

    public int sizeCallback() {
        return getCallbacks().size();
    }

}