/*
* JBoss, Home of Professional Open Source
* Copyright 2008, Red Hat Middleware LLC, and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2008, JBoss Inc.
*/
package org.jboss.soa.esb.actions.soap;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URI;
import java.util.Properties;
import javax.servlet.http.HttpServletRequest;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.apache.log4j.Logger;
import org.jboss.internal.soa.esb.publish.ActionContractPublisher;
import org.jboss.internal.soa.esb.publish.ContractInfo;
import org.jboss.internal.soa.esb.publish.ServletContractPublisher;
import org.jboss.internal.soa.esb.util.StreamUtils;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.Service;
import org.jboss.soa.esb.addressing.EPR;
import org.jboss.soa.esb.http.HttpClientFactory;
import org.jboss.soa.esb.lifecycle.LifecycleResourceException;
import org.jboss.soa.esb.listeners.config.Action;
import org.jboss.soa.esb.smooks.resource.SmooksResource;
import org.milyn.Smooks;
import org.milyn.container.ExecutionContext;
import org.xml.sax.SAXException;
/**
* Abstract WSDL contract publisher.
*
* @author <a href="mailto:tom.fennelly@jboss.com">tom.fennelly@jboss.com</a>
*/
public abstract class AbstractWsdlContractPublisher implements ActionContractPublisher, ServletContractPublisher {
private static Logger logger = Logger.getLogger(AbstractWsdlContractPublisher.class);
public static final String REWRITE_ENDPOINT_URL = "rewrite-endpoint-url";
private Properties actionProperties;
private boolean rewriteEndpointUrl = true;
private Smooks transformer;
/**
* Set the {@link SOAPProcessor} action configuration.
* @param actionConfig action config.
* @throws ConfigurationException Bad config.
*/
public void setActionConfig(Action actionConfig) throws ConfigurationException {
actionProperties = actionConfig.getProperties();
final String rewriteEndpointUrlVal = actionProperties.getProperty(AbstractWsdlContractPublisher.REWRITE_ENDPOINT_URL);
if (rewriteEndpointUrlVal != null) {
rewriteEndpointUrl = !rewriteEndpointUrlVal.equals("false");
}
initializeTransformer();
}
/**
* Get the action properties.
* @return The action properties.
*/
public Properties getActionProperties() {
return actionProperties;
}
/**
* Set the action properties.
*/
protected void setActionProperties(Properties actionProperties) {
this.actionProperties = actionProperties;
}
/**
* Get the WSDL Address.
* @return The WSDL address.
*/
public abstract String getWsdlAddress();
/**
* Get the {@link org.jboss.soa.esb.http.HttpClientFactory} properties.
* <p/>
* We use HttpClient (configurable via the {@link org.jboss.soa.esb.http.HttpClientFactory})
* to load the WSDL. This way, we can support different auth mechanisms etc.
*
* @return The {@link org.jboss.soa.esb.http.HttpClientFactory} properties.
*/
public abstract Properties getHttpClientProperties();
/**
* Get the contract configuration.
* <p/>
* This method impl basically returns the Endpoint WSDL, modified for
* the supplied EPR and its channel.
*
* @param epr Endpoint EPR.
* @return WSDL Contract.
*/
public ContractInfo getContractInfo(EPR epr) {
HttpServletRequest httpServletRequestProxy;
httpServletRequestProxy = (HttpServletRequest) Proxy.newProxyInstance(HttpServletRequest.class.getClassLoader(),
new Class[] { HttpServletRequest.class },
new HttpServletRequestHandler());
return getContractInfo(epr, httpServletRequestProxy);
}
public ContractInfo getContractInfo(EPR epr, HttpServletRequest servletRequest) {
String wsdlAddress = getWsdlAddress();
if (wsdlAddress != null) {
String targetServiceCat = servletRequest.getParameter("serviceCat");
String targetServiceName = servletRequest.getParameter("serviceName");
String targetProtocol = servletRequest.getParameter("protocol");
ContractInfo contract;
try {
// Generate the WSDL...
contract = getContractInfo(Service.getService(targetServiceCat, targetServiceName), wsdlAddress);
String data = contract.getData();
if (data != null) {
contract.setData( updateWsdl(data, epr, targetServiceCat, targetServiceName, targetProtocol) );
} else {
throw new Exception("null Contract data");
}
} catch (Exception e) {
String e_msg = "Failed to load WSDL contract information from address '" + wsdlAddress + "': " + e.getMessage();
logger.error(e_msg, e);
contract = new ContractInfo("text/xml", "<definitions><!-- " + e_msg + " --></definitions>");
}
return contract;
} else {
logger.warn("Requested contract info for unknown webservice endpoint.");
return null;
}
}
public ContractInfo getContractInfo(Service service, String wsdlAddress) throws IOException {
return new ContractInfo( "text/xml", getWsdl(wsdlAddress) );
}
/**
* Get the WSDL.
* @param wsdlAddress The WSDL address.
* @return The WSDL.
* @throws IOException Error reading wsdl.
*/
public String getWsdl(String wsdlAddress) throws IOException {
return this.getWsdl(wsdlAddress, null);
}
/**
* Get the WSDL.
* @param wsdlAddress The WSDL address.
* @param charsetNameOverride The charset name override.
* @return The WSDL.
* @throws IOException Error reading wsdl.
*/
public String getWsdl(String wsdlAddress, String charsetNameOverride) throws IOException {
RemoteWsdlLoader loader;
try {
Properties httpClientProperties = getHttpClientProperties();
httpClientProperties = (Properties) httpClientProperties.clone();
if(!httpClientProperties.containsKey(HttpClientFactory.TARGET_HOST_URL)) {
httpClientProperties.setProperty(HttpClientFactory.TARGET_HOST_URL, wsdlAddress);
}
loader = new RemoteWsdlLoader(httpClientProperties);
} catch (ConfigurationException e) {
throw (IOException)(new IOException("Failed to create RemoteWsdlLoader instance.").initCause(e));
}
try {
InputStream wsdlStream = loader.load(wsdlAddress, charsetNameOverride);
try {
return StreamUtils.readStreamString(wsdlStream, "UTF-8");
} finally {
wsdlStream.close();
}
} finally {
loader.shutdown();
}
}
/**
* Update the supplied wsdl to take account of the ESB endpoint proxying of the JBossWS
* invocation, as well as the fact that the transport may be different.
*
* @param wsdl WSDL input.
* @param epr The SOAP endpoint from the ESB perspective.
* @param targetServiceCat
* @param targetServiceName
* @param targetProtocol @return The updated WSDL.
*/
protected String updateWsdl(String wsdl, EPR epr, String targetServiceCat, String targetServiceName, String targetProtocol) throws SAXException, IOException, ConfigurationException {
wsdl = applyTransformer(wsdl, epr, targetServiceCat, targetServiceName, targetProtocol);
return wsdl.trim();
}
/**
* Initialize the endpoint rewriting transformer.
* @throws ConfigurationException Failed to initialize transformer.
*/
protected void initializeTransformer() throws ConfigurationException {
try {
// Initialise the transformer with the rewriting resource configs...
transformer = SmooksResource.createSmooksResource("/org/jboss/soa/esb/actions/soap/wsdltrans.xml");
// And add the user defined config, if there is one....
String wsdlTransformConfig = actionProperties.getProperty("wsdlTransform");
if(wsdlTransformConfig != null) {
try {
transformer.addConfigurations(wsdlTransformConfig);
} catch (IOException e) {
throw new ConfigurationException("Failed to read the User Defined WSDL Transformation config '" + wsdlTransformConfig + "'.", e);
} catch (SAXException e) {
throw new ConfigurationException("Failed to read the User Defined WSDL Transformation config '" + wsdlTransformConfig + "'.", e);
}
}
} catch (final LifecycleResourceException lre) {
throw new ConfigurationException("Failed to read the Endpoint Transformation config for WSDL.", lre);
} catch (IOException e) {
throw new ConfigurationException("Failed to read the Endpoint Transformation config for WSDL.", e);
} catch (SAXException e) {
throw new ConfigurationException("Failed to read the Endpoint Transformation config for WSDL.", e);
}
}
/**
* Perform the endpoint rewrite.
* @param wsdl The WSDL to be transformed.
* @param epr The target endpoint EPR on the ESB.
* @param targetServiceCat The Service Category.
* @param targetServiceName The Service Name.
* @param targetProtocol The target protocol.
* @return The rewritten WSDL.
*/
private String applyTransformer(String wsdl, EPR epr, String targetServiceCat, String targetServiceName, String targetProtocol) {
URI endpointURI = URI.create(epr.getAddr().getAddress());
StringWriter writer = new StringWriter();
ExecutionContext execContext = transformer.createExecutionContext();
execContext.setAttribute(WsdlEndpointTransformer.REWRITE_ENDPOINT_URL, rewriteEndpointUrl);
execContext.setAttribute(WsdlEndpointTransformer.ENDPOINT_URI, endpointURI);
execContext.setAttribute(WsdlEndpointTransformer.TARGET_CAT, (targetServiceCat != null?targetServiceCat:""));
execContext.setAttribute(WsdlEndpointTransformer.TARGET_NAME, (targetServiceName != null?targetServiceName:""));
execContext.setAttribute(WsdlEndpointTransformer.TARGET_PROTOCOL, (targetProtocol != null?targetProtocol:""));
transformer.filterSource(execContext, new StreamSource(new StringReader(wsdl)), new StreamResult(writer));
return writer.toString().trim();
}
private static class HttpServletRequestHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("getRequestURL")) {
return new StringBuffer("http://www.jboss.org");
}
return null;
}
}
}