/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY 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 along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.wsf.stack.jbws;
// $Id: WSDLFilePublisher.java 9270 2009-02-09 14:00:57Z darran.lofthouse@jboss.com $
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.wsdl.Definition;
import javax.wsdl.Import;
import javax.wsdl.factory.WSDLFactory;
import org.jboss.logging.Logger;
import org.jboss.util.NotImplementedException;
import org.jboss.ws.Constants;
import org.jboss.ws.WSException;
import org.jboss.ws.core.utils.ResourceURL;
import org.jboss.ws.metadata.umdm.ServiceMetaData;
import org.jboss.ws.metadata.umdm.UnifiedMetaData;
import org.jboss.ws.metadata.wsdl.WSDLDefinitions;
import org.jboss.ws.tools.wsdl.WSDLWriter;
import org.jboss.wsf.common.DOMUtils;
import org.jboss.wsf.common.IOUtils;
import org.jboss.wsf.spi.SPIProvider;
import org.jboss.wsf.spi.SPIProviderResolver;
import org.jboss.wsf.spi.deployment.ArchiveDeployment;
import org.jboss.wsf.spi.management.ServerConfig;
import org.jboss.wsf.spi.management.ServerConfigFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/** A helper class that publishes the wsdl files and their imports to the server/data/wsdl directory.
*
* @author Thomas.Diesler@jboss.org
* @since 02-June-2004
*/
public class WSDLFilePublisher
{
// provide logging
private static final Logger log = Logger.getLogger(WSDLFilePublisher.class);
// The deployment info for the web service archive
private ArchiveDeployment dep;
// The expected wsdl location in the deployment
private String expLocation;
// The server config
private ServerConfig serverConfig;
public WSDLFilePublisher(ArchiveDeployment dep)
{
this.dep = dep;
SPIProvider spiProvider = SPIProviderResolver.getInstance().getProvider();
serverConfig = spiProvider.getSPI(ServerConfigFactory.class).getServerConfig();
if (dep.getType().toString().endsWith("JSE"))
{
expLocation = "WEB-INF/wsdl/";
}
else
{
expLocation = "META-INF/wsdl/";
}
}
/** Publish the deployed wsdl file to the data directory
*/
public void publishWsdlFiles(UnifiedMetaData wsMetaData) throws IOException
{
String deploymentName = dep.getCanonicalName();
// For each service
for (ServiceMetaData serviceMetaData : wsMetaData.getServices())
{
File wsdlFile = getPublishLocation(deploymentName, serviceMetaData);
wsdlFile.getParentFile().mkdirs();
// Get the wsdl definition and write it to the wsdl publish location
Writer fWriter = null;
try
{
fWriter = IOUtils.getCharsetFileWriter(wsdlFile, Constants.DEFAULT_XML_CHARSET);
WSDLDefinitions wsdlDefinitions = serviceMetaData.getWsdlDefinitions();
new WSDLWriter(wsdlDefinitions).write(fWriter, Constants.DEFAULT_XML_CHARSET);
URL wsdlPublishURL = wsdlFile.toURL();
log.info("WSDL published to: " + wsdlPublishURL);
// udpate the wsdl file location
serviceMetaData.setWsdlLocation(wsdlFile.toURL());
// Process the wsdl imports
Definition wsdl11Definition = wsdlDefinitions.getWsdlOneOneDefinition();
if (wsdl11Definition != null)
{
List<String> published = new LinkedList<String>();
publishWsdlImports(wsdlFile.toURL(), wsdl11Definition, published);
// Publish XMLSchema imports
Document document = wsdlDefinitions.getWsdlDocument();
publishSchemaImports(wsdlFile.toURL(), document.getDocumentElement(), published);
}
else
{
throw new NotImplementedException("WSDL-2.0 imports");
}
}
catch (RuntimeException rte)
{
throw rte;
}
catch (Exception e)
{
throw new WSException("Cannot publish wsdl to: " + wsdlFile, e);
}
finally
{
if (fWriter != null)
{
fWriter.close();
}
}
}
}
/** Publish the wsdl imports for a given wsdl definition
*/
private void publishWsdlImports(URL parentURL, Definition parentDefinition, List<String> published) throws Exception
{
String baseURI = parentURL.toExternalForm();
Iterator it = parentDefinition.getImports().values().iterator();
while (it.hasNext())
{
for (Import wsdlImport : (List<Import>)it.next())
{
String locationURI = wsdlImport.getLocationURI();
Definition subdef = wsdlImport.getDefinition();
// its an external import, don't publish locally
if (locationURI.startsWith("http://") == false)
{
// infinity loops prevention
if (published.contains(locationURI))
{
continue;
}
else
{
published.add(locationURI);
}
URL targetURL = new URL(baseURI.substring(0, baseURI.lastIndexOf("/") + 1) + locationURI);
File targetFile = new File(targetURL.getPath());
targetFile.getParentFile().mkdirs();
WSDLFactory wsdlFactory = WSDLFactory.newInstance();
javax.wsdl.xml.WSDLWriter wsdlWriter = wsdlFactory.newWSDLWriter();
FileWriter fw = new FileWriter(targetFile);
wsdlWriter.writeWSDL(subdef, fw);
fw.close();
if (log.isDebugEnabled())
log.debug("WSDL import published to: " + targetURL);
// recursively publish imports
publishWsdlImports(targetURL, subdef, published);
// Publish XMLSchema imports
Element subdoc = DOMUtils.parse(targetURL.openStream());
publishSchemaImports(targetURL, subdoc, published);
}
}
}
}
/** Publish the schema imports for a given wsdl definition
*/
private void publishSchemaImports(URL parentURL, Element element, List<String> published) throws Exception
{
String baseURI = parentURL.toExternalForm();
Iterator it = DOMUtils.getChildElements(element);
while (it.hasNext())
{
Element childElement = (Element)it.next();
if ("import".equals(childElement.getLocalName()) || "include".equals(childElement.getLocalName()))
{
String schemaLocation = childElement.getAttribute("schemaLocation");
if (schemaLocation.length() > 0)
{
if (schemaLocation.startsWith("http://") == false)
{
// infinity loops prevention
if (published.contains(schemaLocation))
{
continue;
}
else
{
published.add(schemaLocation);
}
URL xsdURL = new URL(baseURI.substring(0, baseURI.lastIndexOf("/") + 1) + schemaLocation);
File targetFile = new File(xsdURL.getPath());
targetFile.getParentFile().mkdirs();
String deploymentName = dep.getCanonicalName();
// get the resource path
int index = baseURI.indexOf(deploymentName);
String resourcePath = baseURI.substring(index + deploymentName.length());
resourcePath = resourcePath.substring(0, resourcePath.lastIndexOf("/"));
if (resourcePath.length() > 0)
resourcePath = resourcePath + "/";
resourcePath = expLocation + resourcePath + schemaLocation;
while (resourcePath.indexOf("//") != -1)
{
resourcePath = resourcePath.replace("//", "/");
}
URL resourceURL = dep.getMetaDataFileURL(resourcePath);
InputStream is = new ResourceURL(resourceURL).openStream();
if (is == null)
throw new IllegalArgumentException("Cannot find schema import in deployment: " + resourcePath);
FileOutputStream fos = null;
try
{
fos = new FileOutputStream(targetFile);
IOUtils.copyStream(fos, is);
}
finally
{
if (fos != null) fos.close();
}
if (log.isDebugEnabled())
log.debug("XMLSchema import published to: " + xsdURL);
// recursively publish imports
Element subdoc = DOMUtils.parse(xsdURL.openStream());
publishSchemaImports(xsdURL, subdoc, published);
}
}
}
else
{
publishSchemaImports(parentURL, childElement, published);
}
}
}
/**
* Delete the published wsdl
*/
public void unpublishWsdlFiles() throws IOException
{
String deploymentDir = (dep.getParent() != null ? dep.getParent().getSimpleName() : dep.getSimpleName());
File serviceDir = new File(serverConfig.getServerDataDir().getCanonicalPath() + "/wsdl/" + deploymentDir);
deleteWsdlPublishDirectory(serviceDir);
}
/**
* Delete the published wsdl document, traversing down the dir structure
*/
private void deleteWsdlPublishDirectory(File dir) throws IOException
{
String[] files = dir.list();
for (int i = 0; files != null && i < files.length; i++)
{
String fileName = files[i];
File file = new File(dir + "/" + fileName);
if (file.isDirectory())
{
deleteWsdlPublishDirectory(file);
}
else
{
if (file.delete() == false)
log.warn("Cannot delete published wsdl document: " + file.toURL());
}
}
// delete the directory as well
dir.delete();
}
/**
* Get the file publish location
*/
private File getPublishLocation(String archiveName, ServiceMetaData serviceMetaData) throws IOException
{
String wsdlLocation = null;
if (serviceMetaData.getWsdlLocation() != null)
wsdlLocation = serviceMetaData.getWsdlLocation().toExternalForm();
else if (serviceMetaData.getWsdlFile() != null)
wsdlLocation = serviceMetaData.getWsdlFile();
if (wsdlLocation == null)
throw new IllegalStateException("Cannot obtain wsdl location for: " + serviceMetaData.getServiceName());
if (log.isDebugEnabled())
log.debug("Publish WSDL file: " + wsdlLocation);
// Only file URLs are supported in <wsdl-publish-location>
String publishLocation = serviceMetaData.getWsdlPublishLocation();
boolean predefinedLocation = publishLocation != null && publishLocation.startsWith("file:");
File locationFile = null;
if (predefinedLocation == false)
{
locationFile = new File(serverConfig.getServerDataDir().getCanonicalPath() + "/wsdl/" + archiveName);
}
else
{
try
{
locationFile = new File(new URL(publishLocation).getPath());
}
catch (MalformedURLException e)
{
throw new IllegalArgumentException("Invalid publish location: " + e.getMessage());
}
}
File wsdlFile;
if (wsdlLocation.indexOf(expLocation) >= 0)
{
wsdlLocation = wsdlLocation.substring(wsdlLocation.indexOf(expLocation) + expLocation.length());
wsdlFile = new File(locationFile + "/" + wsdlLocation);
}
else if (wsdlLocation.startsWith("vfsfile:") || wsdlLocation.startsWith("file:") || wsdlLocation.startsWith("jar:"))
{
wsdlLocation = wsdlLocation.substring(wsdlLocation.lastIndexOf("/") + 1);
wsdlFile = new File(locationFile + "/" + wsdlLocation);
}
else
{
throw new WSException("Invalid wsdlFile '" + wsdlLocation + "', expected in: " + expLocation);
}
return wsdlFile;
}
}