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.module.cxf;
8   
9   import org.mule.DefaultMuleMessage;
10  import org.mule.api.MuleContext;
11  import org.mule.api.MuleEventContext;
12  import org.mule.api.MuleMessage;
13  import org.mule.api.endpoint.ImmutableEndpoint;
14  import org.mule.api.endpoint.InboundEndpoint;
15  import org.mule.api.lifecycle.Callable;
16  import org.mule.api.lifecycle.Initialisable;
17  import org.mule.api.lifecycle.InitialisationException;
18  import org.mule.api.routing.OutboundRouter;
19  import org.mule.api.routing.OutboundRouterCollection;
20  import org.mule.api.service.Service;
21  import org.mule.api.service.ServiceAware;
22  import org.mule.config.i18n.CoreMessages;
23  import org.mule.config.i18n.MessageFactory;
24  import org.mule.util.IOUtils;
25  import org.mule.util.StringUtils;
26  
27  import java.io.IOException;
28  import java.net.InetAddress;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  
33  /**
34   * This class is implemented to act as a Proxy for a Web Service. It listens for
35   * requests on the inbound endpoint and if it encounters the "WSDL" property in the
36   * address, it will fetch the WSDL from the original web service and return it back.
37   * In case the wsdlFile property is set, when the WSProxyService encounters a request
38   * for the wsdl, instead of fetching the WSDL from the original web service, it will
39   * return back the file expressed in the property. When a normal SOAP request is
40   * encountered, it will forward the call to the web service with no modifications to
41   * the SOAP message. The outbound router of this class must include the address of
42   * the webservice to be proxied. No need to include the method name as a parameter in
43   * the address, since it will be in the SOAP message as well. Furthermore a property
44   * named uriWsdl can optionally be set which as the name suggests, indicate the URL
45   * of the WSDL for the service. If this property is not set, the address of the WSDL
46   * will be assumed to be the value of uriWebservice followed by "?WSDL". It is
47   * important to note that both urls' of the webservice to be proxied and the WSDL
48   * address must contain no cxf or axis endpoints, just plain http endpoints. Even
49   * the inbound endpoint of the WSProxyService must be residing on an http protocol
50   * (with no cxf or axis).
51   */
52  public class WSProxyService implements Callable, ServiceAware, Initialisable
53  {
54  
55      private String urlWebservice;
56      private String wsdlEndpoint;
57      private String wsdlFile;
58      private String wsdlFileContents;
59      private boolean useFile = false;
60  
61      private Service service;
62  
63      private static final String HTTP_REQUEST = "http.request";
64      private static final String WSDL_PARAM_1 = "?wsdl";
65      private static final String WSDL_PARAM_2 = "&wsdl";
66  
67      /** This is an internal semaphore, not a property */
68      private boolean lazyInit = true;
69  
70      protected static transient Log logger = LogFactory.getLog(WSProxyService.class);
71  
72      /**
73       * @return returns the url of the WSDL
74       */
75      public String getWsdlEndpoint()
76      {
77          return wsdlEndpoint;
78      }
79  
80      /**
81       * @param urlWsdl Sets the property urlWsdl (the url of the WSDL of the web
82       *            service)
83       */
84      public void setWsdlEndpoint(String urlWsdl)
85      {
86          this.wsdlEndpoint = urlWsdl;
87      }
88  
89      /**
90       * @return returns the location of the local wsdl
91       */
92      public String getWsdlFile()
93      {
94          return wsdlFile;
95      }
96  
97      /**
98       * @param wsdlFile sets the location of the local wsdl file
99       */
100     public void setWsdlFile(String wsdlFile)
101     {
102         this.wsdlFile = wsdlFile;
103     }
104 
105     public Object onCall(MuleEventContext eventContext) throws Exception
106     {
107         if (wsdlEndpoint == null && lazyInit)
108         {
109             initialise();
110         }
111 
112         // retrieve the message
113         MuleMessage message = eventContext.getMessage();
114 
115         // retrieve the original http request. This will be used to check if the user
116         // asked for the WSDL or just for the service
117         String httpRequest = message.<String>getInboundProperty(HTTP_REQUEST).toLowerCase();
118 
119         // check if the inbound endpoint contains the WSDL parameter
120         if ((httpRequest.indexOf(WSDL_PARAM_1) != -1) || (httpRequest.indexOf(WSDL_PARAM_2) != -1))
121         {
122             logger.debug("Retrieving WSDL from web service");
123 
124             String wsdlString;
125 
126             if (this.useFile)
127             {
128                 // the processing is stopped so that the result is not passed through the
129                 // outbound router but will be passed back as a result
130                 eventContext.setStopFurtherProcessing(true);
131                 return wsdlFileContents;
132             }
133             MuleContext muleContext = eventContext.getMuleContext();
134             InboundEndpoint webServiceEndpoint =
135                 muleContext.getEndpointFactory().getInboundEndpoint(this.wsdlEndpoint);
136 
137             MuleMessage replyWSDL = eventContext.requestEvent(webServiceEndpoint, eventContext.getTimeout());
138 
139             wsdlString = replyWSDL.getPayloadAsString();
140 
141             // create a new mule message with the new WSDL
142             String realWSDLURI = wsdlEndpoint.split("\\?")[0];
143             String proxyWSDLURI = eventContext.getEndpointURI().toString();
144 
145             wsdlString = wsdlString.replaceAll(realWSDLURI, proxyWSDLURI);
146             if (wsdlString.indexOf("localhost") > -1)
147             {
148                 wsdlString = wsdlString.replaceAll("localhost", InetAddress.getLocalHost().getHostName());
149             }
150 
151             DefaultMuleMessage modifiedWsdl = new DefaultMuleMessage(wsdlString, muleContext);
152             logger.debug("WSDL retrieved successfully");
153 
154             // the processing is stopped so that the result is not passed through the
155             // outbound router but will be passed back as a result
156             eventContext.setStopFurtherProcessing(true);
157 
158             return modifiedWsdl;
159         }
160         else
161         // forward the normal call on the outbound router without any modification
162         {
163             logger.debug("Forwarding SOAP message");
164             return eventContext.getMessage().getPayload();
165         }
166     }
167 
168     // called once upon initialisation
169     public void setService(Service service)
170     {
171         this.service = service;
172     }
173 
174     public void initialise() throws InitialisationException
175     {
176         if (service != null)
177         {
178             OutboundRouter router = null;
179             if (service.getOutboundMessageProcessor() instanceof OutboundRouterCollection)
180             {
181                 router = (OutboundRouter) ((OutboundRouterCollection) service.getOutboundMessageProcessor()).getRoutes()
182                     .get(0);
183             }
184             else if (service.getOutboundMessageProcessor() instanceof OutboundRouter)
185             {
186                 router = (OutboundRouter) service.getOutboundMessageProcessor();
187             }
188             else
189             {
190                 throw new IllegalStateException(
191                     "WSProxyService is only supported when using an OutboundRouter");
192             }
193             ImmutableEndpoint endpoint = (ImmutableEndpoint) router.getRoutes().get(0);
194             this.urlWebservice = endpoint.getEndpointURI().getAddress();
195 
196             // remove any params from the url
197             int paramIndex;
198             if ((paramIndex = this.urlWebservice.indexOf("?")) != -1)
199             {
200                 this.urlWebservice = this.urlWebservice.substring(0, paramIndex);
201             }
202 
203             // if the wsdlFile property is not empty, the onCall() will use this file for WSDL requests
204             if (StringUtils.isNotBlank(this.wsdlFile))
205             {
206                 try
207                 {
208                     this.wsdlFileContents = IOUtils.getResourceAsString(this.wsdlFile, getClass());
209 
210                     if (StringUtils.isNotBlank(this.wsdlFileContents))
211                     {
212                         this.useFile = true;
213                         logger.info("Using file " + this.wsdlFile + " as WSDL file");
214                     }
215                 }
216                 catch (IOException fileError)
217                 {
218                     throw new InitialisationException(CoreMessages.failedToLoad(this.wsdlFile), this);
219                 }
220             }
221 
222             if (!this.useFile)
223             {
224                 // if no wsdl property is set, create one which will include the original
225                 // url of the webservice followed by ?WSDL
226                 if (StringUtils.isBlank(this.wsdlEndpoint))
227                 {
228                     if (urlWebservice == null)
229                     {
230                         throw new InitialisationException(MessageFactory.createStaticMessage("urlWebservice has not been set, service has not been initialized properly"), this);
231                     }
232                     this.wsdlEndpoint = this.urlWebservice.concat("?wsdl");
233                     logger.info("Defaulting to: " + this.wsdlEndpoint);
234                 }
235                 else
236                 {
237                     logger.info("Using url " + this.wsdlEndpoint + " as WSDL");
238                 }
239             }
240         }
241         else if (!lazyInit)
242         {
243             // Service not injected yet, try lazy init (i.e., upon onCall()).
244             logger.debug("Service has not yet been injected, lazy initialization will be used.");
245             lazyInit = true;
246         }
247         else
248         {
249             // We're already in lazy init and the service is still not set, so throw an exception.
250             throw new InitialisationException(MessageFactory.createStaticMessage("Service not set, this service has not been initialized properly."), this);
251         }
252     }
253 }