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.xjc; 27 28 import java.io.File; 29 import java.io.FileOutputStream; 30 import java.io.IOException; 31 import java.io.OutputStream; 32 import java.io.OutputStreamWriter; 33 import java.io.PrintStream; 34 import java.security.AccessController; 35 import java.security.PrivilegedAction; 36 import java.util.Iterator; 37 38 import com.sun.codemodel.internal.CodeWriter; 39 import com.sun.codemodel.internal.JCodeModel; 40 import com.sun.codemodel.internal.writer.ZipCodeWriter; 41 import com.sun.istack.internal.NotNull; 42 import com.sun.istack.internal.Nullable; 43 import com.sun.istack.internal.tools.DefaultAuthenticator; 44 import com.sun.tools.internal.xjc.generator.bean.BeanGenerator; 45 import com.sun.tools.internal.xjc.model.Model; 46 import com.sun.tools.internal.xjc.outline.Outline; 47 import com.sun.tools.internal.xjc.reader.gbind.Expression; 48 import com.sun.tools.internal.xjc.reader.gbind.Graph; 49 import com.sun.tools.internal.xjc.reader.internalizer.DOMForest; 50 import com.sun.tools.internal.xjc.reader.xmlschema.ExpressionBuilder; 51 import com.sun.tools.internal.xjc.reader.xmlschema.parser.XMLSchemaInternalizationLogic; 52 import com.sun.tools.internal.xjc.util.ErrorReceiverFilter; 53 import com.sun.tools.internal.xjc.util.NullStream; 54 import com.sun.tools.internal.xjc.util.Util; 55 import com.sun.tools.internal.xjc.writer.SignatureWriter; 56 import com.sun.xml.internal.xsom.XSComplexType; 57 import com.sun.xml.internal.xsom.XSParticle; 58 import com.sun.xml.internal.xsom.XSSchemaSet; 59 60 import org.xml.sax.SAXException; 61 import org.xml.sax.SAXParseException; 62 63 64 /** 65 * Command Line Interface of XJC. 66 */ 67 public class Driver { 68 69 private static final String SYSTEM_PROXY_PROPERTY = "java.net.useSystemProxies"; 70 71 public static void main(final String[] args) throws Exception { 72 // use the platform default proxy if available. 73 // see sun.net.spi.DefaultProxySelector for details. 74 setupProxies(); 75 76 if( Util.getSystemProperty(Driver.class,"noThreadSwap")!=null ) 77 _main(args); // for the ease of debugging 78 79 // run all the work in another thread so that the -Xss option 80 // will take effect when compiling a large schema. See 81 // http://developer.java.sun.com/developer/bugParade/bugs/4362291.html 82 final Throwable[] ex = new Throwable[1]; 83 84 Thread th = new Thread() { 85 @Override 86 public void run() { 87 try { 88 _main(args); 89 } catch( Throwable e ) { 90 ex[0]=e; 91 } 92 } 93 }; 94 th.start(); 95 th.join(); 96 97 if(ex[0]!=null) { 98 // re-throw 99 if( ex[0] instanceof Exception ) 100 throw (Exception)ex[0]; 101 else 102 throw (Error)ex[0]; 103 } 104 } 105 106 /** 107 * Set useSystemProxies if needed 108 */ 109 private static void setupProxies() { 110 Object setProperty = AccessController.doPrivileged(new PrivilegedAction<Object>() { 111 @Override 112 public Object run() { 113 return System.getProperty(SYSTEM_PROXY_PROPERTY); 114 } 115 }); 116 if (setProperty == null) { 117 AccessController.doPrivileged(new PrivilegedAction<Void>() { 118 @Override 119 public Void run() { 120 System.setProperty(SYSTEM_PROXY_PROPERTY, "true"); 121 return null; 122 } 123 }); 124 } 125 } 126 127 private static void _main( String[] args ) throws Exception { 128 try { 129 System.exit(run( args, System.out, System.out )); 130 } catch (BadCommandLineException e) { 131 // there was an error in the command line. 132 // print usage and abort. 133 if(e.getMessage()!=null) { 134 System.out.println(e.getMessage()); 135 System.out.println(); 136 } 137 138 usage(e.getOptions(),false); 139 System.exit(-1); 140 } 141 } 142 143 144 145 /** 146 * Performs schema compilation and prints the status/error into the 147 * specified PrintStream. 148 * 149 * <p> 150 * This method could be used to trigger XJC from other tools, 151 * such as Ant or IDE. 152 * 153 * @param args 154 * specified command line parameters. If there is an error 155 * in the parameters, {@link BadCommandLineException} will 156 * be thrown. 157 * @param status 158 * Status report of the compilation will be sent to this object. 159 * Useful to update users so that they will know something is happening. 160 * Only ignorable messages should be sent to this stream. 161 * 162 * This parameter can be null to suppress messages. 163 * 164 * @param out 165 * Various non-ignorable output (error messages, etc) 166 * will go to this stream. 167 * 168 * @return 169 * If the compiler runs successfully, this method returns 0. 170 * All non-zero values indicate an error. The error message 171 * will be sent to the specified PrintStream. 172 */ 173 public static int run(String[] args, final PrintStream status, final PrintStream out) 174 throws Exception { 175 176 class Listener extends XJCListener { 177 ConsoleErrorReporter cer = new ConsoleErrorReporter(out==null?new PrintStream(new NullStream()):out); 178 179 @Override 180 public void generatedFile(String fileName, int count, int total) { 181 message(fileName); 182 } 183 @Override 184 public void message(String msg) { 185 if(status!=null) 186 status.println(msg); 187 } 188 189 public void error(SAXParseException exception) { 190 cer.error(exception); 191 } 192 193 public void fatalError(SAXParseException exception) { 194 cer.fatalError(exception); 195 } 196 197 public void warning(SAXParseException exception) { 198 cer.warning(exception); 199 } 200 201 public void info(SAXParseException exception) { 202 cer.info(exception); 203 } 204 } 205 206 return run(args,new Listener()); 207 } 208 209 /** 210 * Performs schema compilation and prints the status/error into the 211 * specified PrintStream. 212 * 213 * <p> 214 * This method could be used to trigger XJC from other tools, 215 * such as Ant or IDE. 216 * 217 * @param args 218 * specified command line parameters. If there is an error 219 * in the parameters, {@link BadCommandLineException} will 220 * be thrown. 221 * @param listener 222 * Receives messages from XJC reporting progress/errors. 223 * 224 * @return 225 * If the compiler runs successfully, this method returns 0. 226 * All non-zero values indicate an error. The error message 227 * will be sent to the specified PrintStream. 228 */ 229 public static int run(String[] args, @NotNull final XJCListener listener) throws BadCommandLineException { 230 231 // recognize those special options before we start parsing options. 232 for (String arg : args) { 233 if (arg.equals("-version")) { 234 listener.message(Messages.format(Messages.VERSION)); 235 return -1; 236 } 237 if (arg.equals("-fullversion")) { 238 listener.message(Messages.format(Messages.FULLVERSION)); 239 return -1; 240 } 241 } 242 243 final OptionsEx opt = new OptionsEx(); 244 opt.setSchemaLanguage(Language.XMLSCHEMA); // disable auto-guessing 245 try { 246 opt.parseArguments(args); 247 } catch (WeAreDone e) { 248 if (opt.proxyAuth != null) { 249 DefaultAuthenticator.reset(); 250 } 251 return -1; 252 } catch(BadCommandLineException e) { 253 if (opt.proxyAuth != null) { 254 DefaultAuthenticator.reset(); 255 } 256 e.initOptions(opt); 257 throw e; 258 } 259 260 // display a warning if the user specified the default package 261 // this should work, but is generally a bad idea 262 if(opt.defaultPackage != null && opt.defaultPackage.length()==0) { 263 listener.message(Messages.format(Messages.WARNING_MSG, Messages.format(Messages.DEFAULT_PACKAGE_WARNING))); 264 } 265 266 267 // set up the context class loader so that the user-specified classes 268 // can be loaded from there 269 final ClassLoader contextClassLoader = SecureLoader.getContextClassLoader(); 270 SecureLoader.setContextClassLoader(opt.getUserClassLoader(contextClassLoader)); 271 272 // parse a grammar file 273 //----------------------------------------- 274 try { 275 if( !opt.quiet ) { 276 listener.message(Messages.format(Messages.PARSING_SCHEMA)); 277 } 278 279 final boolean[] hadWarning = new boolean[1]; 280 281 ErrorReceiver receiver = new ErrorReceiverFilter(listener) { 282 @Override 283 public void info(SAXParseException exception) { 284 if(opt.verbose) 285 super.info(exception); 286 } 287 @Override 288 public void warning(SAXParseException exception) { 289 hadWarning[0] = true; 290 if(!opt.quiet) 291 super.warning(exception); 292 } 293 @Override 294 public void pollAbort() throws AbortException { 295 if(listener.isCanceled()) 296 throw new AbortException(); 297 } 298 }; 299 300 if( opt.mode==Mode.FOREST ) { 301 // dump DOM forest and quit 302 ModelLoader loader = new ModelLoader( opt, new JCodeModel(), receiver ); 303 try { 304 DOMForest forest = loader.buildDOMForest(new XMLSchemaInternalizationLogic()); 305 forest.dump(System.out); 306 return 0; 307 } catch (SAXException e) { 308 // the error should have already been reported 309 } catch (IOException e) { 310 receiver.error(e); 311 } 312 313 return -1; 314 } 315 316 if( opt.mode==Mode.GBIND ) { 317 try { 318 XSSchemaSet xss = new ModelLoader(opt, new JCodeModel(), receiver).loadXMLSchema(); 319 Iterator<XSComplexType> it = xss.iterateComplexTypes(); 320 while (it.hasNext()) { 321 XSComplexType ct = it.next(); 322 XSParticle p = ct.getContentType().asParticle(); 323 if(p==null) continue; 324 325 Expression tree = ExpressionBuilder.createTree(p); 326 System.out.println("Graph for "+ct.getName()); 327 System.out.println(tree.toString()); 328 Graph g = new Graph(tree); 329 System.out.println(g.toString()); 330 System.out.println(); 331 } 332 return 0; 333 } catch (SAXException e) { 334 // the error should have already been reported 335 } 336 return -1; 337 } 338 339 Model model = ModelLoader.load( opt, new JCodeModel(), receiver ); 340 341 if (model == null) { 342 listener.message(Messages.format(Messages.PARSE_FAILED)); 343 return -1; 344 } 345 346 if( !opt.quiet ) { 347 listener.message(Messages.format(Messages.COMPILING_SCHEMA)); 348 } 349 350 switch (opt.mode) { 351 case SIGNATURE : 352 try { 353 SignatureWriter.write( 354 BeanGenerator.generate(model,receiver), 355 new OutputStreamWriter(System.out)); 356 return 0; 357 } catch (IOException e) { 358 receiver.error(e); 359 return -1; 360 } 361 362 case CODE : 363 case DRYRUN : 364 case ZIP : 365 { 366 // generate actual code 367 receiver.debug("generating code"); 368 {// don't want to hold outline in memory for too long. 369 Outline outline = model.generateCode(opt,receiver); 370 if(outline==null) { 371 listener.message( 372 Messages.format(Messages.FAILED_TO_GENERATE_CODE)); 373 return -1; 374 } 375 376 listener.compiled(outline); 377 } 378 379 if( opt.mode == Mode.DRYRUN ) 380 break; // enough 381 382 // then print them out 383 try { 384 CodeWriter cw; 385 if( opt.mode==Mode.ZIP ) { 386 OutputStream os; 387 if(opt.targetDir.getPath().equals(".")) 388 os = System.out; 389 else 390 os = new FileOutputStream(opt.targetDir); 391 392 cw = opt.createCodeWriter(new ZipCodeWriter(os)); 393 } else 394 cw = opt.createCodeWriter(); 395 396 if( !opt.quiet ) { 397 cw = new ProgressCodeWriter(cw,listener, model.codeModel.countArtifacts()); 398 } 399 model.codeModel.build(cw); 400 } catch (IOException e) { 401 receiver.error(e); 402 return -1; 403 } 404 405 break; 406 } 407 default : 408 assert false; 409 } 410 411 if(opt.debugMode) { 412 try { 413 new FileOutputStream(new File(opt.targetDir,hadWarning[0]?"hadWarning":"noWarning")).close(); 414 } catch (IOException e) { 415 receiver.error(e); 416 return -1; 417 } 418 } 419 420 return 0; 421 } catch( StackOverflowError e ) { 422 if(opt.verbose) 423 // in the debug mode, propagate the error so that 424 // the full stack trace will be dumped to the screen. 425 throw e; 426 else { 427 // otherwise just print a suggested workaround and 428 // quit without filling the user's screen 429 listener.message(Messages.format(Messages.STACK_OVERFLOW)); 430 return -1; 431 } 432 } finally { 433 if (opt.proxyAuth != null) { 434 DefaultAuthenticator.reset(); 435 } 436 } 437 } 438 439 public static String getBuildID() { 440 return Messages.format(Messages.BUILD_ID); 441 } 442 443 444 /** 445 * Operation mode. 446 */ 447 private static enum Mode { 448 // normal mode. compile the code 449 CODE, 450 451 // dump the signature of the generated code 452 SIGNATURE, 453 454 // dump DOMForest 455 FOREST, 456 457 // same as CODE but don't produce any Java source code 458 DRYRUN, 459 460 // same as CODE but pack all the outputs into a zip and dumps to stdout 461 ZIP, 462 463 // testing a new binding mode 464 GBIND 465 } 466 467 468 /** 469 * Command-line arguments processor. 470 * 471 * <p> 472 * This class contains options that only make sense 473 * for the command line interface. 474 */ 475 static class OptionsEx extends Options 476 { 477 /** Operation mode. */ 478 protected Mode mode = Mode.CODE; 479 480 /** A switch that determines the behavior in the BGM mode. */ 481 public boolean noNS = false; 482 483 /** Parse XJC-specific options. */ 484 @Override 485 public int parseArgument(String[] args, int i) throws BadCommandLineException { 486 if (args[i].equals("-noNS")) { 487 noNS = true; 488 return 1; 489 } 490 if (args[i].equals("-mode")) { 491 i++; 492 if (i == args.length) 493 throw new BadCommandLineException( 494 Messages.format(Messages.MISSING_MODE_OPERAND)); 495 496 String mstr = args[i].toLowerCase(); 497 498 for( Mode m : Mode.values() ) { 499 if(m.name().toLowerCase().startsWith(mstr) && mstr.length()>2) { 500 mode = m; 501 return 2; 502 } 503 } 504 505 throw new BadCommandLineException( 506 Messages.format(Messages.UNRECOGNIZED_MODE, args[i])); 507 } 508 if (args[i].equals("-help")) { 509 usage(this,false); 510 throw new WeAreDone(); 511 } 512 if (args[i].equals("-private")) { 513 usage(this,true); 514 throw new WeAreDone(); 515 } 516 517 return super.parseArgument(args, i); 518 } 519 } 520 521 /** 522 * Used to signal that we've finished processing. 523 */ 524 private static final class WeAreDone extends BadCommandLineException {} 525 526 527 /** 528 * Prints the usage screen and exits the process. 529 * 530 * @param opts 531 * If the parsing of options have started, set a partly populated 532 * {@link Options} object. 533 */ 534 public static void usage( @Nullable Options opts, boolean privateUsage ) { 535 System.out.println(Messages.format(Messages.DRIVER_PUBLIC_USAGE)); 536 if (privateUsage) { 537 System.out.println(Messages.format(Messages.DRIVER_PRIVATE_USAGE)); 538 } 539 540 if( opts!=null && !opts.getAllPlugins().isEmpty()) { 541 System.out.println(Messages.format(Messages.ADDON_USAGE)); 542 for (Plugin p : opts.getAllPlugins()) { 543 System.out.println(p.getUsage()); 544 } 545 } 546 } 547 }