/*=============================================================================*
* Copyright 2006 The Apache Software Foundation
*
* Licensed 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.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.Types;
import javax.wsdl.extensions.schema.Schema;
import javax.wsdl.factory.WSDLFactory;
import javax.wsdl.xml.WSDLReader;
import javax.wsdl.xml.WSDLWriter;
import org.w3c.dom.Document;
import org.apache.muse.tools.generator.util.AbstractCommandLineApp;
import org.apache.muse.tools.generator.util.DefinitionInfo;
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.wsdl.WsdlUtils;
/**
* 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) {
handleErrorAndExit(_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) {
CommandLine arguments = parseParameters(args);
createLogger(arguments);
checkHelpArg(arguments);
String uri = checkUriArg(arguments);
String address = checkAddressArg(arguments);
String outputFileName = checkOutputArg(arguments);
Collection wsdls = checkWsdlArgs(arguments);
boolean overwrite = checkOverwriteArg(arguments);
Definition def = null;
try {
def = WsdlMerge.merge(uri, wsdls, address);
} catch (Exception e) {
handleErrorAndExit(_MESSAGES.get("FailedWSDLMerge"),e);
}
File output = new File(outputFileName);
try {
write(output, def, overwrite);
} catch (Exception e) {
Object[] filler = { output.getAbsolutePath() };
handleErrorAndExit(_MESSAGES.get("FailedWSDLWrite",filler), e);
}
}
/**
* 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,
Collection wsdlFragments, String address) {
DefinitionInfo targetDefinition = new DefinitionInfo(namespaceURI);
for (Iterator i = wsdlFragments.iterator(); i.hasNext();) {
DefinitionInfo definition = new DefinitionInfo((Definition) i
.next(), namespaceURI);
copyOperations(definition, targetDefinition);
copyProperties(definition, targetDefinition);
}
targetDefinition.createBinding();
targetDefinition.createService(address);
return targetDefinition.getDefinition();
}
/**
* 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,
OVERWRITE_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
};
handleErrorAndExit(_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
};
handleErrorAndExit(_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
};
handleErrorAndExit(_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 <code>Collection</code> of <code>Definition</code> objects representing the parsed WSDLs.
*/
private static Collection checkWsdlArgs(CommandLine arguments) {
ArrayList wsdls = new ArrayList();
String[] argWsdls = arguments.getArguments();
for(int i=0; i < argWsdls.length; i++) {
File wsdl = new File(argWsdls[i]);
if(!wsdl.exists()) {
Object[] filler = new Object[] {
wsdl.getAbsoluteFile()
};
handleErrorAndExit(_MESSAGES.get("NonExistantWSDL", filler));
}
wsdls.add(load(wsdl));
}
if(wsdls.size() == 0) {
handleErrorAndExit(_MESSAGES.get("NoWSDLs"));
}
return wsdls;
}
/**
* 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
*
* @return
* A <code>Defintion</code> if parsing was successful
*/
private static Definition load(File wsdl) {
Document document = null;
Definition definition = null;
try {
document = getWSDLDocument(wsdl);
definition = _reader.readWSDL(null, document);
} catch (Exception e) {
Object[] filler = new Object[] {
wsdl
};
handleErrorAndExit(_MESSAGES.get("FailedWSDLParse", filler),e);
}
return definition;
}
/**
* Write a <code>Definition</code> to a file.
*
* @param destination
* The file where the definition will be written
* @param def
* The definition we're writing
* @param overwrite
* Should we overwrite existing files
*
* @throws Exception
* If anything goes wrong
*/
private static void write(File destination, Definition def, boolean overwrite) throws Exception {
if (!destination.exists() || overwrite) {
File parent = destination.getAbsoluteFile().getParentFile();
if(!parent.exists()) {
if(!parent.mkdirs()) {
Object[] filler = { parent } ;
throw new Exception(_MESSAGES.get("CouldNotMakeDir",filler));
}
}
Document wsdlDoc = _writer.getDocument(def);
FileWriter fileWriter = new FileWriter(destination);
fileWriter.write(XmlUtils.toString(wsdlDoc));
fileWriter.close();
} else {
handleMessage(_MESSAGES.get("NotOverwriting", new String[] {destination.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.parse(args);
return arguments;
}
}