/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.muse.tools.generator;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.wsdl.Definition;
import javax.wsdl.Operation;
import javax.wsdl.PortType;
import javax.wsdl.Types;
import javax.wsdl.WSDLException;
import javax.wsdl.extensions.schema.Schema;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.wsdl.xml.WSDLWriter;
import javax.xml.namespace.QName;
import org.apache.muse.core.Environment;
import org.apache.muse.tools.generator.util.AbstractCommandLineApp;
import org.apache.muse.tools.generator.util.DefinitionInfo;
import org.apache.muse.tools.generator.util.MuseRuntimeException;
import org.apache.muse.tools.generator.util.WsdlEnvironment;
import org.apache.muse.util.CommandLine;
import org.apache.muse.util.messages.Messages;
import org.apache.muse.util.messages.MessagesFactory;
import org.apache.muse.util.xml.XmlUtils;
import org.apache.muse.ws.resource.metadata.MetadataDescriptor;
import org.apache.muse.ws.resource.metadata.WsrmdConstants;
import org.apache.muse.ws.resource.metadata.impl.SimpleMetadataDescriptor;
import org.apache.muse.ws.resource.metadata.impl.WsrmdUtils;
import org.apache.muse.ws.wsdl.WsdlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* Utility class to merge all the capability WSDLs into a single WSDL file
* to represent the resource type. This class will take all of the
* port types in the provided wsdls and make one port type. It will also
* combine the WS-RP documents into one.
*
* @author Andrew Eberbach (aeberbac)
*/
public class WsdlMerge extends AbstractCommandLineApp implements WsdlMergeConstants {
private static Messages _MESSAGES = MessagesFactory.get(WsdlMerge.class);
private static WSDLFactory _factory;
private static WSDLReader _reader;
private static WSDLWriter _writer;
static {
try {
_factory = WSDLFactory.newInstance();
_reader = _factory.newWSDLReader();
_writer = _factory.newWSDLWriter();
_reader.setFeature(WsdlUtils.WSDL4J_VERBOSE_FLAG, false);
} catch (Exception e) {
throw new MuseRuntimeException("NoFactory", _MESSAGES.get("NoFactory"), e);
}
}
/**
* Venerable main method. Take in the arguments, make sure they're valid
* and then try to do the WSDL merging.
*
* @param args The raw command line arguments
*/
public static void main(String[] args) {
try {
CommandLine arguments = parseParameters(args);
createLogger(arguments);
checkHelpArg(arguments);
checkVersionArg(arguments);
File[] files = checkFileArgs(arguments);
String outputFileName = checkOutputArg(arguments);
String outputRMDFileName = checkOutputRMDArg(arguments);
boolean overwrite = checkOverwriteArg(arguments);
String uri = checkUriArg(arguments);
String address = checkAddressArg(arguments);
run(files, outputFileName, outputRMDFileName, overwrite, uri, address);
} catch (Exception e) {
Object[] filler = { Wsdl2JavaConstants.VERBOSE_FLAG };
handleErrorAndExit(_MESSAGES.get("WsdlMergeFailed", filler), e);
}
}
public static void run(File[] files, String outputFileName, String outputRMDFileName, boolean overwrite, String uri, String address) throws FileNotFoundException {
Document[] wsdls = new Document[files.length];
MetadataDescriptor[] metadatas = new MetadataDescriptor[files.length];
for(int i=0; i < files.length; i++) {
Environment env = new WsdlEnvironment(files[i].getAbsoluteFile().getParentFile());
wsdls[i] = WsdlUtils.createWSDL(env, files[i].getName(), true);
//
// wsdl4j will get angry when it tries to resolve these because we're not
// providing the base directory since we've already read them in. So
// we remove the imports.
//
WsdlUtils.removeSchemaReferences(wsdls[i].getDocumentElement());
WsdlUtils.removeWsdlReferences(wsdls[i].getDocumentElement());
if(outputRMDFileName != null) {
metadatas[i] = getMetadataDescriptor(files[i].getName(), env, wsdls[i]);
}
}
Definition mergedWsdl = merge(uri, wsdls, address);
if(outputRMDFileName != null) {
MetadataDescriptor mergedMetadata = merge(outputFileName, getFirstPortType(mergedWsdl).getQName(), metadatas);
setMetadata(mergedMetadata.getName(), outputRMDFileName, mergedWsdl);
writeRmd(outputRMDFileName, mergedMetadata, overwrite);
}
writeWsdl(outputFileName, mergedWsdl, overwrite);
}
private static void setMetadata(String metadataName, String metadataLocation, Definition mergedWsdl) {
mergedWsdl.addNamespace(WsrmdConstants.PREFIX, WsrmdConstants.NAMESPACE_URI);
PortType portType = getFirstPortType(mergedWsdl);
portType.setExtensionAttribute(WsrmdConstants.DESCRIPTOR_ATTR_QNAME, metadataName);
portType.setExtensionAttribute(WsrmdConstants.DESCRIPTOR_LOCATION_ATTR_QNAME, metadataLocation);
}
private static PortType getFirstPortType(Definition wsdl) {
return (PortType) wsdl.getPortTypes().values().iterator().next();
}
public static MetadataDescriptor merge(String wsdlLocation, QName interfaceName, MetadataDescriptor[] metadatas) {
SimpleMetadataDescriptor mergedMetadata = new SimpleMetadataDescriptor(METADATA_NAME, wsdlLocation, interfaceName);
for(int i=0; i < metadatas.length; i++) {
copyMetadata(metadatas[i], mergedMetadata);
}
return mergedMetadata;
}
private static String checkOutputRMDArg(CommandLine arguments) {
return arguments.getFlagValue(RMD_OUTPUT_FLAG);
}
/**
* Print the version and exit.
*
* @param arguments Command line arguments
*/
private static void checkVersionArg(CommandLine arguments) {
if(arguments.hasFlag(WsdlMergeConstants.VERSION_FLAG)) {
handleMessage(getVersion());
handleExit();
}
}
/**
* Merge the collection of WSDLs into one document. This will
* try to take the portTypes and merge them along with the
* WS-RP documents (if they exist).
*
* @param namespaceURI
* The target namespace URI of the generated WSDL
*
* @param wsdlFragments
* The WSDL files to merge
*
* @param address
* The address used for the location attribute on the generated
* service
*
* @return The <code>Definition</code> representing the newly created merged WSDL
*/
public static Definition merge(String namespaceURI, Document[] wsdlFragments, String address) {
DefinitionInfo targetDefinition = new DefinitionInfo(namespaceURI);
DefinitionInfo definition = null;
for (int i = 0; i < wsdlFragments.length; i++) {
try {
definition = new DefinitionInfo(load(wsdlFragments[i]));
} catch (WSDLException e) {
Object[] filler = { new Integer(i) };
throw new MuseRuntimeException("WsdlFragmentParseFailed", _MESSAGES.get("WsdlFragmentParseFailed", filler), e);
}
copyOperations(definition, targetDefinition);
copyProperties(definition, targetDefinition);
}
targetDefinition.createBinding();
targetDefinition.createService(address);
return targetDefinition.getDefinition();
}
private static void copyMetadata(MetadataDescriptor sourceMetadata, MetadataDescriptor targetMetadata) {
if(sourceMetadata == null) {
return;
}
for(Iterator i=sourceMetadata.getPropertyNames().iterator(); i.hasNext();) {
QName nextProperty = (QName)i.next();
if(targetMetadata.hasProperty(nextProperty)) {
Object[] filler = { nextProperty };
throw new MuseRuntimeException("DuplicateProperty", _MESSAGES.get("DuplicateProperty", filler));
}
try {
addProperty(nextProperty, sourceMetadata, targetMetadata);
} catch (Exception e) {
Object[] filler = { nextProperty };
throw new MuseRuntimeException("FailedCopyingProperty", _MESSAGES.get("FailedCopyingProperty", filler), e);
}
}
}
/**
* Copies one propert from a source MetadataDescriptor to a destination MetadataDescriptor.
* Performs a deep-copy.
*
* @param name
* @param source
* @param target
* @throws Exception
*/
public static void addProperty(QName name, MetadataDescriptor source, MetadataDescriptor target) throws Exception {
if(name == null) {
throw new NullPointerException();
}
if(source == null) {
throw new NullPointerException();
}
if(target == null) {
throw new NullPointerException();
}
String mutability = source.getMutability(name);
String modifiability = source.getModifiability(name);
Collection initialValues = new ArrayList(source.getInitialValues(name));
Collection staticValues = new ArrayList(source.getStaticValues(name));
Collection validValues = new ArrayList(source.getValidValues(name));
target.addProperty(name, modifiability, mutability);
target.setInitialValues(name, initialValues);
target.setStaticValues(name, staticValues);
target.setValidValues(name, validValues);
//
// ValidValueRange will only exist if ValidValues was empty
//
if (validValues.isEmpty())
{
target.setLowerBound(name, source.getLowerBound(name));
target.setUpperBound(name, source.getUpperBound(name));
}
Iterator j = source.getExtendedMetadataNames(name).iterator();
while (j.hasNext())
{
QName metadataName = (QName)j.next();
String metadataValue = source.getExtendedMetadata(name, metadataName);
target.setExtendedMetadata(name, metadataName, metadataValue);
}
}
/**
* Copy properties (ie WS-RP Documents) from one WSDL to another.
*
* @param sourceDefinition
* The source WSDL
*
* @param targetDefinition
* The destination (merged) WSDL
*/
private static void copyProperties(DefinitionInfo sourceDefinition,
DefinitionInfo targetDefinition) {
Types types = sourceDefinition.getTypes();
for (Iterator i = types.getExtensibilityElements().iterator(); i
.hasNext();) {
targetDefinition.addSchema((Schema) i.next());
}
targetDefinition.mergeProperties(sourceDefinition);
}
/**
* Copy operations (ie portType operations) from one WSDL to another.
*
* @param sourceDefinition
* The source WSDL
*
* @param targetDefinition
* The destination (merged) WSDL
*/
private static void copyOperations(DefinitionInfo sourceDefinition,
DefinitionInfo targetDefinition) {
// Need to keep a map of the messages we're referencing locally
// so that we don't try to redefine them in the target document
Map localMessageMap = new HashMap();
for (Iterator i = sourceDefinition.getOperations().iterator(); i
.hasNext();) {
Operation nextOperation = (Operation) i.next();
targetDefinition.addOperation(nextOperation, localMessageMap);
}
}
/**
* Check to see if the user provided the help parameters
* and display the appropriate help message (basic or advanced).
*
* @param arguments Command line arguments
*/
private static void checkHelpArg(CommandLine arguments) {
if(arguments.hasFlag(HELP_FLAG) || hasNoArguments(arguments)) {
Object[] filler = new Object[] {
URI_FLAG,
ADDRESS_FLAG,
OUTPUT_FLAG,
URI_FLAG,
ADDRESS_FLAG,
OUTPUT_FLAG,
RMD_OUTPUT_FLAG,
OVERWRITE_FLAG,
VERSION_FLAG,
HELP_FLAG
};
handleMessage(_MESSAGES.get("WsdlMergeHelp",filler,false));
handleExit();
}
}
/**
* Check to make sure an address flag and value were provided.
*
* @param arguments The command line arguments
* @return The address provided
*/
private static String checkAddressArg(CommandLine arguments) {
String addressArg = arguments.getFlagValue(ADDRESS_FLAG);
if (addressArg == null) {
Object filler[] = new Object[] {
ADDRESS_FLAG,
HELP_FLAG
};
throw new MuseRuntimeException("NoAddressFlag", _MESSAGES.get("NoAddressFlag",filler));
}
return addressArg;
}
/**
* Check to make sure a URI flag and value were provided.
*
* @param arguments The command line arguments
* @return The URI provided
*/
private static String checkUriArg(CommandLine arguments) {
String uriArg = arguments.getFlagValue(URI_FLAG);
if (uriArg == null) {
Object filler[] = new Object[] {
URI_FLAG,
HELP_FLAG
};
throw new MuseRuntimeException("NoURIFlag", _MESSAGES.get("NoURIFlag",filler));
}
return uriArg;
}
/**
* Check to make sure an ouptut document flag and value were provided.
*
* @param arguments The command line arguments
* @return The output document
*/
private static String checkOutputArg(CommandLine arguments) {
String outputArg = arguments.getFlagValue(OUTPUT_FLAG);
if(outputArg == null) {
Object filler[] = new Object[] {
OUTPUT_FLAG,
HELP_FLAG
};
throw new MuseRuntimeException("NoOutputFlag", _MESSAGES.get("NoOutputFlag",filler));
}
return outputArg;
}
/**
* Go through all of the parameters that are not flags and
* are not used and assume they are all WSDL documents. Make sure
* all of the documents exist and parse.
*
* @param arguments The command line arguments
* @return A array of <code>Document</code> objects representing the parsed WSDLs.
*/
private static File[] checkFileArgs(CommandLine arguments) {
String[] argFiles = arguments.getArguments();
File[] files = new File[argFiles.length];
for(int i=0; i < argFiles.length; i++) {
files[i] = new File(argFiles[i]);
if(!files[i].exists()) {
Object[] filler = { files[i] };
throw new MuseRuntimeException("NonExistant", _MESSAGES.get("NonExistant", filler));
}
}
if(files.length == 0) {
Object filler[] = { HELP_FLAG };
throw new MuseRuntimeException("NoWSDLs", _MESSAGES.get("NoWSDLs", filler));
}
return files;
}
/**
* Given a <code>File</code> try to parse it and turn it into a
* <code>Definition</code>.
*
* @param wsdl
* The <code>File</code> to parse
* @param environment
*
* @return
* A <code>Defintion</code> if parsing was successful
* @throws WSDLException
*/
private static Definition load(Document document) throws WSDLException {
String parent = null;
return _reader.readWSDL(parent, document);
}
private static void writeRmd(String fileName, MetadataDescriptor metadata, boolean overwrite) {
File metadataDestination = new File(fileName);
if(!metadataDestination.exists() || overwrite) {
try {
checkParentDirectory(metadataDestination.getAbsoluteFile().getParentFile());
Element rmdDoc = WsrmdUtils.createMetadataDocument(metadata);
FileWriter fileWriter = new FileWriter(metadataDestination);
fileWriter.write(XmlUtils.toString(rmdDoc));
fileWriter.close();
} catch (Exception e) {
Object[] filler = {metadataDestination.getAbsolutePath()};
throw new MuseRuntimeException("FailedWritingRmd", _MESSAGES.get("FailedWritingRmd", filler), e);
}
} else {
handleMessage(_MESSAGES.get("NotOverwriting", new String[] {metadataDestination.getPath()}));
}
}
private static void writeWsdl(String fileName, Definition mergedDefinition, boolean overwrite) {
File wsdlDestination = new File(fileName);
if (!wsdlDestination.exists() || overwrite) {
try {
checkParentDirectory(wsdlDestination.getAbsoluteFile().getParentFile());
Document wsdlDoc = _writer.getDocument(mergedDefinition);
FileWriter fileWriter = new FileWriter(wsdlDestination);
fileWriter.write(XmlUtils.toString(wsdlDoc));
fileWriter.close();
} catch (Exception e) {
Object[] filler = {wsdlDestination.getAbsolutePath()};
throw new MuseRuntimeException("FailedWritingWsdl", _MESSAGES.get("FailedWritingWsdl", filler), e);
}
} else {
handleMessage(_MESSAGES.get("NotOverwriting", new String[] {wsdlDestination.getPath()}));
}
}
/**
* Wraps the raw command line parameters into a <code>CommandLine</code>
* which will manage finding the flags passed in on the command line.
*
* @param args Raw command line parameters
* @return Wrapped <code>CommandLine</code> of the given parameters.
*/
private static CommandLine parseParameters(String[] args) {
CommandLine arguments = new CommandLine();
arguments.saveFlagValue(URI_FLAG);
arguments.saveFlagValue(ADDRESS_FLAG);
arguments.saveFlagValue(OUTPUT_FLAG);
arguments.saveFlagValue(RMD_OUTPUT_FLAG);
arguments.parse(args);
return arguments;
}
}