/** * Copyright 1996-2014 FoxBPM ORG. * * 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. * * @author kenshin * @author ych */ package org.foxbpm.engine.impl.persistence; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.foxbpm.engine.db.HasRevision; import org.foxbpm.engine.db.PersistentObject; import org.foxbpm.engine.impl.Context; import org.foxbpm.engine.impl.interceptor.Session; import org.foxbpm.engine.impl.util.ExceptionUtil; import org.foxbpm.engine.sqlsession.ISqlSession; import org.foxbpm.engine.sqlsession.StatementMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 持久化管理器抽象类 * * @author kenshin */ public abstract class AbstractManager implements Session { public static Logger log = LoggerFactory.getLogger(AbstractManager.class); protected Map<String, CachedObject> cachedObjects = new HashMap<String, CachedObject>(); protected List<PersistentObject> insertedObjects = new ArrayList<PersistentObject>(); protected List<DeleteOperation> deleteOperations = new ArrayList<DeleteOperation>(); private ISqlSession getSqlSession() { return getSession(ISqlSession.class); } protected <T> T getSession(Class<T> sessionClass) { return (T)Context.getCommandContext().getSession(sessionClass); } public void insert(PersistentObject persistentObject) { insertedObjects.add(persistentObject); cachePut(persistentObject, false); } public void update(PersistentObject persistentObject) { cachePut(persistentObject, false); } @SuppressWarnings({"rawtypes", "unchecked"}) public List selectList(String statement, Object parameter) { List resultList = null; try{ resultList = getSqlSession().selectList(statement, parameter); }catch(Exception ex){ throw ExceptionUtil.getException("10209001",ex,statement); } return filterLoadedObjects(resultList); } public Object selectOne(String statement, Object parameter) { Object result = null; try{ result = getSqlSession().selectOne(statement, parameter); }catch(Exception ex){ throw ExceptionUtil.getException("10209001",ex,statement); } if (result instanceof PersistentObject) { PersistentObject loadedObject = (PersistentObject) result; result = cacheFilter(loadedObject); } return result; } @SuppressWarnings("unchecked") public <T extends PersistentObject> T selectById(Class<T> entityClass, String id) { T persistentObject = (T)cacheGet(id); if (persistentObject != null) { return (T)persistentObject; } String selectStatement = StatementMap.getSelectStatement(entityClass); if(selectStatement == null){ throw ExceptionUtil.getException("10202004",entityClass.getName()); } persistentObject = (T) getSqlSession().selectOne(selectStatement, id); if (persistentObject == null) { return null; } cachePut(persistentObject, true); return (T)persistentObject; } protected DeploymentEntityManager getDeploymentManager() { return getSession(DeploymentEntityManager.class); } protected ResourceManager getResourceManager() { return getSession(ResourceManager.class); } protected ProcessDefinitionManager getProcessDefinitionManager() { return getSession(ProcessDefinitionManager.class); } protected TaskManager getTaskManager() { return getSession(TaskManager.class); } protected IdentityLinkManager getIdentityLinkManager() { return getSession(IdentityLinkManager.class); } protected VariableManager getVariableManager() { return getSession(VariableManager.class); } protected TokenManager getTokenManager() { return getSession(TokenManager.class); } /** * * 获取运行轨迹MANAGER * * @return RunningTrackManager * @exception * @since 1.0.0 */ protected RunningTrackManager getRunningTrackManager() { return getSession(RunningTrackManager.class); } /** * * 获取调度器MANAGER * * @return SchedulerManager * @exception * @since 1.0.0 */ protected SchedulerManager getSchedulerManager() { return getSession(SchedulerManager.class); } protected ProcessInstanceManager getProcessInstanceManager() { return getSession(ProcessInstanceManager.class); } public void flush() { // if (cachedObjects.isEmpty()) { // return; // } removeUnnecessaryOperations(); List<PersistentObject> updatedObjects = getUpdatedObjects(); if (log.isDebugEnabled()) { log.debug("{}:flush summary: {} insert, {} update,{} delete.", this.getClass(), insertedObjects.size(), updatedObjects.size(), deleteOperations.size()); log.debug("now executing flush..."); } flushInserts(); flushDeletes(); flushUpdates(updatedObjects); } public void close() { } public void flushUpdates(List<PersistentObject> updateObjects) { int affectedRow; for (PersistentObject updateObject : updateObjects) { String updateStatement = StatementMap.getUpdateStatement(updateObject); if (updateStatement == null) { throw ExceptionUtil.getException("10202003",updateObject.getClass().getName()); } log.debug("updating: {}", updateObject); try{ affectedRow = getSqlSession().update(updateStatement, updateObject); }catch(Exception ex){ throw ExceptionUtil.getException("10209001",ex,updateStatement); } // 并发处理 if (updateObject instanceof HasRevision) { if (affectedRow != 1) { throw ExceptionUtil.getException("10211001",updateObject.getClass().getName(),updateObject.getId()); } ((HasRevision) updateObject).setRevision(((HasRevision) updateObject).getRevisionNext()); } } updateObjects.clear(); } public void flushInserts() { for (PersistentObject insertedObject : insertedObjects) { String insertStatement = StatementMap.getInsertStatement(insertedObject); if (insertStatement == null) { throw ExceptionUtil.getException("10202001",insertedObject.getClass().getName()); } log.debug("inserting: {}", insertedObject); try{ getSqlSession().insert(insertStatement, insertedObject); }catch(Exception ex){ throw ExceptionUtil.getException("10209001",ex,insertStatement); } // 并发处理 if (insertedObject instanceof HasRevision) { ((HasRevision) insertedObject).setRevision(((HasRevision) insertedObject).getRevisionNext()); } } insertedObjects.clear(); } protected void flushDeletes() { for (DeleteOperation delete : deleteOperations) { log.debug("executing: {}", delete); delete.execute(); } deleteOperations.clear(); } // delete // /////////////////////////////////////////////////////////////////// public void delete(String statement, Object parameter) { deleteOperations.add(new BulkDeleteOperation(statement, parameter)); } public void delete(PersistentObject persistentObject) { for (DeleteOperation deleteOperation : deleteOperations) { if (deleteOperation.sameIdentity(persistentObject)) { log.debug("skipping redundant delete: {}", persistentObject); return; // Skip this delete. It was already added. } } deleteOperations.add(new CheckedDeleteOperation(persistentObject)); } public void removeUnnecessaryOperations() { // 如果对象既在insert中,又在delete中,则直接删除,不做处理 for (Iterator<DeleteOperation> deleteIt = deleteOperations.iterator(); deleteIt.hasNext();) { DeleteOperation deleteOperation = deleteIt.next(); for (Iterator<PersistentObject> insertIt = insertedObjects.iterator(); insertIt.hasNext();) { PersistentObject insertedObject = insertIt.next(); if (deleteOperation.sameIdentity(insertedObject)) { insertIt.remove(); deleteIt.remove(); } } deleteOperation.clearCache(); } for (PersistentObject insertedObject : insertedObjects) { cacheRemove(insertedObject.getId()); } } public static class CachedObject { protected PersistentObject persistentObject; protected Object persistentObjectState; public CachedObject(PersistentObject persistentObject, boolean storeState) { this.persistentObject = persistentObject; if (storeState) { this.persistentObjectState = persistentObject.getPersistentState(); } } public PersistentObject getPersistentObject() { return persistentObject; } public Object getPersistentObjectState() { return persistentObjectState; } } public List<PersistentObject> getUpdatedObjects() { List<PersistentObject> updatedObjects = new ArrayList<PersistentObject>(); for (CachedObject cachedObject : cachedObjects.values()) { PersistentObject persistentObject = cachedObject.getPersistentObject(); if (!isPersistentObjectDeleted(persistentObject)) { Object originalState = cachedObject.getPersistentObjectState(); if (!persistentObject.getPersistentState().equals(originalState)) { updatedObjects.add(persistentObject); } else { log.trace("loaded object '{}' was not updated", persistentObject); } } } return updatedObjects; } protected boolean isPersistentObjectDeleted(PersistentObject persistentObject) { for (DeleteOperation deleteOperation : deleteOperations) { if (deleteOperation.sameIdentity(persistentObject)) { return true; } } return false; } protected CachedObject cachePut(PersistentObject persistentObject, boolean storeState) { CachedObject cachedObject = new CachedObject(persistentObject, storeState); cachedObjects.put(persistentObject.getId(), cachedObject); return cachedObject; } /** * returns the object in the cache. if this object was loaded before, then * the original object is returned. if this is the first time this object is * loaded, then the loadedObject is added to the cache. */ protected PersistentObject cacheFilter(PersistentObject persistentObject) { PersistentObject cachedPersistentObject = cacheGet(persistentObject.getId()); if (cachedPersistentObject != null) { return cachedPersistentObject; } cachePut(persistentObject, true); return persistentObject; } @SuppressWarnings("unchecked") protected <T> T cacheGet(String id) { CachedObject cachedObject = null; cachedObject = cachedObjects.get(id); if (cachedObject != null) { return (T) cachedObject.getPersistentObject(); } return null; } protected void cacheRemove(String persistentObjectId) { cachedObjects.remove(persistentObjectId); } @SuppressWarnings("unchecked") public <T> List<T> findInCache(Class<T> entityClass) { List<T> entities = new ArrayList<T>(cachedObjects.size()); for (CachedObject cachedObject : cachedObjects.values()) { entities.add((T) cachedObject.getPersistentObject()); } return (List<T>)entities; } public interface DeleteOperation { boolean sameIdentity(PersistentObject other); void clearCache(); void execute(); } /** * Use this {@link DeleteOperation} to execute a dedicated delete statement. * It is important to note there won't be any optimistic locking checks done * for these kind of delete operations! * * For example, a usage of this operation would be to delete all variables * for a certain execution, when that certain execution is removed. The * optimistic locking happens on the execution, but the variables can be * removed by a simple 'delete from var_table where execution_id is xxx'. It * could very well be there are no variables, which would also work with * this query, but not with the regular {@link CheckedDeleteOperation}. */ public class BulkDeleteOperation implements DeleteOperation { private String statement; private Object parameter; public BulkDeleteOperation(String statement, Object parameter) { this.statement = statement; this.parameter = parameter; } public boolean sameIdentity(PersistentObject other) { // this implementation is unable to determine what the identity of // the removed object(s) will be. return false; } public void clearCache() { // this implementation cannot clear the object(s) to be removed from // the cache. } public void execute() { try{ getSqlSession().delete(statement, parameter); }catch(Exception ex){ throw ExceptionUtil.getException("10209001",ex,statement); } } public String toString() { return "bulk delete: " + statement + "(" + parameter + ")"; } } /** * A {@link DeleteOperation} that checks for concurrent modifications if the * persistent object implements {@link HasRevision}. That is, it employs * optimisting concurrency control. Used when the persistent object has been * fetched already. */ public class CheckedDeleteOperation implements DeleteOperation { protected final PersistentObject persistentObject; public CheckedDeleteOperation(PersistentObject persistentObject) { this.persistentObject = persistentObject; } public boolean sameIdentity(PersistentObject other) { return persistentObject.getClass().equals(other.getClass()) && persistentObject.getId().equals(other.getId()); } public void clearCache() { cacheRemove(persistentObject.getId()); } public void execute() { String deleteStatement = StatementMap.getDeleteStatement(persistentObject); if (deleteStatement == null) { throw ExceptionUtil.getException("10202002",persistentObject.getClass().getName()); } getSqlSession().delete(deleteStatement, persistentObject); } public PersistentObject getPersistentObject() { return persistentObject; } public String toString() { return "delete " + persistentObject; } } protected List<?> filterLoadedObjects(List<Object> loadedObjects) { if (loadedObjects.isEmpty()) { return loadedObjects; } if (!(loadedObjects.get(0) instanceof PersistentObject)) { return loadedObjects; } List<PersistentObject> filteredObjects = new ArrayList<PersistentObject>(loadedObjects.size()); for (Object loadedObject : loadedObjects) { PersistentObject cachedPersistentObject = cacheFilter((PersistentObject) loadedObject); filteredObjects.add(cachedPersistentObject); } return filteredObjects; } public void beforeFlush() { } }