1 /*
   2  * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.internal.ws.wscompile;
  27 
  28 import com.oracle.webservices.internal.api.databinding.WSDLResolver;
  29 import com.sun.istack.internal.tools.ParallelWorldClassLoader;
  30 import com.sun.tools.internal.ws.ToolVersion;
  31 import com.sun.tools.internal.ws.processor.modeler.annotation.WebServiceAp;
  32 import com.sun.tools.internal.ws.processor.modeler.wsdl.ConsoleErrorReporter;
  33 import com.sun.tools.internal.ws.resources.WscompileMessages;
  34 import com.sun.tools.internal.xjc.util.NullStream;
  35 import com.sun.xml.internal.txw2.TXW;
  36 import com.sun.xml.internal.txw2.TypedXmlWriter;
  37 import com.sun.xml.internal.txw2.annotation.XmlAttribute;
  38 import com.sun.xml.internal.txw2.annotation.XmlElement;
  39 import com.sun.xml.internal.txw2.output.StreamSerializer;
  40 import com.sun.xml.internal.ws.api.BindingID;
  41 import com.sun.xml.internal.ws.api.databinding.DatabindingConfig;
  42 import com.sun.xml.internal.ws.api.databinding.DatabindingFactory;
  43 import com.sun.xml.internal.ws.api.databinding.WSDLGenInfo;
  44 import com.sun.xml.internal.ws.api.server.Container;
  45 import com.sun.xml.internal.ws.api.wsdl.writer.WSDLGeneratorExtension;
  46 import com.sun.xml.internal.ws.binding.WebServiceFeatureList;
  47 import com.sun.xml.internal.ws.model.ExternalMetadataReader;
  48 import com.sun.xml.internal.ws.model.AbstractSEIModelImpl;
  49 import com.sun.xml.internal.ws.util.ServiceFinder;
  50 import org.xml.sax.SAXParseException;
  51 
  52 import javax.tools.DiagnosticCollector;
  53 import javax.tools.JavaCompiler;
  54 import javax.tools.JavaFileObject;
  55 import javax.tools.StandardJavaFileManager;
  56 import javax.tools.ToolProvider;
  57 import javax.xml.bind.annotation.XmlSeeAlso;
  58 import javax.xml.namespace.QName;
  59 import javax.xml.transform.Result;
  60 import javax.xml.transform.stream.StreamResult;
  61 import javax.xml.ws.EndpointReference;
  62 import javax.xml.ws.Holder;
  63 import java.io.BufferedOutputStream;
  64 import java.io.File;
  65 import java.io.FileNotFoundException;
  66 import java.io.FileOutputStream;
  67 import java.io.IOException;
  68 import java.io.OutputStream;
  69 import java.io.PrintStream;
  70 import java.net.URLClassLoader;
  71 import java.util.ArrayList;
  72 import java.util.Collections;
  73 import java.util.HashMap;
  74 import java.util.List;
  75 import java.util.Map;
  76 
  77 /**
  78  * @author Vivek Pandey
  79  */
  80 
  81 /*
  82  * All annotation types are supported.
  83  */
  84 public class WsgenTool {
  85     private final PrintStream out;
  86     private final WsgenOptions options = new WsgenOptions();
  87 
  88 
  89     public WsgenTool(OutputStream out, Container container) {
  90         this.out = (out instanceof PrintStream) ? (PrintStream) out : new PrintStream(out);
  91         this.container = container;
  92     }
  93 
  94 
  95     public WsgenTool(OutputStream out) {
  96         this(out, null);
  97     }
  98 
  99     public boolean run(String[] args) {
 100         final Listener listener = new Listener();
 101         for (String arg : args) {
 102             if (arg.equals("-version")) {
 103                 listener.message(
 104                         WscompileMessages.WSGEN_VERSION(ToolVersion.VERSION.MAJOR_VERSION));
 105                 return true;
 106             }
 107             if (arg.equals("-fullversion")) {
 108                 listener.message(
 109                         WscompileMessages.WSGEN_FULLVERSION(ToolVersion.VERSION.toString()));
 110                 return true;
 111             }
 112         }
 113         try {
 114             options.parseArguments(args);
 115             options.validate();
 116             if (!buildModel(options.endpoint.getName(), listener)) {
 117                 return false;
 118             }
 119         } catch (Options.WeAreDone done) {
 120             usage(done.getOptions());
 121         } catch (BadCommandLineException e) {
 122             if (e.getMessage() != null) {
 123                 System.out.println(e.getMessage());
 124                 System.out.println();
 125             }
 126             usage(e.getOptions());
 127             return false;
 128         } catch (AbortException e) {
 129             //error might have been reported
 130         } finally {
 131             if (!options.keep) {
 132                 options.removeGeneratedFiles();
 133             }
 134         }
 135         return true;
 136     }
 137 
 138     private final Container container;
 139 
 140     /*
 141      * To take care of JDK6-JDK6u3, where 2.1 API classes are not there
 142      */
 143     private static boolean useBootClasspath(Class clazz) {
 144         try {
 145             ParallelWorldClassLoader.toJarUrl(clazz.getResource('/' + clazz.getName().replace('.', '/') + ".class"));
 146             return true;
 147         } catch (Exception e) {
 148             return false;
 149         }
 150     }
 151 
 152     /**
 153      *
 154      * @param endpoint
 155      * @param listener
 156      * @return
 157      * @throws BadCommandLineException
 158      */
 159     public boolean buildModel(String endpoint, Listener listener) throws BadCommandLineException {
 160         final ErrorReceiverFilter errReceiver = new ErrorReceiverFilter(listener);
 161 
 162         boolean bootCP = useBootClasspath(EndpointReference.class) || useBootClasspath(XmlSeeAlso.class);
 163         List<String> args = new ArrayList<String>(6 + (bootCP ? 1 : 0) + (options.nocompile ? 1 : 0)
 164                 + (options.encoding != null ? 2 : 0));
 165         args.add("--add-modules");
 166         args.add("java.xml.ws");
 167         args.add("-d");
 168         args.add(options.destDir.getAbsolutePath());
 169         args.add("-classpath");
 170         args.add(options.classpath);
 171         args.add("-s");
 172         args.add(options.sourceDir.getAbsolutePath());
 173         if (options.nocompile) {
 174             args.add("-proc:only");
 175         }
 176         if (options.encoding != null) {
 177             args.add("-encoding");
 178             args.add(options.encoding);
 179         }
 180         if (bootCP) {
 181             args.add(new StringBuilder()
 182                     .append("-Xbootclasspath/p:")
 183                     .append(JavaCompilerHelper.getJarFile(EndpointReference.class))
 184                     .append(File.pathSeparator)
 185                     .append(JavaCompilerHelper.getJarFile(XmlSeeAlso.class)).toString());
 186         }
 187         if (options.javacOptions != null) {
 188             args.addAll(options.getJavacOptions(args, listener));
 189         }
 190 
 191         JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
 192         if (compiler == null) {
 193             out.println(WscompileMessages.WSCOMPILE_CANT_GET_COMPILER(property("java.home"), property("java.version"), property("java.vendor")));
 194             return false;
 195         }
 196         DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
 197         StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
 198         JavaCompiler.CompilationTask task = compiler.getTask(
 199                 null,
 200                 fileManager,
 201                 diagnostics,
 202                 args,
 203                 Collections.singleton(endpoint.replaceAll("\\$", ".")),
 204                 null);
 205         task.setProcessors(Collections.singleton(new WebServiceAp(options, out)));
 206         boolean result = task.call();
 207 
 208         if (!result) {
 209             out.println(WscompileMessages.WSCOMPILE_ERROR(WscompileMessages.WSCOMPILE_COMPILATION_FAILED()));
 210             return false;
 211         }
 212         if (options.genWsdl) {
 213             DatabindingConfig config = new DatabindingConfig();
 214 
 215             List<String> externalMetadataFileNames = options.externalMetadataFiles;
 216             boolean disableXmlSecurity = options.disableXmlSecurity;
 217             if (externalMetadataFileNames != null && externalMetadataFileNames.size() > 0) {
 218                 config.setMetadataReader(new ExternalMetadataReader(getExternalFiles(externalMetadataFileNames), null, null, true, disableXmlSecurity));
 219             }
 220 
 221             String tmpPath = options.destDir.getAbsolutePath() + File.pathSeparator + options.classpath;
 222             ClassLoader classLoader = new URLClassLoader(Options.pathToURLs(tmpPath),
 223                     this.getClass().getClassLoader());
 224             Class<?> endpointClass;
 225             try {
 226                 endpointClass = classLoader.loadClass(endpoint);
 227             } catch (ClassNotFoundException e) {
 228                 throw new BadCommandLineException(WscompileMessages.WSGEN_CLASS_NOT_FOUND(endpoint));
 229             }
 230 
 231             BindingID bindingID = options.getBindingID(options.protocol);
 232             if (!options.protocolSet) {
 233                 bindingID = BindingID.parse(endpointClass);
 234             }
 235             WebServiceFeatureList wsfeatures = new WebServiceFeatureList(endpointClass);
 236 //            RuntimeModeler rtModeler = new RuntimeModeler(endpointClass, options.serviceName, bindingID, wsfeatures.toArray());
 237 //            rtModeler.setClassLoader(classLoader);
 238             if (options.portName != null)
 239                 config.getMappingInfo().setPortName(options.portName);//rtModeler.setPortName(options.portName);
 240 //            AbstractSEIModelImpl rtModel = rtModeler.buildRuntimeModel();
 241 
 242             DatabindingFactory fac = DatabindingFactory.newInstance();
 243             config.setEndpointClass(endpointClass);
 244             config.getMappingInfo().setServiceName(options.serviceName);
 245             config.setFeatures(wsfeatures.toArray());
 246             config.setClassLoader(classLoader);
 247             config.getMappingInfo().setBindingID(bindingID);
 248             com.sun.xml.internal.ws.db.DatabindingImpl rt = (com.sun.xml.internal.ws.db.DatabindingImpl) fac.createRuntime(config);
 249 
 250             final File[] wsdlFileName = new File[1]; // used to capture the generated WSDL file.
 251             final Map<String, File> schemaFiles = new HashMap<String, File>();
 252 
 253             WSDLGenInfo wsdlGenInfo = new WSDLGenInfo();
 254             wsdlGenInfo.setSecureXmlProcessingDisabled(disableXmlSecurity);
 255 
 256             wsdlGenInfo.setWsdlResolver(
 257                     new WSDLResolver() {
 258                         private File toFile(String suggestedFilename) {
 259                             return new File(options.nonclassDestDir, suggestedFilename);
 260                         }
 261 
 262                         private Result toResult(File file) {
 263                             Result result;
 264                             try {
 265                                 result = new StreamResult(new FileOutputStream(file));
 266                                 result.setSystemId(file.getPath().replace('\\', '/'));
 267                             } catch (FileNotFoundException e) {
 268                                 errReceiver.error(e);
 269                                 return null;
 270                             }
 271                             return result;
 272                         }
 273 
 274                         @Override
 275                         public Result getWSDL(String suggestedFilename) {
 276                             File f = toFile(suggestedFilename);
 277                             wsdlFileName[0] = f;
 278                             return toResult(f);
 279                         }
 280 
 281                         public Result getSchemaOutput(String namespace, String suggestedFilename) {
 282                             if (namespace == null)
 283                                 return null;
 284                             File f = toFile(suggestedFilename);
 285                             schemaFiles.put(namespace, f);
 286                             return toResult(f);
 287                         }
 288 
 289                         @Override
 290                         public Result getAbstractWSDL(Holder<String> filename) {
 291                             return toResult(toFile(filename.value));
 292                         }
 293 
 294                         @Override
 295                         public Result getSchemaOutput(String namespace, Holder<String> filename) {
 296                             return getSchemaOutput(namespace, filename.value);
 297                         }
 298                         // TODO pass correct impl's class name
 299                     });
 300 
 301             wsdlGenInfo.setContainer(container);
 302             wsdlGenInfo.setExtensions(ServiceFinder.find(WSDLGeneratorExtension.class).toArray());
 303             wsdlGenInfo.setInlineSchemas(options.inlineSchemas);
 304             rt.generateWSDL(wsdlGenInfo);
 305 
 306 
 307             if (options.wsgenReport != null)
 308                 generateWsgenReport(endpointClass, (AbstractSEIModelImpl) rt.getModel(), wsdlFileName[0], schemaFiles);
 309         }
 310         return true;
 311     }
 312 
 313     private String property(String key) {
 314         try {
 315             String property = System.getProperty(key);
 316             return property != null ? property : "UNKNOWN";
 317         } catch (SecurityException ignored) {
 318             return "UNKNOWN";
 319         }
 320     }
 321 
 322     private List<File> getExternalFiles(List<String> exts) {
 323         List<File> files = new ArrayList<File>();
 324         for (String ext : exts) {
 325             // first try absolute path ...
 326             File file = new File(ext);
 327             if (!file.exists()) {
 328                 // then relative path ...
 329                 file = new File(options.sourceDir.getAbsolutePath() + File.separator + ext);
 330             }
 331             files.add(file);
 332         }
 333         return files;
 334     }
 335 
 336     /**
 337      * Generates a small XML file that captures the key activity of wsgen,
 338      * so that test harness can pick up artifacts.
 339      */
 340     private void generateWsgenReport(Class<?> endpointClass, AbstractSEIModelImpl rtModel, File wsdlFile, Map<String, File> schemaFiles) {
 341         try {
 342             ReportOutput.Report report = TXW.create(ReportOutput.Report.class,
 343                     new StreamSerializer(new BufferedOutputStream(new FileOutputStream(options.wsgenReport))));
 344 
 345             report.wsdl(wsdlFile.getAbsolutePath());
 346             ReportOutput.writeQName(rtModel.getServiceQName(), report.service());
 347             ReportOutput.writeQName(rtModel.getPortName(), report.port());
 348             ReportOutput.writeQName(rtModel.getPortTypeName(), report.portType());
 349 
 350             report.implClass(endpointClass.getName());
 351 
 352             for (Map.Entry<String, File> e : schemaFiles.entrySet()) {
 353                 ReportOutput.Schema s = report.schema();
 354                 s.ns(e.getKey());
 355                 s.location(e.getValue().getAbsolutePath());
 356             }
 357 
 358             report.commit();
 359         } catch (IOException e) {
 360             // this is code for the test, so we can be lousy in the error handling
 361             throw new Error(e);
 362         }
 363     }
 364 
 365     /**
 366      * "Namespace" for code needed to generate the report file.
 367      */
 368     static class ReportOutput {
 369         @XmlElement("report")
 370         interface Report extends TypedXmlWriter {
 371             @XmlElement
 372             void wsdl(String file); // location of WSDL
 373 
 374             @XmlElement
 375             QualifiedName portType();
 376 
 377             @XmlElement
 378             QualifiedName service();
 379 
 380             @XmlElement
 381             QualifiedName port();
 382 
 383             /**
 384              * Name of the class that has {@link javax.jws.WebService}.
 385              */
 386             @XmlElement
 387             void implClass(String name);
 388 
 389             @XmlElement
 390             Schema schema();
 391         }
 392 
 393         interface QualifiedName extends TypedXmlWriter {
 394             @XmlAttribute
 395             void uri(String ns);
 396 
 397             @XmlAttribute
 398             void localName(String localName);
 399         }
 400 
 401         interface Schema extends TypedXmlWriter {
 402             @XmlAttribute
 403             void ns(String ns);
 404 
 405             @XmlAttribute
 406             void location(String filePath);
 407         }
 408 
 409         private static void writeQName(QName n, QualifiedName w) {
 410             w.uri(n.getNamespaceURI());
 411             w.localName(n.getLocalPart());
 412         }
 413     }
 414 
 415     protected void usage(Options options) {
 416         // Just don't see any point in passing WsgenOptions
 417         // BadCommandLineException also shouldn't have options
 418         if (options == null)
 419             options = this.options;
 420         if (options instanceof WsgenOptions) {
 421             System.out.println(WscompileMessages.WSGEN_HELP("WSGEN",
 422                     ((WsgenOptions)options).protocols,
 423                     ((WsgenOptions)options).nonstdProtocols.keySet()));
 424             System.out.println(WscompileMessages.WSGEN_USAGE_EXTENSIONS());
 425             System.out.println(WscompileMessages.WSGEN_USAGE_EXAMPLES());
 426         }
 427     }
 428 
 429     class Listener extends WsimportListener {
 430         ConsoleErrorReporter cer = new ConsoleErrorReporter(out == null ? new PrintStream(new NullStream()) : out);
 431 
 432         @Override
 433         public void generatedFile(String fileName) {
 434             message(fileName);
 435         }
 436 
 437         @Override
 438         public void message(String msg) {
 439             out.println(msg);
 440         }
 441 
 442         @Override
 443         public void error(SAXParseException exception) {
 444             cer.error(exception);
 445         }
 446 
 447         @Override
 448         public void fatalError(SAXParseException exception) {
 449             cer.fatalError(exception);
 450         }
 451 
 452         @Override
 453         public void warning(SAXParseException exception) {
 454             cer.warning(exception);
 455         }
 456 
 457         @Override
 458         public void info(SAXParseException exception) {
 459             cer.info(exception);
 460         }
 461     }
 462 }