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 }