View Javadoc

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