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