View Javadoc

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