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 }