View Javadoc

1   /*
2    * $Id: Validator.java 21683 2011-04-14 22:07:56Z ddossot $
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 java.util.Collections;
14  
15  import org.apache.commons.lang.Validate;
16  import org.mule.MessageExchangePattern;
17  import org.mule.RequestContext;
18  import org.mule.api.ExceptionPayload;
19  import org.mule.api.MuleContext;
20  import org.mule.api.MuleEvent;
21  import org.mule.api.MuleException;
22  import org.mule.api.MuleMessage;
23  import org.mule.api.MuleRuntimeException;
24  import org.mule.api.construct.FlowConstructInvalidException;
25  import org.mule.api.endpoint.InboundEndpoint;
26  import org.mule.api.endpoint.OutboundEndpoint;
27  import org.mule.api.lifecycle.InitialisationException;
28  import org.mule.api.processor.MessageProcessor;
29  import org.mule.api.processor.MessageProcessorChainBuilder;
30  import org.mule.api.routing.filter.Filter;
31  import org.mule.api.source.MessageSource;
32  import org.mule.config.i18n.MessageFactory;
33  import org.mule.expression.ExpressionConfig;
34  import org.mule.expression.transformers.ExpressionArgument;
35  import org.mule.expression.transformers.ExpressionTransformer;
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  public class Validator extends AbstractConfigurationPattern
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      @SuppressWarnings("unchecked")
63      public Validator(String name,
64                       MuleContext muleContext,
65                       MessageSource messageSource,
66                       OutboundEndpoint outboundEndpoint,
67                       Filter validationFilter,
68                       String ackExpression,
69                       String nackExpression,
70                       String errorExpression)
71      {
72          super(name, muleContext, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
73  
74          Validate.notNull(messageSource, "messageSource can't be null");
75          Validate.notNull(outboundEndpoint, "outboundEndpoint can't be null");
76          Validate.notNull(validationFilter, "validationFilter can't be null");
77          Validate.notEmpty(ackExpression, "ackExpression can't be empty");
78          Validate.notEmpty(nackExpression, "nackExpression can't be empty");
79  
80          this.messageSource = messageSource;
81          this.outboundEndpoint = outboundEndpoint;
82          this.validationFilter = validationFilter;
83          this.ackExpression = ackExpression;
84          this.nackExpression = nackExpression;
85          this.errorExpression = errorExpression;
86      }
87  
88      @Override
89      protected void configureMessageProcessorsBeforeTransformation(MessageProcessorChainBuilder builder)
90      {
91          // NOOP
92      }
93  
94      @Override
95      protected void configureMessageProcessorsAfterTransformation(MessageProcessorChainBuilder builder)
96      {
97          final ErrorAwareEventReturningMessageProcessor outboundMessageProcessor = new ErrorAwareEventReturningMessageProcessor();
98          outboundMessageProcessor.setListener(outboundEndpoint);
99  
100         final ResponseMessageProcessorAdapter ackResponseMessageProcessor = new ResponseMessageProcessorAdapter();
101         ackResponseMessageProcessor.setListener(outboundMessageProcessor);
102         ackResponseMessageProcessor.setProcessor(getExpressionTransformer(getName() + "-ack-expression",
103             ackExpression));
104 
105         MessageProcessor validRouteMessageProcessor = ackResponseMessageProcessor;
106 
107         if (hasErrorExpression())
108         {
109             final ErrorExpressionTransformerMessageProcessor errorResponseMessageProcessor = new ErrorExpressionTransformerMessageProcessor(
110                 getExpressionTransformer(getName() + "-error-expression", errorExpression));
111             errorResponseMessageProcessor.setListener(ackResponseMessageProcessor);
112             validRouteMessageProcessor = errorResponseMessageProcessor;
113         }
114 
115         // a simple success/failure choice router determines which response to return
116         final ChoiceRouter choiceRouter = new ChoiceRouter();
117         choiceRouter.addRoute(validRouteMessageProcessor, validationFilter);
118         choiceRouter.setDefaultRoute(getExpressionTransformer(getName() + "-nack-expression", nackExpression));
119         builder.chain(choiceRouter);
120     }
121 
122     @Override
123     protected void validateConstruct() throws FlowConstructInvalidException
124     {
125         super.validateConstruct();
126 
127         validateMessageSource();
128         validateOutboundEndpoint();
129         validateExpression(ackExpression);
130         validateExpression(nackExpression);
131 
132         if (hasErrorExpression())
133         {
134             validateExpression(errorExpression);
135         }
136     }
137 
138     private void validateMessageSource() throws FlowConstructInvalidException
139     {
140         if ((messageSource instanceof InboundEndpoint)
141             && (!((InboundEndpoint) messageSource).getExchangePattern().equals(
142                 MessageExchangePattern.REQUEST_RESPONSE)))
143         {
144             throw new FlowConstructInvalidException(
145                 MessageFactory.createStaticMessage("Validator only works with a request-response inbound endpoint."),
146                 this);
147         }
148     }
149 
150     private void validateOutboundEndpoint() throws FlowConstructInvalidException
151     {
152         if ((hasErrorExpression())
153             && (!outboundEndpoint.getExchangePattern().equals(MessageExchangePattern.REQUEST_RESPONSE)))
154         {
155             throw new FlowConstructInvalidException(
156                 MessageFactory.createStaticMessage("Validator with an error expression only works with a request-response outbound endpoint."),
157                 this);
158         }
159     }
160 
161     protected boolean hasErrorExpression()
162     {
163         return StringUtils.isNotBlank(errorExpression);
164     }
165 
166     private void validateExpression(String expression) throws FlowConstructInvalidException
167     {
168         if (!muleContext.getExpressionManager().isExpression(expression))
169         {
170             throw new FlowConstructInvalidException(
171                 MessageFactory.createStaticMessage("Invalid expression in Validator: " + expression), this);
172         }
173     }
174 
175     private ExpressionTransformer getExpressionTransformer(String name, String expression)
176     {
177         final ExpressionConfig expressionConfig = new ExpressionConfig();
178         expressionConfig.parse(expression);
179 
180         final ExpressionArgument expressionArgument = new ExpressionArgument(name, expressionConfig, false);
181         expressionArgument.setMuleContext(muleContext);
182 
183         final ExpressionTransformer expressionTransformer = new ExpressionTransformer();
184         expressionTransformer.setMuleContext(muleContext);
185         expressionTransformer.addArgument(expressionArgument);
186 
187         try
188         {
189             expressionTransformer.initialise();
190         }
191         catch (final InitialisationException ie)
192         {
193             throw new MuleRuntimeException(ie);
194         }
195 
196         return expressionTransformer;
197     }
198 
199     private static class ErrorExpressionTransformerMessageProcessor extends
200         AbstractInterceptingMessageProcessor
201     {
202         private final ExpressionTransformer errorExpressionTransformer;
203 
204         public ErrorExpressionTransformerMessageProcessor(ExpressionTransformer errorExpressionTransformer)
205         {
206             this.errorExpressionTransformer = errorExpressionTransformer;
207         }
208 
209         public MuleEvent process(MuleEvent event) throws MuleException
210         {
211             final MuleEvent nextResult = super.processNext(event);
212 
213             final ExceptionPayload nextResultMessageExceptionPayload = getExceptionPayload(nextResult);
214 
215             if (nextResultMessageExceptionPayload != null)
216             {
217                 return errorExpressionTransformer.process(event);
218             }
219 
220             return nextResult;
221         }
222     }
223 
224     private static class ErrorAwareEventReturningMessageProcessor extends
225         AbstractInterceptingMessageProcessor
226     {
227         /*
228          * Returns the incoming event whatever the outcome of the rest of the chain maybe. Sets an exception payload on
229          * the incoming event if an error occurred downstream.
230          */
231         public MuleEvent process(MuleEvent event) throws MuleException
232         {
233             final MuleEvent result = RequestContext.setEvent(event);
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 }