/*
* 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.synapse.mediators.filters;
import org.apache.synapse.ContinuationState;
import org.apache.synapse.Mediator;
import org.apache.synapse.MessageContext;
import org.apache.synapse.SynapseLog;
import org.apache.synapse.aspects.AspectConfiguration;
import org.apache.synapse.aspects.ComponentType;
import org.apache.synapse.aspects.flow.statistics.StatisticIdentityGenerator;
import org.apache.synapse.aspects.flow.statistics.collectors.RuntimeStatisticCollector;
import org.apache.synapse.aspects.flow.statistics.data.artifact.ArtifactHolder;
import org.apache.synapse.config.xml.AnonymousListMediator;
import org.apache.synapse.config.xml.SynapsePath;
import org.apache.synapse.continuation.ContinuationStackManager;
import org.apache.synapse.continuation.ReliantContinuationState;
import org.apache.synapse.core.SynapseEnvironment;
import org.apache.synapse.mediators.AbstractListMediator;
import org.apache.synapse.mediators.FlowContinuableMediator;
import org.apache.synapse.mediators.ListMediator;
import org.apache.synapse.mediators.base.SequenceMediator;
import org.jaxen.JaxenException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* The filter mediator combines the regex and xpath filtering functionality. If an xpath
* is set, it is evaluated; else the given regex is evaluated against the source xpath.
*/
public class FilterMediator extends AbstractListMediator implements
org.apache.synapse.mediators.FilterMediator, FlowContinuableMediator {
private SynapsePath source = null;
private Pattern regex = null;
private SynapsePath xpath = null;
private AnonymousListMediator elseMediator = null;
private boolean thenElementPresent = false;
private String thenKey = null;
private String elseKey = null;
private SynapseEnvironment synapseEnv;
@Override
public void init(SynapseEnvironment se) {
super.init(se);
synapseEnv = se;
if (elseMediator != null) {
elseMediator.init(se);
} else if (elseKey != null) {
SequenceMediator elseSequence =
(SequenceMediator) se.getSynapseConfiguration().
getSequence(elseKey);
if (elseSequence == null || elseSequence.isDynamic()) {
se.addUnavailableArtifactRef(elseKey);
}
}
if (thenKey != null) {
SequenceMediator thenSequence =
(SequenceMediator) se.getSynapseConfiguration().
getSequence(thenKey);
if (thenSequence == null || thenSequence.isDynamic()) {
se.addUnavailableArtifactRef(thenKey);
}
}
}
@Override
public void destroy() {
super.destroy();
if (elseMediator != null) {
elseMediator.destroy();
} else if (elseKey != null) {
SequenceMediator elseSequence =
(SequenceMediator) synapseEnv.getSynapseConfiguration().
getSequence(elseKey);
if (elseSequence == null || elseSequence.isDynamic()) {
synapseEnv.removeUnavailableArtifactRef(elseKey);
}
}
if (thenKey != null) {
SequenceMediator thenSequence =
(SequenceMediator) synapseEnv.getSynapseConfiguration().
getSequence(thenKey);
if (thenSequence == null || thenSequence.isDynamic()) {
synapseEnv.removeUnavailableArtifactRef(thenKey);
}
}
}
/**
* Executes the list of sub/child mediators, if the filter condition is satisfied
*
* @param synCtx the current message
* @return true if filter condition fails. else returns as per List mediator semantics
*/
public boolean mediate(MessageContext synCtx) {
if (synCtx.getEnvironment().isDebuggerEnabled()) {
if (super.divertMediationRoute(synCtx)) {
return true;
}
}
SynapseLog synLog = getLog(synCtx);
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("Start : Filter mediator");
if (synLog.isTraceTraceEnabled()) {
synLog.traceTrace("Message : " + synCtx.getEnvelope());
}
}
boolean result = false;
if (test(synCtx)) {
if (thenKey != null) {
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug((xpath == null ?
"Source : " + source + " against : " + regex.pattern() + " matches" :
"XPath expression : " + xpath + " evaluates to true") +
" - executing then sequence with key : " + thenKey);
}
ContinuationStackManager.updateSeqContinuationState(synCtx, getMediatorPosition());
Mediator seq = synCtx.getSequence(thenKey);
if (seq != null) {
result = seq.mediate(synCtx);
} else {
handleException("Couldn't find the referred then sequence with key : "
+ thenKey, synCtx);
}
} else {
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug((xpath == null ?
"Source : " + source + " against : " + regex.pattern() + " matches" :
"XPath expression : " + xpath + " evaluates to true") +
" - executing child mediators");
}
ContinuationStackManager.
addReliantContinuationState(synCtx, 0, getMediatorPosition());
result = super.mediate(synCtx);
if (result) {
ContinuationStackManager.removeReliantContinuationState(synCtx);
}
}
} else {
if (elseKey != null) {
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug((xpath == null ?
"Source : " + source + " against : " + regex.pattern() + " does not match" :
"XPath expression : " + xpath + " evaluates to false") +
" - executing the else sequence with key : " + elseKey);
}
ContinuationStackManager.updateSeqContinuationState(synCtx, getMediatorPosition());
Mediator elseSeq = synCtx.getSequence(elseKey);
if (elseSeq != null) {
result = elseSeq.mediate(synCtx);
} else {
handleException("Couldn't find the referred else sequence with key : "
+ elseKey, synCtx);
}
} else if (elseMediator != null) {
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug((xpath == null ?
"Source : " + source + " against : " + regex.pattern() + " does not match" :
"XPath expression : " + xpath + " evaluates to false") +
" - executing the else path child mediators");
}
ContinuationStackManager.addReliantContinuationState(synCtx, 1, getMediatorPosition());
result = elseMediator.mediate(synCtx);
if (result) {
ContinuationStackManager.removeReliantContinuationState(synCtx);
}
} else {
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug((xpath == null ?
"Source : " + source + " against : " + regex.pattern() + " does not match" :
"XPath expression : " + xpath + " evaluates to false and no else path") +
" - skipping child mediators");
}
result = true;
}
}
synLog.traceOrDebug("End : Filter mediator ");
return result;
}
public boolean mediate(MessageContext synCtx,
ContinuationState continuationState) {
SynapseLog synLog = getLog(synCtx);
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("Filter mediator : Mediating from ContinuationState");
}
boolean result;
int subBranch = ((ReliantContinuationState) continuationState).getSubBranch();
boolean isStatisticsEnabled = RuntimeStatisticCollector.isStatisticsEnabled();
if (subBranch == 0) {
if (!continuationState.hasChild()) {
result = super.mediate(synCtx, continuationState.getPosition() + 1);
} else {
FlowContinuableMediator mediator =
(FlowContinuableMediator) getChild(continuationState.getPosition());
result = mediator.mediate(synCtx, continuationState.getChildContState());
if (isStatisticsEnabled) {
((Mediator) mediator).reportCloseStatistics(synCtx, null);
}
}
} else {
if (!continuationState.hasChild()) {
result = elseMediator.mediate(synCtx, continuationState.getPosition() + 1);
} else {
FlowContinuableMediator mediator =
(FlowContinuableMediator) elseMediator.getChild(
continuationState.getPosition());
result = mediator.mediate(synCtx, continuationState.getChildContState());
if (isStatisticsEnabled) {
((Mediator) mediator).reportCloseStatistics(synCtx, null);
}
}
if (isStatisticsEnabled) {
elseMediator.reportCloseStatistics(synCtx, null);
}
}
return result;
}
/**
* Tests the supplied condition after evaluation against the given XPath
* or Regex (against a source XPath). When a regular expression is supplied
* the source XPath is evaluated into a String value, and matched against
* the given regex
*
* @param synCtx the current message for evaluation of the test condition
* @return true if evaluation of the XPath/Regex results in true
*/
public boolean test(MessageContext synCtx) {
SynapseLog synLog = getLog(synCtx);
if (xpath != null) {
try {
return xpath.booleanValueOf(synCtx);
} catch (JaxenException e) {
handleException("Error evaluating XPath expression : " + xpath, e, synCtx);
}
} else if (source != null && regex != null) {
String sourceString = source.stringValueOf(synCtx);
if (sourceString == null) {
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("Source String : " + source + " evaluates to null");
}
return false;
}
Matcher matcher = regex.matcher(sourceString);
if (matcher == null) {
if (synLog.isTraceOrDebugEnabled()) {
synLog.traceOrDebug("Regex pattern matcher for : " + regex.pattern() +
"against source : " + sourceString + " is null");
}
return false;
}
return matcher.matches();
}
return false; // never executes
}
public SynapsePath getSource() {
return source;
}
public void setSource(SynapsePath source) {
this.source = source;
}
public Pattern getRegex() {
return regex;
}
public void setRegex(Pattern regex) {
this.regex = regex;
}
public SynapsePath getXpath() {
return xpath;
}
public void setXpath(SynapsePath xpath) {
this.xpath = xpath;
}
public ListMediator getElseMediator() {
return elseMediator;
}
public void setElseMediator(AnonymousListMediator elseMediator) {
this.elseMediator = elseMediator;
}
public boolean isThenElementPresent() {
return thenElementPresent;
}
public void setThenElementPresent(boolean thenElementPresent) {
this.thenElementPresent = thenElementPresent;
}
public String getThenKey() {
return thenKey;
}
public void setThenKey(String thenKey) {
this.thenKey = thenKey;
}
public String getElseKey() {
return elseKey;
}
public void setElseKey(String elseKey) {
this.elseKey = elseKey;
}
@Override
public boolean isContentAware() {
if (xpath != null) {
return xpath.isContentAware();
} else if (source != null) {
return source.isContentAware();
}
return false;
}
@Override public void setComponentStatisticsId(ArtifactHolder holder) {
if (getAspectConfiguration() == null) {
configure(new AspectConfiguration(getMediatorName()));
}
String mediatorId =
StatisticIdentityGenerator.getIdForFlowContinuableMediator(getMediatorName(), ComponentType.MEDIATOR, holder);
getAspectConfiguration().setUniqueId(mediatorId);
String childId;
StatisticIdentityGenerator.reportingBranchingEvents(holder);
if (thenKey != null) {
childId = StatisticIdentityGenerator.getIdReferencingComponent(thenKey, ComponentType.SEQUENCE, holder);
StatisticIdentityGenerator.reportingEndEvent(childId, ComponentType.SEQUENCE, holder);
} else {
setStatisticIdForMediators(holder);
}
StatisticIdentityGenerator.reportingEndBranchingEvent(holder);
StatisticIdentityGenerator.reportingBranchingEvents(holder);
if (elseKey != null) {
childId = StatisticIdentityGenerator.getIdReferencingComponent(elseKey, ComponentType.SEQUENCE, holder);
StatisticIdentityGenerator.reportingEndEvent(childId, ComponentType.SEQUENCE, holder);
} else if (elseMediator != null) {
elseMediator.setStatisticIdForMediators(holder);
}
StatisticIdentityGenerator.reportingFlowContinuableEndEvent(mediatorId, ComponentType.MEDIATOR, holder);
StatisticIdentityGenerator.reportingEndBranchingEvent(holder);
}
}