View Javadoc

1   /*
2    * $Id: Validator.java 20320 2010-11-24 15:03:31Z dfeist $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.construct;
12  
13  import org.mule.MessageExchangePattern;
14  import org.mule.RequestContext;
15  import org.mule.api.ExceptionPayload;
16  import org.mule.api.MuleContext;
17  import org.mule.api.MuleEvent;
18  import org.mule.api.MuleException;
19  import org.mule.api.MuleMessage;
20  import org.mule.api.MuleRuntimeException;
21  import org.mule.api.construct.FlowConstructInvalidException;
22  import org.mule.api.endpoint.InboundEndpoint;
23  import org.mule.api.endpoint.OutboundEndpoint;
24  import org.mule.api.lifecycle.InitialisationException;
25  import org.mule.api.processor.MessageProcessor;
26  import org.mule.api.processor.MessageProcessorChainBuilder;
27  import org.mule.api.routing.filter.Filter;
28  import org.mule.api.source.MessageSource;
29  import org.mule.config.i18n.MessageFactory;
30  import org.mule.construct.processor.FlowConstructStatisticsMessageProcessor;
31  import org.mule.expression.ExpressionConfig;
32  import org.mule.expression.transformers.ExpressionArgument;
33  import org.mule.expression.transformers.ExpressionTransformer;
34  import org.mule.interceptor.LoggingInterceptor;
35  import org.mule.interceptor.ProcessingTimeInterceptor;
36  import org.mule.message.DefaultExceptionPayload;
37  import org.mule.processor.AbstractInterceptingMessageProcessor;
38  import org.mule.processor.ResponseMessageProcessorAdapter;
39  import org.mule.routing.ChoiceRouter;
40  import org.mule.util.StringUtils;
41  
42  import org.apache.commons.lang.Validate;
43  
44  public class Validator extends AbstractFlowConstruct
45  {
46      private final OutboundEndpoint outboundEndpoint;
47      private final Filter validationFilter;
48      private final String ackExpression;
49      private final String nackExpression;
50      private final String errorExpression;
51  
52      public Validator(String name,
53                       MuleContext muleContext,
54                       MessageSource messageSource,
55                       OutboundEndpoint outboundEndpoint,
56                       Filter validationFilter,
57                       String ackExpression,
58                       String nackExpression)
59      {
60          this(name, muleContext, messageSource, outboundEndpoint, validationFilter, ackExpression,
61              nackExpression, null);
62      }
63  
64      public Validator(String name,
65                       MuleContext muleContext,
66                       MessageSource messageSource,
67                       OutboundEndpoint outboundEndpoint,
68                       Filter validationFilter,
69                       String ackExpression,
70                       String nackExpression,
71                       String errorExpression)
72      {
73          super(name, muleContext);
74  
75          Validate.notNull(messageSource, "messageSource can't be null");
76          Validate.notNull(outboundEndpoint, "outboundEndpoint can't be null");
77          Validate.notNull(validationFilter, "validationFilter can't be null");
78          Validate.notEmpty(ackExpression, "ackExpression can't be empty");
79          Validate.notEmpty(nackExpression, "nackExpression can't be empty");
80  
81          this.messageSource = messageSource;
82          this.outboundEndpoint = outboundEndpoint;
83          this.validationFilter = validationFilter;
84          this.ackExpression = ackExpression;
85          this.nackExpression = nackExpression;
86          this.errorExpression = errorExpression;
87      }
88  
89      @Override
90      protected void configureMessageProcessors(MessageProcessorChainBuilder builder)
91      {
92          builder.chain(new ProcessingTimeInterceptor());
93          builder.chain(new LoggingInterceptor());
94          builder.chain(new FlowConstructStatisticsMessageProcessor());
95  
96          final ErrorAwareEventReturningMessageProcessor outboundMessageProcessor = new ErrorAwareEventReturningMessageProcessor();
97          outboundMessageProcessor.setListener(outboundEndpoint);
98  
99          final ResponseMessageProcessorAdapter ackResponseMessageProcessor = new ResponseMessageProcessorAdapter();
100         ackResponseMessageProcessor.setListener(outboundMessageProcessor);
101         ackResponseMessageProcessor.setProcessor(getExpressionTransformer(getName() + "-ack-expression",
102             ackExpression));
103 
104         MessageProcessor validRouteMessageProcessor = ackResponseMessageProcessor;
105 
106         if (hasErrorExpression())
107         {
108             final ErrorExpressionTransformerMessageProcessor errorResponseMessageProcessor = new ErrorExpressionTransformerMessageProcessor(
109                 getExpressionTransformer(getName() + "-error-expression", errorExpression));
110             errorResponseMessageProcessor.setListener(ackResponseMessageProcessor);
111             validRouteMessageProcessor = errorResponseMessageProcessor;
112         }
113 
114         // a simple success/failure choice router determines which response to return
115         final ChoiceRouter choiceRouter = new ChoiceRouter();
116         choiceRouter.addRoute(validRouteMessageProcessor, validationFilter);
117         choiceRouter.setDefaultRoute(getExpressionTransformer(getName() + "-nack-expression", nackExpression));
118         builder.chain(choiceRouter);
119     }
120 
121     @Override
122     protected void validateConstruct() throws FlowConstructInvalidException
123     {
124         super.validateConstruct();
125 
126         validateMessageSource();
127         validateOutboundEndpoint();
128         validateExpression(ackExpression);
129         validateExpression(nackExpression);
130 
131         if (hasErrorExpression())
132         {
133             validateExpression(errorExpression);
134         }
135     }
136 
137     private void validateMessageSource() throws FlowConstructInvalidException
138     {
139         if ((messageSource instanceof InboundEndpoint)
140             && (!((InboundEndpoint) messageSource).getExchangePattern().equals(
141                 MessageExchangePattern.REQUEST_RESPONSE)))
142         {
143             throw new FlowConstructInvalidException(
144                 MessageFactory.createStaticMessage("Validator only works with a request-response inbound endpoint."),
145                 this);
146         }
147     }
148 
149     private void validateOutboundEndpoint() throws FlowConstructInvalidException
150     {
151         if ((hasErrorExpression())
152             && (!outboundEndpoint.getExchangePattern().equals(MessageExchangePattern.REQUEST_RESPONSE)))
153         {
154             throw new FlowConstructInvalidException(
155                 MessageFactory.createStaticMessage("Validator with an error expression only works with a request-response outbound endpoint."),
156                 this);
157         }
158     }
159 
160     protected boolean hasErrorExpression()
161     {
162         return StringUtils.isNotBlank(errorExpression);
163     }
164 
165     private void validateExpression(String expression) throws FlowConstructInvalidException
166     {
167         if (!muleContext.getExpressionManager().isExpression(expression))
168         {
169             throw new FlowConstructInvalidException(
170                 MessageFactory.createStaticMessage("Invalid expression in Validator: " + expression), this);
171         }
172     }
173 
174     private ExpressionTransformer getExpressionTransformer(String name, String expression)
175     {
176         final ExpressionConfig expressionConfig = new ExpressionConfig();
177         expressionConfig.parse(expression);
178 
179         final ExpressionArgument expressionArgument = new ExpressionArgument(name, expressionConfig, false);
180         expressionArgument.setMuleContext(muleContext);
181 
182         final ExpressionTransformer expressionTransformer = new ExpressionTransformer();
183         expressionTransformer.setMuleContext(muleContext);
184         expressionTransformer.addArgument(expressionArgument);
185 
186         try
187         {
188             expressionTransformer.initialise();
189         }
190         catch (final InitialisationException ie)
191         {
192             throw new MuleRuntimeException(ie);
193         }
194 
195         return expressionTransformer;
196     }
197 
198     private static class ErrorExpressionTransformerMessageProcessor extends
199         AbstractInterceptingMessageProcessor
200     {
201         private final ExpressionTransformer errorExpressionTransformer;
202 
203         public ErrorExpressionTransformerMessageProcessor(ExpressionTransformer errorExpressionTransformer)
204         {
205             this.errorExpressionTransformer = errorExpressionTransformer;
206         }
207 
208         public MuleEvent process(MuleEvent event) throws MuleException
209         {
210             final MuleEvent nextResult = super.processNext(event);
211 
212             final ExceptionPayload nextResultMessageExceptionPayload = getExceptionPayload(nextResult);
213 
214             if (nextResultMessageExceptionPayload != null)
215             {
216                 return errorExpressionTransformer.process(event);
217             }
218 
219             return nextResult;
220         }
221     }
222 
223     private static class ErrorAwareEventReturningMessageProcessor extends
224         AbstractInterceptingMessageProcessor
225     {
226         /*
227          * Returns the incoming event whatever the outcome of the rest of the chain
228          * maybe. Sets an exception payload on the incoming event if an error
229          * occurred downstream.
230          */
231         public MuleEvent process(MuleEvent event) throws MuleException
232         {
233             final MuleEvent result = RequestContext.cloneAndUpdateEventEndpoint(event, this);
234 
235             try
236             {
237                 final MuleEvent nextResult = super.processNext(event);
238 
239                 final ExceptionPayload nextResultMessageExceptionPayload = getExceptionPayload(nextResult);
240 
241                 if (nextResultMessageExceptionPayload != null)
242                 {
243                     result.getMessage().setExceptionPayload(nextResultMessageExceptionPayload);
244                 }
245             }
246             catch (final MuleException me)
247             {
248                 logger.error(me);
249                 result.getMessage().setExceptionPayload(new DefaultExceptionPayload(me));
250             }
251 
252             return result;
253         }
254     }
255 
256     private static ExceptionPayload getExceptionPayload(MuleEvent event)
257     {
258         if (event == null)
259         {
260             return null;
261         }
262 
263         final MuleMessage message = event.getMessage();
264         if (message == null)
265         {
266             return null;
267         }
268 
269         return message.getExceptionPayload();
270     }
271 
272     @Override
273     public String getConstructType()
274     {
275         return "Validator";
276     }
277 }