org.craftercms.cstudio.alfresco.objectstate.ObjectStateDAOServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.craftercms.cstudio.alfresco.objectstate.ObjectStateDAOServiceImpl.java

Source

/*
 * Copyright (C) 2007-2013 Crafter Software 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, either version 3 of the License, or
 * (at your option) any later version.
 *
 * 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.craftercms.cstudio.alfresco.objectstate;

import com.ibatis.common.jdbc.ScriptRunner;
import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapSession;
import javolution.util.FastList;
import org.apache.commons.lang.StringUtils;
import org.craftercms.cstudio.alfresco.service.api.ObjectStateService;
import org.craftercms.cstudio.alfresco.to.ObjectStateTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ObjectStateDAOServiceImpl implements ObjectStateDAOService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ObjectStateDAOServiceImpl.class);

    protected SqlMapClient _sqlMapClient;

    public SqlMapClient getSqlMapClient() {
        return _sqlMapClient;
    }

    public void setSqlMapClient(SqlMapClient sqlMapClient) {
        this._sqlMapClient = sqlMapClient;
    }

    /**
     * statements 
     */
    private static final String STATEMENT_GET_OBJECT_STATE = "objectState.getObjectState";
    private static final String STATEMENT_GET_OBJECT_STATES = "objectState.getObjectStates";
    private static final String STATEMENT_GET_OBJECT_STATE_BY_STATUS = "objectState.getObjectByStatus";
    private static final String STATEMENT_INSERT_ENTRY = "objectState.insertEntry";
    private static final String STATEMENT_SET_OBJECT_STATE = "objectState.setObjectState";
    private static final String STATEMENT_SET_OBJECT_STATE_BULK = "objectState.setObjectStateBulk";
    private static final String STATEMENT_SET_SYSTEM_PROCESSING = "objectState.setSystemProcessing";
    private static final String STATEMENT_SET_SYSTEM_PROCESSING_BULK = "objectState.setSystemProcessingBulk";
    private static final String STATEMENT_UPDATE_OBJECT_PATH = "objectState.updateObjectPath";
    private static final String STATEMENT_IS_FOLDER_LIVE = "objectState.isFolderLive";
    private static final String STATEMENT_DELETE_OBJECT_STATES_FOR_SITE = "objectState.deleteObjectStatesForSite";
    private static final String STATEMENT_DELETE_OBJECT_STATE = "objectState.deleteObjectState";
    private static final String STATEMENT_DELETE_OBJECT_STATE_FOR_PATH = "objectState.deleteObjectStateForPath";
    private static final String STATEMENT_DELETE_OBJECT_STATE_FOR_PATHS = "objectState.deleteObjectStateForPaths";

    /**
     * table check and creation *
     */
    private static final String STATEMENT_CREATE_TABLE = "objectState.createTable";
    private static final String STATEMENT_CHECK_TABLE_EXISTS = "objectState.checkTableExists";

    /**
     * table indexes *
     */
    private static final String STATEMENT_ADD_OBJECT_IDX = "objectState.addObjectIndex";
    private static final String STATEMENT_CHECK_OBJECT_IDX = "objectState.checkObjectIndex";

    private static final String STATEMENT_CHECK_PATH_SIZE = "objectState.checkPathSize";

    protected String initializeScriptPath;

    public String getInitializeScriptPath() {
        return initializeScriptPath;
    }

    public void setInitializeScriptPath(String initializeScriptPath) {
        this.initializeScriptPath = initializeScriptPath;
    }

    protected int bulkOperationBatchSize = 500;

    public int getBulkOperationBatchSize() {
        return bulkOperationBatchSize;
    }

    public void setBulkOperationBatchSize(int bulkOperationBatchSize) {
        this.bulkOperationBatchSize = bulkOperationBatchSize;
    }

    @Override
    public void initIndexes() {
        DataSource dataSource = _sqlMapClient.getDataSource();
        Connection connection = null;
        int oldval = -1;
        try {
            connection = dataSource.getConnection();
            oldval = connection.getTransactionIsolation();
            if (oldval != Connection.TRANSACTION_READ_COMMITTED) {
                connection.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
            }
            List<HashMap> checkTable = _sqlMapClient.queryForList(STATEMENT_CHECK_TABLE_EXISTS);
            if (checkTable == null || checkTable.size() < 1) {
                ScriptRunner scriptRunner = new ScriptRunner(connection, false, true);
                scriptRunner.runScript(Resources.getResourceAsReader(initializeScriptPath));
            } else {
                Integer checkColumnCTEUsername = (Integer) _sqlMapClient.queryForObject(STATEMENT_CHECK_PATH_SIZE);
                if (checkColumnCTEUsername < 2000) {
                    ScriptRunner scriptRunner = new ScriptRunner(connection, false, true);
                    scriptRunner.runScript(Resources.getResourceAsReader(
                            initializeScriptPath.replace("initialize.sql", "alter_path_column_size.sql")));
                }

            }
            List<HashMap> indexCheckResult = _sqlMapClient.queryForList(STATEMENT_CHECK_OBJECT_IDX);
            if (indexCheckResult == null || indexCheckResult.size() < 1) {
                _sqlMapClient.insert(STATEMENT_ADD_OBJECT_IDX);
            }
            connection.commit();
            if (oldval != -1) {
                connection.setTransactionIsolation(oldval);
            }
        } catch (SQLException e) {
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("Error while initializing Object State table DB indexes.", e);
            }
        } catch (IOException e) {
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("Error while initializing Sequence table DB indexes.", e);
            }
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                }
                connection = null;
            }
        }
    }

    @Override
    public void insertNewObject(String objectId, String site, String path) {
        if (StringUtils.isNotEmpty(objectId)) {
            Long id = null;
            try {
                Map<String, Object> params = new HashMap<String, Object>();
                params.put("object_id", objectId);
                ObjectStateDAO entity = (ObjectStateDAO) _sqlMapClient.queryForObject(STATEMENT_GET_OBJECT_STATE,
                        params);
                if (entity != null) {
                    LOGGER.warn("Object state entry already exists for site " + site + " , path " + path);
                    return;
                }

                ObjectStateDAO entry = new ObjectStateDAO();
                entry.setObjectId(objectId);
                entry.setSite(site);
                entry.setPath(path);
                entry.setState(ObjectStateService.State.NEW_UNPUBLISHED_UNLOCKED.name());
                id = (Long) _sqlMapClient.insert(STATEMENT_INSERT_ENTRY, entry);
                _sqlMapClient.flushDataCache();
            } catch (SQLException e) {
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error(
                            "[" + Thread.currentThread().getName() + "] Error while adding new object state entry "
                                    + "for site " + site + " , " + "path " + path,
                            e);
                }
            }
        } else {
            LOGGER.warn(
                    "Object state can not be inserted for empty object id; site " + site + " ," + " path " + path);
        }
    }

    @Override
    public ObjectStateTO getObjectState(String objectId) {
        try {
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("object_id", objectId);
            ObjectStateDAO entity = (ObjectStateDAO) _sqlMapClient.queryForObject(STATEMENT_GET_OBJECT_STATE,
                    params);
            if (entity != null) {
                return toObjectStateTo(entity);
            } else {
                if (LOGGER.isWarnEnabled()) {
                    LOGGER.warn("[" + Thread.currentThread().getName() + "] Object State is not found for "
                            + objectId + ".");
                }
                return null;
            }
        } catch (SQLException e) {
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("Error while getting a Object State for " + objectId + ".", e);
            }
            return null;
        }
    }

    @Override
    public void setObjectState(String objectId, ObjectStateService.State state) {
        try {
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("object_id", objectId);
            params.put("state", state.name());
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Setting state: " + state.name() + " for object " + objectId);
            }
            _sqlMapClient.update(STATEMENT_SET_OBJECT_STATE, params);
            _sqlMapClient.flushDataCache();
        } catch (SQLException e) {
            if (LOGGER.isErrorEnabled()) {
                // TODO CodeRev: give the state as well
                // TODO CodeRev: ALSO.... how does the caller know this operation failed?!?!
                LOGGER.error("Error while updating state for " + objectId + ".", e);
            }
        }
    }

    @Override
    public void setSystemProcessing(String objectId, boolean isSystemProcessing) {
        try {
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("object_id", objectId);
            params.put("system_processing", isSystemProcessing);
            _sqlMapClient.update(STATEMENT_SET_SYSTEM_PROCESSING, params);
            _sqlMapClient.flushDataCache();
        } catch (SQLException e) {
            if (LOGGER.isErrorEnabled()) {
                // TODO CodeRev: tell me what the value of the flag was
                // TODO CodeRev: ALSO.... how does the caller know this operation failed?!?!
                LOGGER.error("Error while setting system processing for " + objectId + ".", e);
            }
        }
    }

    @Override
    public void setSystemProcessingBulk(List<String> objectIds, boolean isSystemProcessing) {
        if (objectIds != null && !objectIds.isEmpty()) {
            if (objectIds.size() < bulkOperationBatchSize) {
                setSystemProcessingBulkPartial(objectIds, isSystemProcessing);
            } else {
                List<List<String>> partitions = new FastList<List<String>>();
                for (int i = 0; i < objectIds.size();) {
                    partitions.add(objectIds.subList(i, Math.min(i + bulkOperationBatchSize, objectIds.size())));
                    i = (i + 1) * bulkOperationBatchSize;
                }
                for (List<String> part : partitions) {
                    setSystemProcessingBulkPartial(part, isSystemProcessing);
                }
            }
        }
    }

    private void setSystemProcessingBulkPartial(List<String> objectIds, boolean isSystemProcessing) {
        // TODO CodeRev: see comment above, I don't understand what partial is all about
        if (objectIds != null && !objectIds.isEmpty()) {
            try {
                Map<String, Object> params = new HashMap<String, Object>();
                params.put("object_ids", objectIds);
                params.put("system_processing", isSystemProcessing);
                _sqlMapClient.update(STATEMENT_SET_SYSTEM_PROCESSING_BULK, params);
                _sqlMapClient.flushDataCache();
            } catch (SQLException e) {
                // TODO CodeRev: This is really bad, if a partial fails there is no indication to the caller that
                // TODO CodeRev: the failure happened... now what?
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error("Error while setting system processing for package.", e);
                }
            }
        }
    }

    @Override
    public void deleteObjectStatesForSite(String site) {
        try {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Deleting object states for site " + site);
            }
            _sqlMapClient.delete(STATEMENT_DELETE_OBJECT_STATES_FOR_SITE, site);
        } catch (SQLException e) {
            if (LOGGER.isErrorEnabled()) {
                // TODO CodeRev: How does the caller know this operation failed?
                LOGGER.error("Error while deleting object states for site " + site, e);
            }
        }
    }

    @Override
    public void deleteObjectState(String objectId) {
        try {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Deleting object state for " + objectId);
            }
            _sqlMapClient.delete(STATEMENT_DELETE_OBJECT_STATE, objectId);
        } catch (SQLException e) {
            if (LOGGER.isErrorEnabled()) {
                // TODO CodeRev: How does the caller know this operation failed?
                LOGGER.error("Error while deleting object state for " + objectId, e);
            }
        }
    }

    @Override
    public void deleteObjectStatesForPath(String site, String path) {
        try {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Deleting object states for site " + site + ", path " + path);
            }
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("site", site);
            params.put("path", path);
            _sqlMapClient.delete(STATEMENT_DELETE_OBJECT_STATE_FOR_PATH, params);
        } catch (SQLException e) {
            if (LOGGER.isErrorEnabled()) {
                // TODO CodeRev: How does the caller know this operation failed?
                LOGGER.error("Error while deleting object states for site " + site + " path " + path, e);
            }
        }
    }

    @Override
    public void deleteObjectStatesForPaths(String site, List<String> paths) {
        if (paths != null && !paths.isEmpty()) {
            if (paths.size() < bulkOperationBatchSize) {
                deleteObjectStatesForPathsPartial(site, paths);
            } else {
                List<List<String>> partitions = new FastList<List<String>>();
                for (int i = 0; i < paths.size();) {
                    partitions.add(paths.subList(i, Math.min(i + bulkOperationBatchSize, paths.size())));
                    i = (i + 1) * bulkOperationBatchSize;
                }
                for (List<String> part : partitions) {
                    deleteObjectStatesForPathsPartial(site, part);
                }
            }
        }
    }

    private void deleteObjectStatesForPathsPartial(String site, List<String> paths) {
        if (paths != null && !paths.isEmpty()) {
            try {
                Map<String, Object> params = new HashMap<String, Object>();
                params.put("site", site);
                params.put("paths", paths);
                _sqlMapClient.delete(STATEMENT_DELETE_OBJECT_STATE_FOR_PATHS, params);
            } catch (SQLException e) {
                if (LOGGER.isErrorEnabled()) {
                    // TODO CodeRev: How does the caller know this operation failed?
                    LOGGER.error("Error while deleting object states for site " + site, e);
                }
            }
        }
    }

    @Override
    public List<ObjectStateTO> getObjectStateByStates(String site, List<ObjectStateService.State> states) {
        if (states != null && !states.isEmpty()) {
            Map<String, Object> params = new HashMap<String, Object>();
            List<String> statesValues = new ArrayList<String>();
            for (ObjectStateService.State state : states) {
                statesValues.add(state.name());
            }
            params.put("states", statesValues);
            params.put("site", site);
            List<ObjectStateTO> result = new ArrayList<ObjectStateTO>();
            try {
                List<ObjectStateDAO> tmp = _sqlMapClient.queryForList(STATEMENT_GET_OBJECT_STATE_BY_STATUS, params);
                for (ObjectStateDAO objectStateDAO : tmp) {
                    result.add(toObjectStateTo(objectStateDAO));
                }
            } catch (SQLException e) {
                if (LOGGER.isErrorEnabled()) {
                    // TODO CodeRev: How does the caller know this operation failed, what they get is an empty list and
                    // TODO CodeRev: the false notion that an empty list is correct
                    LOGGER.error("Error getting ObjectState Status for " + params.toString(), e);
                }
            }
            return result;
        } else {
            return new FastList<ObjectStateTO>(0);
        }
    }

    @Override
    public void updateObjectPath(String objectId, String newPath) {
        try {
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("object_id", objectId);
            params.put("path", newPath);
            _sqlMapClient.update(STATEMENT_UPDATE_OBJECT_PATH, params);
            _sqlMapClient.flushDataCache();
        } catch (SQLException e) {
            if (LOGGER.isErrorEnabled()) {
                // TODO CodeRev: How does the caller know this operation failed?
                LOGGER.error("Error while setting system processing for " + objectId + ".", e);
            }
        }
    }

    @Override
    public boolean isFolderLive(String site, String path) {
        try {
            Map<String, Object> params = new HashMap<String, Object>();
            params.put("site", site);
            params.put("path", path + "%");
            int result = (Integer) _sqlMapClient.queryForObject(STATEMENT_IS_FOLDER_LIVE, params);
            if (result > 0) {
                return true;
            }
            return false;
        } catch (SQLException e) {
            if (LOGGER.isErrorEnabled()) {
                // TODO CodeRev: How does the caller know this operation failed? Now I get the wrong answer and I have no idea
                LOGGER.error("Error while checking if folder " + path + " in site " + site + " is live.");
            }
            return false;
        }
    }

    @Override
    public List<ObjectStateTO> getObjectStates(List<String> objectIds) {
        if (objectIds != null && !objectIds.isEmpty()) {
            if (objectIds.size() < bulkOperationBatchSize) {
                return getObjectStatesPartial(objectIds);
            } else {
                List<List<String>> partitions = new FastList<List<String>>();
                List<ObjectStateTO> result = new FastList<ObjectStateTO>();
                for (int i = 0; i < objectIds.size();) {
                    partitions.add(objectIds.subList(i, Math.min(i + bulkOperationBatchSize, objectIds.size())));
                    i = (i + 1) * bulkOperationBatchSize;
                }
                for (List<String> part : partitions) {
                    result.addAll(getObjectStatesPartial(part));
                }
                return result;
            }
        } else {
            return new FastList<ObjectStateTO>(0);
        }
    }

    private List<ObjectStateTO> getObjectStatesPartial(List<String> objectIds) {
        // TODO CodeRev: here we return null when there is an error.. BUT in the method above... 
        // TODO CodeRev we never check for null which will surely lead to an NPE
        if (objectIds != null && !objectIds.isEmpty()) {
            try {
                Map<String, Object> params = new HashMap<String, Object>();
                params.put("object_ids", objectIds);
                List<ObjectStateDAO> entities = (List<ObjectStateDAO>) _sqlMapClient
                        .queryForList(STATEMENT_GET_OBJECT_STATES, params);
                if (entities != null) {
                    return toObjectStateTo(entities);
                } else {
                    if (LOGGER.isWarnEnabled()) {
                        LOGGER.warn("Object State is not found for " + objectIds + ".");
                    }
                    return null;
                }
            } catch (SQLException e) {
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error("Error while getting a Object State for " + objectIds + ".", e);
                }
                return null;
            }
        } else {
            return new FastList<ObjectStateTO>(0);
        }
    }

    @Override
    public void setObjectStateBulk(List<String> objectIds, ObjectStateService.State state) {
        if (objectIds != null && !objectIds.isEmpty()) {
            if (objectIds.size() < bulkOperationBatchSize) {
                setObjectStateBulkPartial(objectIds, state);
            } else {
                List<List<String>> partitions = new FastList<List<String>>();
                for (int i = 0; i < objectIds.size();) {
                    partitions.add(objectIds.subList(i, Math.min(i + bulkOperationBatchSize, objectIds.size())));
                    i = (i + 1) * bulkOperationBatchSize;
                }
                for (List<String> part : partitions) {
                    setObjectStateBulkPartial(part, state);
                }
            }
        }
    }

    private void setObjectStateBulkPartial(List<String> objectIds, ObjectStateService.State state) {
        // TODO CodeRev: previous comment about batches applies here
        if (objectIds != null && !objectIds.isEmpty()) {
            try {
                Map<String, Object> params = new HashMap<String, Object>();
                params.put("object_ids", objectIds);
                params.put("state", state.name());
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("Setting state: " + state.name() + " for objects " + objectIds);
                }
                _sqlMapClient.update(STATEMENT_SET_OBJECT_STATE_BULK, params);
                _sqlMapClient.flushDataCache();
            } catch (SQLException e) {
                if (LOGGER.isErrorEnabled()) {
                    LOGGER.error("Error while updating state for " + objectIds + ".", e);
                }
            }
        }
    }

    private ObjectStateTO toObjectStateTo(ObjectStateDAO objectStateDAO) {
        // TODO CodeRev: One issue I see here is that this object looks like it direct maps to the other object except
        // TODO CodeRev: except for some slight name differences... but now we have an O(n) iteration just to change names
        ObjectStateTO objectStateTO = new ObjectStateTO();
        objectStateTO.setObjectId(objectStateDAO.getObjectId());
        objectStateTO.setSite(objectStateDAO.getSite());
        objectStateTO.setPath(objectStateDAO.getPath());
        objectStateTO.setState(ObjectStateService.State.valueOf(objectStateDAO.getState()));
        objectStateTO.setSystemProcessing(objectStateDAO.isSystemProcessing());
        return objectStateTO;
    }

    private List<ObjectStateTO> toObjectStateTo(List<ObjectStateDAO> objectStateDAOs) {
        List<ObjectStateTO> toRet = new FastList<ObjectStateTO>(objectStateDAOs.size());
        for (ObjectStateDAO state : objectStateDAOs) {
            toRet.add(toObjectStateTo(state));
        }
        return toRet;
    }

    // TODO CodeRev: Also, I personally do not care much for private methods because they cannot be extended.  How about protected?
}