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