/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., 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-2009
*/
package org.jboss.soa.esb.actions.soap.proxy;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Map.Entry;
import java.util.Set;
import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.jboss.internal.soa.esb.publish.ContractInfo;
import org.jboss.internal.soa.esb.util.StreamUtils;
import org.jboss.mx.util.MBeanServerLocator;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.actions.soap.AuthBASICWsdlContractPublisher;
import org.jboss.soa.esb.actions.soap.WebServiceUtils;
import org.jboss.soa.esb.actions.soap.adapter.JBossWSFactory;
import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.http.HttpClientFactory;
import org.jboss.soa.esb.http.configurators.Connection;
import org.jboss.soa.esb.listeners.ListenerTagNames;
import org.jboss.util.xml.DOMUtils;
import org.jboss.util.xml.DOMWriter;
import org.jboss.wsf.spi.deployment.Endpoint;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* SOAPProxy WSDL loader.
*
* @author dward at jboss.org
* @author <a href="mailto:mageshbk@jboss.com">Magesh Kumar B</a>
*/
public abstract class SOAPProxyWsdlLoader
{
private ConfigTree config;
private String address;
private boolean rewriteHost;
private String serviceCat;
private String serviceName;
private Puller puller = null;
private File tempDir = null;
private ContractInfo contract;
private boolean rewriteLocation;
private File primary_file;
private List<File> cleanup_files = new ArrayList<File>();
protected SOAPProxyWsdlLoader(ConfigTree config, String address, boolean rewriteHost)
{
this.config = config;
this.address = address;
this.rewriteHost = rewriteHost;
ConfigTree parent_config = config.getParent();
serviceCat = (parent_config != null ? parent_config.getAttribute(ListenerTagNames.SERVICE_CATEGORY_NAME_TAG) : null);
serviceName = (parent_config != null ? parent_config.getAttribute(ListenerTagNames.SERVICE_NAME_TAG) : null);
}
public String getAddress()
{
return address;
}
public synchronized Puller getPuller()
{
if (puller == null)
{
puller = new Puller(config);
}
return puller;
}
private File getTempDir() throws IOException
{
if (tempDir == null)
{
synchronized (SOAPProxyWsdlLoader.class)
{
if (tempDir == null)
{
MBeanServer mbeanServer = MBeanServerLocator.locateJBoss();
try
{
tempDir = (File)mbeanServer.getAttribute(new ObjectName("jboss.system:type=ServerConfig"), "ServerTempDir");
}
catch (JMException ignored) {}
String tempName = SOAPProxyWsdlLoader.class.getSimpleName();
if ( tempDir == null || !tempDir.exists() )
{
File tempFile = File.createTempFile(tempName + "-", ".tmp");
tempDir = tempFile.getParentFile();
tempFile.delete();
}
tempDir = new File(tempDir, tempName);
tempDir.mkdirs();
}
}
}
return tempDir;
}
public File createTempFile(String prefix, String suffix) throws IOException
{
File file = File.createTempFile( prefix + "-", suffix, getTempDir() );
cleanup_files.add(file);
return file;
}
public ContractInfo load(boolean rewriteLocation) throws IOException
{
contract = new ContractInfo();
this.rewriteLocation = rewriteLocation;
String address = getAddress();
String protocol = URI.create(address).getScheme();
primary_file = createTempFile(protocol, ".wsdl");
getPuller().pull(address, primary_file);
Map<String,File> fileMap = new LinkedHashMap<String,File>();
fileMap.put(address, primary_file);
pullLocations(primary_file, address, protocol, fileMap);
for ( Entry<String,File> entry : fileMap.entrySet() )
{
editLocations(entry.getValue(), entry.getKey(), protocol, fileMap);
}
// populate the primary member variables (mimeType + data)
String mimeType = "text/xml";
String data = null;
InputStream is = null;
try
{
URL url = getURL();
if (url != null)
{
is = new BufferedInputStream( url.openStream() );
data = StreamUtils.readStreamString(is, "UTF-8");
}
}
finally
{
try { if (is != null) is.close(); } catch (Throwable t) {}
}
contract.setMimeType(mimeType);
contract.setData(data);
return contract;
}
private void pullLocations(File file, String address, String protocol, Map<String,File> fileMap) throws IOException
{
InputStream is = null;
Element parentElement;
try
{
is = new BufferedInputStream( new FileInputStream(file) );
parentElement = DOMUtils.parse(is);
}
finally
{
try { if (is != null) is.close(); } catch (Throwable t) {}
}
pullLocations(parentElement, address, protocol, fileMap);
}
private void pullLocations(Element parentElement, String address, String protocol, Map<String,File> fileMap) throws IOException
{
NodeList nlist = parentElement.getChildNodes();
for (int i = 0; i < nlist.getLength(); i++)
{
Node childNode = nlist.item(i);
if (childNode.getNodeType() == Node.ELEMENT_NODE)
{
Element childElement = (Element)childNode;
String nodeName = childElement.getLocalName();
if ( "import".equals(nodeName) || "include".equals(nodeName) )
{
Attr locationAttr = childElement.getAttributeNode("schemaLocation");
if (locationAttr == null)
{
locationAttr = childElement.getAttributeNode("location");
}
if (locationAttr != null)
{
String location = fixRelative( address, locationAttr.getNodeValue() );
if ( !fileMap.containsKey(location) )
{
File locationFile = createTempFile(protocol, ".tmp");
getPuller().pull(location, locationFile);
fileMap.put(location, locationFile);
pullLocations(locationFile, location, protocol, fileMap);
}
}
}
pullLocations(childElement, address, protocol, fileMap);
}
}
}
private void editLocations(File file, String address, String protocol, Map<String,File> fileMap) throws IOException
{
InputStream is = null;
Element parentElement;
try
{
is = new BufferedInputStream( new FileInputStream(file) );
parentElement = DOMUtils.parse(is);
}
finally
{
try { if (is != null) is.close(); } catch (Throwable t) {}
}
if ( editLocations(parentElement, address, protocol, fileMap) )
{
OutputStream os1 = new BufferedOutputStream( new FileOutputStream(file) );
ByteArrayOutputStream os2 = new ByteArrayOutputStream();
for (OutputStream os : new OutputStream[]{os1, os2})
{
try
{
DOMWriter writer = new DOMWriter(os);
writer.setPrettyprint(true);
writer.print(parentElement);
}
finally
{
try { if (os != null) os.close(); } catch (Throwable t) {}
}
}
contract.putResource( file.getName(), os2.toString("UTF-8") );
}
else
{
contract.putResource( file.getName(), getPuller().pull(file) );
}
}
private boolean editLocations(Element parentElement, String address, String protocol, Map<String,File> fileMap) throws IOException
{
boolean rewrite = false;
NodeList nlist = parentElement.getChildNodes();
for (int i = 0; i < nlist.getLength(); i++)
{
Node childNode = nlist.item(i);
if (childNode.getNodeType() == Node.ELEMENT_NODE)
{
Element childElement = (Element)childNode;
String nodeName = childElement.getLocalName();
if ( "import".equals(nodeName) || "include".equals(nodeName) )
{
Attr locationAttr = childElement.getAttributeNode("schemaLocation");
if (locationAttr == null)
{
locationAttr = childElement.getAttributeNode("location");
}
if (locationAttr != null)
{
String location = fixRelative( address, locationAttr.getNodeValue() );
File locationFile = fileMap.get(location);
String locationFileName = locationFile.getName();
if (rewriteLocation)
{
StringBuilder locationBuilder = new StringBuilder("@REQUEST_URL@?wsdl");
locationBuilder.append("&resource=").append(locationFileName);
if (serviceCat != null)
{
locationBuilder.append("&serviceCat=").append(serviceCat);
}
if (serviceName != null)
{
locationBuilder.append("&serviceName=").append(serviceName);
}
String contractProtocol = (!protocol.toLowerCase().equals("https") ? "http" : "https");
locationBuilder.append("&protocol=").append(contractProtocol);
locationAttr.setNodeValue( locationBuilder.toString() );
}
else
{
locationAttr.setNodeValue(locationFileName);
}
rewrite = true;
}
}
if ( editLocations(childElement, address, protocol, fileMap) )
{
rewrite = true;
}
}
}
return rewrite;
}
String fixRelative(String base_url, String location)
{
location = location.replaceAll("/./", "/");
if ( !location.startsWith("http://") && !location.startsWith("https://") )
{
while ( location.startsWith("./") )
{
location = location.substring( 2, location.length() );
}
if ( location.startsWith("/") )
{
if (rewriteHost)
{
String base_host = base_url.substring( 0, base_url.indexOf("/", base_url.indexOf("://")+3) );
location = base_host + location;
}
}
else
{
int count = 0;
while ( location.startsWith("../") )
{
location = location.substring( 3, location.length() );
count++;
}
String newLocation = base_url.substring( 0, base_url.lastIndexOf("/") );
for (int c = 0; c < count; c++)
{
newLocation = newLocation.substring( 0, newLocation.lastIndexOf("/") );
}
location = newLocation + "/" + location;
}
}
return location;
}
public URL getURL() throws MalformedURLException
{
return primary_file.toURI().toURL();
}
public void cleanup()
{
for (File file : cleanup_files)
{
file.delete();
}
}
public static SOAPProxyWsdlLoader newLoader(ConfigTree config) throws ConfigurationException
{
SOAPProxyWsdlLoader loader;
String address = config.getRequiredAttribute("wsdl");
if ( address.startsWith("http://") || address.startsWith("https://") )
{
final ConfigTree httpClientProps = extractHttpClientConfig(config, address) ;
loader = new DefaultSOAPProxyWsdlLoader(httpClientProps, address, true);
}
else if ( address.startsWith("file://") || address.startsWith("classpath://") )
{
loader = new DefaultSOAPProxyWsdlLoader(config, address, false);
}
else if ( address.startsWith("internal://") )
{
loader = new InternalSOAPProxyWsdlLoader(config, address);
}
else
{
throw new ConfigurationException("unrecognized wsdl address: " + address);
}
return loader;
}
private static ConfigTree extractHttpClientConfig(final ConfigTree config, final String address)
{
final ConfigTree newParent = new ConfigTree("http-client-parent");
final ConfigTree newConfig = new ConfigTree("http-client-config", newParent);
if (config.getBooleanAttribute("wsdlUseHttpClientProperties", false)) {
extractHttpClientAttributes(newConfig, config.getChildren("http-client-property"));
}
final ConfigTree[] wsdlHttpClientConfigTrees = config.getChildren("wsdl-http-client-property");
if (wsdlHttpClientConfigTrees.length > 0) {
extractHttpClientAttributes(newConfig, wsdlHttpClientConfigTrees);
}
final Set<String> attributeNames = config.getAttributeNames();
for(String name: attributeNames) {
final String value = config.getAttribute(name);
if (value != null) {
newConfig.setAttribute(name, value);
}
}
newConfig.setAttribute(Connection.MAX_TOTAL_CONNECTIONS, "1");
newConfig.setAttribute(Connection.MAX_CONNECTIONS_PER_HOST, "1");
final ConfigTree parent = config.getParent();
if (parent != null) {
final String serviceCat = parent.getAttribute(ListenerTagNames.SERVICE_CATEGORY_NAME_TAG);
if (serviceCat != null) {
newParent.setAttribute(ListenerTagNames.SERVICE_CATEGORY_NAME_TAG, serviceCat) ;
}
final String serviceName = parent.getAttribute(ListenerTagNames.SERVICE_NAME_TAG);
if (serviceName != null) {
newParent.setAttribute(ListenerTagNames.SERVICE_NAME_TAG, serviceName) ;
}
}
return newConfig;
}
private static void extractHttpClientAttributes(final ConfigTree configTree, final ConfigTree[] httpClientChildren)
{
for(ConfigTree httpClientChild : httpClientChildren) {
final String name = httpClientChild.getAttribute("name");
final String value = httpClientChild.getAttribute("value");
if ((name != null) && (value != null)) {
configTree.setAttribute(name, value);
}
}
}
static class DefaultSOAPProxyWsdlLoader extends SOAPProxyWsdlLoader
{
public DefaultSOAPProxyWsdlLoader(ConfigTree config, String address, boolean rewriteHost)
{
super(config, address, rewriteHost);
}
}
private static class InternalSOAPProxyWsdlLoader extends SOAPProxyWsdlLoader
{
public InternalSOAPProxyWsdlLoader(ConfigTree config, String address)
{
super(config, address, true);
}
@Override
public String getAddress()
{
String end_addr = super.getAddress();
String end_name = end_addr.substring( 11, end_addr.length() );
Endpoint end = WebServiceUtils.getDeploymentEndpoint(end_name);
if (end != null)
{
final String wsdlLocation = JBossWSFactory.getFactory().getWsdlLocation(end, end_name);
if (wsdlLocation != null)
{
return wsdlLocation ;
}
}
throw new IllegalStateException("unrecognized internal endpoint: " + end_name);
}
}
private static class Puller extends AuthBASICWsdlContractPublisher
{
private ConfigTree config;
public Puller(ConfigTree config)
{
this.config = config;
}
@Override
public Properties getActionProperties()
{
Properties props = new Properties();
for ( String key : config.getAttributeNames() )
{
String value = config.getAttribute(key);
if (value != null)
{
props.setProperty(key, value);
}
}
return props;
}
public void pull(String url, File file) throws IOException
{
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try
{
// re-using the HTTPClient and AuthBASIC stuff...
String data = getWsdl( url, config.getAttribute("wsdlCharset") );
bis = new BufferedInputStream( new ByteArrayInputStream(data.getBytes()) );
bos = new BufferedOutputStream( new FileOutputStream(file) );
byte[] buf = new byte[1024];
int read = 0;
while ( (read = bis.read(buf)) != -1 )
{
bos.write(buf, 0, read);
}
bos.flush();
}
finally
{
try { if (bos != null) bos.close(); } catch (Throwable t) {}
try { if (bis != null) bis.close(); } catch (Throwable t) {}
}
}
public String pull(File file) throws IOException
{
Reader reader = new BufferedReader( new FileReader(file) );
try
{
return StreamUtils.readReader(reader);
}
finally
{
reader.close();
}
}
}
}