com.silverpeas.workflow.engine.model.ProcessModelManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.silverpeas.workflow.engine.model.ProcessModelManagerImpl.java

Source

/**
 * Copyright (C) 2000 - 2013 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception.  You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "http://www.silverpeas.org/docs/core/legal/floss_exception.html"
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.silverpeas.workflow.engine.model;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

import com.stratelia.webactiv.beans.admin.AdminReference;

import org.apache.commons.io.FileUtils;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.mapping.MappingException;
import org.exolab.castor.xml.MarshalException;
import org.exolab.castor.xml.Marshaller;
import org.exolab.castor.xml.Unmarshaller;
import org.exolab.castor.xml.ValidationException;
import org.xml.sax.InputSource;

import com.silverpeas.form.FormException;
import com.silverpeas.form.RecordTemplate;
import com.silverpeas.form.record.GenericRecordSetManager;
import com.silverpeas.util.FileUtil;
import com.silverpeas.workflow.api.ProcessModelManager;
import com.silverpeas.workflow.api.WorkflowException;
import com.silverpeas.workflow.api.model.DataFolder;
import com.silverpeas.workflow.api.model.Form;
import com.silverpeas.workflow.api.model.Forms;
import com.silverpeas.workflow.api.model.ProcessModel;
import com.stratelia.silverpeas.silvertrace.SilverTrace;
import com.silverpeas.admin.components.ComponentsInstanciatorIntf;
import com.stratelia.webactiv.util.DBUtil;
import com.stratelia.webactiv.util.JNDINames;
import com.stratelia.webactiv.util.ResourceLocator;
import com.stratelia.webactiv.util.exception.UtilException;
import com.stratelia.webactiv.util.fileFolder.FileFolderManager;

/**
 * A ProcessModelManager implementation
 */
public class ProcessModelManagerImpl implements ProcessModelManager {

    private static final String selectQuery = "select distinct modelId from SB_Workflow_ProcessInstance";

    /**
     * ResourceLocator object to retrieve messages in a properties file
     */
    private static ResourceLocator settings = new ResourceLocator("com.silverpeas.workflow.engine.castorSettings",
            "fr");

    /**
     * The map (modelId -> cached process model).
     */
    private final Map<String, ProcessModel> models = new HashMap<String, ProcessModel>();
    private String dbName = JNDINames.WORKFLOW_DATASOURCE;

    /**
     * Default constructor
     */
    public ProcessModelManagerImpl() {
    }

    /**
     * @see com.silverpeas.workflow.api.ProcessModelManager#listProcessModels()
     */
    @Override
    public List<String> listProcessModels() throws WorkflowException {
        try {
            // Recursively search all subdirs for .xml files
            //
            return findProcessModels(getProcessModelDir());
        } catch (UtilException e) {
            throw new WorkflowException("WorkflowManager.getProcessModels",
                    "WorkflowEngine.EX_GETTING_RPOCES_MODELS_FAILED", "Workflow Dir : " + getProcessModelDir(), e);
        } catch (IOException e) {
            throw new WorkflowException("WorkflowManager.getProcessModels",
                    "WorkflowEngine.EX_GETTING_RPOCES_MODELS_FAILED", "Workflow Dir : " + getProcessModelDir(), e);
        }
    }

    /**
     * Recursive method to retrieve all process models in and below the given directory
     * @param processModelDir the directory to start with
     * @return a list of strings containing the relative path and file name of the model
     * @throws UtilException
     * @throws IOException
     */
    private List<String> findProcessModels(String strProcessModelDir) throws UtilException, IOException {
        Iterator<File> subFoldersIterator = FileFolderManager.getAllSubFolder(strProcessModelDir).iterator();
        Iterator<String> subFolderModelsIterator;
        Iterator<File> currentDirModelsIterator = FileFolderManager.getAllFile(strProcessModelDir).iterator();
        List<String> processModels = new ArrayList<String>();
        File subFolder;

        while (subFoldersIterator.hasNext()) {
            subFolder = subFoldersIterator.next();
            // Get models from subfolders
            subFolderModelsIterator = findProcessModels(subFolder.getCanonicalPath()).iterator();

            // prepend their names with the name of this folder
            while (subFolderModelsIterator.hasNext()) {
                processModels.add(subFolder.getName() + File.separatorChar + subFolderModelsIterator.next());
            }
        }
        // Get models from the current folder, do not prepend names
        while (currentDirModelsIterator.hasNext()) {
            String name = currentDirModelsIterator.next().getName();

            if (name.endsWith(".xml")) {
                processModels.add(name);
            }
        }
        // Return the list of process models
        return processModels;
    }

    /**
     * Get a ProcessModel from its modelId. Retrieves the xml descriptor filename from the model Id
     * and load abstract process model information in ProcessModel object
     * @param modelId model id
     * @return ProcessModel object
     */
    @Override
    public ProcessModel getProcessModel(String modelId) throws WorkflowException {
        // Search the processModel in the process model cache.
        ProcessModel cachedModel = getCachedProcessModel(modelId);
        if (cachedModel != null) {
            return cachedModel;
        }

        // The model is not cached, we must build it.
        String fileName = AdminReference.getAdminService().getComponentParameterValue(modelId,
                ComponentsInstanciatorIntf.PROCESS_XML_FILE_NAME);

        // if file name not found, throw exception
        if (fileName == null) {
            throw new WorkflowException("ProcessModelManagerImpl.getProcessModel",
                    "workflowEngine.EX_NO_XML_FILENAME_FOUND", "model/peas id : " + modelId);
        }

        // load the process model from its xml descriptor
        ProcessModelImpl model = (ProcessModelImpl) this.loadProcessModel(fileName, false);

        // set the peas id
        model.setModelId(modelId);

        // cache the model.
        cacheProcessModel(modelId, model, fileName);

        // return the process model
        return (ProcessModel) model;
    }

    /*
     * (non-Javadoc)
     * @see com.silverpeas.workflow.api.ProcessModelManager#createProcessModelDescriptor ()
     */
    @Override
    public ProcessModel createProcessModelDescriptor() throws WorkflowException {
        return new ProcessModelImpl();
    }

    /**
     * Create a ProcessModel from xml descriptor filename. Generate an id for this model and load
     * abstract process model information in ProcessModel object
     * @param processFileName xml descriptor filename.
     * @param peasId Id of processManager instance (peas).
     * @return ProcessModel object
     */
    @Override
    public ProcessModel createProcessModel(String processFileName, String peasId) throws WorkflowException {
        ProcessModel model = null;
        DataFolder folder = null;
        RecordTemplate template = null;
        Forms forms = null;

        try {
            // Load abstract process model
            model = this.loadProcessModel(processFileName, false);
            model.setModelId(peasId);

            // Creates datafolder in database
            folder = model.getDataFolder();
            template = folder.toRecordTemplate(null, null, true);
            getGenericRecordSetManager().createRecordSet(model.getFolderRecordSetName(), template);

            // Creates forms in database
            forms = model.getForms();
            if (forms != null) {
                Iterator<Form> iterForm = forms.iterateForm();
                Form form;

                while (iterForm.hasNext()) {
                    form = iterForm.next();
                    template = form.toRecordTemplate(null, null);
                    getGenericRecordSetManager().createRecordSet(model.getFormRecordSetName(form.getName()),
                            template);
                }
            }
        } catch (FormException fe) {
            throw new WorkflowException("ProcessModelManagerImpl.createProcessModel",
                    "workflowEngine.EX_ERR_INSTANCIATING_MODEL",
                    "Process FileName : " + processFileName == null ? "<null>" : processFileName, fe);
        } catch (WorkflowException we) {
            throw new WorkflowException("ProcessModelManagerImpl.createProcessModel",
                    "workflowEngine.EX_ERR_INSTANCIATING_MODEL",
                    "Process FileName : " + processFileName == null ? "<null>" : processFileName, we);
        }

        return model;
    }

    /**
     * Delete a ProcessModel with given model id
     * @param instanceId model id
     */
    @Override
    public void deleteProcessModel(String instanceId) throws WorkflowException {
        ProcessModel model = getProcessModel(instanceId);
        String formName = null;
        try {
            Forms forms = null;

            // delete the folder
            formName = model.getFolderRecordSetName();
            getGenericRecordSetManager().removeRecordSet(formName);

            // delete forms associated to actions
            forms = model.getForms();
            if (forms != null) {
                Iterator<Form> iterForm = forms.iterateForm();

                while (iterForm.hasNext()) {
                    formName = iterForm.next().getName();
                    getGenericRecordSetManager().removeRecordSet(model.getFormRecordSetName(formName));
                    SilverTrace.info("workflowEngine", "ProcessModelManagerImpl.deleteProcessModel",
                            "root.MSG_GEN_PARAM_VALUE",
                            instanceId + " : Removing form '" + formName + "' successfully done");
                }
            }
        } catch (FormException fe) {
            throw new WorkflowException("ProcessModelManagerImpl.deleteProcessModel",
                    "workflowEngine.EX_ERR_UNINSTANCIATING_MODEL", "instanceId : " + instanceId == null ? "<null>"
                            : instanceId + ", formName = " + formName == null ? "<null>" : formName,
                    fe);
        }
    }

    /*
     * (non-Javadoc)
     * @see com.silverpeas.workflow.api.ProcessModelManager#deleteProcessModelDescriptor
     * (java.lang.String)
     */
    @Override
    public void deleteProcessModelDescriptor(String strProcessModelFileName) throws WorkflowException {
        try {
            FileFolderManager.deleteFile(getProcessModelDir() + strProcessModelFileName);

            // Clear process model cache
            //
            clearProcessModelCache();
        } catch (UtilException e) {
            throw new WorkflowException("WorkflowManager.getProcessModels",
                    "WorkflowEngine.EX_GETTING_RPOCES_MODELS_FAILED", "Process Model File name : "
                            + (strProcessModelFileName == null ? "<null>" : strProcessModelFileName),
                    e);
        }
    }

    /**
     * load a process model definition from xml file to java objects
     * @param processFileName the xml file name that contains process model definition
     * @param absolutePath true if xml file name contains the full path, else concat with the
     * directory defined in castorSettings.properties
     * @return a ProcessModel object
     */
    @Override
    public ProcessModel loadProcessModel(String processFileName, boolean absolutePath) throws WorkflowException {
        Mapping mapping = new Mapping();

        // get configuration files url
        String mappingFileName = settings.getString("CastorXMLMappingFileURL");
        boolean debugMode = settings.getBoolean("DebugMode", false);
        String processPath = processFileName;
        try {
            // Format these url
            if (!FileUtil.isWindows()) {
                mappingFileName = mappingFileName.replace('\\', '/');
            } else {
                mappingFileName = "file:///" + mappingFileName.replace('\\', '/');
            }
            if (!absolutePath) {
                processPath = getProcessPath(processFileName);
            }

            // Load mapping and instantiate a Marshaller
            mapping.loadMapping(mappingFileName);
            Unmarshaller unmar = new Unmarshaller(mapping);
            unmar.setValidation(false);
            unmar.setDebug(debugMode);
            // Unmarshall the process model
            ProcessModelImpl process = (ProcessModelImpl) unmar
                    .unmarshal(new InputSource(new FileInputStream(processPath)));

            if (debugMode) {
                // Marshall for debugging purpose
                String debugFile = getProcessPath("debug.xml");
                Marshaller mar = new Marshaller(new FileWriter(debugFile));
                mar.setMapping(mapping);
                mar.marshal(process);
            }
            return process;
        } catch (MappingException me) {
            throw new WorkflowException("ProcessModelManagerImpl.loadProcessModel",
                    "workflowEngine.EX_ERR_CASTOR_LOAD_XML_MAPPING",
                    "Mapping file name : " + (mappingFileName == null ? "<null>" : mappingFileName), me);
        } catch (MarshalException me) {
            throw new WorkflowException("ProcessModelManagerImpl.loadProcessModel",
                    "workflowEngine.EX_ERR_CASTOR_UNMARSHALL_PROCESSMODEL",
                    "Process File Name : " + (processFileName == null ? "<null>" : processFileName), me);
        } catch (ValidationException ve) {
            throw new WorkflowException("ProcessModelManagerImpl.loadProcessModel",
                    "workflowEngine.EX_ERR_CASTOR_INVALID_XML_PROCESSMODEL",
                    "Process File Name : " + (processFileName == null ? "<null>" : processFileName), ve);
        } catch (IOException ioe) {
            throw new WorkflowException("ProcessModelManagerImpl.loadProcessModel",
                    "workflowEngine.EX_ERR_CASTOR_LOAD_PROCESSMODEL",
                    "Process File Name : " + (processFileName == null ? "<null>" : processFileName), ioe);
        }
    }

    /**
     * Saves a process model definition from java objects to an XML file
     * @param processFileName the xml file name that contains process model definition
     * @param process A processModel object to be saved
     * @throws WorkflowException when something goes wrong
     */
    @Override
    public void saveProcessModel(ProcessModel process, String processFileName) throws WorkflowException {
        Mapping mapping = new Mapping();
        String mappingFileName = settings.getString("CastorXMLMappingFileURL"); // get
        // configuration
        // files
        // url
        String schemaFileName = settings.getString("ProcessModesSchemaFileURL");
        String strProcessModelFileEncoding = settings.getString("ProcessModelFileEncoding");
        boolean runOnUnix = !FileUtil.isWindows();
        String processPath = getProcessPath(processFileName);
        try {
            if (runOnUnix) {
                mappingFileName = mappingFileName.replace('\\', '/');
            } else {
                mappingFileName = "file:///" + mappingFileName.replace('\\', '/');
            }
            mapping.loadMapping(mappingFileName);
            File file = new File(processPath);
            Marshaller mar = new Marshaller(
                    new OutputStreamWriter(FileUtils.openOutputStream(file), strProcessModelFileEncoding));
            mar.setMapping(mapping);
            mar.setNoNamespaceSchemaLocation(schemaFileName);
            mar.setSuppressXSIType(true);
            mar.setValidation(false);
            mar.setEncoding(strProcessModelFileEncoding);
            mar.marshal(process);
            clearProcessModelCache();
        } catch (MappingException me) {
            throw new WorkflowException("ProcessModelManagerImpl.saveProcessModel",
                    "workflowEngine.EX_ERR_CASTOR_LOAD_XML_MAPPING",
                    "Mapping file name : " + (mappingFileName == null ? "<null>" : mappingFileName), me);
        } catch (MarshalException me) {
            throw new WorkflowException("ProcessModelManagerImpl.saveProcessModel",
                    "workflowEngine.EX_ERR_CASTOR_MARSHALL_PROCESSMODEL",
                    "Process file name : " + (processPath == null ? "<null>" : processPath), me);
        } catch (ValidationException ve) {
            throw new WorkflowException("ProcessModelManagerImpl.saveProcessModel",
                    "workflowEngine.EX_ERR_CASTOR_INVALID_XML_PROCESSMODEL",
                    "Process file name : " + (processPath == null ? "<null>" : processPath), ve);
        } catch (IOException ioe) {
            throw new WorkflowException("ProcessModelManagerImpl.saveProcessModel",
                    "workflowEngine.EX_ERR_CASTOR_SAVE_PROCESSMODEL",
                    "Process file name : " + (processPath == null ? "<null>" : processPath), ioe);
        }
    }

    /**
     * Get the directory where are stored the models
     */
    @Override
    public String getProcessModelDir() {
        String dir = FileUtil.convertPathToServerOS(settings.getString("ProcessModelDir"));
        if (dir != null && !dir.endsWith(File.separator)) {
            dir = dir + File.separatorChar;
        }
        return dir;
    }

    /**
     * Get all the "process manager" peas ids
     */
    @Override
    public String[] getAllPeasIds() throws WorkflowException {
        Connection con = null;
        PreparedStatement prepStmt = null;
        ResultSet rs = null;
        try {
            List<String> peasIds = new ArrayList<String>();
            con = this.getConnection();
            prepStmt = con.prepareStatement(selectQuery);
            rs = prepStmt.executeQuery();
            while (rs.next()) {
                peasIds.add(rs.getString(1));
            }
            return (String[]) peasIds.toArray(new String[peasIds.size()]);
        } catch (SQLException se) {
            throw new WorkflowException("ProcessModelManagerImpl.getAllPeasId",
                    "workflowEngine.EX_ERR_GET_ALL_PEAS_IDS",
                    "sql query : " + selectQuery == null ? "<null>" : selectQuery, se);
        } finally {
            try {
                DBUtil.close(rs, prepStmt);
                if (con != null) {
                    con.close();
                }
            } catch (SQLException se) {
                SilverTrace.error("workflowEngine", "ProcessModelManagerImpl.getAllPeasId",
                        "root.EX_RESOURCE_CLOSE_FAILED", se);
            }
        }
    }

    /**
     * Search the cache for the required process model.
     */
    private ProcessModel getCachedProcessModel(String modelId) {
        ProcessModel model = models.get(modelId);
        return model;
    }

    /**
     * Put the given process model in the the cache.
     */
    private void cacheProcessModel(String modelId, ProcessModel model, String filename) {
        synchronized (models) {
            models.put(modelId, model);
        }
    }

    /**
     * Clear the process model cache.
     */
    @Override
    public void clearProcessModelCache() {
        synchronized (models) {
            models.clear();
        }
    }

    /**
     * @return the DB connection
     */
    private Connection getConnection() throws WorkflowException {
        Connection con = null;
        try {
            // con = DBUtil.makeConnection(dbName);
            Context ctx = new InitialContext();
            DataSource src = (DataSource) ctx.lookup(dbName);
            con = src.getConnection();
            return con;
        } catch (NamingException e) {
            // throw new UtilException("Schema.Schema", SilverpeasException.ERROR,
            // "root.EX_DATASOURCE_NOT_FOUND", e);
            // the JNDI name have not been found in the current context
            // The caller is not takes place in any context (web application nor ejb
            // container)
            // So lookup operation cannot find JNDI properties !
            // This is absolutly normal according to the j2ee specification
            // Unfortunately, only BES takes care about this spec. This exception
            // doesn't appear with orion or BEA !
            try {
                // Get the initial Context
                Context ctx = new InitialContext();
                // Look up the datasource directly without JNDI access
                DataSource dataSource = (DataSource) ctx.lookup(JNDINames.DIRECT_DATASOURCE);
                // Create a connection object
                con = dataSource.getConnection();
                return con;
            } catch (NamingException ne) {
                throw new WorkflowException("ProcessModelManagerImpl.getConnection", "root.EX_DATASOURCE_NOT_FOUND",
                        "Data source " + JNDINames.DIRECT_DATASOURCE + " not found", ne);
            } catch (SQLException se) {
                throw new WorkflowException("ProcessModelManagerImpl.getConnection",
                        "can't get connection for dataSource " + JNDINames.DIRECT_DATASOURCE, se);
            }
        } catch (SQLException se) {
            throw new WorkflowException("ProcessModelManagerImpl.getConnection()", "root.EX_CONNECTION_OPEN_FAILED",
                    se);
        }
    }

    protected String getProcessPath(String processFileName) {
        String processModelDir = settings.getString("ProcessModelDir");
        processModelDir = processModelDir.replace('\\', '/');
        if (processModelDir.length() > 0 && !processModelDir.endsWith("/")) {
            processModelDir = processModelDir + '/';
        }
        return processModelDir + processFileName;
    }

    /**
     * Gets an instance of a GenericRecordSet objects manager.
     * @return a GenericRecordSetManager instance.
     */
    private GenericRecordSetManager getGenericRecordSetManager() {
        return GenericRecordSetManager.getInstance();
    }
}