1
2
3
4
5
6
7 package org.mule.module.ws.construct;
8
9 import org.mule.MessageExchangePattern;
10 import org.mule.api.MessagingException;
11 import org.mule.api.MuleContext;
12 import org.mule.api.MuleEvent;
13 import org.mule.api.MuleException;
14 import org.mule.api.MuleMessage;
15 import org.mule.api.construct.FlowConstructInvalidException;
16 import org.mule.api.endpoint.InboundEndpoint;
17 import org.mule.api.endpoint.OutboundEndpoint;
18 import org.mule.api.expression.ExpressionManager;
19 import org.mule.api.expression.ExpressionRuntimeException;
20 import org.mule.api.processor.MessageProcessor;
21 import org.mule.api.processor.MessageProcessorChainBuilder;
22 import org.mule.api.source.MessageSource;
23 import org.mule.config.i18n.MessageFactory;
24 import org.mule.construct.AbstractFlowConstruct;
25 import org.mule.construct.processor.FlowConstructStatisticsMessageProcessor;
26 import org.mule.endpoint.DynamicOutboundEndpoint;
27 import org.mule.interceptor.LoggingInterceptor;
28 import org.mule.interceptor.ProcessingTimeInterceptor;
29 import org.mule.processor.ResponseMessageProcessorAdapter;
30 import org.mule.processor.StopFurtherMessageProcessingMessageProcessor;
31 import org.mule.transformer.TransformerTemplate;
32 import org.mule.transformer.TransformerTemplate.TransformerCallback;
33 import org.mule.util.ObjectUtils;
34 import org.mule.util.StringUtils;
35
36 import java.net.InetAddress;
37 import java.net.URI;
38 import java.net.UnknownHostException;
39
40 import org.apache.commons.logging.Log;
41 import org.apache.commons.logging.LogFactory;
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class WSProxy extends AbstractFlowConstruct
59 {
60 private final AbstractProxyRequestProcessor proxyMessageProcessor;
61 private final OutboundEndpoint outboundEndpoint;
62
63 public WSProxy(String name,
64 MuleContext muleContext,
65 MessageSource messageSource,
66 OutboundEndpoint outboundEndpoint) throws MuleException
67 {
68 this(name, muleContext, messageSource, outboundEndpoint, new DynamicWsdlProxyRequestProcessor(
69 outboundEndpoint));
70 }
71
72 public WSProxy(String name,
73 MuleContext muleContext,
74 MessageSource messageSource,
75 OutboundEndpoint outboundEndpoint,
76 String wsdlContents) throws MuleException
77 {
78 this(name, muleContext, messageSource, outboundEndpoint, new StaticWsdlProxyRequestProcessor(
79 wsdlContents));
80 }
81
82 public WSProxy(String name,
83 MuleContext muleContext,
84 MessageSource messageSource,
85 OutboundEndpoint outboundEndpoint,
86 URI wsdlUri) throws MuleException
87 {
88 this(name, muleContext, messageSource, outboundEndpoint,
89 new DynamicWsdlProxyRequestProcessor(wsdlUri));
90 }
91
92 private WSProxy(String name,
93 MuleContext muleContext,
94 MessageSource messageSource,
95 OutboundEndpoint outboundEndpoint,
96 AbstractProxyRequestProcessor proxyMessageProcessor) throws MuleException
97 {
98 super(name, muleContext);
99
100 if (messageSource == null)
101 {
102 throw new FlowConstructInvalidException(
103 MessageFactory.createStaticMessage("messageSource can't be null on: " + this.toString()),
104 this);
105 }
106
107 super.setMessageSource(messageSource);
108
109 if (outboundEndpoint == null)
110 {
111 throw new FlowConstructInvalidException(
112 MessageFactory.createStaticMessage("outboundEndpoint can't be null on: " + this.toString()),
113 this);
114 }
115
116 this.outboundEndpoint = outboundEndpoint;
117 proxyMessageProcessor.setOutboundAddress((outboundEndpoint.getAddress()));
118
119 this.proxyMessageProcessor = proxyMessageProcessor;
120 }
121
122 @Override
123 protected void configureMessageProcessors(MessageProcessorChainBuilder builder)
124 {
125 builder.chain(new ProcessingTimeInterceptor());
126 builder.chain(new LoggingInterceptor());
127 builder.chain(new FlowConstructStatisticsMessageProcessor());
128 builder.chain(proxyMessageProcessor);
129 builder.chain(new StopFurtherMessageProcessingMessageProcessor());
130 final TransformerTemplate copyInboundToOutboundPropertiesTransformer = new TransformerTemplate(new CopyInboundToOutboundPropertiesTransformerCallback());
131 builder.chain(copyInboundToOutboundPropertiesTransformer);
132 builder.chain(new ResponseMessageProcessorAdapter(copyInboundToOutboundPropertiesTransformer));
133 builder.chain(outboundEndpoint);
134 }
135
136 @Override
137 protected void validateConstruct() throws FlowConstructInvalidException
138 {
139 super.validateConstruct();
140
141 if ((messageSource instanceof InboundEndpoint)
142 && (!((InboundEndpoint) messageSource).getExchangePattern().equals(
143 MessageExchangePattern.REQUEST_RESPONSE)))
144 {
145 throw new FlowConstructInvalidException(
146 MessageFactory.createStaticMessage("WSProxy only works with a request-response inbound endpoint."),
147 this);
148 }
149
150 if (!outboundEndpoint.getExchangePattern().equals(MessageExchangePattern.REQUEST_RESPONSE))
151 {
152 throw new FlowConstructInvalidException(
153 MessageFactory.createStaticMessage("WSProxy only works with a request-response outbound endpoint."),
154 this);
155 }
156 }
157
158 private static final class CopyInboundToOutboundPropertiesTransformerCallback
159 implements TransformerCallback
160 {
161 public Object doTransform(MuleMessage message) throws Exception
162 {
163 for (final String inboundPropertyName : message.getInboundPropertyNames())
164 {
165 message.setOutboundProperty(inboundPropertyName,
166 message.getInboundProperty(inboundPropertyName));
167 }
168
169 return message;
170 }
171 }
172
173 private static abstract class AbstractProxyRequestProcessor implements MessageProcessor
174 {
175 private static final String HTTP_REQUEST = "http.request";
176 private static final String WSDL_PARAM_1 = "?wsdl";
177 private static final String WSDL_PARAM_2 = "&wsdl";
178 private static final String LOCALHOST = "localhost";
179
180 protected final Log logger = LogFactory.getLog(WSProxy.class);
181
182 private String outboundAddress;
183
184 protected void setOutboundAddress(String outboundAddress)
185 {
186 this.outboundAddress = outboundAddress;
187 }
188
189 public MuleEvent process(MuleEvent event) throws MuleException
190 {
191 if (isWsdlRequest(event))
192 {
193 return buildWsdlResult(event);
194 }
195
196 if (logger.isDebugEnabled())
197 {
198 logger.debug("Forwarding SOAP message");
199 }
200
201 return event;
202 }
203
204 private MuleEvent buildWsdlResult(MuleEvent event) throws MuleException
205 {
206 try
207 {
208 String wsdlContents = getWsdlContents(event);
209 wsdlContents = modifyServiceAddress(wsdlContents, event);
210 event.getMessage().setPayload(wsdlContents);
211
212
213
214 event.setStopFurtherProcessing(true);
215 return event;
216 }
217 catch (final Exception e)
218 {
219 throw new MessagingException(
220 MessageFactory.createStaticMessage("Impossible to retrieve WSDL for proxied service"),
221 event, e);
222 }
223 }
224
225 private String modifyServiceAddress(String wsdlContents, MuleEvent event) throws UnknownHostException
226 {
227
228 String inboundAddress = event.getEndpoint().getAddress();
229 try
230 {
231 String substitutedAddress = outboundAddress;
232 ExpressionManager expressionManager = event.getMuleContext().getExpressionManager();
233 if (expressionManager.isValidExpression(outboundAddress))
234 {
235 substitutedAddress = expressionManager.parse(outboundAddress, event.getMessage(), true);
236 }
237 wsdlContents = wsdlContents.replaceAll(substitutedAddress, inboundAddress);
238 }
239 catch (ExpressionRuntimeException ex)
240 {
241 logger.warn("Unable to construct outbound address for WSDL request to proxied dynamic endpoint " + outboundAddress);
242 }
243
244
245 if (wsdlContents.indexOf(LOCALHOST) > -1)
246 {
247 wsdlContents = wsdlContents.replaceAll(LOCALHOST, InetAddress.getLocalHost().getHostName());
248 }
249
250 if (logger.isDebugEnabled())
251 {
252 logger.debug("WSDL modified successfully");
253 }
254
255 return wsdlContents;
256 }
257
258 private boolean isWsdlRequest(MuleEvent event) throws MuleException
259 {
260
261
262 final String httpRequest = event.getMessage().<String> getInboundProperty(HTTP_REQUEST);
263
264 if (httpRequest == null)
265 {
266 logger.warn("WS Proxy can't rewrite WSDL for non-HTTP " + event);
267 return false;
268 }
269
270 final String lowerHttpRequest = httpRequest.toLowerCase();
271
272
273 return (lowerHttpRequest.indexOf(WSDL_PARAM_1) != -1)
274 || (lowerHttpRequest.indexOf(WSDL_PARAM_2) != -1);
275 }
276
277 protected abstract String getWsdlContents(MuleEvent event) throws Exception;
278 }
279
280 private static class StaticWsdlProxyRequestProcessor extends AbstractProxyRequestProcessor
281 {
282 private final String wsdlContents;
283
284
285
286
287
288
289
290
291 StaticWsdlProxyRequestProcessor(String wsdlContents) throws FlowConstructInvalidException
292 {
293 if (StringUtils.isBlank(wsdlContents))
294 {
295 throw new FlowConstructInvalidException(
296 MessageFactory.createStaticMessage("wsdlContents can't be empty"));
297 }
298
299 this.wsdlContents = wsdlContents;
300 }
301
302 @Override
303 protected String getWsdlContents(MuleEvent event) throws Exception
304 {
305 if (logger.isDebugEnabled())
306 {
307 logger.debug("Serving static WSDL");
308 }
309
310 return wsdlContents;
311 }
312 }
313
314 private static class DynamicWsdlProxyRequestProcessor extends AbstractProxyRequestProcessor
315 {
316 private interface WsdlAddressProvider
317 {
318 String get(MuleEvent event);
319 }
320
321 private final WsdlAddressProvider wsdlAddressProvider;
322
323
324
325
326
327
328
329
330 DynamicWsdlProxyRequestProcessor(final URI wsdlUri) throws FlowConstructInvalidException
331 {
332 if (wsdlUri == null)
333 {
334 throw new FlowConstructInvalidException(
335 MessageFactory.createStaticMessage("wsdlUri can't be null"));
336 }
337
338 final String wsdlAddress = wsdlUri.toString();
339
340 wsdlAddressProvider = new WsdlAddressProvider()
341 {
342 public String get(MuleEvent event)
343 {
344 return wsdlAddress;
345 }
346 };
347
348 logger.info("Using url " + wsdlAddress + " as WSDL");
349 }
350
351
352
353
354
355
356
357
358 DynamicWsdlProxyRequestProcessor(OutboundEndpoint outboundEndpoint)
359 throws FlowConstructInvalidException
360 {
361 if (outboundEndpoint == null)
362 {
363 throw new FlowConstructInvalidException(
364 MessageFactory.createStaticMessage("outboundEndpoint can't be null"));
365 }
366
367 final String wsAddress = outboundEndpoint.getAddress();
368
369 if (outboundEndpoint instanceof DynamicOutboundEndpoint)
370 {
371 wsdlAddressProvider = new WsdlAddressProvider()
372 {
373 public String get(MuleEvent event)
374 {
375 final String resolvedWsAddress = event.getMuleContext().getExpressionManager().parse(
376 wsAddress, event.getMessage(), true);
377
378 return makeWsdlAddress(resolvedWsAddress);
379 }
380 };
381
382 logger.info("Using dynamic WSDL with service address: " + wsAddress);
383 }
384 else
385 {
386 final String wsdlAddress = makeWsdlAddress(wsAddress);
387
388 wsdlAddressProvider = new WsdlAddressProvider()
389 {
390 public String get(MuleEvent event)
391 {
392 return wsdlAddress;
393 }
394 };
395
396 logger.info("Setting WSDL address to: " + wsdlAddress);
397 }
398 }
399
400 private static String makeWsdlAddress(String wsAddress)
401 {
402 return StringUtils.substringBefore(wsAddress, "?").concat("?wsdl");
403 }
404
405 @Override
406 protected String getWsdlContents(MuleEvent event) throws Exception
407 {
408 final String wsdlAddress = wsdlAddressProvider.get(event);
409 String wsdlString;
410
411 final MuleContext muleContext = event.getMuleContext();
412 final InboundEndpoint webServiceEndpoint =
413 muleContext.getEndpointFactory().getInboundEndpoint(wsdlAddress);
414
415 if (logger.isDebugEnabled())
416 {
417 logger.debug("Retrieving WSDL from web service with: " + webServiceEndpoint);
418 }
419
420 final MuleMessage replyWSDL = webServiceEndpoint.request(event.getTimeout());
421 wsdlString = replyWSDL.getPayloadAsString();
422
423 return wsdlString;
424 }
425 }
426
427 @Override
428 public String toString()
429 {
430 return ObjectUtils.toString(this);
431 }
432
433 @Override
434 public String getConstructType()
435 {
436 return "Web-Service-Proxy";
437 }
438 }