001: /*
002: * $Id: WSProxyService.java 10961 2008-02-22 19:01:02Z dfeist $
003: * --------------------------------------------------------------------------------------
004: * Copyright (c) MuleSource, Inc. All rights reserved. http://www.mulesource.com
005: *
006: * The software in this package is published under the terms of the CPAL v1.0
007: * license, a copy of which has been included with this distribution in the
008: * LICENSE.txt file.
009: */
010:
011: package org.mule.transport.soap;
012:
013: import org.mule.DefaultMuleMessage;
014: import org.mule.MuleServer;
015: import org.mule.api.MuleContext;
016: import org.mule.api.MuleEventContext;
017: import org.mule.api.MuleMessage;
018: import org.mule.api.endpoint.ImmutableEndpoint;
019: import org.mule.api.endpoint.InboundEndpoint;
020: import org.mule.api.lifecycle.Callable;
021: import org.mule.api.lifecycle.Initialisable;
022: import org.mule.api.lifecycle.InitialisationException;
023: import org.mule.api.lifecycle.LifecycleTransitionResult;
024: import org.mule.api.routing.OutboundRouter;
025: import org.mule.api.service.Service;
026: import org.mule.api.service.ServiceAware;
027: import org.mule.config.i18n.CoreMessages;
028: import org.mule.config.i18n.MessageFactory;
029: import org.mule.util.IOUtils;
030: import org.mule.util.StringUtils;
031:
032: import java.io.IOException;
033:
034: import org.apache.commons.logging.Log;
035: import org.apache.commons.logging.LogFactory;
036:
037: /**
038: * This class is implemented to act as a Proxy for a Web Service. It listens for
039: * requests on the inbound endpoint and if it encounters the "WSDL" property in the
040: * address, it will fetch the WSDL from the original web service and return it back.
041: * In case the wsdlFile property is set, when the WSProxyService encounters a request
042: * for the wsdl, instead of fetching the WSDL from the original web service, it will
043: * return back the file expressed in the property. When a normal SOAP request is
044: * encountered, it will forward the call to the web service with no modifications to
045: * the SOAP message. The outbound router of this class must include the address of
046: * the webservice to be proxied. No need to include the method name as a parameter in
047: * the address, since it will be in the SOAP message as well. Furthermore a property
048: * named uriWsdl can optionally be set which as the name suggests, indicate the URL
049: * of the WSDL for the service. If this property is not set, the address of the WSDL
050: * will be assumed to be the value of uriWebservice followed by "?WSDL". It is
051: * important to note that both urls' of the webservice to be proxied and the WSDL
052: * address must contain no xfire or axis endpoints, just plain http endpoints. Even
053: * the inbound endpoint of the WSProxyService must be residing on an http protocol
054: * (with no xfire or axis).
055: *
056: */
057: public class WSProxyService implements Callable, ServiceAware,
058: Initialisable {
059:
060: private String urlWebservice;
061: private String wsdlEndpoint;
062: private String wsdlFile;
063: private String wsdlFileContents;
064: private boolean useFile = false;
065:
066: private Service service;
067:
068: private static final String HTTP_REQUEST = "http.request";
069: private static final String WSDL_PARAM_1 = "?wsdl";
070: private static final String WSDL_PARAM_2 = "&wsdl";
071:
072: /** This is an internal semaphore, not a property */
073: private boolean lazyInit = false;
074:
075: protected static transient Log logger = LogFactory
076: .getLog(WSProxyService.class);
077:
078: /**
079: * @return returns the url of the WSDL
080: */
081: public String getWsdlEndpoint() {
082: return wsdlEndpoint;
083: }
084:
085: /**
086: * @param urlWsdl Sets the property urlWsdl (the url of the WSDL of the web
087: * service)
088: */
089: public void setWsdlEndpoint(String urlWsdl) {
090: this .wsdlEndpoint = urlWsdl;
091: }
092:
093: /**
094: * @return returns the location of the local wsdl
095: */
096: public String getWsdlFile() {
097: return wsdlFile;
098: }
099:
100: /**
101: * @param wsdlFile sets the location of the local wsdl file
102: */
103: public void setWsdlFile(String wsdlFile) {
104: this .wsdlFile = wsdlFile;
105: }
106:
107: public Object onCall(MuleEventContext eventContext)
108: throws Exception {
109: if (wsdlEndpoint == null && lazyInit) {
110: initialise();
111: }
112:
113: // retrieve the message
114: MuleMessage message = eventContext.getMessage();
115:
116: // retrieve the original http request. This will be used to check if the user
117: // asked for the WSDL or just for the service
118: String httpRequest = ((String) message
119: .getProperty(HTTP_REQUEST)).toLowerCase();
120:
121: // check if the inbound endpoint contains the WSDL parameter
122: if ((httpRequest.indexOf(WSDL_PARAM_1) != -1)
123: || (httpRequest.indexOf(WSDL_PARAM_2) != -1)) {
124: logger.debug("Retrieving WSDL from web service");
125:
126: String wsdlString;
127:
128: if (this .useFile) {
129: // the processing is stopped so that the result is not passed through the
130: // outbound router but will be passed back as a result
131: eventContext.setStopFurtherProcessing(true);
132: return wsdlFileContents;
133: }
134: MuleContext muleContext = MuleServer.getMuleContext();
135: InboundEndpoint webServiceEndpoint = muleContext
136: .getRegistry().lookupEndpointFactory()
137: .getInboundEndpoint(this .wsdlEndpoint);
138:
139: MuleMessage replyWSDL = eventContext.requestEvent(
140: webServiceEndpoint, eventContext.getTimeout());
141:
142: wsdlString = replyWSDL.getPayloadAsString();
143:
144: // find all dependencies and change them
145: wsdlString = wsdlString.replaceAll(this .urlWebservice,
146: eventContext.getEndpointURI().getAddress());
147:
148: // create a new mule message with the new WSDL
149: DefaultMuleMessage modifiedWsdl = new DefaultMuleMessage(
150: wsdlString, message);
151:
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: } else
160: // forward the normal call on the outbound router without any modification
161: {
162: logger.debug("Forwarding SOAP message");
163: return eventContext.getMessage();
164: }
165: }
166:
167: // called once upon initialisation
168: public void setService(Service service) {
169: this .service = service;
170: }
171:
172: public LifecycleTransitionResult initialise()
173: throws InitialisationException {
174: if (service != null) {
175: OutboundRouter router = (OutboundRouter) service
176: .getOutboundRouter().getRouters().get(0);
177: ImmutableEndpoint endpoint = (ImmutableEndpoint) router
178: .getEndpoints().get(0);
179: this .urlWebservice = endpoint.getEndpointURI().getAddress();
180:
181: // remove any params from the url
182: int paramIndex;
183: if ((paramIndex = this .urlWebservice.indexOf("?")) != -1) {
184: this .urlWebservice = this .urlWebservice.substring(0,
185: paramIndex);
186: }
187:
188: // if the wsdlFile property is not empty, the onCall() will use this file for WSDL requests
189: if (StringUtils.isNotBlank(this .wsdlFile)) {
190: try {
191: this .wsdlFileContents = IOUtils
192: .getResourceAsString(this .wsdlFile,
193: getClass());
194:
195: if (StringUtils.isNotBlank(this .wsdlFileContents)) {
196: this .useFile = true;
197: logger.info("Using file " + this .wsdlFile
198: + " as WSDL file");
199: }
200: } catch (IOException fileError) {
201: throw new InitialisationException(CoreMessages
202: .failedToLoad(this .wsdlFile), this );
203: }
204: }
205:
206: if (!this .useFile) {
207: // if no wsdl property is set, create one which will include the original
208: // url of the webservice followed by ?WSDL
209: if (StringUtils.isBlank(this .wsdlEndpoint)) {
210: if (urlWebservice == null) {
211: throw new InitialisationException(
212: MessageFactory
213: .createStaticMessage("urlWebservice has not been set, service has not been initialized properly"),
214: this );
215: }
216: this .wsdlEndpoint = this .urlWebservice
217: .concat("?WSDL");
218: logger.info("Defaulting to: " + this .wsdlEndpoint);
219: } else {
220: logger.info("Using url " + this .wsdlEndpoint
221: + " as WSDL");
222: }
223: }
224: } else if (!lazyInit) {
225: // Service not injected yet, try lazy init (i.e., upon onCall()).
226: logger
227: .debug("Service has not yet been injected, lazy initialization will be used.");
228: lazyInit = true;
229: } else {
230: // We're already in lazy init and the service is still not set, so throw an exception.
231: throw new InitialisationException(
232: MessageFactory
233: .createStaticMessage("Service not set, this service has not been initialized properly."),
234: this);
235: }
236: return LifecycleTransitionResult.OK;
237: }
238: }
|