001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 package org.apache.oozie.action.hadoop; 019 020 import java.io.BufferedReader; 021 import java.io.File; 022 import java.io.FileReader; 023 import java.io.IOException; 024 import java.io.InputStream; 025 import java.io.InputStreamReader; 026 import java.io.OutputStream; 027 import java.io.OutputStreamWriter; 028 import java.io.PrintWriter; 029 import java.io.StringWriter; 030 import java.io.Writer; 031 import java.lang.reflect.InvocationTargetException; 032 import java.lang.reflect.Method; 033 import java.security.Permission; 034 import java.text.MessageFormat; 035 import java.util.Properties; 036 import java.util.StringTokenizer; 037 import java.util.concurrent.ScheduledThreadPoolExecutor; 038 import java.util.concurrent.TimeUnit; 039 040 import org.apache.hadoop.conf.Configuration; 041 import org.apache.hadoop.fs.FileSystem; 042 import org.apache.hadoop.fs.Path; 043 import org.apache.hadoop.mapred.Counters; 044 import org.apache.hadoop.mapred.JobConf; 045 import org.apache.hadoop.mapred.Mapper; 046 import org.apache.hadoop.mapred.OutputCollector; 047 import org.apache.hadoop.mapred.Reporter; 048 import org.apache.hadoop.mapred.RunningJob; 049 import org.apache.oozie.service.HadoopAccessorException; 050 import org.apache.oozie.service.HadoopAccessorService; 051 import org.apache.oozie.service.Services; 052 import org.apache.oozie.util.XLog; 053 054 public class LauncherMapper<K1, V1, K2, V2> implements Mapper<K1, V1, K2, V2>, Runnable { 055 056 public static final String CONF_OOZIE_ACTION_MAIN_CLASS = "oozie.launcher.action.main.class"; 057 058 private static final String CONF_OOZIE_ACTION_MAIN_ARG_COUNT = "oozie.action.main.arg.count"; 059 private static final String CONF_OOZIE_ACTION_MAIN_ARG_PREFIX = "oozie.action.main.arg."; 060 private static final String CONF_OOZIE_ACTION_MAX_OUTPUT_DATA = "oozie.action.max.output.data"; 061 062 private static final String COUNTER_GROUP = "oozie.launcher"; 063 private static final String COUNTER_DO_ID_SWAP = "oozie.do.id.swap"; 064 private static final String COUNTER_OUTPUT_DATA = "oozie.output.data"; 065 private static final String COUNTER_LAUNCHER_ERROR = "oozie.launcher.error"; 066 067 private static final String OOZIE_JOB_ID = "oozie.job.id"; 068 private static final String OOZIE_ACTION_ID = "oozie.action.id"; 069 070 private static final String OOZIE_ACTION_DIR_PATH = "oozie.action.dir.path"; 071 private static final String OOZIE_ACTION_RECOVERY_ID = "oozie.action.recovery.id"; 072 073 static final String ACTION_CONF_XML = "action.xml"; 074 private static final String ACTION_OUTPUT_PROPS = "output.properties"; 075 private static final String ACTION_NEW_ID_PROPS = "newId.properties"; 076 private static final String ACTION_ERROR_PROPS = "error.properties"; 077 078 private void setRecoveryId(Configuration launcherConf, Path actionDir, String recoveryId) throws LauncherException { 079 try { 080 FileSystem fs = FileSystem.get(launcherConf); 081 String jobId = launcherConf.get("mapred.job.id"); 082 Path path = new Path(actionDir, recoveryId); 083 if (!fs.exists(path)) { 084 try { 085 Writer writer = new OutputStreamWriter(fs.create(path)); 086 writer.write(jobId); 087 writer.close(); 088 } 089 catch (IOException ex) { 090 failLauncher(0, "IO error", ex); 091 } 092 } 093 else { 094 InputStream is = fs.open(path); 095 BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 096 String id = reader.readLine(); 097 reader.close(); 098 if (!jobId.equals(id)) { 099 failLauncher(0, MessageFormat.format( 100 "Hadoop job Id mismatch, action file [{0}] declares Id [{1}] current Id [{2}]", path, id, 101 jobId), null); 102 } 103 104 } 105 } 106 catch (IOException ex) { 107 failLauncher(0, "IO error", ex); 108 } 109 } 110 111 /** 112 * @param launcherConf 113 * @param actionDir 114 * @param recoveryId 115 * @return 116 * @throws HadoopAccessorException 117 * @throws IOException 118 */ 119 public static String getRecoveryId(Configuration launcherConf, Path actionDir, String recoveryId) 120 throws HadoopAccessorException, IOException { 121 String jobId = null; 122 Path recoveryFile = new Path(actionDir, recoveryId); 123 //FileSystem fs = FileSystem.get(launcherConf); 124 FileSystem fs = Services.get().get(HadoopAccessorService.class) 125 .createFileSystem(launcherConf.get("user.name"), 126 launcherConf.get("group.name"), launcherConf); 127 128 if (fs.exists(recoveryFile)) { 129 InputStream is = fs.open(recoveryFile); 130 BufferedReader reader = new BufferedReader(new InputStreamReader(is)); 131 jobId = reader.readLine(); 132 reader.close(); 133 } 134 return jobId; 135 136 } 137 138 public static void setupMainClass(Configuration launcherConf, String javaMainClass) { 139 launcherConf.set(CONF_OOZIE_ACTION_MAIN_CLASS, javaMainClass); 140 } 141 142 public static void setupMainArguments(Configuration launcherConf, String[] args) { 143 launcherConf.setInt(CONF_OOZIE_ACTION_MAIN_ARG_COUNT, args.length); 144 for (int i = 0; i < args.length; i++) { 145 launcherConf.set(CONF_OOZIE_ACTION_MAIN_ARG_PREFIX + i, args[i]); 146 } 147 } 148 149 public static void setupMaxOutputData(Configuration launcherConf, int maxOutputData) { 150 launcherConf.setInt(CONF_OOZIE_ACTION_MAX_OUTPUT_DATA, maxOutputData); 151 } 152 153 /** 154 * @param launcherConf 155 * @param jobId 156 * @param actionId 157 * @param actionDir 158 * @param recoveryId 159 * @param actionConf 160 * @throws IOException 161 * @throws HadoopAccessorException 162 */ 163 public static void setupLauncherInfo(JobConf launcherConf, String jobId, String actionId, Path actionDir, 164 String recoveryId, Configuration actionConf) throws IOException, HadoopAccessorException { 165 166 launcherConf.setMapperClass(LauncherMapper.class); 167 launcherConf.setSpeculativeExecution(false); 168 launcherConf.setNumMapTasks(1); 169 launcherConf.setNumReduceTasks(0); 170 171 launcherConf.set(OOZIE_JOB_ID, jobId); 172 launcherConf.set(OOZIE_ACTION_ID, actionId); 173 launcherConf.set(OOZIE_ACTION_DIR_PATH, actionDir.toString()); 174 launcherConf.set(OOZIE_ACTION_RECOVERY_ID, recoveryId); 175 176 actionConf.set(OOZIE_JOB_ID, jobId); 177 actionConf.set(OOZIE_ACTION_ID, actionId); 178 179 FileSystem fs = Services.get().get(HadoopAccessorService.class).createFileSystem(launcherConf.get("user.name"), 180 launcherConf.get("group.name"), launcherConf); 181 fs.mkdirs(actionDir); 182 183 OutputStream os = fs.create(new Path(actionDir, ACTION_CONF_XML)); 184 actionConf.writeXml(os); 185 os.close(); 186 187 Path inputDir = new Path(actionDir, "input"); 188 fs.mkdirs(inputDir); 189 Writer writer = new OutputStreamWriter(fs.create(new Path(inputDir, "dummy.txt"))); 190 writer.write("dummy"); 191 writer.close(); 192 193 launcherConf.set("mapred.input.dir", inputDir.toString()); 194 launcherConf.set("mapred.output.dir", new Path(actionDir, "output").toString()); 195 } 196 197 public static boolean isMainDone(RunningJob runningJob) throws IOException { 198 return runningJob.isComplete(); 199 } 200 201 public static boolean isMainSuccessful(RunningJob runningJob) throws IOException { 202 boolean succeeded = runningJob.isSuccessful(); 203 if (succeeded) { 204 Counters counters = runningJob.getCounters(); 205 if (counters != null) { 206 Counters.Group group = counters.getGroup(COUNTER_GROUP); 207 if (group != null) { 208 succeeded = group.getCounter(COUNTER_LAUNCHER_ERROR) == 0; 209 } 210 } 211 } 212 return succeeded; 213 } 214 215 public static boolean hasOutputData(RunningJob runningJob) throws IOException { 216 boolean output = false; 217 Counters counters = runningJob.getCounters(); 218 if (counters != null) { 219 Counters.Group group = counters.getGroup(COUNTER_GROUP); 220 if (group != null) { 221 output = group.getCounter(COUNTER_OUTPUT_DATA) == 1; 222 } 223 } 224 return output; 225 } 226 227 /** 228 * @param runningJob 229 * @return 230 * @throws IOException 231 */ 232 public static boolean hasIdSwap(RunningJob runningJob) throws IOException { 233 boolean swap = false; 234 Counters counters = runningJob.getCounters(); 235 if (counters != null) { 236 Counters.Group group = counters.getGroup(COUNTER_GROUP); 237 if (group != null) { 238 swap = group.getCounter(COUNTER_DO_ID_SWAP) == 1; 239 } 240 } 241 return swap; 242 } 243 244 /** 245 * @param runningJob 246 * @param user 247 * @param group 248 * @param actionDir 249 * @return 250 * @throws IOException 251 * @throws HadoopAccessorException 252 */ 253 public static boolean hasIdSwap(RunningJob runningJob, String user, String group, Path actionDir) 254 throws IOException, HadoopAccessorException { 255 boolean swap = false; 256 257 XLog log = XLog.getLog("org.apache.oozie.action.hadoop.LauncherMapper"); 258 259 Counters counters = runningJob.getCounters(); 260 if (counters != null) { 261 Counters.Group counterGroup = counters.getGroup(COUNTER_GROUP); 262 if (counterGroup != null) { 263 swap = counterGroup.getCounter(COUNTER_DO_ID_SWAP) == 1; 264 } 265 } 266 // additional check for swapped hadoop ID 267 // Can't rely on hadoop counters existing 268 // we'll check for the newID file in hdfs if the hadoop counters is null 269 else { 270 271 Path p = getIdSwapPath(actionDir); 272 // log.debug("Checking for newId file in: [{0}]", p); 273 274 FileSystem fs = Services.get().get(HadoopAccessorService.class).createFileSystem(user, group, p.toUri(), 275 new Configuration()); 276 if (fs.exists(p)) { 277 log.debug("Hadoop Counters is null, but found newID file."); 278 279 swap = true; 280 } 281 else { 282 log.debug("Hadoop Counters is null, and newID file doesn't exist at: [{0}]", p); 283 } 284 } 285 return swap; 286 } 287 288 public static Path getOutputDataPath(Path actionDir) { 289 return new Path(actionDir, ACTION_OUTPUT_PROPS); 290 } 291 292 public static Path getErrorPath(Path actionDir) { 293 return new Path(actionDir, ACTION_ERROR_PROPS); 294 } 295 296 public static Path getIdSwapPath(Path actionDir) { 297 return new Path(actionDir, ACTION_NEW_ID_PROPS); 298 } 299 300 private JobConf jobConf; 301 private Path actionDir; 302 private ScheduledThreadPoolExecutor timer; 303 304 private boolean configFailure = false; 305 306 public LauncherMapper() { 307 } 308 309 public void configure(JobConf jobConf) { 310 System.out.println(); 311 System.out.println("Oozie Launcher starts"); 312 System.out.println(); 313 this.jobConf = jobConf; 314 actionDir = new Path(getJobConf().get(OOZIE_ACTION_DIR_PATH)); 315 String recoveryId = jobConf.get(OOZIE_ACTION_RECOVERY_ID, null); 316 try { 317 setRecoveryId(jobConf, actionDir, recoveryId); 318 } 319 catch (LauncherException ex) { 320 configFailure = true; 321 } 322 } 323 324 public void map(K1 key, V1 value, OutputCollector<K2, V2> collector, Reporter reporter) throws IOException { 325 try { 326 if (configFailure) { 327 throw new LauncherException(); 328 } 329 else { 330 String mainClass = getJobConf().get(CONF_OOZIE_ACTION_MAIN_CLASS); 331 String msgPrefix = "Main class [" + mainClass + "], "; 332 int errorCode = 0; 333 Throwable errorCause = null; 334 String errorMessage = null; 335 336 try { 337 new LauncherSecurityManager(); 338 } 339 catch (SecurityException ex) { 340 errorMessage = "Could not set LauncherSecurityManager"; 341 errorCause = ex; 342 } 343 344 try { 345 setupHeartBeater(reporter); 346 347 setupMainConfiguration(); 348 349 String[] args = getMainArguments(getJobConf()); 350 351 printContentsOfCurrentDir(); 352 353 System.out.println(); 354 System.out.println("Oozie Java/Map-Reduce/Pig action launcher-job configuration"); 355 System.out.println("================================================================="); 356 System.out.println("Workflow job id : " + System.getProperty("oozie.job.id")); 357 System.out.println("Workflow action id: " + System.getProperty("oozie.action.id")); 358 System.out.println(); 359 System.out.println("Classpath :"); 360 System.out.println("------------------------"); 361 StringTokenizer st = new StringTokenizer(System.getProperty("java.class.path"), ":"); 362 while (st.hasMoreTokens()) { 363 System.out.println(" " + st.nextToken()); 364 } 365 System.out.println("------------------------"); 366 System.out.println(); 367 System.out.println("Main class : " + mainClass); 368 System.out.println(); 369 System.out.println("Maximum output : " 370 + getJobConf().getInt(CONF_OOZIE_ACTION_MAX_OUTPUT_DATA, 2 * 1024)); 371 System.out.println(); 372 System.out.println("Arguments :"); 373 for (String arg : args) { 374 System.out.println(" " + arg); 375 } 376 377 System.out.println(); 378 System.out.println("Java System Properties:"); 379 System.out.println("------------------------"); 380 System.getProperties().store(System.out, ""); 381 System.out.flush(); 382 System.out.println("------------------------"); 383 System.out.println(); 384 385 System.out.println("================================================================="); 386 System.out.println(); 387 System.out.println(">>> Invoking Main class now >>>"); 388 System.out.println(); 389 System.out.flush(); 390 391 try { 392 Class klass = getJobConf().getClass(CONF_OOZIE_ACTION_MAIN_CLASS, Object.class); 393 Method mainMethod = klass.getMethod("main", String[].class); 394 mainMethod.invoke(null, (Object) args); 395 } 396 catch (InvocationTargetException ex) { 397 if (LauncherMainException.class.isInstance(ex.getCause())) { 398 errorMessage = msgPrefix + "exit code [" +((LauncherMainException)ex.getCause()).getErrorCode() 399 + "]"; 400 errorCause = null; 401 } 402 else if (SecurityException.class.isInstance(ex.getCause())) { 403 if (LauncherSecurityManager.getExitInvoked()) { 404 System.out.println("Intercepting System.exit(" + LauncherSecurityManager.getExitCode() 405 + ")"); 406 System.err.println("Intercepting System.exit(" + LauncherSecurityManager.getExitCode() 407 + ")"); 408 // if 0 main() method finished successfully 409 // ignoring 410 errorCode = LauncherSecurityManager.getExitCode(); 411 if (errorCode != 0) { 412 errorMessage = msgPrefix + "exit code [" + errorCode + "]"; 413 errorCause = null; 414 } 415 } 416 } 417 else { 418 throw ex; 419 } 420 } 421 finally { 422 System.out.println(); 423 System.out.println("<<< Invocation of Main class completed <<<"); 424 System.out.println(); 425 } 426 if (errorMessage == null) { 427 File outputData = new File(System.getProperty("oozie.action.output.properties")); 428 if (outputData.exists()) { 429 FileSystem fs = FileSystem.get(getJobConf()); 430 fs.copyFromLocalFile(new Path(outputData.toString()), new Path(actionDir, 431 ACTION_OUTPUT_PROPS)); 432 reporter.incrCounter(COUNTER_GROUP, COUNTER_OUTPUT_DATA, 1); 433 434 int maxOutputData = getJobConf().getInt(CONF_OOZIE_ACTION_MAX_OUTPUT_DATA, 2 * 1024); 435 if (outputData.length() > maxOutputData) { 436 String msg = MessageFormat.format("Output data size [{0}] exceeds maximum [{1}]", 437 outputData.length(), maxOutputData); 438 failLauncher(0, msg, null); 439 } 440 System.out.println(); 441 System.out.println("Oozie Launcher, capturing output data:"); 442 System.out.println("======================="); 443 Properties props = new Properties(); 444 props.load(new FileReader(outputData)); 445 props.store(System.out, ""); 446 System.out.println(); 447 System.out.println("======================="); 448 System.out.println(); 449 } 450 File newId = new File(System.getProperty("oozie.action.newId.properties")); 451 if (newId.exists()) { 452 Properties props = new Properties(); 453 props.load(new FileReader(newId)); 454 if (props.getProperty("id") == null) { 455 throw new IllegalStateException("ID swap file does not have [id] property"); 456 } 457 FileSystem fs = FileSystem.get(getJobConf()); 458 fs.copyFromLocalFile(new Path(newId.toString()), new Path(actionDir, ACTION_NEW_ID_PROPS)); 459 reporter.incrCounter(COUNTER_GROUP, COUNTER_DO_ID_SWAP, 1); 460 461 System.out.println("Oozie Launcher, copying new Hadoop job id to file: " 462 + new Path(actionDir, ACTION_NEW_ID_PROPS).toUri()); 463 464 System.out.println(); 465 System.out.println("Oozie Launcher, propagating new Hadoop job id to Oozie"); 466 System.out.println("======================="); 467 System.out.println("id: " + props.getProperty("id")); 468 System.out.println("======================="); 469 System.out.println(); 470 } 471 } 472 } 473 catch (NoSuchMethodException ex) { 474 errorMessage = msgPrefix + "main() method not found"; 475 errorCause = ex; 476 } 477 catch (InvocationTargetException ex) { 478 errorMessage = msgPrefix + "main() threw exception"; 479 errorCause = ex.getTargetException(); 480 } 481 catch (Throwable ex) { 482 errorMessage = msgPrefix + "exception invoking main()"; 483 errorCause = ex; 484 } 485 finally { 486 destroyHeartBeater(); 487 if (errorMessage != null) { 488 failLauncher(errorCode, errorMessage, errorCause); 489 } 490 } 491 } 492 } 493 catch (LauncherException ex) { 494 reporter.incrCounter(COUNTER_GROUP, COUNTER_LAUNCHER_ERROR, 1); 495 System.out.println(); 496 System.out.println("Oozie Launcher failed, finishing Hadoop job gracefully"); 497 System.out.println(); 498 } 499 } 500 501 public void close() throws IOException { 502 System.out.println(); 503 System.out.println("Oozie Launcher ends"); 504 System.out.println(); 505 } 506 507 protected JobConf getJobConf() { 508 return jobConf; 509 } 510 511 private void setupMainConfiguration() throws IOException { 512 FileSystem fs = FileSystem.get(getJobConf()); 513 fs.copyToLocalFile(new Path(getJobConf().get(OOZIE_ACTION_DIR_PATH), ACTION_CONF_XML), new Path(new File( 514 ACTION_CONF_XML).getAbsolutePath())); 515 516 System.setProperty("oozie.launcher.job.id", getJobConf().get("mapred.job.id")); 517 System.setProperty("oozie.job.id", getJobConf().get(OOZIE_JOB_ID)); 518 System.setProperty("oozie.action.id", getJobConf().get(OOZIE_ACTION_ID)); 519 System.setProperty("oozie.action.conf.xml", new File(ACTION_CONF_XML).getAbsolutePath()); 520 System.setProperty("oozie.action.output.properties", new File(ACTION_OUTPUT_PROPS).getAbsolutePath()); 521 System.setProperty("oozie.action.newId.properties", new File(ACTION_NEW_ID_PROPS).getAbsolutePath()); 522 } 523 524 public static String[] getMainArguments(Configuration conf) { 525 String[] args = new String[conf.getInt(CONF_OOZIE_ACTION_MAIN_ARG_COUNT, 0)]; 526 for (int i = 0; i < args.length; i++) { 527 args[i] = conf.get(CONF_OOZIE_ACTION_MAIN_ARG_PREFIX + i); 528 } 529 return args; 530 } 531 532 private void setupHeartBeater(Reporter reporter) { 533 timer = new ScheduledThreadPoolExecutor(1); 534 timer.scheduleAtFixedRate(new LauncherMapper(reporter), 0, 30, TimeUnit.SECONDS); 535 } 536 537 private void destroyHeartBeater() { 538 timer.shutdownNow(); 539 } 540 541 private Reporter reporter; 542 543 private LauncherMapper(Reporter reporter) { 544 this.reporter = reporter; 545 } 546 547 public void run() { 548 System.out.println("Heart beat"); 549 reporter.progress(); 550 } 551 552 private void failLauncher(int errorCode, String reason, Throwable ex) throws LauncherException { 553 try { 554 if (ex != null) { 555 reason += ", " + ex.getMessage(); 556 } 557 Properties errorProps = new Properties(); 558 errorProps.setProperty("error.code", Integer.toString(errorCode)); 559 errorProps.setProperty("error.reason", reason); 560 if (ex != null) { 561 if (ex.getMessage() != null) { 562 errorProps.setProperty("exception.message", ex.getMessage()); 563 } 564 StringWriter sw = new StringWriter(); 565 PrintWriter pw = new PrintWriter(sw); 566 ex.printStackTrace(pw); 567 pw.close(); 568 errorProps.setProperty("exception.stacktrace", sw.toString()); 569 } 570 FileSystem fs = FileSystem.get(getJobConf()); 571 OutputStream os = fs.create(new Path(actionDir, ACTION_ERROR_PROPS)); 572 errorProps.store(os, ""); 573 os.close(); 574 575 System.out.print("Failing Oozie Launcher, " + reason + "\n"); 576 System.err.print("Failing Oozie Launcher, " + reason + "\n"); 577 if (ex != null) { 578 ex.printStackTrace(System.out); 579 ex.printStackTrace(System.err); 580 } 581 throw new LauncherException(); 582 } 583 catch (IOException rex) { 584 throw new RuntimeException("Error while failing launcher, " + rex.getMessage(), rex); 585 } 586 } 587 588 /** 589 * Print files and directories in current directory. Will list files in the sub-directory (only 1 level deep) 590 */ 591 protected void printContentsOfCurrentDir() { 592 File folder = new File("."); 593 System.out.println(); 594 System.out.println("Files in current dir:" + folder.getAbsolutePath()); 595 System.out.println("======================"); 596 597 File[] listOfFiles = folder.listFiles(); 598 for (File fileName : listOfFiles) { 599 if (fileName.isFile()) { 600 System.out.println("File: " + fileName.getName()); 601 } 602 else if (fileName.isDirectory()) { 603 System.out.println("Dir: " + fileName.getName()); 604 File subDir = new File(fileName.getName()); 605 File[] moreFiles = subDir.listFiles(); 606 for (File subFileName : moreFiles) { 607 if (subFileName.isFile()) { 608 System.out.println(" File: " + subFileName.getName()); 609 } 610 else if (subFileName.isDirectory()) { 611 System.out.println(" Dir: " + subFileName.getName()); 612 } 613 } 614 } 615 } 616 } 617 618 } 619 620 class LauncherSecurityManager extends SecurityManager { 621 private static boolean exitInvoked; 622 private static int exitCode; 623 private SecurityManager securityManager; 624 625 public LauncherSecurityManager() { 626 reset(); 627 securityManager = System.getSecurityManager(); 628 System.setSecurityManager(this); 629 } 630 631 @Override 632 public void checkPermission(Permission perm, Object context) { 633 if (securityManager != null) { 634 // check everything with the original SecurityManager 635 securityManager.checkPermission(perm, context); 636 } 637 } 638 639 @Override 640 public void checkPermission(Permission perm) { 641 if (securityManager != null) { 642 // check everything with the original SecurityManager 643 securityManager.checkPermission(perm); 644 } 645 } 646 647 @Override 648 public void checkExit(int status) throws SecurityException { 649 exitInvoked = true; 650 exitCode = status; 651 throw new SecurityException("Intercepted System.exit(" + status + ")"); 652 } 653 654 public static boolean getExitInvoked() { 655 return exitInvoked; 656 } 657 658 public static int getExitCode() { 659 return exitCode; 660 } 661 662 public static void reset() { 663 exitInvoked = false; 664 exitCode = 0; 665 } 666 } 667 668 class LauncherException extends Exception { 669 }