/*******************************************************************************
* Copyright (c) 2006-2010 eBay Inc. All Rights Reserved.
* 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
*******************************************************************************/
/**
*
*/
package org.ebayopensource.turmeric.tools.codegen.builders;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.ebayopensource.turmeric.runtime.common.impl.utils.LogManager;
import org.ebayopensource.turmeric.tools.codegen.CodeGenContext;
import org.ebayopensource.turmeric.tools.codegen.SourceGenerator;
import org.ebayopensource.turmeric.tools.codegen.exception.CodeGenFailedException;
import org.ebayopensource.turmeric.tools.codegen.util.CodeGenConstants;
import org.ebayopensource.turmeric.tools.codegen.util.CodeGenUtil;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* @author aupadhay This class generates a wsdl at
* gen-meta-src/META-INF/soa/services/wsdl/<ServiceName>/<ServiceName>_public.wsdl
* with modified serviceName and removes <appinfo> tags
* <publicserviceName> is obtained from <service_metadata>.properties
* file.
*/
public class WsdlWithPublicServiceGenerator implements SourceGenerator
{
private static Logger s_logger = LogManager
.getInstance(WsdlWithPublicServiceGenerator.class);
private static final String WSDL_SERVICE_TAG = "wsdl:service";
private static final String XML_SCHEMA_NAMESPACE = "http://www.w3.org/2001/XMLSchema";
private static final String NAME_TAG = "name";
private static final String PUBLIC_WSDL = "_public.wsdl";
private static final String WSDL_LOC = "soa\\services\\wsdl\\";
private static final String GENERATED_WSDL_LOC = CodeGenConstants.META_INF_FOLDER
+ File.separator + WSDL_LOC;
private static final String XML_SCHEMA = "schema";
private static final String APPINFO_TAG = "appinfo";
private static final String ANNOTATION_TAG = "annotation";
private static final String WSDL_DEF = "wsdl:definitions";
private static final String TNS = "targetNamespace";
private Node m_wsdlDefNode = null;
private String m_service = null;
private static WsdlWithPublicServiceGenerator s_wsdlwithPublicService = new WsdlWithPublicServiceGenerator();
public boolean continueOnError() {
return true;
}
public static WsdlWithPublicServiceGenerator getInstance() {
return s_wsdlwithPublicService;
}
public void generate(CodeGenContext codeGenCtx)
throws CodeGenFailedException {
if (codeGenCtx.getInputOptions().getPublicServiceName() == null)
return;
s_logger.log(Level.INFO, "BEGIN WsdlWithPublicServiceGenerator()....");
createNewWsdlwithProperServiceName(codeGenCtx);
s_logger.log(Level.INFO, "END WsdlWithPublicServiceGenerator()....");
}
private void createNewWsdlwithProperServiceName(CodeGenContext codegenCtx)
throws CodeGenFailedException {
String serviceName = null;
m_service = codegenCtx.getInputOptions().getSvcCurrVersion();
String actualServiceName = codegenCtx.getInputOptions()
.getServiceAdminName();
if (codegenCtx.getInputOptions().getPublicServiceName() == null)
serviceName = codegenCtx.getServiceAdminName();
else
serviceName = codegenCtx.getInputOptions().getPublicServiceName();
if (codegenCtx.getProjectRoot() == null)
throw new CodeGenFailedException("Project root can not be null..");
String wsdlPath = codegenCtx.getProjectRoot() + File.separator
+ CodeGenConstants.GEN_META_SRC_FOLDER + File.separator
+ GENERATED_WSDL_LOC + actualServiceName + File.separator
+ actualServiceName + PUBLIC_WSDL;
wsdlPath = CodeGenUtil.toOSFilePath(wsdlPath);
try {
String folderStructurePath = codegenCtx.getProjectRoot()
+ File.separator + CodeGenConstants.GEN_META_SRC_FOLDER
+ File.separator + CodeGenConstants.META_INF_FOLDER
+ File.separator + WSDL_LOC + actualServiceName;
folderStructurePath = CodeGenUtil
.toOSFilePath(folderStructurePath);
CodeGenUtil.createDir(folderStructurePath);
writeNewWsdl(codegenCtx.getInputOptions().getInputFile(), wsdlPath,
serviceName);
} catch (IOException e) {
s_logger.log(Level.SEVERE,
"Could not create wsdl with PublicServiceName.....");
throw new CodeGenFailedException(
"wsdl with publicServiceName could not be created..");
}
}
/**
* This method creates wsdl with modified serviceName at
* gen-meta-src/META-INF/soa/services/wsdl/<ServiceName>/<ServiceName>_public.wsdl
*
* @param inputFile-
* input Wsdl location
* @param wsdlPath -
* modified wsdl Location
* @throws CodeGenFailedException
* @throws IOException
*/
private void writeNewWsdl(String inputFile, String wsdlPath,
String serviceName) throws CodeGenFailedException, IOException {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder;
Document document = null;
try {
builder = factory.newDocumentBuilder();
document = builder.parse(inputFile);
String prefix = getprefixFortargetNamespace(document);
NodeList nodeList = document.getElementsByTagName(WSDL_SERVICE_TAG);
for (int i = 0; i < nodeList.getLength(); i++) {
if (nodeList.item(i) instanceof Element) {
m_wsdlDefNode = nodeList.item(i);
Element serviceTagElement = (Element) nodeList.item(i);
serviceTagElement.setAttribute(NAME_TAG, serviceName);
}
}
// Need to remove <appinfo> tags coming from typelibrary in the wsdl
removeAppinfoTags(document);
String tagName = prefix + ":" + "version";
Element versionElement = document.createElement(tagName);
versionElement.setTextContent(m_service);
String annotationPrefix = getPrefixForSchema(document);
String annotationTag = annotationPrefix + ":" + ANNOTATION_TAG;
Element annotationElement = document.createElement(annotationTag);
NodeList list = m_wsdlDefNode.getChildNodes();
//need to insert at right position
Node portNode =null;
for(int j=0;j<list.getLength();j++)
{
if (list.item(j).getNodeName().contains("port")) {
portNode = list.item(j);
break;
}
}
m_wsdlDefNode.insertBefore(annotationElement, portNode);
String appinfoTag = annotationPrefix + ":" + APPINFO_TAG;
Element appinfoElement = document.createElement(appinfoTag);
annotationElement.appendChild(appinfoElement);
appinfoElement.appendChild(versionElement);
} catch (Exception e) {
s_logger.log(Level.SEVERE,
"Could not modify serviceName in the wsdl...");
throw new CodeGenFailedException(e.getMessage());
}
writeNewwsdl(document, wsdlPath, serviceName);
}
private String getprefixFortargetNamespace(Document document) {
String prefix = null;
String prefixForTNS = "prefix";
NodeList nodelist = document.getElementsByTagName(WSDL_DEF);
String namespace = nodelist.item(0).getAttributes().getNamedItem(TNS)
.getNodeValue();
NamedNodeMap nodeMap = nodelist.item(0).getAttributes();
for (int i = 0; i < nodeMap.getLength(); i++) {
String value = nodeMap.item(i).getNodeValue();
if (value.equals(namespace)) {
if (!nodeMap.item(i).getNodeName().equals(TNS))
prefix = nodeMap.item(i).getNodeName();
}
}
if (prefix != null)
prefixForTNS = prefix.substring(prefix.indexOf(':') + 1);
return prefixForTNS;
}
private String getPrefixForSchema(Document document) {
String prefix = null;
NodeList childNodes = document.getElementsByTagName("*");
NamedNodeMap map = null;
for (int i = 0; i < childNodes.getLength(); i++) {
if (childNodes.item(i).getNodeName().contains(XML_SCHEMA)) {
Node schemaNode = childNodes.item(i);
map = schemaNode.getAttributes();
break;
}
}
if(map != null)
prefix = getdefinedprefixInsidewsdl(map);
// in case prefix for XML_SCHEMA is not defined under <schema> section,
// it must be declared at the root of the wsdl.
if (prefix == null) {
map = m_wsdlDefNode.getParentNode().getAttributes();
prefix = getdefinedprefixInsidewsdl(map);
}
return prefix;
}
private String getdefinedprefixInsidewsdl(NamedNodeMap map) {
String prefix = null;
for (int i = 0; i < map.getLength(); i++) {
if (map.item(i).getNodeValue().equals(XML_SCHEMA_NAMESPACE))
prefix = map.item(i).getNodeName();
}
return prefix == null ? null : prefix
.substring(prefix.indexOf(':') + 1);
}
private void removeAppinfoTags(Document document)
throws CodeGenFailedException, DOMException {
NodeList nodeList = document.getElementsByTagName("*");
// visit schema section inside the wsdl
// Wsdl created by plugin having elements from Typelibrary is supposed
// to have single Namespace.
for (int i = 0; i < nodeList.getLength(); i++) {
if (nodeList.item(i).getNodeName().contains(XML_SCHEMA)) {
traverseNode(nodeList.item(i));
break;
}
}
}
private void traverseNode(Node currentNode) throws CodeGenFailedException,
DOMException {
if (currentNode == null)
return;
if (currentNode.getNodeName().contains(APPINFO_TAG)) {
// need to remove the <appinfo> tags
Node properParentNode = getproperParentNode(currentNode);
properParentNode.removeChild(currentNode);
}
for (int i = 0; i < currentNode.getChildNodes().getLength(); i++) {
s_logger.log(Level.FINE, "Calling recursivelyVisitNode()...");
traverseNode(currentNode.getChildNodes().item(i));
}
}
/**
* This method returns the proper parent (complextype or SimpleType)
*
* @param node
* --curretnNode in the schema
* @return --proper parent
*/
private Node getproperParentNode(Node node) {
s_logger.log(Level.FINE, "BEGIN getproperParentNode()...");
if (node.getParentNode().getNodeName().contains(ANNOTATION_TAG))
return node.getParentNode();
else
return getproperParentNode(node.getParentNode());
}
private void writeNewwsdl(Document document, String wsdlPath,
String serviceName) throws CodeGenFailedException, IOException {
TransformerFactory transferFact = TransformerFactory.newInstance();
Transformer transformer = null;
try {
transformer = transferFact.newTransformer();
// Bug in java5 transformer, indentation does not work.
// refer to
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6296446
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
} catch (TransformerConfigurationException e) {
s_logger.log(Level.SEVERE,
"Could not remove <appifo> tags from the wsdl");
throw new CodeGenFailedException(
"wsdl with modified publicserviceName could not be created");
}
DOMSource sourcewsdl = new DOMSource(document);
try {
File file = new File(wsdlPath);
try {
boolean created = file.createNewFile();
if (created) {
s_logger.log(Level.INFO, serviceName + PUBLIC_WSDL
+ "created successfully...");
}
} catch (IOException e) {
s_logger.log(Level.INFO,
"Could not create publicservice wsdl....");
throw new CodeGenFailedException(e.getMessage());
}
FileOutputStream output = new FileOutputStream(file);
StreamResult newWsdl = new StreamResult(output);
sourcewsdl.setNode(document);
transformer.transform(sourcewsdl, newWsdl);
} catch (FileNotFoundException e) {
throw new CodeGenFailedException(e.getMessage());
} catch (TransformerException e) {
throw new CodeGenFailedException(e.getMessage());
}
}
public String getFilePath(String serviceAdminName, String interfaceName) {
String wsdlPath = CodeGenConstants.GEN_META_SRC_FOLDER + File.separator
+ GENERATED_WSDL_LOC + serviceAdminName + File.separator
+ serviceAdminName + PUBLIC_WSDL;
return CodeGenUtil.toOSFilePath(wsdlPath);
}
}