/** * Copyright (c) 2008, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * Licensed 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.wso2.carbon.mediators.router.impl; import org.apache.axis2.AxisFault; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.synapse.MessageContext; import org.apache.synapse.SynapseException; import org.apache.synapse.SynapseLog; import org.apache.synapse.endpoints.Endpoint; import org.apache.synapse.mediators.base.SequenceMediator; import org.apache.synapse.mediators.eip.Target; import org.apache.synapse.util.MessageHelper; import org.apache.synapse.util.xpath.SynapseXPath; import org.jaxen.JaxenException; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * <p>Specifies the <code>Route</code> including the route conditions and the routing of a * particular route for the <code>RouterMediator</code>. This stores the properties of a particular * route and will be used by the <code>RouterMedaitor</code> in order to route the messages.</p> * * <p>This stores the routing path as a <code>Target</code> whihc can contain either a sequence or * else and endpoint as the routing path and in the endpoint case the message will be delivered to * the specified endpoint</p> * * @see RouterMediator * @see org.apache.synapse.mediators.eip.Target */ public class Route { /** * <p>The state of the <code>Route</code> after calling the <code>doRoute</code> method is this * particular route over the specified message does not match the conditions specified in this * route.</p> * * @see Route#doRoute(org.apache.synapse.MessageContext, SynapseLog) */ public static final int ROUTE_NOT_MACHING_STATE = 0; /** * <p>The state of the <code>Route</code> after calling the <code>doRoute</code> method is; this * particular route over the specified message matches the conditions specified in this * route and hence the message is routed, and the message should not go though any more routes * within this router.</p> * * @see Route#doRoute(org.apache.synapse.MessageContext, SynapseLog) */ public static final int ROUTED_WITH_BREAK_STATE = 1; /** * <p>The state of the <code>Route</code> after calling the <code>doRoute</code> method is; this * particular route over the specified message matches the conditions specified in this * route and hence the message is routed, but the message may go through any other matching * routes within this router for further routing.</p> * * @see Route#doRoute(org.apache.synapse.MessageContext, SynapseLog) */ public static final int ROUTED_WITH_CONTINUE_STATE = 2; /** * <p>The state of the <code>Route</code> after calling the <code>doRoute</code> method is; this * particular route over the specified message matches the conditions specified in this * route and hence the message is routed, at the same time the message must not go through any * other matching routes within this router.</p> * * @see Route#doRoute(org.apache.synapse.MessageContext, SynapseLog) */ public static final int ROUTED_AND_DROPPED_STATE = 3; /** * <p>The state of the routing, when there is an error in routing after executing the * <code>doRoute</code> method.<p> * * @see Route#doRoute(org.apache.synapse.MessageContext, SynapseLog) */ public static final int UNEXPECTED_ROUTING_STATE = 4; /** * <p>XPath describing the element or the attirbute of the message which will be matched * againset the <code>match</code> to check the matching. If there is no <code>match</code> * then the presence of this expression will be taken as the matching</p> * * @see org.apache.synapse.util.xpath.SynapseXPath */ private SynapseXPath expression; /** * <p>Regular expression which will be evaluated to see the string value of the * <code>expression</code> evaluated over the current message to check whether that value is * matching to the specified pattern to check the matching</p> * * @see java.util.regex.Pattern */ private Pattern match; /** * <p>Specifies whether to break the route or to continue on further routes on matching this * <code>route</code>. If the value is true, and if the <code>route</code> is matching then no * further routes will be evaluated inside the <code>router</code>, where as if the value is * false the next route will be evaluated to check the matching regardless of whether the * current route matches or not.</p> * * <p>Default value for the <code>breakRouter</code> is true implying that the router will be * breaked after the route. (i.e. no further routes will occur in this router)</p> */ private boolean breakRouter = true; /** * <p>Represents the <code>target</code> of the route and it can specify a <code>sequence</code> * or an <code>endpoint</code> for the matching messages whihc needs to be routed to. At the * same time it can also specify the <code>to</code> address as well as <code>soapAction</code> * for the routing.</p> */ private Target target; /** * <p>Holds the log4j based log for the loggin purposes</p> */ private static final Log log = LogFactory.getLog(Route.class); /** * <p>Checks whether this route is matching for the given message or not. In general if the * <code>expression</code> is provided without a <code>match</code> then the presence of a * element or attribute in the message specified by the <code>expression</code> will be taken as * matching</p> * * <p>If both the <code>expression</code> and the <code>match</code> is provided then the * evaluated string value of the <code>expression</code> over the message will be matched * againset the given regular expression <code>match</code> to test whether the string value is * matching for the pattern or not</p> * * @param synCtx message to be matched to check the route condition * @return whether the route matches the given message or not */ public boolean isMatching(MessageContext synCtx, SynapseLog synLog) { // expression is required to perform the match if (expression != null) { // if the match Pattern is not there then we consider the // existance of the xpath in the message if (match == null) { try { // check whether the xpath is present in the message or not return expression.booleanValueOf(synCtx.getEnvelope()); } catch (JaxenException e) { handleException("Error evaluating XPath expression : " + expression, e, synCtx); } } else { String sourceString = expression.stringValueOf(synCtx); if (sourceString == null) { if (synLog.isTraceOrDebugEnabled()) { synLog.traceOrDebug("Source String : " + expression + " evaluates to null"); } return false; } Matcher matcher = match.matcher(sourceString); if (matcher == null) { if (synLog.isTraceTraceEnabled()) { synLog.traceOrDebug("Regex pattern matcher for : " + match.pattern() + "against source : " + sourceString + " is null"); } return false; } // matchese the string value of the xpath over the message and // evalutaes with the specifeid regEx return matcher.matches(); } } else { handleException("Couldn't find the routing expression", synCtx); } return false; // never executes } /** * <p>Routes the message in to the specified <code>target</code> whihc can be one of the * <code>sequence</code> or else and <code>endpoint</code> after checking whether the conditions * specified in this route matches the provided message.</p> * * <p>Also if this particular router specifies the <code>breakRouter</code> to be false then the * specified status will be returned to not to break the route and route further matchings in * the router. Otherwise it will break the route</p> * * @param synCtx message to be routed * @return state of the routing and this can be one of the ROUTE_NOT_MACHING_STATE, or * ROUTED_WITH_BREAK_STATE, or ROUTED_WITH_CONTINUE state. * * @see Route#isMatching(org.apache.synapse.MessageContext, SynapseLog) */ public int doRoute(MessageContext synCtx, SynapseLog synLog) { // first check whether this Route matches the specified conditions if (isMatching(synCtx, synLog)) { // route the messages to the specified target if (target != null) { boolean mediationResult = true; target.setAsynchronous(false); target.mediate(synCtx); return breakRouter ? ROUTED_WITH_BREAK_STATE : ROUTED_WITH_CONTINUE_STATE; } else { handleException("Couldn't find the route target for the message", synCtx); } } else { // the route conditions do not match the message return ROUTE_NOT_MACHING_STATE; } // does not execute under normal circumstances return UNEXPECTED_ROUTING_STATE; } private void sendToEndpoint(MessageContext synCtx) { if (target.getEndpoint() != null) { target.getEndpoint().send(synCtx); } else if (target.getEndpointRef() != null) { Endpoint epr = synCtx.getConfiguration().getEndpoint(target.getEndpointRef()); if (epr != null) { epr.send(synCtx); } } } public Pattern getMatch() { return match; } public void setMatch(Pattern match) { this.match = match; } public SynapseXPath getExpression() { return expression; } public void setExpression(SynapseXPath expression) { this.expression = expression; } public boolean isBreakRouter() { return breakRouter; } public void setBreakRouter(boolean breakRouter) { this.breakRouter = breakRouter; } public Target getTarget() { return target; } public void setTarget(Target target) { this.target = target; } private void handleException(String msg, Exception e, MessageContext msgContext) { log.error(msg, e); if (msgContext.getServiceLog() != null) { msgContext.getServiceLog().error(msg, e); } throw new SynapseException(msg, e); } private void handleException(String msg, MessageContext msgContext) { log.error(msg); if (msgContext.getServiceLog() != null) { msgContext.getServiceLog().error(msg); } throw new SynapseException(msg); } }