org.nuclos.server.ruleengine.ejb3.RuleEngineFacadeBean.java Source code

Java tutorial

Introduction

Here is the source code for org.nuclos.server.ruleengine.ejb3.RuleEngineFacadeBean.java

Source

//Copyright (C) 2010  Novabit Informationssysteme GmbH
//
//This file is part of Nuclos.
//
//Nuclos 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.
//
//Nuclos 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 Nuclos.  If not, see <http://www.gnu.org/licenses/>.
package org.nuclos.server.ruleengine.ejb3;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import javax.annotation.security.RolesAllowed;

import org.apache.commons.lang.NullArgumentException;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.nuclos.common.Actions;
import org.nuclos.common.ApplicationProperties;
import org.nuclos.common.JMSConstants;
import org.nuclos.common.NuclosEntity;
import org.nuclos.common.NuclosFatalException;
import org.nuclos.common.ParameterProvider;
import org.nuclos.common.PropertiesMap;
import org.nuclos.common.RuleNotification;
import org.nuclos.common.SearchConditionUtils;
import org.nuclos.common.UsageCriteria;
import org.nuclos.common.collect.collectable.searchcondition.CollectableComparison;
import org.nuclos.common.collect.collectable.searchcondition.CollectableSearchCondition;
import org.nuclos.common.collect.collectable.searchcondition.ComparisonOperator;
import org.nuclos.common.collection.CollectionUtils;
import org.nuclos.common.collection.Transformer;
import org.nuclos.common.collection.TransformerUtils;
import org.nuclos.common.dal.vo.EntityObjectVO;
import org.nuclos.common.dal.vo.SystemFields;
import org.nuclos.common2.IdUtils;
import org.nuclos.common2.LangUtils;
import org.nuclos.common2.StringUtils;
import org.nuclos.common2.exception.CommonBusinessException;
import org.nuclos.common2.exception.CommonCreateException;
import org.nuclos.common2.exception.CommonFatalException;
import org.nuclos.common2.exception.CommonFinderException;
import org.nuclos.common2.exception.CommonPermissionException;
import org.nuclos.common2.exception.CommonRemoteException;
import org.nuclos.common2.exception.CommonRemoveException;
import org.nuclos.common2.exception.CommonStaleVersionException;
import org.nuclos.common2.exception.CommonValidationException;
import org.nuclos.server.common.MasterDataMetaCache;
import org.nuclos.server.common.MetaDataServerProvider;
import org.nuclos.server.common.RuleCache;
import org.nuclos.server.common.SecurityCache;
import org.nuclos.server.common.ServerParameterProvider;
import org.nuclos.server.common.ServerServiceLocator;
import org.nuclos.server.common.ejb3.NuclosFacadeBean;
import org.nuclos.server.customcode.CustomCodeManager;
import org.nuclos.server.customcode.NuclosRule;
import org.nuclos.server.customcode.codegenerator.NuclosJavaCompilerComponent;
import org.nuclos.server.customcode.codegenerator.RuleCodeGenerator;
import org.nuclos.server.customcode.codegenerator.RuleCodeGenerator.AbstractRuleTemplateType;
import org.nuclos.server.dal.provider.NucletDalProvider;
import org.nuclos.server.dblayer.DbTuple;
import org.nuclos.server.dblayer.query.DbColumnExpression;
import org.nuclos.server.dblayer.query.DbCondition;
import org.nuclos.server.dblayer.query.DbFrom;
import org.nuclos.server.dblayer.query.DbQuery;
import org.nuclos.server.dblayer.query.DbQueryBuilder;
import org.nuclos.server.genericobject.ejb3.GeneratorFacadeLocal;
import org.nuclos.server.genericobject.searchcondition.CollectableSearchExpression;
import org.nuclos.server.genericobject.valueobject.GeneratorActionVO;
import org.nuclos.server.jms.NuclosJMSUtils;
import org.nuclos.server.masterdata.MasterDataWrapper;
import org.nuclos.server.masterdata.ejb3.MasterDataFacadeLocal;
import org.nuclos.server.masterdata.valueobject.DependantMasterDataMap;
import org.nuclos.server.masterdata.valueobject.DependantMasterDataMapImpl;
import org.nuclos.server.masterdata.valueobject.MasterDataMetaVO;
import org.nuclos.server.masterdata.valueobject.MasterDataVO;
import org.nuclos.server.ruleengine.NuclosBusinessRuleException;
import org.nuclos.server.ruleengine.NuclosCompileException;
import org.nuclos.server.ruleengine.NuclosFatalRuleException;
import org.nuclos.server.ruleengine.RuleInterface;
import org.nuclos.server.ruleengine.valueobject.RuleEngineGenerationVO;
import org.nuclos.server.ruleengine.valueobject.RuleEngineTransitionVO;
import org.nuclos.server.ruleengine.valueobject.RuleEventUsageVO;
import org.nuclos.server.ruleengine.valueobject.RuleObjectContainerCVO;
import org.nuclos.server.ruleengine.valueobject.RuleObjectContainerCVOImpl;
import org.nuclos.server.ruleengine.valueobject.RuleVO;
import org.nuclos.server.ruleengine.valueobject.RuleWithUsagesVO;
import org.nuclos.server.statemodel.ejb3.StateFacadeLocal;
import org.nuclos.server.statemodel.valueobject.StateModelVO;
import org.nuclos.server.statemodel.valueobject.StateTransitionVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.transaction.annotation.Transactional;

/**
 * Facade bean for rule engine management.
 * <br>
 * <br>Created by Novabit Informationssysteme GmbH
 * <br>Please visit <a href="http://www.novabit.de">www.novabit.de</a>
 */
@Transactional(noRollbackFor = { Exception.class })
public class RuleEngineFacadeBean extends NuclosFacadeBean implements RuleEngineFacadeRemote {

    private static final Logger LOG = Logger.getLogger(RuleEngineFacadeBean.class);

    private CustomCodeManager ccm;

    private MasterDataFacadeLocal masterDataFacade;

    private NuclosJavaCompilerComponent nuclosJavaCompilerComponent;

    public RuleEngineFacadeBean() {
    }

    @Autowired
    final void setMasterDataFacade(MasterDataFacadeLocal masterDataFacade) {
        this.masterDataFacade = masterDataFacade;
    }

    @Autowired
    final void setNuclosJavaCompilerComponent(NuclosJavaCompilerComponent nuclosJavaCompilerComponent) {
        this.nuclosJavaCompilerComponent = nuclosJavaCompilerComponent;
    }

    private final MasterDataFacadeLocal getMasterDataFacade() {
        return masterDataFacade;
    }

    public void setCustomCodeManager(CustomCodeManager ccm) {
        this.ccm = ccm;
    }

    /**
     * fires an event by finding all rules that correspond
     * to the given module id and event name and by executing these rules.
     * @param iModuleId module id to fire event for
     * @param sEventName event name to be fired
     * @param loccvoCurrent current leased object as parameter for rules
     * @return the possibly change current object.
     * @precondition iModuleId != null
     * @precondition Modules.getInstance().getUsesRuleEngine(iModuleId.intValue())
     */
    public RuleObjectContainerCVO fireRule(String sEntity, String sEventName, RuleObjectContainerCVO loccvoCurrent,
            String customUsage) throws NuclosBusinessRuleException {
        if (sEntity == null) {
            throw new NullArgumentException("sEntity");
        }
        //      if (!Modules.getInstance().getUsesRuleEngine(Modules.getInstance().getModuleIdByEntityName(metacache.getMetaDataById(iEntityId).getEntityName()))) {
        //         throw new IllegalArgumentException("iModuleId");
        //      }

        UsageCriteria uc;
        if (loccvoCurrent.getMasterData() != null) {
            Integer entityId = IdUtils.unsafeToId(MetaDataServerProvider.getInstance().getEntity(sEntity).getId());
            uc = new UsageCriteria(entityId, null, null, null);
        } else {
            Integer iProcessId = loccvoCurrent.getGenericObject().getProcessId();
            Integer iStatusId = loccvoCurrent.getGenericObject().getStatusId();
            if (iStatusId == null) {
                StateFacadeLocal facade = ServerServiceLocator.getInstance().getFacade(StateFacadeLocal.class);
                iStatusId = facade.getInitialState(
                        new UsageCriteria(loccvoCurrent.getGenericObject().getModuleId(), iProcessId, null, null))
                        .getId();
            }
            uc = new UsageCriteria(loccvoCurrent.getGenericObject().getModuleId(), iProcessId, iStatusId, null);
        }

        final List<RuleVO> lstRules = new ArrayList<RuleVO>(findRulesByUsageAndEvent(sEventName, uc));

        // We can now execute the rules in their order
        info("BEGIN    executing business rules for event \"" + sEventName + "\" and entity " + sEntity + "..."); //Modules.getInstance().getEntityNameByModuleId(iModuleId)
        final RuleObjectContainerCVO result = this.executeBusinessRules(lstRules, loccvoCurrent, false,
                customUsage);
        info("FINISHED executing business rules for event \"" + sEventName + "\" and entity " + sEntity + "..."); //Modules.getInstance().getEntityNameByModuleId(iModuleId)
        return result;
    }

    /**
     * fires a transition event by finding all rules that correspond
     * to the given source and target state ids and by executing these rules.
     * @param iSourceStateId source state id to fire event for in combination with target state id
     * @param iTargetStateId target state id to fire event for in combination with source state id
     * @param loccvoCurrent current leased object as parameter for rules
     * @return the possibly change current object.
     */
    public RuleObjectContainerCVO fireRule(Integer sourceStateId, Integer targetStateId,
            RuleObjectContainerCVO ruleContainer, Boolean after, String customUsage)
            throws NuclosBusinessRuleException {

        StateFacadeLocal facade = ServerServiceLocator.getInstance().getFacade(StateFacadeLocal.class);
        StateTransitionVO stVO = (sourceStateId == null)
                ? facade.findStateTransitionByNullAndTargetState(targetStateId)
                : facade.findStateTransitionBySourceAndTargetState(sourceStateId, targetStateId);

        return stVO != null ? fireRule(stVO.getId(), ruleContainer, after, customUsage) : ruleContainer;
    }

    /**
     * fires a transition event by finding all rules that correspond
     * to the given transition id and by executing these rules.
     * @param iTransitionId transition id to fire event for
     * @param loccvoCurrent current leased object as parameter for rules
     * @return the possibly change current object.
     */
    private RuleObjectContainerCVO fireRule(Integer transitionId, RuleObjectContainerCVO ruleContainer,
            Boolean after, String customUsage) throws NuclosBusinessRuleException {
        final List<RuleVO> rules = new ArrayList<RuleVO>();

        DbQueryBuilder builder = dataBaseHelper.getDbAccess().getQueryBuilder();
        DbQuery<DbTuple> query = builder.createTupleQuery();
        DbFrom t = query.from("T_MD_RULE_TRANSITION").alias(SystemFields.BASE_ALIAS);
        query.multiselect(t.baseColumn("INTID_T_MD_RULE", Integer.class),
                t.baseColumn("BLNRUNAFTERWARDS", Boolean.class));
        query.where(builder.equal(t.baseColumn("INTID_T_MD_STATE_TRANSITION", Integer.class), transitionId));
        query.orderBy(builder.asc(t.baseColumn("INTORDER", Integer.class)));

        for (DbTuple res : dataBaseHelper.getDbAccess().executeQuery(query)) {
            Boolean bRuleRunAfterwards = res.get(1, Boolean.class);
            if (bRuleRunAfterwards == null)
                bRuleRunAfterwards = Boolean.FALSE;
            if ((bRuleRunAfterwards && after) || (!bRuleRunAfterwards && !after)) {
                rules.add(RuleCache.getInstance().getRule(res.get(0, Integer.class)));
            }
        }

        // We can now execute the rules in their order:
        info("BEGIN    executing business rules for transition id " + transitionId + "...");
        final RuleObjectContainerCVO result = executeBusinessRules(rules, ruleContainer, false, customUsage);
        info("FINISHED executing business rules for transition id " + transitionId + "...");
        return result;
    }

    /**
     * fires the rules for a specific object generation.
     */
    public RuleObjectContainerCVO fireGenerationRules(Integer iGenerationId, RuleObjectContainerCVO tgtRuleObject,
            Collection<RuleObjectContainerCVO> srcRuleObjects, RuleObjectContainerCVO parameterRuleObject,
            List<String> actions, PropertiesMap properties, Boolean after, String customUsage)
            throws NuclosBusinessRuleException {

        RuleObjectContainerCVO result = null;

        try {
            Collection<RuleVO> rules = findRulesByGeneration(iGenerationId, after);

            // We can now execute the rules in their order:
            info("BEGIN executing business rules for generator id " + iGenerationId + "...");
            result = this.executeBusinessRules(rules, tgtRuleObject, srcRuleObjects, parameterRuleObject, false,
                    actions, properties, customUsage);
            info("FINISHED executing business rules for generator id " + iGenerationId + "...");
        } catch (CommonPermissionException e) {
            throw new NuclosBusinessRuleException(e);
        }

        return result;
    }

    /**
     * executes the given lstRules of business rules.
     * @param lstRules List<RuleEngineRuleLocal>
     * @param loccvoCurrent current leased object as parameter for rules
     * @param bIgnoreExceptions
     * @return the possibly change current object.
     * @throws NuclosBusinessRuleException
     */
    public RuleObjectContainerCVO executeBusinessRules(List<RuleVO> lstRules, RuleObjectContainerCVO loccvoCurrent,
            boolean bIgnoreExceptions, String customUsage) throws NuclosBusinessRuleException {

        return this.executeBusinessRules(lstRules, loccvoCurrent, null, bIgnoreExceptions, null, customUsage);
    }

    /**
     * Convenience method for preserving calls without mpProperties
     */
    private RuleObjectContainerCVO executeBusinessRules(List<RuleVO> lstRules, RuleObjectContainerCVO loccvoCurrent,
            Collection<RuleObjectContainerCVO> roccvoSourceObjects, boolean bIgnoreExceptions,
            List<String> lstActions, String customUsage) throws NuclosBusinessRuleException {
        return executeBusinessRules(lstRules, loccvoCurrent, roccvoSourceObjects, null, bIgnoreExceptions,
                lstActions, null, customUsage);
    }

    /**
     * Extract all rules of an special type that are attached to a given nuclet
     * 
     * @param sEventName
     * @param nucletId
     * @return
     */
    public List<RuleVO> getByNucletEventsOrdered(String sEventName, Integer nucletId) {
        return RuleCache.getInstance().getByNucletEventsOrdered(sEventName, nucletId);
    }

    /**
     * executes active rules in the given lstRules of business rules.
     * @param lstRules List<RuleEngineRuleLocal>
     * @param loccvoSourceObject source leased object as parameter for generation rules
     * @param loccvoTargetObject target leased object as parameter for generation rules
     * @param bIgnoreExceptions
     * @param lstActions
     * @param mpProperties
     * @return the possibly change current object.
     * @throws NuclosBusinessRuleException
     */
    private RuleObjectContainerCVO executeBusinessRules(Collection<RuleVO> lstRules,
            RuleObjectContainerCVO loccvoCurrent, Collection<RuleObjectContainerCVO> roccvoSourceObjects,
            RuleObjectContainerCVO loccvoParameterObject, boolean bIgnoreExceptions, List<String> lstActions,
            PropertiesMap mpProperties, String customUsage) throws NuclosBusinessRuleException {
        RuleObjectContainerCVO result = loccvoCurrent;
        if (!lstRules.isEmpty()) {
            final Iterator<RuleVO> iter = lstRules.iterator();
            final List<RuleNotification> lstRuleNotifications = new ArrayList<RuleNotification>();
            while (iter.hasNext()) {
                String sCurrentRule = null;
                int iHeaderLinesCount = 0;
                try {
                    final RuleVO rulevo = iter.next();
                    sCurrentRule = rulevo.getRule();

                    if (rulevo.getId() != null && rulevo.isActive()) {
                        info("Start executing rule \"" + rulevo.getRule() + "\" (" + rulevo.getId() + ")");

                        RuleCodeGenerator<NuclosRule> generator = getGenerator(rulevo);
                        final NuclosRule ruleInstance = ccm.getInstance(generator);
                        iHeaderLinesCount = generator.getPrefixAndHeaderLineCount();

                        final RuleInterface ri = new RuleInterface(rulevo, loccvoCurrent, roccvoSourceObjects,
                                loccvoParameterObject, lstActions, customUsage);
                        ri.setProperties(mpProperties);
                        ruleInstance.rule(ri);
                        result = ri.getRuleObjectContainerCVOIfAny();
                        lstRuleNotifications.addAll(ri.getRuleNotification());

                        info("Finished executing rule \"" + rulevo.getRule() + "\"");
                    } else {
                        log(Level.INFO, "Skipped rule \"" + rulevo.getRule() + "\" - it is not active.");
                    }
                } catch (NuclosBusinessRuleException ex) {
                    throw new NuclosBusinessRuleException(
                            StringUtils.getParameterizedExceptionMessage("rule.execution.error", sCurrentRule,
                                    getErrorLineNumber(ex, iHeaderLinesCount), ex.getMessage()),
                            ex.getMessage(), ex);
                    //                  "Fehler bei der Ausf\u00fchrung der Gesch\u00e4ftsregel \"" + sCurrentRule + "\"" +
                    //                     " (Zeile " + getErrorLineNumber(ex, iHeaderLinesCount) + ") aufgetreten:\n" + ex.getMessage(), ex);
                } catch (NuclosFatalRuleException ex) {
                    log(Level.ERROR, ex.getMessage(), ex);
                    if (!bIgnoreExceptions) {
                        throw ex;
                    }
                } catch (Exception ex) {
                    log(Level.ERROR, ex.getMessage(), ex);
                    if (!bIgnoreExceptions) {
                        String sErrorMessage = StringUtils.getParameterizedExceptionMessage("rule.execution.error",
                                sCurrentRule, getErrorLineNumber(ex, iHeaderLinesCount), null);
                        sErrorMessage = StringUtils.getParameterizedExceptionMessage("rule.execution.error",
                                sCurrentRule, getErrorLineNumber(ex, iHeaderLinesCount), ex.getMessage());
                        throw new NuclosFatalRuleException(sErrorMessage, ex);
                    }
                }
            }
            if (!bIgnoreExceptions) {
                sendMessagesByJMS(lstRuleNotifications);
            }
        }
        return result;
    }

    private int getErrorLineNumber(Exception ex, int iHeaderLinesCount) {
        int iErrorLineNumber = 0;
        for (StackTraceElement stackElement : ex.getStackTrace()) {
            if (stackElement.getClassName().startsWith("Rule_")) {
                iErrorLineNumber = stackElement.getLineNumber();
                break;
            }
        }
        return iErrorLineNumber > 0 ? (iErrorLineNumber - iHeaderLinesCount) : 0;
    }

    /**
     * send the notification messages of all rules in this transaction.
     */
    private void sendMessagesByJMS(List<RuleNotification> lstRuleNotifications) {
        if (lstRuleNotifications.size() > 0) {
            LOG.info("JMS send rule notifications: " + lstRuleNotifications + ": " + this);
            for (RuleNotification notification : lstRuleNotifications) {
                NuclosJMSUtils.sendObjectMessageAfterCommit(notification, JMSConstants.TOPICNAME_RULENOTIFICATION,
                        this.getCurrentUserName());
            }
        }
    }

    /**
     * @return Collection<RuleVO> all rule definitions
     * @throws CommonPermissionException
     */
    public Collection<RuleVO> getAllRules() throws CommonPermissionException {
        if (!this.isInRole("UseManagementConsole")) {
            this.checkReadAllowed(NuclosEntity.RULE, NuclosEntity.STATEMODEL);
        }
        return RuleCache.getInstance().getAllRules();
    }

    /**
     * Get a collection of rules by the eventname independent of the module.
     * @return Collection<RuleVO> all rule for a given event Name
     * @throws CommonPermissionException
     */
    public List<RuleVO> getByEventOrdered(String sEventName) throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        return RuleCache.getInstance().getByEventOrdered(sEventName);
    }

    /**
     * Get a collection of rules by Eventname and ModuleId (ordered).
     * @return Collection<RuleVO> all rule for a given event Name
     * @throws CommonPermissionException
     */
    public List<RuleVO> getByEventAndEntityOrdered(String sEventName, String sEntity)
            throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        return RuleCache.getInstance().getByEventAndEntityOrdered(sEventName, sEntity);
    }

    /**
     * finds rules by usage criteria
     * @param usagecriteria
     * @param sEventName
     * @return collection of rule
     */
    public Collection<RuleVO> findRulesByUsageAndEvent(String sEventName, UsageCriteria usagecriteria) {

        List<RuleVO> ruleVOs = new ArrayList<RuleVO>();

        DbQueryBuilder builder = dataBaseHelper.getDbAccess().getQueryBuilder();
        DbQuery<Integer> query = builder.createQuery(Integer.class);
        DbFrom t = query.from("V_MD_RULE_EVENT").alias(SystemFields.BASE_ALIAS);
        query.select(t.baseColumn("INTID_T_MD_RULE", Integer.class));
        DbCondition cond = builder.equal(t.baseColumn("STRMASTERDATA", String.class), MetaDataServerProvider
                .getInstance().getEntity(IdUtils.toLongId(usagecriteria.getModuleId())).getEntity());

        query.where(builder.and(cond, builder.equal(t.baseColumn("STREVENT", String.class), sEventName)));

        DbColumnExpression<Integer> cp = t.baseColumn("INTID_T_MD_PROCESS", Integer.class);
        final Integer iProcessId = usagecriteria.getProcessId();
        if (iProcessId == null) {
            query.addToWhereAsAnd(builder.and(cond, cp.isNull()));
        } else {
            query.addToWhereAsAnd(builder.and(cond, builder.or(cp.isNull(), builder.equal(cp, iProcessId))));
        }
        DbColumnExpression<Integer> cs = t.baseColumn("INTID_T_MD_STATE", Integer.class);
        final Integer iStatusId = usagecriteria.getStatusId();
        if (iStatusId == null) {
            query.addToWhereAsAnd(builder.and(cond, cs.isNull()));
        } else {
            query.addToWhereAsAnd(builder.and(cond, builder.or(cs.isNull(), builder.equal(cs, iStatusId))));
        }

        query.orderBy(builder.asc(t.baseColumn("INTORDER", Integer.class)));

        List<Integer> collUsableRuleIds = dataBaseHelper.getDbAccess().executeQuery(query);

        for (Integer ruleId : collUsableRuleIds) {
            try {
                ruleVOs.add(MasterDataWrapper
                        .getRuleVO(getMasterDataFacade().get(NuclosEntity.RULE.getEntityName(), ruleId)));
            } catch (CommonPermissionException ex) {
                throw new CommonFatalException(ex);
            } catch (CommonFinderException ex) {
                throw new CommonFatalException(ex);
            }
        }

        return ruleVOs;
    }

    /**
     * Create an ruleUsage for the given module and eventname.
     * The oder of the new usage is dependent of the ruleBeforeId
     *
     * @param sEventname
     * @param sEntity
     * @param processId
     * @param statusId
     * @param ruleToInsertId
     * @param ruleBeforeId - null the new usage is inserted at the end
     *                - not null the new usage is inserted after the usage with the ruleBeforeId
     * @throws CommonCreateException
     * @throws CommonPermissionException
     */
    public void createRuleUsageInEntity(String sEventname, String sEntity, Integer processId, Integer statusId,
            Integer ruleToInsertId, Integer ruleBeforeId) throws CommonCreateException, CommonPermissionException {

        // @todo the name is misspelled (Module).
        this.checkWriteAllowed(NuclosEntity.RULE);

        try {
            Collection<RuleEventUsageVO> reVOList = new ArrayList<RuleEventUsageVO>();
            Collection<MasterDataVO> mdVOList = getRuleUsageForEntity(sEntity);

            for (MasterDataVO vo : mdVOList) {
                if (vo.getField("event").equals(sEventname))
                    reVOList.add(MasterDataWrapper.getREUsageVO(vo));
            }

            final List<RuleEventUsageVO> lstAllRulesSorted = CollectionUtils.sorted(reVOList,
                    new UsageByOrderComparator());

            int iLastRuleOrder = 0;
            int iNewRuleOrder = 0;
            boolean bFoundRuleBefore = false;
            for (RuleEventUsageVO reUsageVO : lstAllRulesSorted) {
                if (!bFoundRuleBefore) {
                    iLastRuleOrder = reUsageVO.getOrder();
                    if (ruleBeforeId != null && reUsageVO.getRuleId().equals(ruleBeforeId)) {
                        bFoundRuleBefore = true;
                        iNewRuleOrder = iLastRuleOrder + 1;
                        iLastRuleOrder = iNewRuleOrder;
                    }
                } else {
                    reUsageVO.setOrder(iLastRuleOrder + 1);
                    iLastRuleOrder = reUsageVO.getOrder();
                }
            }

            if (!bFoundRuleBefore) {
                iNewRuleOrder = iLastRuleOrder + 1;
            }

            RuleEventUsageVO reUsageVO = new RuleEventUsageVO(sEventname, sEntity, processId, statusId,
                    ruleToInsertId, iNewRuleOrder);
            getMasterDataFacade().create(NuclosEntity.RULEUSAGE.getEntityName(),
                    MasterDataWrapper.wrapREUsageVO(reUsageVO), null, null);

            RuleCache.getInstance().invalidate();
        } catch (CommonBusinessException ex) {
            throw new NuclosFatalException(ex);
        }
    }

    /**
     * remove an rule usage for the rule with the given id in the given module and the eventName
     * @param eventName
     * @param entity
     * @param processId
     * @param statusId
     * @param iRuleIdToRemove id of rule to remove
     * @throws CommonPermissionException
     * @throws CommonStaleVersionException
     * @throws CommonRemoveException
     * @throws CommonFinderException
     * @throws NuclosBusinessRuleException
     */
    public void removeRuleUsage(String eventName, String entity, Integer processId, Integer statusId,
            Integer iRuleIdToRemove) throws CommonPermissionException, NuclosBusinessRuleException,
            CommonFinderException, CommonRemoveException, CommonStaleVersionException {

        this.checkWriteAllowed(NuclosEntity.RULE);

        Collection<RuleEventUsageVO> reUsageList = new ArrayList<RuleEventUsageVO>();
        Collection<MasterDataVO> mdVOList = getMasterDataFacade()
                .getDependantMasterData(NuclosEntity.RULEUSAGE.getEntityName(), "rule", iRuleIdToRemove);

        for (MasterDataVO vo : mdVOList) {
            if (vo.getField("event").equals(eventName) && LangUtils.equals(vo.getField("stateId"), statusId)
                    && LangUtils.equals(vo.getField("processId"), processId))
                reUsageList.add(MasterDataWrapper.getREUsageVO(vo));
        }

        MasterDataVO usageToRemove = null;

        // timelimits has no module, there should be only one rule exists
        if (reUsageList.size() == 1) {
            usageToRemove = MasterDataWrapper.wrapREUsageVO(reUsageList.iterator().next());
        }

        for (Iterator<RuleEventUsageVO> ruleIter = reUsageList.iterator(); usageToRemove == null
                && ruleIter.hasNext();) {
            final RuleEventUsageVO usage = ruleIter.next();
            if (usage.getEntity().equals(entity)) {
                usageToRemove = MasterDataWrapper.wrapREUsageVO(usage);
            }
        }

        if (usageToRemove != null) {
            //usageToRemove.remove();
            getMasterDataFacade().remove(NuclosEntity.RULEUSAGE.getEntityName(), usageToRemove, true, null);
            RuleCache.getInstance().invalidate();
        } else {
            throw new CommonRemoteException("rule.error.missing.usage");//"Keine Verwendung gefunden.");
        }
    }

    /**
     *
     * @param eventName
     * @param entity
     * @param processId
     * @param statusId
     * @param ruleToMoveId
     * @param ruleBeforeId
     * @throws CommonCreateException
     * @throws CommonPermissionException
     */
    public void moveRuleUsageInEntity(String eventName, String entity, Integer processId, Integer statusId,
            Integer ruleToMoveId, Integer ruleBeforeId) throws CommonCreateException, CommonPermissionException {

        this.checkWriteAllowed(NuclosEntity.RULE);

        Collection<RuleEventUsageVO> reVOList = new ArrayList<RuleEventUsageVO>();
        Collection<MasterDataVO> mdVOList = getRuleUsageForEntity(entity);

        for (MasterDataVO vo : mdVOList) {
            if (vo.getField("event").equals(eventName) && LangUtils.equals(vo.getField("stateId"), statusId)
                    && LangUtils.equals(vo.getField("processId"), processId))
                reVOList.add(MasterDataWrapper.getREUsageVO(vo));
        }

        final List<RuleEventUsageVO> lstAllRulesSorted = CollectionUtils.sorted(reVOList,
                new UsageByOrderComparator());

        // find rule before:
        int orderRuleBefore = 0;
        if (ruleBeforeId != null) {
            for (RuleEventUsageVO ruleVO : lstAllRulesSorted) {
                if (ruleVO.getRuleId().equals(ruleBeforeId)) {
                    orderRuleBefore = ruleVO.getOrder();
                }
            }
        }

        // move order:
        int lastOrder = 1;
        RuleEventUsageVO ruleToMove = null;
        Integer ruleToMoveOrder = null;
        for (RuleEventUsageVO ruleLocale : lstAllRulesSorted) {
            if (ruleLocale.getRuleId().equals(ruleToMoveId)) {
                ruleToMove = ruleLocale;
            } else {
                if (ruleLocale.getOrder() > orderRuleBefore) {
                    if (ruleToMoveOrder == null) {
                        ruleToMoveOrder = lastOrder++;
                    }
                    ruleLocale.setOrder(lastOrder++);
                } else {
                    ruleLocale.setOrder(lastOrder++);
                }
            }
        }
        if (ruleToMoveOrder != null) {
            ruleToMove.setOrder(ruleToMoveOrder);
        } else {
            ruleToMove.setOrder(lastOrder);
        }

        RuleCache.getInstance().invalidate();
    }

    private Collection<MasterDataVO> getRuleUsageForEntity(String entity) {
        MasterDataMetaVO metaData = MasterDataMetaCache.getInstance().getMetaData(NuclosEntity.RULEUSAGE);
        return getMasterDataFacade().getMasterData(NuclosEntity.RULEUSAGE.getEntityName(),
                SearchConditionUtils.newMDComparison(metaData, "entity", ComparisonOperator.EQUAL, entity), true);
    }

    /**
     * Get all object generations.
     * @return Collection<GeneratorActionVO>
     * @throws CommonPermissionException
     */
    public Collection<GeneratorActionVO> getAllGenerations() throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        final Collection<GeneratorActionVO> result = new HashSet<GeneratorActionVO>();
        GeneratorFacadeLocal facade = ServerServiceLocator.getInstance().getFacade(GeneratorFacadeLocal.class);
        Collection<MasterDataVO> mdGenerationsVO = getMasterDataFacade()
                .getMasterData(NuclosEntity.GENERATION.getEntityName(), null, true);

        for (MasterDataVO mdVO : mdGenerationsVO)
            result.add(MasterDataWrapper.getGeneratorActionVO(mdVO, facade.getGeneratorUsages(mdVO.getIntId())));

        return result;
    }

    /**
     * Get all object generations that have a rule assigned.
     * @return Collection<GeneratorActionVO>
     * @throws CommonPermissionException
     */
    public Collection<GeneratorActionVO> getAllGenerationsWithRule() throws CommonPermissionException {
        return getAllGenerationsForRuleImpl(null);
    }

    /**
     * Get all object generations that have a certain rule assigned.
     * @return Collection<GeneratorActionVO>
     * @throws CommonPermissionException
     */
    public Collection<GeneratorActionVO> getAllGenerationsForRuleId(Integer iRuleId)
            throws CommonPermissionException {
        if (iRuleId == null)
            return Collections.emptySet();
        return getAllGenerationsForRuleImpl(iRuleId);
    }

    private Collection<GeneratorActionVO> getAllGenerationsForRuleImpl(Integer iRuleId)
            throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        Collection<GeneratorActionVO> resultUnsorted = new HashSet<GeneratorActionVO>();

        DbQueryBuilder builder = dataBaseHelper.getDbAccess().getQueryBuilder();
        DbQuery<Integer> query = builder.createQuery(Integer.class);
        DbFrom t = query.from("T_MD_RULE_GENERATION").alias(SystemFields.BASE_ALIAS);
        query.select(t.baseColumn("INTID_T_MD_GENERATION", Integer.class));
        if (iRuleId != null) {
            query.where(builder.equal(t.baseColumn("INTID_T_MD_RULE", Integer.class), iRuleId));
        }

        List<Integer> generationIds = dataBaseHelper.getDbAccess().executeQuery(query.distinct(true));

        GeneratorFacadeLocal generatorFacade = ServerServiceLocator.getInstance()
                .getFacade(GeneratorFacadeLocal.class);

        try {
            for (Integer id : generationIds) {
                MasterDataVO mdVO = getMasterDataFacade().get(NuclosEntity.GENERATION.getEntityName(), id);
                resultUnsorted.add(MasterDataWrapper.getGeneratorActionVO(mdVO,
                        generatorFacade.getGeneratorUsages(mdVO.getIntId())));
            }
        } catch (CommonFinderException ex) {
            // Dateninkonsistenz?
            throw new CommonFatalException(ex);
        }

        return CollectionUtils.sorted(resultUnsorted, new GenerationOrderComparator());
    }

    /**
     * Get all RuleGeneration for the given rule.
     * @return Collection<RuleEngineGenerationVO>
     * @throws CommonPermissionException
     */
    public Collection<RuleEngineGenerationVO> getAllRuleGenerationsForRuleId(Integer ruleId)
            throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        final Collection<RuleEngineGenerationVO> result = new HashSet<RuleEngineGenerationVO>();

        CollectableComparison cond = SearchConditionUtils.newMDComparison(
                MasterDataMetaCache.getInstance().getMetaData(NuclosEntity.RULEGENERATION), "rule",
                ComparisonOperator.EQUAL, ruleId);
        Collection<MasterDataVO> mdVOList = getMasterDataFacade()
                .getMasterData(NuclosEntity.RULEGENERATION.getEntityName(), cond, true);

        for (MasterDataVO mdVO : mdVOList)
            result.add(MasterDataWrapper.getRuleEngineGenerationVO(mdVO));

        return result;
    }

    /**
     * Get all RuleGenerations
     * @return Collection<RuleEngineGenerationVO>
     * @throws CommonPermissionException
     */
    public Collection<RuleEngineGenerationVO> getAllRuleGenerations() throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        final Collection<RuleEngineGenerationVO> result = new HashSet<RuleEngineGenerationVO>();

        Collection<MasterDataVO> mdVOList = getMasterDataFacade()
                .getMasterData(NuclosEntity.RULEGENERATION.getEntityName(), null, true);

        for (MasterDataVO mdVO : mdVOList)
            result.add(MasterDataWrapper.getRuleEngineGenerationVO(mdVO));

        return result;
    }

    /**
     * Get all RuleGeneration for the given generation.
     * @return Collection<RuleEngineGenerationVO>
     * @throws CommonPermissionException
     */
    public Collection<RuleEngineGenerationVO> getAllRuleGenerationsForGenerationId(Integer generationId)
            throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        final Collection<RuleEngineGenerationVO> result = new HashSet<RuleEngineGenerationVO>();

        CollectableComparison cond = SearchConditionUtils.newMDComparison(
                MasterDataMetaCache.getInstance().getMetaData(NuclosEntity.RULEGENERATION), "generation",
                ComparisonOperator.EQUAL, generationId);
        Collection<MasterDataVO> mdVOList = getMasterDataFacade()
                .getMasterData(NuclosEntity.RULEGENERATION.getEntityName(), cond, true);

        for (MasterDataVO mdVO : mdVOList)
            result.add(MasterDataWrapper.getRuleEngineGenerationVO(mdVO));

        return CollectionUtils.sorted(result, new GeneratorByOrderComparator());
    }

    /**
     * Get all RuleTransitions
     * @return Collection<RuleEngineTransitionVO>
     * @throws CommonPermissionException
     */
    public Collection<RuleEngineTransitionVO> getAllRuleTransitions() throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        final Collection<RuleEngineTransitionVO> result = new HashSet<RuleEngineTransitionVO>();

        Collection<MasterDataVO> mdVOList = getMasterDataFacade()
                .getMasterData(NuclosEntity.RULETRANSITION.getEntityName(), null, true);

        for (MasterDataVO mdVO : mdVOList)
            result.add(MasterDataWrapper.getRuleEngineTransitionVO(mdVO));

        return result;
    }

    /**
     * Get all RuleTransition that have the given rule assigned.
     * @return Collection<RuleEngineTransitionVO>
     * @throws CommonPermissionException
     */
    public Collection<RuleEngineTransitionVO> getAllRuleTransitionsForRuleId(Integer ruleId)
            throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        final Collection<RuleEngineTransitionVO> result = new HashSet<RuleEngineTransitionVO>();

        CollectableComparison cond = SearchConditionUtils.newMDComparison(
                MasterDataMetaCache.getInstance().getMetaData(NuclosEntity.RULETRANSITION), "ruleId",
                ComparisonOperator.EQUAL, ruleId);
        Collection<MasterDataVO> mdVOList = getMasterDataFacade()
                .getMasterData(NuclosEntity.RULETRANSITION.getEntityName(), cond, true);

        for (MasterDataVO mdVO : mdVOList)
            result.add(MasterDataWrapper.getRuleEngineTransitionVO(mdVO));

        return result;
    }

    /**
     * Get all Rule Transition that have the given transition assigned.
     * @return Collection<RuleEngineTransitionVO>
     * @throws CommonPermissionException
     */
    public Collection<RuleEngineTransitionVO> getAllRuleTransitionsForTransitionId(Integer transitionId)
            throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        final Collection<RuleEngineTransitionVO> result = new HashSet<RuleEngineTransitionVO>();

        CollectableComparison cond = SearchConditionUtils.newMDReferenceComparison(
                MasterDataMetaCache.getInstance().getMetaData(NuclosEntity.RULETRANSITION), "transition",
                transitionId);
        Collection<MasterDataVO> mdVOList = getMasterDataFacade()
                .getMasterData(NuclosEntity.RULETRANSITION.getEntityName(), cond, true);

        for (MasterDataVO mdVO : mdVOList)
            result.add(MasterDataWrapper.getRuleEngineTransitionVO(mdVO));

        return CollectionUtils.sorted(result, new TransitionByOrderComparator());
    }

    /**
     * Get all state Models that have a given rule assigned.
     * @return collection of generation actions
     * @throws CommonPermissionException
     */
    public Collection<StateModelVO> getAllStateModelsForRuleId(Integer aRuleId) throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        final Collection<StateModelVO> result = new HashSet<StateModelVO>();
        final Collection<Integer> modelIdSet = new HashSet<Integer>();

        StateFacadeLocal stateFacade = ServerServiceLocator.getInstance().getFacade(StateFacadeLocal.class);
        final Collection<StateModelVO> collstatemodel = stateFacade.findStateModelsByRuleId(aRuleId);
        for (StateModelVO statemodelvo : collstatemodel) {
            // avoid double transitions
            if (!modelIdSet.contains(statemodelvo.getId())) {
                result.add(statemodelvo);
                modelIdSet.add(statemodelvo.getId());
            }
        }
        return result;
    }

    /**
     * Get all Rule engine Generations.
     * @return Collection<RuleEngineGenerationVO>
     * @throws CommonPermissionException
     */
    public Collection<RuleEngineGenerationVO> getAllRuleEngineGenerations() throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        final Collection<RuleEngineGenerationVO> result = new HashSet<RuleEngineGenerationVO>();

        Collection<MasterDataVO> mdVOList = getMasterDataFacade()
                .getMasterData(NuclosEntity.RULEGENERATION.getEntityName(), null, true);

        for (MasterDataVO mdVO : mdVOList)
            result.add(MasterDataWrapper.getRuleEngineGenerationVO(mdVO));

        return result;
    }

    /**
     * Get all rules contained in a specific object generation.
     * @return Collection<RuleVO>
     * @throws CommonPermissionException
     */
    private Collection<RuleVO> findRulesByGeneration(Integer iGenerationId, Boolean after)
            throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        List<RuleVO> rules = new ArrayList<RuleVO>();

        DbQueryBuilder builder = dataBaseHelper.getDbAccess().getQueryBuilder();
        DbQuery<DbTuple> query = builder.createTupleQuery();
        DbFrom t = query.from("T_MD_RULE_GENERATION").alias(SystemFields.BASE_ALIAS);
        query.multiselect(t.baseColumn("INTID_T_MD_RULE", Integer.class),
                t.baseColumn("BLNRUNAFTERWARDS", Boolean.class));
        query.where(builder.equal(t.baseColumn("INTID_T_MD_GENERATION", Integer.class), iGenerationId));
        query.orderBy(builder.asc(t.baseColumn("INTORDER", Integer.class)));

        for (DbTuple res : dataBaseHelper.getDbAccess().executeQuery(query)) {
            Boolean bRuleRunAfterwards = res.get(1, Boolean.class);
            if (bRuleRunAfterwards == null)
                bRuleRunAfterwards = Boolean.FALSE;
            if ((bRuleRunAfterwards && after) || (!bRuleRunAfterwards && !after)) {
                rules.add(RuleCache.getInstance().getRule(res.get(0, Integer.class)));
            }
        }

        return rules;
    }

    /**
     * Get all rule usages
     * @throws CommonPermissionException
     */
    public Collection<RuleEventUsageVO> getAllRuleEventUsage() throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        final Collection<RuleEventUsageVO> result = new HashSet<RuleEventUsageVO>();

        Collection<MasterDataVO> mdVOList = getMasterDataFacade()
                .getMasterData(NuclosEntity.RULEUSAGE.getEntityName(), null, true);

        for (MasterDataVO vo : mdVOList)
            result.add(MasterDataWrapper.getREUsageVO(vo));

        return result;
    }

    /**
     * Get all rule usages of a rule for a certain event.
     * @return collection of state model vo
     * @throws CommonPermissionException
     */
    public Collection<RuleEventUsageVO> getByEventAndRule(String sEventName, Integer iRuleId)
            throws CommonPermissionException {
        this.checkReadAllowed(NuclosEntity.RULE);
        final Collection<RuleEventUsageVO> result = new HashSet<RuleEventUsageVO>();

        Collection<MasterDataVO> mdVOList = getMasterDataFacade()
                .getDependantMasterData(NuclosEntity.RULEUSAGE.getEntityName(), "rule", iRuleId);

        for (MasterDataVO vo : mdVOList)
            if (vo.getField("event").equals(sEventName))
                result.add(MasterDataWrapper.getREUsageVO(vo));

        return result;
    }

    /**
     * Get all referenced entity names for a certain rule event.
     * @return collection of entity names
     * @throws CommonPermissionException
     */
    public Collection<String> getRuleUsageEntityNamesByEvent(String sEventName) throws CommonPermissionException {
        CollectableSearchCondition condEventEQUAL = SearchConditionUtils.newEOComparison(
                NuclosEntity.RULEUSAGE.getEntityName(), "event", ComparisonOperator.EQUAL, sEventName,
                MetaDataServerProvider.getInstance());

        List<EntityObjectVO> entities = NucletDalProvider.getInstance()
                .getEntityObjectProcessor(NuclosEntity.RULEUSAGE.getEntityName())
                .getBySearchExpression(new CollectableSearchExpression(condEventEQUAL));
        final List<String> entityNames = CollectionUtils.transform(entities,
                new Transformer<EntityObjectVO, String>() {
                    @Override
                    public String transform(EntityObjectVO entityVO) {
                        return entityVO.getField("entity", String.class);
                    }
                });
        final List<String> distinctEntityNames = CollectionUtils.selectDistinct(entityNames, TransformerUtils.id());

        return distinctEntityNames;
    }

    /**
     * gets a rule definition from the database by primary key.
     * @param iId primary key of rule definition
     * @return rule value object
     * @throws CommonPermissionException
     */
    public RuleVO get(Integer iId) throws CommonFinderException, CommonPermissionException {
        this.checkRuleExecution();
        return RuleCache.getInstance().getRule(iId);
    }

    /**
     * gets a rule definition from the database by name.
     * NUCLOSINT-743
     * @param ruleName Name of rule definition
     * @return rule value object
     * @throws CommonPermissionException
     */
    public RuleVO get(String ruleName) throws CommonFinderException, CommonPermissionException {
        this.checkRuleExecution();
        return RuleCache.getInstance().getRule(ruleName);
    }

    private void checkRuleExecution() throws CommonPermissionException {
        boolean blnExecuteRulesManually = false;
        if (SecurityCache.getInstance().getAllowedActions(this.getCurrentUserName())
                .contains(Actions.ACTION_EXECUTE_RULE_BY_USER)) {
            blnExecuteRulesManually = true;
        }
        if (!blnExecuteRulesManually)
            this.checkReadAllowed(NuclosEntity.RULE);
    }

    /**
     * create a new rule definition in the database
     * @param rulevo containing the rule
     * @param mpDependants
     * @return same layout as value object
     * @throws CommonPermissionException
     * @precondition (mpDependants != null) -> mpDependants.dependantsAreNew()
     */
    public RuleVO create(RuleVO rulevo, DependantMasterDataMap mpDependants)
            throws CommonCreateException, CommonFinderException, CommonRemoveException, CommonValidationException,
            CommonStaleVersionException, NuclosCompileException, CommonPermissionException {

        this.checkWriteAllowed(NuclosEntity.RULE);
        //check layout for validity
        rulevo.validate(); //throws CommonValidationException

        try {
            for (EntityObjectVO mdvo : mpDependants.getAllData()) {
                mdvo.getFields().put("rule", rulevo.getRule());
                mdvo.getFields().put("ruleId", rulevo.getId());
                mdvo.getFieldIds().put("ruleId", IdUtils.toLongId(rulevo.getId()));
            }

            MasterDataVO mdVO = getMasterDataFacade().create(NuclosEntity.RULE.getEntityName(),
                    MasterDataWrapper.wrapRuleVO(rulevo), mpDependants, null);

            final RuleVO result = MasterDataWrapper.getRuleVO(mdVO);
            if (result.isActive()) {
                check(result);
                nuclosJavaCompilerComponent.forceCompile();
            }
            RuleCache.getInstance().invalidate();
            return result;
        } catch (NuclosBusinessRuleException ex) {
            throw new CommonFatalException(ex);
        }
    }

    /**
     * modify an existing rule definition in the database
     * @param rulevo containing the rule
     * @param mpDependants May be null.
     * @return new rule as value object
     * @throws NuclosCompileException
     * @throws CommonPermissionException
     */
    public RuleVO modify(RuleVO rulevo, DependantMasterDataMap mpDependants)
            throws CommonCreateException, CommonFinderException, CommonRemoveException, CommonStaleVersionException,
            CommonValidationException, NuclosCompileException, CommonPermissionException {

        this.checkWriteAllowed(NuclosEntity.RULE);
        //check layout for validity
        rulevo.validate(); //throws CommonValidationException
        if (rulevo.isActive()) {
            check(rulevo);
            nuclosJavaCompilerComponent.forceCompile();
        }
        try {
            validateUniqueConstraint(rulevo);

            Integer id = (Integer) getMasterDataFacade().modify(NuclosEntity.RULE.getEntityName(),
                    MasterDataWrapper.wrapRuleVO(rulevo), mpDependants, null);
            MasterDataVO mdVO = getMasterDataFacade().get(NuclosEntity.RULE.getEntityName(), id);

            RuleCache.getInstance().invalidate();

            return MasterDataWrapper.getRuleVO(mdVO);
        } catch (NuclosBusinessRuleException ex) {
            throw new CommonFatalException(ex);
        }
    }

    private void validateUniqueConstraint(RuleVO rulevo) throws CommonValidationException {
        CollectableComparison cond = SearchConditionUtils.newMDComparison(
                MasterDataMetaCache.getInstance().getMetaData(NuclosEntity.RULE), "rule", ComparisonOperator.EQUAL,
                rulevo.getRule());
        Collection<MasterDataVO> mdVOList = getMasterDataFacade().getMasterData(NuclosEntity.RULE.getEntityName(),
                cond, true);

        if (mdVOList != null) {
            for (MasterDataVO mdVO : mdVOList) {
                if (rulevo.getId().intValue() != mdVO.getIntId().intValue()) {
                    throw new CommonValidationException(StringUtils
                            .getParameterizedExceptionMessage("validation.unique.constraint", "Name", "Rule"));
                }
            }
        }
    }

    /**
     * delete rule definition from database
     * @param rulevo containing the rule
     * @throws CommonPermissionException
     * @throws NuclosBusinessRuleException
     */
    public void remove(RuleVO rulevo)
            throws CommonFinderException, CommonRemoveException, CommonStaleVersionException,
            CommonPermissionException, NuclosBusinessRuleException, NuclosCompileException {

        this.checkDeleteAllowed(NuclosEntity.RULE);

        if (rulevo.isActive()) {
            nuclosJavaCompilerComponent.check(
                    new RuleCodeGenerator<NuclosRule>(new RuleEngineFacadeBean.RuleTemplateType(), rulevo), true);
        }

        MasterDataVO mdVO = getMasterDataFacade().get(NuclosEntity.RULE.getEntityName(), rulevo.getId());

        // check for stale version:
        if (mdVO.getVersion() != rulevo.getVersion()) {
            throw new CommonStaleVersionException("rule -> master data", rulevo.toDescription(),
                    mdVO.toDescription());
        }
        //mdVO.remove();

        getMasterDataFacade().remove(NuclosEntity.RULE.getEntityName(), mdVO, true, null);
        RuleCache.getInstance().invalidate();
    }

    /**
     * imports the given rules, adding new and overwriting existing rules. The other existing rules are untouched.
     * Currently, only the rule code is imported, not the usages. If one rule cannot be imported, the import will be aborted.
     * @param collRuleWithUsages
     */
    @RolesAllowed("UseManagementConsole")
    public void importRules(Collection<RuleWithUsagesVO> collRuleWithUsages) throws CommonBusinessException {
        for (RuleWithUsagesVO ruleWithUsagesVO : collRuleWithUsages) {
            try {
                this.importRule(ruleWithUsagesVO);
            } catch (CommonBusinessException ex) {
                throw new CommonBusinessException(StringUtils.getParameterizedExceptionMessage("rule.import.error",
                        ruleWithUsagesVO.getName()), ex);
                //"Fehler beim Importieren der Regel \"" + ruleWithUsagesVO.getName() + "\" aufgetreten.", ex);
            }
        }
    }

    /**
     * imports the given rule. If a rule with this name exists already, it will be overwritten, otherwise
     * the new rule will be added.
     * Currently, only the rule code is imported, not the usages.
     * @param ruleWithUsages
     * @throws CommonCreateException
     */
    private void importRule(RuleWithUsagesVO ruleWithUsages)
            throws CommonCreateException, CommonValidationException, CommonStaleVersionException,
            CommonFinderException, CommonRemoveException, CommonPermissionException, NuclosBusinessRuleException {
        final RuleVO rulevoNew = ruleWithUsages.getRuleVO();
        rulevoNew.validate();

        CollectableComparison cond = SearchConditionUtils.newMDComparison(
                MasterDataMetaCache.getInstance().getMetaData(NuclosEntity.RULE), "rule", ComparisonOperator.EQUAL,
                ruleWithUsages.getName());
        Collection<MasterDataVO> mdVOList = getMasterDataFacade().getMasterData(NuclosEntity.RULE.getEntityName(),
                cond, true);

        assert mdVOList.size() <= 1;

        if (mdVOList != null && mdVOList.size() > 0) {
            final RuleVO rulevo = MasterDataWrapper.getRuleVO(mdVOList.iterator().next());
            rulevo.setSource(rulevoNew.getSource());
            getMasterDataFacade().modify(NuclosEntity.RULE.getEntityName(), MasterDataWrapper.wrapRuleVO(rulevo),
                    null, null);
            RuleCache.getInstance().invalidate();
        } else {
            getMasterDataFacade().create(NuclosEntity.RULE.getEntityName(), MasterDataWrapper.wrapRuleVO(rulevoNew),
                    null, null);
            RuleCache.getInstance().invalidate();
        }

        // import event usages:
        /** @todo   */

        // import transition usages:
        /** @todo   */
    }

    /**
     * Compile the user defined code.
     * @param ruleVO
     * @throws NuclosCompileException
     */
    @RolesAllowed("Login")
    public void check(RuleVO ruleVO) throws NuclosCompileException {
        nuclosJavaCompilerComponent.check(
                new RuleCodeGenerator<NuclosRule>(new RuleEngineFacadeBean.RuleTemplateType(), ruleVO), false);
    }

    /**
     * Returns a template for new rules to display in the rule editor.
     * @return String containing class template
     */
    public String getClassTemplate() {
        final StringBuffer sb = new StringBuffer();
        sb.append("/** @name        \n");
        sb.append("  * @description \n");
        sb.append("  * @usage       \n");
        sb.append("  * @change      \n");
        sb.append("*/\n\n");
        sb.append("public void rule(RuleInterface server) throws NuclosBusinessRuleException {\n\n}");
        return sb.toString();
    }

    private static class UsageByOrderComparator implements Comparator<RuleEventUsageVO> {
        @Override
        public int compare(RuleEventUsageVO u1, RuleEventUsageVO u2) {
            return u1.getOrder().compareTo(u2.getOrder());
        }
    }

    private static class GeneratorByOrderComparator implements Comparator<RuleEngineGenerationVO> {
        @Override
        public int compare(RuleEngineGenerationVO u1, RuleEngineGenerationVO u2) {
            return u1.getOrder().compareTo(u2.getOrder());
        }
    }

    private static class TransitionByOrderComparator implements Comparator<RuleEngineTransitionVO> {
        @Override
        public int compare(RuleEngineTransitionVO u1, RuleEngineTransitionVO u2) {
            return u1.getOrder().compareTo(u2.getOrder());
        }
    }

    private static class GenerationOrderComparator implements Comparator<GeneratorActionVO> {
        @Override
        public int compare(GeneratorActionVO g1, GeneratorActionVO g2) {
            return g1.getName().compareTo(g2.getName());
        }
    }

    /**
     * Delete the Output Path Directory.
     */
    @RolesAllowed("UseManagementConsole")
    public void deleteDirectoryOutputPath() {
        final File fOutputPath = nuclosJavaCompilerComponent.getOutputPath();
        final File[] find = fOutputPath.listFiles(new FilenameFilter() {
            @Override
            public boolean accept(File fDir, String sName) {
                return (sName.endsWith(".java") || sName.endsWith(".class"));
            }
        });

        if (find != null) {
            for (File fDelete : find) {
                fDelete.delete();
            }
        }
    }

    /**
     * invalidates the rule cache
     */
    @RolesAllowed("Login")
    public void invalidateCache() {
        RuleCache.getInstance().invalidate();
    }

    private RuleCodeGenerator<NuclosRule> getGenerator(RuleVO ruleVO) {
        return new RuleCodeGenerator<NuclosRule>(new RuleTemplateType(), ruleVO);
    }

    static final String[] IMPORTS = new String[] { "org.nuclos.api.context.*",
            "org.nuclos.common.collect.collectable.CollectableEntity",
            "org.nuclos.common.collect.collectable.CollectableEntityField",
            "org.nuclos.common.collect.collectable.Collectable",
            "org.nuclos.common.collect.collectable.CollectableField",
            "org.nuclos.common.collect.collectable.CollectableValueField",
            "org.nuclos.common.collect.collectable.CollectableValueIdField",
            "org.nuclos.common.collect.collectable.searchcondition.*", "org.nuclos.common.*",
            "org.nuclos.common.mail.*", "org.nuclos.common.fileimport.*", "org.nuclos.server.common.*",
            "org.nuclos.server.common.calendar.CommonDate", "org.nuclos.server.customcode.NuclosRule",
            "org.nuclos.server.genericobject.valueobject.*", "org.nuclos.server.masterdata.valueobject.*",
            "org.nuclos.server.ruleengine.*", "org.nuclos.common.dal.vo.EntityObjectVO", "java.util.*" };

    @Configurable
    public static class RuleTemplateType extends AbstractRuleTemplateType<NuclosRule> {

        private ServerParameterProvider serverParameterProvider;

        private ApplicationProperties applicationProperties;

        public RuleTemplateType() {
            super("Rule", NuclosRule.class);
        }

        @Autowired
        final void setServerParameterProvider(ServerParameterProvider serverParameterProvider) {
            this.serverParameterProvider = serverParameterProvider;
        }

        @Autowired
        final void setApplicationProperties(ApplicationProperties applicationProperties) {
            this.applicationProperties = applicationProperties;
        }

        @Override
        protected List<String> getImports() {
            List<String> imports = new ArrayList<String>();
            CollectionUtils.addAll(imports, IMPORTS);
            final String additionalImports = ServerParameterProvider.getInstance()
                    .getValue(ParameterProvider.KEY_ADDITIONAL_IMPORTS_FOR_RULES);
            if (additionalImports != null) {
                CollectionUtils.addAll(imports, additionalImports.split(","));
            }
            imports.addAll(getWebserviceImports());
            return imports;
        }

        @Override
        protected String getHeaderImpl(RuleVO ruleVO) {
            final String ruleName = getClassName(ruleVO);
            final StringBuilder sb = new StringBuilder();
            sb.append("\npublic class ");
            sb.append(ruleName);
            sb.append(" implements NuclosRule {\n\t");
            sb.append("public ");
            sb.append(ruleName);
            sb.append("() {\n\t}\n\n");
            sb.append("\n// BEGIN RULE\n");
            return sb.toString();
        }

        @Override
        public String getFooter() {
            return "\n// END RULE\n}\n";
        }

        @Override
        public String getLabel() {
            return "Business rule \"{0}\"";
        }

        @Override
        public String getEntityname() {
            return NuclosEntity.RULE.getEntityName();
        }
    }
}