blob: 3b7d8a97a4a53d45ae703cac59d4cc542a58e49b [file] [log] [blame]
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*******************************************************************************/
package org.apache.ofbiz.entityext.eca;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.ofbiz.base.util.Debug;
import org.apache.ofbiz.base.util.UtilValidate;
import org.apache.ofbiz.base.util.UtilXml;
import org.apache.ofbiz.entity.Delegator;
import org.apache.ofbiz.entity.GenericEntity;
import org.apache.ofbiz.entity.GenericEntityException;
import org.apache.ofbiz.entity.GenericValue;
import org.apache.ofbiz.service.DispatchContext;
import org.w3c.dom.Element;
/**
* Entity event-condition-action rule.
*/
@SuppressWarnings("serial")
public final class EntityEcaRule implements java.io.Serializable {
public static final String module = EntityEcaRule.class.getName();
private final String entityName;
private final String operationName;
private final String eventName;
private final boolean runOnError;
private final List<EntityEcaCondition> conditions;
private final List<Object> actionsAndSets;
private boolean enabled = true;
private final List<String> conditionFieldNames = new ArrayList<String>();
public EntityEcaRule(Element eca) {
this.entityName = eca.getAttribute("entity");
this.operationName = eca.getAttribute("operation");
this.eventName = eca.getAttribute("event");
this.runOnError = "true".equals(eca.getAttribute("run-on-error"));
ArrayList<EntityEcaCondition> conditions = new ArrayList<EntityEcaCondition>();
ArrayList<Object> actionsAndSets = new ArrayList<Object>();
for (Element element: UtilXml.childElementList(eca)) {
if ("condition".equals(element.getNodeName())) {
EntityEcaCondition ecaCond = new EntityEcaCondition(element, true);
conditions.add(ecaCond);
conditionFieldNames.addAll(ecaCond.getFieldNames());
} else if ("condition-field".equals(element.getNodeName())) {
EntityEcaCondition ecaCond = new EntityEcaCondition(element, false);
conditions.add(ecaCond);
conditionFieldNames.addAll(ecaCond.getFieldNames());
} else if ("action".equals(element.getNodeName())) {
actionsAndSets.add(new EntityEcaAction(element));
} else if ("set".equals(element.getNodeName())) {
actionsAndSets.add(new EntityEcaSetField(element));
} else {
Debug.logWarning("Invalid eca child element " + element.getNodeName(), module);
}
}
conditions.trimToSize();
this.conditions = Collections.unmodifiableList(conditions);
actionsAndSets.trimToSize();
this.actionsAndSets = Collections.unmodifiableList(actionsAndSets);
if (Debug.verboseOn()) {
Debug.logVerbose("Conditions: " + conditions, module);
Debug.logVerbose("actions and sets (intermixed): " + actionsAndSets, module);
}
}
public String getEntityName() {
return this.entityName;
}
public String getOperationName() {
return this.operationName;
}
public String getEventName() {
return this.eventName;
}
public boolean getRunOnError() {
return this.runOnError;
}
public List<Object> getActionsAndSets() {
return this.actionsAndSets;
}
public List<EntityEcaCondition> getConditions() {
return this.conditions;
}
public void eval(String currentOperation, DispatchContext dctx, GenericEntity value, boolean isError, Set<String> actionsRun) throws GenericEntityException {
if (!enabled) {
Debug.logInfo("Entity ECA [" + this.entityName + "] on [" + this.eventName + "] is disabled; not running.", module);
return;
}
//Debug.logInfo("eval eeca rule: operation=" + currentOperation + ", in event=" + this.eventName + ", on entity=" + this.entityName + ", for value=" + value, module);
if (isError && !this.runOnError) {
return;
}
if (!"any".equals(this.operationName) && this.operationName.indexOf(currentOperation) == -1) {
return;
}
// Are fields tested in a condition missing? If so, we need to load them
List<String> fieldsToLoad = new ArrayList<String>();
for( String conditionFieldName : conditionFieldNames) {
if( value.get(conditionFieldName) == null) {
fieldsToLoad.add(conditionFieldName);
}
}
if(!fieldsToLoad.isEmpty()) {
Delegator delegator = dctx.getDelegator();
GenericValue oldValue = delegator.findOne(entityName, value.getPrimaryKey(), false);
if(UtilValidate.isNotEmpty(oldValue)) {
for (String fieldName : fieldsToLoad) {
value.put(fieldName, oldValue.get(fieldName));
}
}
}
Map<String, Object> context = new HashMap<String, Object>();
context.putAll(value);
boolean allCondTrue = true;
for (EntityEcaCondition ec: conditions) {
if (!ec.eval(dctx, value)) {
allCondTrue = false;
break;
}
}
if (allCondTrue) {
for (Object actionOrSet: actionsAndSets) {
if (actionOrSet instanceof EntityEcaAction) {
EntityEcaAction ea = (EntityEcaAction) actionOrSet;
// in order to enable OR logic without multiple calls to the given service,
//only execute a given service name once per service call phase
if (actionsRun.add(ea.getServiceName())) {
if (Debug.infoOn()) {
Debug.logInfo("Running Entity ECA Service: " + ea.getServiceName() + ", triggered by rule on Entity: " + value.getEntityName(), module);
}
ea.runAction(dctx, context, value);
}
} else {
EntityEcaSetField sf = (EntityEcaSetField) actionOrSet;
sf.eval(context);
}
}
}
}
/**
* @deprecated Not thread-safe, no replacement.
* @param enabled
*/
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isEnabled() {
return this.enabled;
}
}