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