8151102: Cleanup javadoc exception handling
authorksrini
Mon, 10 Oct 2016 06:47:47 -0700
changeset 41451 a847c7aa25a7
parent 41450 83877f4dd010
child 41452 ddaef4bba083
8151102: Cleanup javadoc exception handling Reviewed-by: jjg
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/AbstractDoclet.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Configuration.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/IllegalOptionValue.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocTool.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Main.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Messager.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/OptionException.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolEnvironment.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolException.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolOption.java
langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties
langtools/test/jdk/javadoc/doclet/T6735320/T6735320.java
langtools/test/jdk/javadoc/doclet/dupThrowsTags/TestDupThrowsTags.java
langtools/test/jdk/javadoc/doclet/lib/JavadocTester.java
langtools/test/jdk/javadoc/doclet/testBadSourceFile/TestBadSourceFile.java
langtools/test/jdk/javadoc/doclet/testConstantValuesPage/TestConstantValuesPage.java
langtools/test/jdk/javadoc/doclet/testDocErrorReporter/TestDocErrorReporter.java
langtools/test/jdk/javadoc/doclet/testHelpOption/TestHelpOption.java
langtools/test/jdk/javadoc/doclet/testIOException/TestIOException.java
langtools/test/jdk/javadoc/doclet/testPackageHtml/TestPackageHtml.java
langtools/test/jdk/javadoc/doclet/testParamTaglet/TestParamTaglet.java
langtools/test/jdk/javadoc/doclet/testSearch/TestSearch.java
langtools/test/jdk/javadoc/doclet/testSerializedFormDeprecationInfo/TestSerializedFormDeprecationInfo.java
langtools/test/jdk/javadoc/doclet/testSerializedFormDeprecationInfo/pkg1/C1.java
langtools/test/jdk/javadoc/doclet/testSerializedFormDeprecationInfo/pkg1/C2.java
langtools/test/jdk/javadoc/doclet/testSinceTag/TestSinceTag.java
langtools/test/jdk/javadoc/doclet/testSinceTag/pkg1/C1.java
langtools/test/jdk/javadoc/doclet/testSupplementary/TestSupplementary.java
langtools/test/jdk/javadoc/doclet/testThrowsTag/TestThrowsTag.java
langtools/test/jdk/javadoc/doclet/testThrowsTag/pkg/T1.java
langtools/test/jdk/javadoc/doclet/testThrowsTag/pkg/T2.java
langtools/test/jdk/javadoc/doclet/testThrowsTag/pkg/T3.java
langtools/test/jdk/javadoc/doclet/testThrowsTag/pkg/T4.java
langtools/test/jdk/javadoc/doclet/testValueTag/TestValueTag.java
langtools/test/jdk/javadoc/doclet/testWarnings/TestWarnings.java
langtools/test/jdk/javadoc/tool/ReleaseOption.java
langtools/test/jdk/javadoc/tool/exceptionHandling/TestExceptionHandling.java
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/AbstractDoclet.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/AbstractDoclet.java	Mon Oct 10 06:47:47 2016 -0700
@@ -40,7 +40,6 @@
 import jdk.javadoc.internal.doclets.toolkit.builders.BuilderFactory;
 import jdk.javadoc.internal.doclets.toolkit.util.ClassTree;
 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
-import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
 import jdk.javadoc.internal.doclets.toolkit.util.InternalException;
 import jdk.javadoc.internal.doclets.toolkit.util.PackageListWriter;
 import jdk.javadoc.internal.doclets.toolkit.util.ResourceIOException;
@@ -112,8 +111,6 @@
             return false;
         }
 
-        boolean dumpOnError = false;  // set true to always show stack traces
-
         try {
             startGeneration(docEnv);
             return true;
@@ -128,16 +125,16 @@
                     messages.error("doclet.exception.write.file",
                             e.fileName.getPath(), e.getCause());
             }
-            dumpStack(dumpOnError, e);
+            dumpStack(configuration.dumpOnError, e);
 
         } catch (ResourceIOException e) {
             messages.error("doclet.exception.read.resource",
                     e.resource.getPath(), e.getCause());
-            dumpStack(dumpOnError, e);
+            dumpStack(configuration.dumpOnError, e);
 
         } catch (SimpleDocletException e) {
             configuration.reporter.print(ERROR, e.getMessage());
-            dumpStack(dumpOnError, e);
+            dumpStack(configuration.dumpOnError, e);
 
         } catch (InternalException e) {
             configuration.reporter.print(ERROR, e.getMessage());
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Configuration.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/Configuration.java	Mon Oct 10 06:47:47 2016 -0700
@@ -281,6 +281,8 @@
 
     private String pkglistUrlForLinkOffline;
 
+    public boolean dumpOnError = false;
+
     private List<GroupContainer> groups;
 
     public abstract Messages getMessages();
@@ -616,6 +618,13 @@
                     showversion = true;
                     return true;
                 }
+            },
+            new Hidden(resources, "--dump-on-error") {
+                @Override
+                public boolean process(String opt, ListIterator<String> args) {
+                    dumpOnError = true;
+                    return true;
+                }
             }
         };
         Set<Doclet.Option> set = new TreeSet<>();
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ElementsTable.java	Mon Oct 10 06:47:47 2016 -0700
@@ -75,6 +75,7 @@
 import jdk.javadoc.doclet.DocletEnvironment.ModuleMode;
 
 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
+import static jdk.javadoc.internal.tool.Main.Result.*;
 import static jdk.javadoc.internal.tool.JavadocTool.isValidClassName;
 
 /**
@@ -158,6 +159,7 @@
     private final Location location;
     private final Modules modules;
     private final Map<ToolOption, Object> opts;
+    private final Messager messager;
 
     private final Map<String, Entry> entries = new LinkedHashMap<>();
 
@@ -201,6 +203,8 @@
         this.fm = toolEnv.fileManager;
         this.modules = Modules.instance(context);
         this.opts = opts;
+        this.messager = Messager.instance0(context);
+
         this.location = modules.multiModuleMode
                 ? StandardLocation.MODULE_SOURCE_PATH
                 : toolEnv.fileManager.hasLocation(StandardLocation.SOURCE_PATH)
@@ -339,9 +343,9 @@
      * This is a terminal operation, thus no further modifications
      * are allowed to the specified data sets.
      *
-     * @throws IOException if an error occurs
+     * @throws ToolException if an error occurs
      */
-    void analyze() throws IOException {
+    void analyze() throws ToolException {
         // compute the specified element, by expanding module dependencies
         computeSpecifiedModules();
 
@@ -354,7 +358,6 @@
         // compute the packages belonging to all the specified modules
         Set<PackageElement> expandedModulePackages = computeModulePackages();
         initializeIncludedSets(expandedModulePackages);
-
     }
 
     ElementsTable classTrees(com.sun.tools.javac.util.List<JCCompilationUnit> classTrees) {
@@ -363,16 +366,17 @@
     }
 
     @SuppressWarnings("unchecked")
-    ElementsTable scanSpecifiedItems() throws IOException {
+    ElementsTable scanSpecifiedItems() throws ToolException {
 
         // scan modules specified on the command line
         List<String> moduleNames = (List<String>) opts.computeIfAbsent(ToolOption.MODULE,
                 s -> Collections.EMPTY_LIST);
         List<String> mlist = new ArrayList<>();
         for (String m : moduleNames) {
-            Location moduleLoc = fm.getModuleLocation(location, m);
+            Location moduleLoc = getModuleLocation(location, m);
             if (moduleLoc == null) {
-                toolEnv.error("main.module_not_found", m);
+                String text = messager.getText("main.module_not_found", m);
+                throw new ToolException(CMDERR, text);
             } else {
                 mlist.add(m);
                 ModuleSymbol msym = syms.enterModule(names.fromString(m));
@@ -457,7 +461,7 @@
     }
 
     @SuppressWarnings("unchecked")
-    private void computeSubpackages() throws IOException {
+    private void computeSubpackages() throws ToolException {
         ((List<String>) opts.computeIfAbsent(ToolOption.EXCLUDE, v -> Collections.EMPTY_LIST))
                 .stream()
                 .map((packageName) -> new ModulePackage(packageName))
@@ -469,7 +473,14 @@
 
         for (ModulePackage modpkg : subPackages) {
             Location packageLocn = getLocation(modpkg);
-            for (JavaFileObject fo : fm.list(packageLocn, modpkg.packageName, sourceKinds, true)) {
+            Iterable<JavaFileObject> list = null;
+            try {
+                list = fm.list(packageLocn, modpkg.packageName, sourceKinds, true);
+            } catch (IOException ioe) {
+                String text = messager.getText("main.file.manager.list", modpkg.packageName);
+                throw new ToolException(SYSERR, text, ioe);
+            }
+            for (JavaFileObject fo : list) {
                 String binaryName = fm.inferBinaryName(packageLocn, fo);
                 String pn = getPackageName(binaryName);
                 String simpleName = getSimpleName(binaryName);
@@ -554,22 +565,28 @@
         specifiedModuleElements = Collections.unmodifiableSet(result);
     }
 
-    private Set<PackageElement> getAllModulePackages(ModuleElement mdle) throws IOException {
+    private Set<PackageElement> getAllModulePackages(ModuleElement mdle) throws ToolException {
         Set<PackageElement> result = new HashSet<>();
-        ModuleSymbol msym = (ModuleSymbol)mdle;
-        Location msymloc = fm.getModuleLocation(location, msym.name.toString());
-        for (JavaFileObject fo : fm.list(msymloc, "", sourceKinds, true)) {
-            if (fo.getName().endsWith("module-info.java"))
-                continue;
-            String binaryName = fm.inferBinaryName(msymloc, fo);
-            String pn = getPackageName(binaryName);
-            PackageSymbol psym = syms.enterPackage(msym, names.fromString(pn));
-            result.add((PackageElement)psym);
+        ModuleSymbol msym = (ModuleSymbol) mdle;
+        Location msymloc = getModuleLocation(location, msym.name.toString());
+        try {
+            for (JavaFileObject fo : fm.list(msymloc, "", sourceKinds, true)) {
+                if (fo.getName().endsWith("module-info.java"))
+                    continue;
+                String binaryName = fm.inferBinaryName(msymloc, fo);
+                String pn = getPackageName(binaryName);
+                PackageSymbol psym = syms.enterPackage(msym, names.fromString(pn));
+                result.add((PackageElement) psym);
+            }
+
+        } catch (IOException ioe) {
+            String text = messager.getText("main.file.manager.list", msymloc.getName());
+            throw new ToolException(SYSERR, text, ioe);
         }
         return result;
     }
 
-    private Set<PackageElement> computeModulePackages() throws IOException {
+    private Set<PackageElement> computeModulePackages() throws ToolException {
         final AccessKind accessValue = accessFilter.getAccessValue(ElementKind.PACKAGE);
         final boolean documentAllModulePackages = (accessValue == AccessKind.PACKAGE ||
                 accessValue == AccessKind.PRIVATE);
@@ -662,10 +679,10 @@
         includedTypeElements = Collections.unmodifiableSet(iclasses);
     }
 
-    /**
+    /*
      * Computes the included packages and freezes the specified packages list.
      */
-    private void computeSpecifiedPackages() throws IOException {
+    private void computeSpecifiedPackages() throws ToolException {
 
         computeSubpackages();
 
@@ -683,7 +700,7 @@
             if (pkg != null) {
                 packlist.add(pkg);
             } else {
-                toolEnv.warning("main.package_not_found", modpkg.toString());
+                messager.printWarningUsingKey("main.package_not_found", modpkg.toString());
             }
         });
         specifiedPackageElements = Collections.unmodifiableSet(packlist);
@@ -693,7 +710,7 @@
      * Adds all classes as well as inner classes, to the specified
      * list.
      */
-    private void computeSpecifiedTypes() {
+    private void computeSpecifiedTypes() throws ToolException {
         Set<TypeElement> classes = new LinkedHashSet<>();
         classDecList.stream().filter((def) -> (shouldDocument(def.sym))).forEach((def) -> {
             TypeElement te = (TypeElement) def.sym;
@@ -701,24 +718,28 @@
                 addAllClasses(classes, te, true);
             }
         });
-        classArgList.forEach((className) -> {
+        for (String className : classArgList) {
             TypeElement te = toolEnv.loadClass(className);
             if (te == null) {
-                toolEnv.error("javadoc.class_not_found", className);
+                String text = messager.getText("javadoc.class_not_found", className);
+                throw new ToolException(CMDERR, text);
             } else {
                 addAllClasses(classes, te, true);
             }
-        });
+        }
         specifiedTypeElements = Collections.unmodifiableSet(classes);
     }
 
     private void addFilesForParser(Collection<JavaFileObject> result,
-            Collection<ModulePackage> collection, boolean recurse) throws IOException {
+            Collection<ModulePackage> collection,
+            boolean recurse) throws ToolException {
         for (ModulePackage modpkg : collection) {
             toolEnv.notice("main.Loading_source_files_for_package", modpkg.toString());
             List<JavaFileObject> files = getFiles(modpkg, recurse);
             if (files.isEmpty()) {
-                    toolEnv.error("main.no_source_files_for_package", modpkg.toString());
+                String text = messager.getText("main.no_source_files_for_package",
+                        modpkg.toString());
+                throw new ToolException(CMDERR, text);
             } else {
                 result.addAll(files);
             }
@@ -732,7 +753,7 @@
      * @return a list of java file objects
      * @throws IOException if an error occurs
      */
-    List<JavaFileObject> getFilesToParse() throws IOException {
+    List<JavaFileObject> getFilesToParse() throws ToolException {
         List<JavaFileObject> result = new ArrayList<>();
         addFilesForParser(result, cmdLinePackages, false);
         addFilesForParser(result, subPackages, true);
@@ -744,9 +765,10 @@
      *
      * @param packageName the specified package
      * @return the set of file objects for the specified package
-     * @throws IOException if an error occurs while accessing the files
+     * @throws ToolException if an error occurs while accessing the files
      */
-    private List<JavaFileObject> getFiles(ModulePackage modpkg, boolean recurse) throws IOException {
+    private List<JavaFileObject> getFiles(ModulePackage modpkg,
+            boolean recurse) throws ToolException {
         Entry e = getEntry(modpkg);
         // The files may have been found as a side effect of searching for subpackages
         if (e.files != null) {
@@ -759,12 +781,18 @@
             return Collections.emptyList();
         }
         String pname = modpkg.packageName;
-        for (JavaFileObject fo : fm.list(packageLocn, pname, sourceKinds, recurse)) {
-            String binaryName = fm.inferBinaryName(packageLocn, fo);
-            String simpleName = getSimpleName(binaryName);
-            if (isValidClassName(simpleName)) {
-                lb.append(fo);
+
+        try {
+            for (JavaFileObject fo : fm.list(packageLocn, pname, sourceKinds, recurse)) {
+                String binaryName = fm.inferBinaryName(packageLocn, fo);
+                String simpleName = getSimpleName(binaryName);
+                if (isValidClassName(simpleName)) {
+                    lb.append(fo);
+                }
             }
+        } catch (IOException ioe) {
+            String text = messager.getText("main.file.manager.list", pname);
+            throw new ToolException(SYSERR, text, ioe);
         }
 
         return lb.toList();
@@ -781,20 +809,30 @@
             return null;
     }
 
-    private Location getLocation(ModulePackage modpkg) throws IOException {
+    private Location getLocation(ModulePackage modpkg) throws ToolException {
         if (location != StandardLocation.MODULE_SOURCE_PATH) {
             return location;
         }
 
         if (modpkg.hasModule()) {
-            return fm.getModuleLocation(location, modpkg.moduleName);
+            return getModuleLocation(location, modpkg.moduleName);
         }
         // TODO: handle invalid results better.
         ModuleSymbol msym = findModuleOfPackageName(modpkg.packageName);
         if (msym == null) {
             return null;
         }
-        return fm.getModuleLocation(location, msym.name.toString());
+        return getModuleLocation(location, msym.name.toString());
+    }
+
+    private Location getModuleLocation(Location location, String msymName)
+            throws ToolException {
+        try {
+            return fm.getModuleLocation(location, msymName);
+        } catch (IOException ioe) {
+            String text = messager.getText("main.doclet_could_not_get_location", msymName);
+            throw new ToolException(ERROR, text, ioe);
+        }
     }
 
     private Entry getEntry(String name) {
@@ -841,7 +879,10 @@
                 }
             }
         } catch (CompletionFailure e) {
-            // quietly ignore completion failures
+            if (e.getMessage() != null)
+                messager.printWarning(e.getMessage());
+            else
+                messager.printWarningUsingKey("main.unexpected.exception", e);
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/IllegalOptionValue.java	Mon Oct 10 06:47:47 2016 -0700
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.javadoc.internal.tool;
+
+import static jdk.javadoc.internal.tool.Main.Result.CMDERR;
+
+/**
+ * Provides a mechanism for the javadoc tool to indicate an option
+ * decoding issue, arising from command line error.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own risk.
+ * This code and its internal interfaces are subject to change or
+ * deletion without notice.</b>
+ */
+
+class IllegalOptionValue extends OptionException {
+
+    private static final long serialVersionUID = 0;
+
+    /**
+     * Constructs an object containing a runnable and a message.
+     * @param method a method to display suitable usage text
+     * @param message the detailed message
+     */
+    IllegalOptionValue(Runnable method, String message) {
+        super(CMDERR, method, message);
+    }
+}
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocTool.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/JavadocTool.java	Mon Oct 10 06:47:47 2016 -0700
@@ -27,7 +27,6 @@
 
 
 import java.io.File;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
@@ -51,6 +50,8 @@
 import com.sun.tools.javac.util.Position;
 import jdk.javadoc.doclet.DocletEnvironment;
 
+import static jdk.javadoc.internal.tool.Main.Result.*;
+
 /**
  *  This class could be the main entry point for Javadoc when Javadoc is used as a
  *  component in a larger software system. It provides operations to
@@ -120,9 +121,10 @@
         }
     }
 
-    public DocletEnvironment getEnvironment(Map<ToolOption, Object> jdtoolOpts,
-                                      List<String> javaNames,
-                                      Iterable<? extends JavaFileObject> fileObjects) throws IOException {
+    public DocletEnvironment getEnvironment(Map<ToolOption,
+            Object> jdtoolOpts,
+            List<String> javaNames,
+            Iterable<? extends JavaFileObject> fileObjects) throws ToolException {
         toolEnv = ToolEnvironment.instance(context);
         toolEnv.initialize(jdtoolOpts);
         ElementsTable etable = new ElementsTable(context, jdtoolOpts);
@@ -133,10 +135,12 @@
         if (etable.xclasses) {
             // If -Xclasses is set, the args should be a list of class names
             for (String arg: javaNames) {
-                if (!isValidPackageName(arg)) // checks
-                    toolEnv.error("main.illegal_class_name", arg);
+                if (!isValidPackageName(arg)) { // checks
+                    String text = messager.getText("main.illegal_class_name", arg);
+                    throw new ToolException(CMDERR, text);
+                }
             }
-            if (messager.nerrors() != 0) {
+            if (messager.hasErrors()) {
                 return null;
             }
             etable.setClassArgList(javaNames);
@@ -157,19 +161,23 @@
             for (String arg: javaNames) {
                 if (fm != null && arg.endsWith(".java") && new File(arg).exists()) {
                     if (new File(arg).getName().equals("module-info.java")) {
-                        toolEnv.warning("main.file_ignored", arg);
+                        messager.printWarningUsingKey("main.file_ignored", arg);
                     } else {
                         parse(fm.getJavaFileObjects(arg), classTrees, true);
                     }
                 } else if (isValidPackageName(arg)) {
                     packageNames.add(arg);
                 } else if (arg.endsWith(".java")) {
-                    if (fm == null)
-                        throw new IllegalArgumentException();
-                    else
-                        toolEnv.error("main.file_not_found", arg);
+                    if (fm == null) {
+                        String text = messager.getText("main.assertion.error", "fm == null");
+                        throw new ToolException(ABNORMAL, text);
+                    } else {
+                        String text = messager.getText("main.file_not_found", arg);
+                        throw new ToolException(ERROR, text);
+                    }
                 } else {
-                    toolEnv.error("main.illegal_package_name", arg);
+                    String text = messager.getText("main.illegal_package_name", arg);
+                    throw new ToolException(CMDERR, text);
                 }
             }
 
@@ -185,7 +193,7 @@
             parse(etable.getFilesToParse(), packageTrees, false);
             modules.enter(packageTrees.toList(), null);
 
-            if (messager.nerrors() != 0) {
+            if (messager.hasErrors()) {
                 return null;
             }
 
@@ -197,10 +205,19 @@
             enterDone = true;
             etable.analyze();
         } catch (CompletionFailure cf) {
-            toolEnv.printError(cf.getMessage());
-        } catch (Abort ex) {}
+            throw new ToolException(ABNORMAL, cf.getMessage(), cf);
+        } catch (Abort abort) {
+            if (messager.hasErrors()) {
+                // presumably a message has been emitted, keep silent
+                throw new ToolException(ABNORMAL, "", abort);
+            } else {
+                String text = messager.getText("main.internal.error");
+                Throwable t = abort.getCause() == null ? abort : abort.getCause();
+                throw new ToolException(ABNORMAL, text, t);
+            }
+        }
 
-        if (messager.nerrors() != 0)
+        if (messager.hasErrors())
             return null;
 
         toolEnv.docEnv = new DocEnvImpl(toolEnv, etable);
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Main.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Main.java	Mon Oct 10 06:47:47 2016 -0700
@@ -60,7 +60,7 @@
      */
     public static int execute(String... args) {
         Start jdoc = new Start();
-        return jdoc.begin(args);
+        return jdoc.begin(args).exitCode;
     }
 
     /**
@@ -72,7 +72,7 @@
      */
     public static int execute(String[] args, PrintWriter writer) {
         Start jdoc = new Start(writer, writer);
-        return jdoc.begin(args);
+        return jdoc.begin(args).exitCode;
     }
 
     /**
@@ -85,6 +85,36 @@
      */
     public static int execute(String[] args, PrintWriter outWriter, PrintWriter errWriter) {
         Start jdoc = new Start(outWriter, errWriter);
-        return jdoc.begin(args);
+        return jdoc.begin(args).exitCode;
+    }
+
+    public static enum Result {
+        /** completed with no errors */
+        OK(0),
+        /** Completed with reported errors */
+        ERROR(1),
+        /** Bad command-line arguments */
+        CMDERR(2),
+        /** System error or resource exhaustion */
+        SYSERR(3),
+        /** Terminated abnormally */
+        ABNORMAL(4);
+
+        private static final long serialVersionUID = 1L;
+
+        Result(int exitCode) {
+            this.exitCode = exitCode;
+        }
+
+        public boolean isOK() {
+            return (exitCode == 0);
+        }
+
+        public final int exitCode;
+
+        @Override
+        public String toString() {
+            return name() + '(' + exitCode + ')';
+        }
     }
 }
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Messager.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Messager.java	Mon Oct 10 06:47:47 2016 -0700
@@ -48,10 +48,8 @@
 
 /**
  * Utility for integrating with javadoc tools and for localization.
- * Handle Resources. Access to error and warning counts.
- * Message formatting.
- * <br>
- * Also provides implementation for DocErrorReporter.
+ * Handle resources, access to error and warning counts and
+ * message formatting.
  *
  *  <p><b>This is NOT part of any supported API.
  *  If you write code that depends on this, you do so at your own risk.
@@ -139,10 +137,6 @@
         }
     }
 
-    public static class ExitJavadoc extends Error {
-        private static final long serialVersionUID = 0;
-    }
-
     final String programName;
 
     private Locale locale;
@@ -240,7 +234,7 @@
             report(DiagnosticType.ERROR, prefix, msg);
             return;
         }
-        incrementErrorCount(prefix, msg);
+        printError(prefix, msg);
     }
 
     public void printError(Element e, String msg) {
@@ -249,10 +243,15 @@
             report(DiagnosticType.ERROR, prefix, msg);
             return;
         }
-        incrementErrorCount(prefix, msg);
+        printError(prefix, msg);
     }
 
-    private void incrementErrorCount(String prefix, String msg) {
+    public void printErrorUsingKey(String key, Object... args) {
+        printError((Element)null, getText(key, args));
+    }
+
+    // print the error and increment count
+    private void printError(String prefix, String msg) {
         if (nerrors < MaxErrors) {
             PrintWriter errWriter = getWriter(WriterKind.ERROR);
             printRawLines(errWriter, prefix + ": " + getText("javadoc.error") + " - " + msg);
@@ -272,13 +271,21 @@
         printWarning((DocTreePath)null, msg);
     }
 
+    public void printWarningUsingKey(String key, Object... args) {
+        printWarning((Element)null, getText(key, args));
+    }
+
+    public void printWarning(Element e, String key, Object... args) {
+        printWarning(getText(key, args));
+    }
+
     public void printWarning(DocTreePath path, String msg) {
         String prefix = getDiagSource(path);
         if (diagListener != null) {
             report(DiagnosticType.WARNING, prefix, msg);
             return;
         }
-        incrementWarningCount(prefix, msg);
+        printWarning(prefix, msg);
     }
 
     public void printWarning(Element e, String msg) {
@@ -287,10 +294,11 @@
             report(DiagnosticType.WARNING, prefix, msg);
             return;
         }
-        incrementWarningCount(prefix, msg);
+        printWarning(prefix, msg);
     }
 
-    private void incrementWarningCount(String prefix, String msg) {
+    // print the warning and increment count
+    private void printWarning(String prefix, String msg) {
         if (nwarnings < MaxWarnings) {
             PrintWriter warnWriter = getWriter(WriterKind.WARNING);
             printRawLines(warnWriter, prefix + ": " + getText("javadoc.warning") + " - " + msg);
@@ -342,50 +350,6 @@
     }
 
     /**
-     * Print error message, increment error count.
-     *
-     * @param key selects message from resource
-     */
-    public void error(Element e, String key, Object... args) {
-        printError(e, getText(key, args));
-    }
-
-    /**
-     * Print error message, increment error count.
-     *
-     * @param key selects message from resource
-     */
-    public void error(DocTreePath path, String key, Object... args) {
-        printError(path, getText(key, args));
-    }
-
-    public void error(String key, Object... args) {
-        printError((Element)null, getText(key, args));
-    }
-
-    public void warning(String key, Object... args) {
-        printWarning((Element)null, getText(key, args));
-    }
-
-    /**
-     * Print warning message, increment warning count.
-     *
-     * @param key selects message from resource
-     */
-    public void warning(Element e, String key, Object... args) {
-        printWarning(e, getText(key, args));
-    }
-
-    /**
-     * Print warning message, increment warning count.
-     *
-     * @param key selects message from resource
-     */
-    public void warning(DocTreePath path, String key, Object... args) {
-        printWarning(path, getText(key, args));
-    }
-
-    /**
      * Print a message.
      *
      * @param key selects message from resource
@@ -395,21 +359,23 @@
     }
 
     /**
-     * Return total number of errors, including those recorded
-     * in the compilation log.
+     * Returns true if errors have been recorded.
      */
-    public int nerrors() { return nerrors; }
+    public boolean hasErrors() {
+        return nerrors != 0;
+    }
 
     /**
-     * Return total number of warnings, including those recorded
-     * in the compilation log.
+     * Returns true if warnings have been recorded.
      */
-    public int nwarnings() { return nwarnings; }
+    public boolean hasWarnings() {
+        return nwarnings != 0;
+    }
 
     /**
      * Print exit message.
      */
-    public void exitNotice() {
+    public void printErrorWarningCounts() {
         if (nerrors > 0) {
             notice((nerrors > 1) ? "main.errors" : "main.error",
                    "" + nerrors);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/OptionException.java	Mon Oct 10 06:47:47 2016 -0700
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.javadoc.internal.tool;
+
+import jdk.javadoc.internal.tool.Main.Result;
+
+/**
+ * Provides a general mechanism for the javadoc tool to indicate an option
+ * decoding issue.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own risk.
+ * This code and its internal interfaces are subject to change or
+ * deletion without notice.</b>
+ */
+
+class OptionException extends Exception {
+
+    private static final long serialVersionUID = 0;
+
+    public final Result result;
+    public final String message;
+    public final Runnable m;
+
+    /**
+     * Constructs an object with a result, runnable and a message
+     * to be printed out by the catcher. The runnable can be invoked
+     * by the catcher to display the usage text.
+     * @param result the exit code
+     * @param method the method to invoke
+     * @param message the detailed message
+     */
+    public OptionException(Result result, Runnable method, String message) {
+        this.result = result;
+        this.m = method;
+        this.message = message;
+        if (result == null || result.isOK() || method == null || message == null) {
+            throw new AssertionError("result == null || result.isOK() || " +
+                    "method == null || message == null");
+        }
+    }
+
+    /**
+     * Constructs an object with a result and a runnable.
+     * The runnable can be invoked by the catcher to display the usage text.
+     * @param result the exit code
+     * @param method the method to invoke
+     */
+    public OptionException(Result result, Runnable method) {
+        this.result = result;
+        this.m = method;
+        this.message = null;
+        if (result == null || method == null) {
+            throw new AssertionError("result == null || method == null");
+        }
+    }
+}
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java	Mon Oct 10 06:47:47 2016 -0700
@@ -26,7 +26,6 @@
 package jdk.javadoc.internal.tool;
 
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.nio.file.Path;
@@ -65,11 +64,12 @@
 import jdk.javadoc.doclet.Doclet;
 import jdk.javadoc.doclet.Doclet.Option;
 import jdk.javadoc.doclet.DocletEnvironment;
-import jdk.javadoc.internal.doclets.toolkit.Resources;
+import jdk.javadoc.internal.tool.Main.Result;
 
 import static javax.tools.DocumentationTool.Location.*;
 
 import static com.sun.tools.javac.main.Option.*;
+import static jdk.javadoc.internal.tool.Main.Result.*;
 
 /**
  * Main program of Javadoc.
@@ -170,41 +170,28 @@
      */
     @Override
     void usage() {
-        usage(true);
-    }
-
-    void usage(boolean exit) {
-        usage("main.usage", "-help", "main.usage.foot");
-
-        if (exit)
-            throw new Messager.ExitJavadoc();
+        usage("main.usage", OptionKind.STANDARD, "main.usage.foot");
     }
 
     @Override
     void Xusage() {
-        Xusage(true);
+        usage("main.Xusage", OptionKind.EXTENDED, "main.Xusage.foot");
     }
 
-    void Xusage(boolean exit) {
-        usage("main.Xusage", "-X", "main.Xusage.foot");
-
-        if (exit)
-            throw new Messager.ExitJavadoc();
-    }
-
-    private void usage(String header, String option, String footer) {
-        messager.notice(header);
-        showToolOptions(option.equals("-X") ? OptionKind.EXTENDED : OptionKind.STANDARD);
+    private void usage(String headerKey, OptionKind kind, String footerKey) {
+        messager.notice(headerKey);
+        showToolOptions(kind);
 
         // let doclet print usage information
         if (docletClass != null) {
             String name = doclet.getName();
             messager.notice("main.doclet.usage.header", name);
-            showDocletOptions(option.equals("-X") ? Option.Kind.EXTENDED : Option.Kind.STANDARD);
+            showDocletOptions(kind == OptionKind.EXTENDED
+                    ? Option.Kind.EXTENDED
+                    : Option.Kind.STANDARD);
         }
-
-        if (footer != null)
-            messager.notice(footer);
+        if (footerKey != null)
+            messager.notice(footerKey);
     }
 
     void showToolOptions(OptionKind kind) {
@@ -326,25 +313,23 @@
      * of class loader creation, needed to detect the doclet/taglet class variants.
      */
     @SuppressWarnings("deprecation")
-    int begin(String... argv) {
+    Result begin(String... argv) {
         // Preprocess @file arguments
         try {
             argv = CommandLine.parse(argv);
-        } catch (FileNotFoundException e) {
-            messager.error("main.cant.read", e.getMessage());
-            throw new Messager.ExitJavadoc();
         } catch (IOException e) {
-            e.printStackTrace(System.err);
-            throw new Messager.ExitJavadoc();
+            error("main.cant.read", e.getMessage());
+            return ERROR;
         }
 
         if (argv.length > 0 && "-Xold".equals(argv[0])) {
-            messager.warning("main.legacy_api");
+            warn("main.legacy_api");
             String[] nargv = Arrays.copyOfRange(argv, 1, argv.length);
-            return com.sun.tools.javadoc.Main.execute(nargv);
+            return com.sun.tools.javadoc.Main.execute(nargv) == 0
+                    ? OK
+                    : ERROR;
         }
-        boolean ok = begin(Arrays.asList(argv), Collections.<JavaFileObject> emptySet());
-        return ok ? 0 : 1;
+        return begin(Arrays.asList(argv), Collections.<JavaFileObject> emptySet());
     }
 
     // Called by 199 API.
@@ -356,11 +341,11 @@
         for (String opt: options)
             opts.add(opt);
 
-        return begin(opts, fileObjects);
+        return begin(opts, fileObjects).isOK();
     }
 
     @SuppressWarnings("deprecation")
-    private boolean begin(List<String> options, Iterable<? extends JavaFileObject> fileObjects) {
+    private Result begin(List<String> options, Iterable<? extends JavaFileObject> fileObjects) {
         fileManager = context.get(JavaFileManager.class);
         if (fileManager == null) {
             JavacFileManager.preRegister(context);
@@ -369,8 +354,28 @@
                 ((BaseFileManager) fileManager).autoClose = true;
             }
         }
+
         // locale, doclet and maybe taglet, needs to be determined first
-        docletClass = preProcess(fileManager, options);
+        try {
+            docletClass = preprocess(fileManager, options);
+        } catch (ToolException te) {
+            if (!te.result.isOK()) {
+                if (te.message != null) {
+                    messager.printError(te.message);
+                }
+                Throwable t = te.getCause();
+                dumpStack(t == null ? te : t);
+            }
+            return te.result;
+        } catch (OptionException oe) {
+            if (oe.message != null) {
+                messager.printError(oe.message);
+            }
+            oe.m.run();
+            Throwable t = oe.getCause();
+            dumpStack(t == null ? oe : t);
+            return oe.result;
+        }
         if (jdk.javadoc.doclet.Doclet.class.isAssignableFrom(docletClass)) {
             // no need to dispatch to old, safe to init now
             initMessager();
@@ -379,43 +384,62 @@
                 Object o = docletClass.getConstructor().newInstance();
                 doclet = (Doclet) o;
             } catch (ReflectiveOperationException exc) {
-                exc.printStackTrace();
-                if (!apiMode) {
-                    error("main.could_not_instantiate_class", docletClass);
-                    throw new Messager.ExitJavadoc();
+                if (apiMode) {
+                    throw new ClientCodeException(exc);
                 }
-                throw new ClientCodeException(exc);
+                error("main.could_not_instantiate_class", docletClass);
+                return ERROR;
             }
         } else {
-            if (this.apiMode) {
+            if (apiMode) {
                 com.sun.tools.javadoc.main.Start ostart
                         = new com.sun.tools.javadoc.main.Start(context);
-                return ostart.begin(docletClass, options, fileObjects);
+                return ostart.begin(docletClass, options, fileObjects)
+                        ? OK
+                        : ERROR;
             }
             warn("main.legacy_api");
             String[] array = options.toArray(new String[options.size()]);
-            return com.sun.tools.javadoc.Main.execute(array) == 0;
+            return com.sun.tools.javadoc.Main.execute(array) == 0
+                    ? OK
+                    : ERROR;
         }
 
-        boolean failed = false;
+        Result result = OK;
         try {
-            failed = !parseAndExecute(options, fileObjects);
-        } catch (Messager.ExitJavadoc exc) {
-            // ignore, we just exit this way
+            result = parseAndExecute(options, fileObjects)
+                    ? OK
+                    : ERROR;
+        } catch (OptionException toe) {
+            if (toe.message != null)
+                messager.printError(toe.message);
+
+            toe.m.run();
+            Throwable t = toe.getCause();
+            dumpStack(t == null ? toe : t);
+            return toe.result;
+        } catch (ToolException exc) {
+            if (exc.message != null) {
+                messager.printError(exc.message);
+            }
+            Throwable t = exc.getCause();
+            if (result == ABNORMAL) {
+                reportInternalError(t == null ? exc : t);
+            } else {
+                dumpStack(t == null ? exc : t);
+            }
+            return exc.result;
         } catch (OutOfMemoryError ee) {
-            messager.error("main.out.of.memory");
-            failed = true;
+            error("main.out.of.memory");
+            result = SYSERR;
+            dumpStack(ee);
         } catch (ClientCodeException e) {
             // simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl
             throw e;
-        } catch (Error ee) {
-            ee.printStackTrace(System.err);
-            messager.error("main.fatal.error");
-            failed = true;
-        } catch (Exception ee) {
-            ee.printStackTrace(System.err);
-            messager.error("main.fatal.exception");
-            failed = true;
+        } catch (Error | Exception ee) {
+            error("main.fatal.error", ee);
+            reportInternalError(ee);
+            result = ABNORMAL;
         } finally {
             if (fileManager != null
                     && fileManager instanceof BaseFileManager
@@ -424,17 +448,34 @@
                     fileManager.close();
                 } catch (IOException ignore) {}
             }
-            boolean haveErrorWarnings = messager.nerrors() > 0 ||
-                    (rejectWarnings && messager.nwarnings() > 0);
-            if (failed && !haveErrorWarnings) {
+            boolean haveErrorWarnings = messager.hasErrors()
+                    || (rejectWarnings && messager.hasWarnings());
+            if (!result.isOK() && !haveErrorWarnings) {
                 // the doclet failed, but nothing reported, flag it!.
-                messager.error("main.unknown.error");
+                error("main.unknown.error");
             }
-            failed |= haveErrorWarnings;
-            messager.exitNotice();
+            if (haveErrorWarnings && result.isOK()) {
+                result = ERROR;
+            }
+            messager.printErrorWarningCounts();
             messager.flush();
         }
-        return !failed;
+        return result;
+    }
+
+    private void reportInternalError(Throwable t) {
+        messager.printErrorUsingKey("doclet.internal.report.bug");
+        dumpStack(true, t);
+    }
+
+    private void dumpStack(Throwable t) {
+        dumpStack(false, t);
+    }
+
+    private void dumpStack(boolean enabled, Throwable t) {
+        if (t != null && (enabled || dumpOnError)) {
+            t.printStackTrace(System.err);
+        }
     }
 
     /**
@@ -442,7 +483,7 @@
      */
     @SuppressWarnings("unchecked")
     private boolean parseAndExecute(List<String> argList,
-            Iterable<? extends JavaFileObject> fileObjects) throws IOException {
+            Iterable<? extends JavaFileObject> fileObjects) throws ToolException, OptionException {
         long tm = System.currentTimeMillis();
 
         List<String> javaNames = new ArrayList<>();
@@ -463,17 +504,21 @@
 
         if (platformString != null) {
             if (compOpts.isSet("-source")) {
-                usageError("main.release.bootclasspath.conflict", "-source");
+                String text = messager.getText("main.release.bootclasspath.conflict", "-source");
+                throw new ToolException(CMDERR, text);
             }
             if (fileManagerOpts.containsKey(BOOT_CLASS_PATH)) {
-                usageError("main.release.bootclasspath.conflict", BOOT_CLASS_PATH.getPrimaryName());
+                String text = messager.getText("main.release.bootclasspath.conflict",
+                        BOOT_CLASS_PATH.getPrimaryName());
+                throw new ToolException(CMDERR, text);
             }
 
             PlatformDescription platformDescription =
                     PlatformUtils.lookupPlatformDescription(platformString);
 
             if (platformDescription == null) {
-                usageError("main.unsupported.release.version", platformString);
+                String text = messager.getText("main.unsupported.release.version", platformString);
+                throw new IllegalArgumentException(text);
             }
 
             compOpts.put(SOURCE, platformDescription.getSourceVersion());
@@ -485,10 +530,15 @@
             if (platformCP != null) {
                 if (fileManager instanceof StandardJavaFileManager) {
                     StandardJavaFileManager sfm = (StandardJavaFileManager) fileManager;
-
-                    sfm.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, platformCP);
+                    try {
+                        sfm.setLocationFromPaths(StandardLocation.PLATFORM_CLASS_PATH, platformCP);
+                    } catch (IOException ioe) {
+                        throw new ToolException(SYSERR, ioe.getMessage(), ioe);
+                    }
                 } else {
-                    usageError("main.release.not.standard.file.manager", platformString);
+                    String text = messager.getText("main.release.not.standard.file.manager",
+                                                    platformString);
+                    throw new ToolException(ABNORMAL, text);
                 }
             }
         }
@@ -502,7 +552,8 @@
                     s -> Collections.EMPTY_LIST);
             if (subpkgs.isEmpty()) {
                 if (javaNames.isEmpty() && isEmpty(fileObjects)) {
-                    usageError("main.No_modules_packages_or_classes_specified");
+                    String text = messager.getText("main.No_modules_packages_or_classes_specified");
+                    throw new ToolException(CMDERR, text);
                 }
             }
         }
@@ -535,7 +586,8 @@
     }
 
     Set<Doclet.Option> docletOptions = null;
-    int handleDocletOptions(int idx, List<String> args, boolean isToolOption) {
+    int handleDocletOptions(int idx, List<String> args, boolean isToolOption)
+            throws OptionException {
         if (docletOptions == null) {
             docletOptions = doclet.getSupportedOptions();
         }
@@ -549,24 +601,25 @@
             argBase = arg;
             argVal = null;
         }
-
+        String text = null;
         for (Doclet.Option opt : docletOptions) {
             if (opt.matches(argBase)) {
                 if (argVal != null) {
                     switch (opt.getArgumentCount()) {
                         case 0:
-                            usageError("main.unnecessary_arg_provided", argBase);
-                            break;
+                            text = messager.getText("main.unnecessary_arg_provided", argBase);
+                            throw new OptionException(ERROR, this::usage, text);
                         case 1:
                             opt.process(arg, Arrays.asList(argVal).listIterator());
                             break;
                         default:
-                            usageError("main.only_one_argument_with_equals", argBase);
-                            break;
+                            text = messager.getText("main.only_one_argument_with_equals", argBase);
+                            throw new OptionException(ERROR, this::usage, text);
                     }
                 } else {
                     if (args.size() - idx -1 < opt.getArgumentCount()) {
-                        usageError("main.requires_argument", arg);
+                        text = messager.getText("main.requires_argument", arg);
+                        throw new OptionException(ERROR, this::usage, text);
                     }
                     opt.process(arg, args.listIterator(idx + 1));
                     idx += opt.getArgumentCount();
@@ -575,12 +628,15 @@
             }
         }
         // check if arg is accepted by the tool before emitting error
-        if (!isToolOption)
-            usageError("main.invalid_flag", arg);
+        if (!isToolOption) {
+            text = messager.getText("main.invalid_flag", arg);
+            throw new OptionException(ERROR, this::usage, text);
+        }
         return idx;
     }
 
-    private Class<?> preProcess(JavaFileManager jfm, List<String> argv) {
+    private Class<?> preprocess(JavaFileManager jfm,
+            List<String> argv) throws ToolException, OptionException {
         // doclet specifying arguments
         String userDocletPath = null;
         String userDocletName = null;
@@ -593,19 +649,31 @@
         // Step 1: loop through the args, set locale early on, if found.
         for (int i = 0 ; i < argv.size() ; i++) {
             String arg = argv.get(i);
-            if (arg.equals(ToolOption.LOCALE.primaryName)) {
+            if (arg.equals(ToolOption.DUMPONERROR.primaryName)) {
+                dumpOnError = true;
+            } else if (arg.equals(ToolOption.LOCALE.primaryName)) {
                 checkOneArg(argv, i++);
                 String lname = argv.get(i);
                 locale = getLocale(lname);
             } else if (arg.equals(ToolOption.DOCLET.primaryName)) {
                 checkOneArg(argv, i++);
                 if (userDocletName != null) {
-                    usageError("main.more_than_one_doclet_specified_0_and_1",
+                    if (apiMode) {
+                        throw new IllegalArgumentException("More than one doclet specified (" +
+                                userDocletName + " and " + argv.get(i) + ").");
+                    }
+                    String text = messager.getText("main.more_than_one_doclet_specified_0_and_1",
                             userDocletName, argv.get(i));
+                    throw new ToolException(CMDERR, text);
                 }
                 if (docletName != null) {
-                    usageError("main.more_than_one_doclet_specified_0_and_1",
+                    if (apiMode) {
+                        throw new IllegalArgumentException("More than one doclet specified (" +
+                                docletName + " and " + argv.get(i) + ").");
+                    }
+                    String text = messager.getText("main.more_than_one_doclet_specified_0_and_1",
                             docletName, argv.get(i));
+                    throw new ToolException(CMDERR, text);
                 }
                 userDocletName = argv.get(i);
             } else if (arg.equals(ToolOption.DOCLETPATH.primaryName)) {
@@ -644,23 +712,37 @@
                     try {
                         ((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths);
                     } catch (IOException ioe) {
-                        error("main.doclet_could_not_set_location", paths);
-                        throw new Messager.ExitJavadoc();
+                        if (apiMode) {
+                            throw new IllegalArgumentException("Could not set location for " +
+                                    userDocletPath, ioe);
+                        }
+                        String text = messager.getText("main.doclet_could_not_set_location",
+                                userDocletPath);
+                        throw new ToolException(CMDERR, text, ioe);
                     }
                 }
                 cl = fileManager.getClassLoader(DOCLET_PATH);
                 if (cl == null) {
                     // despite doclet specified on cmdline no classloader found!
-                    error("main.doclet_no_classloader_found", userDocletName);
-                    throw new Messager.ExitJavadoc();
+                    if (apiMode) {
+                        throw new IllegalArgumentException("Could not obtain classloader to load "
+                                + userDocletPath);
+                    }
+                    String text = messager.getText("main.doclet_no_classloader_found",
+                            userDocletName);
+                    throw new ToolException(CMDERR, text);
                 }
             }
             try {
                 Class<?> klass = cl.loadClass(userDocletName);
                 return klass;
             } catch (ClassNotFoundException cnfe) {
-                error("main.doclet_class_not_found", userDocletName);
-                throw new Messager.ExitJavadoc();
+                if (apiMode) {
+                    throw new IllegalArgumentException("Cannot find doclet class " + userDocletName,
+                            cnfe);
+                }
+                String text = messager.getText("main.doclet_class_not_found", userDocletName);
+                throw new ToolException(CMDERR, text, cnfe);
             }
         }
 
@@ -669,8 +751,11 @@
             try {
                 return Class.forName(docletName, true, getClass().getClassLoader());
             } catch (ClassNotFoundException cnfe) {
-                error("main.doclet_class_not_found", userDocletName);
-                throw new Messager.ExitJavadoc();
+                if (apiMode) {
+                    throw new IllegalArgumentException("Cannot find doclet class " + userDocletName);
+                }
+                String text = messager.getText("main.doclet_class_not_found", userDocletName);
+                throw new ToolException(CMDERR, text, cnfe);
             }
         }
 
@@ -690,20 +775,20 @@
      * nature to take its own course.
      */
     @SuppressWarnings("deprecation")
-    private boolean hasOldTaglet(List<String> tagletNames, List<File> tagletPaths) {
+    private boolean hasOldTaglet(List<String> tagletNames, List<File> tagletPaths) throws ToolException {
         if (!fileManager.hasLocation(TAGLET_PATH)) {
             try {
                 ((StandardJavaFileManager) fileManager).setLocation(TAGLET_PATH, tagletPaths);
             } catch (IOException ioe) {
-                error("main.doclet_could_not_set_location", tagletPaths);
-                throw new Messager.ExitJavadoc();
+                String text = messager.getText("main.doclet_could_not_set_location", tagletPaths);
+                throw new ToolException(CMDERR, text, ioe);
             }
         }
         ClassLoader cl = fileManager.getClassLoader(TAGLET_PATH);
         if (cl == null) {
             // no classloader found!
-            error("main.doclet_no_classloader_found", tagletNames.get(0));
-            throw new Messager.ExitJavadoc();
+            String text = messager.getText("main.doclet_no_classloader_found", tagletNames.get(0));
+            throw new ToolException(CMDERR, text);
         }
         for (String tagletName : tagletNames) {
             try {
@@ -712,14 +797,15 @@
                     return true;
                 }
             } catch (ClassNotFoundException cnfe) {
-                error("main.doclet_class_not_found", tagletName);
-                throw new Messager.ExitJavadoc();
+                String text = messager.getText("main.doclet_class_not_found", tagletName);
+                throw new ToolException(CMDERR, text, cnfe);
             }
         }
         return false;
     }
 
-    private void parseArgs(List<String> args, List<String> javaNames) {
+    private void parseArgs(List<String> args, List<String> javaNames) throws ToolException,
+            OptionException {
         for (int i = 0 ; i < args.size() ; i++) {
             String arg = args.get(i);
             ToolOption o = ToolOption.get(arg);
@@ -727,7 +813,6 @@
                 // handle a doclet argument that may be needed however
                 // don't increment the index, and allow the tool to consume args
                 handleDocletOptions(i, args, true);
-
                 if (o.hasArg) {
                     if (arg.startsWith("--") && arg.contains("=")) {
                         o.process(this, arg.substring(arg.indexOf('=') + 1));
@@ -763,24 +848,19 @@
      * Check the one arg option.
      * Error and exit if one argument is not provided.
      */
-    private void checkOneArg(List<String> args, int index) {
+    private void checkOneArg(List<String> args, int index) throws OptionException {
         if ((index + 1) >= args.size() || args.get(index + 1).startsWith("-d")) {
-            usageError("main.requires_argument", args.get(index));
+            String text = messager.getText("main.requires_argument", args.get(index));
+            throw new OptionException(CMDERR, this::usage, text);
         }
     }
 
-    @Override
-    void usageError(String key, Object... args) {
-        error(key, args);
-        usage(true);
-    }
-
     void error(String key, Object... args) {
-        messager.error(key, args);
+        messager.printErrorUsingKey(key, args);
     }
 
     void warn(String key, Object... args)  {
-        messager.warning(key, args);
+        messager.printWarningUsingKey(key, args);
     }
 
     /**
@@ -788,7 +868,7 @@
      * else return null and if locale option is not used
      * then return default locale.
      */
-    private Locale getLocale(String localeName) {
+    private Locale getLocale(String localeName) throws ToolException {
         Locale userlocale = null;
         if (localeName == null || localeName.isEmpty()) {
             return Locale.getDefault();
@@ -804,8 +884,8 @@
             if (seconduscore > 0) {
                 if (seconduscore != firstuscore + 3
                         || localeName.length() <= seconduscore + 1) {
-                    usageError("main.malformed_locale_name", localeName);
-                    return null;
+                    String text = messager.getText("main.malformed_locale_name", localeName);
+                    throw new ToolException(CMDERR, text);
                 }
                 country = localeName.substring(firstuscore + 1,
                         seconduscore);
@@ -813,19 +893,19 @@
             } else if (localeName.length() == firstuscore + 3) {
                 country = localeName.substring(firstuscore + 1);
             } else {
-                usageError("main.malformed_locale_name", localeName);
-                return null;
+                String text = messager.getText("main.malformed_locale_name", localeName);
+                throw new ToolException(CMDERR, text);
             }
         } else if (firstuscore == -1 && localeName.length() == 2) {
             language = localeName;
         } else {
-            usageError("main.malformed_locale_name", localeName);
-            return null;
+            String text = messager.getText("main.malformed_locale_name", localeName);
+            throw new ToolException(CMDERR, text);
         }
         userlocale = searchLocale(language, country, variant);
         if (userlocale == null) {
-            usageError("main.illegal_locale_name", localeName);
-            return null;
+            String text = messager.getText("main.illegal_locale_name", localeName);
+            throw new ToolException(CMDERR, text);
         } else {
             return userlocale;
         }
@@ -861,4 +941,9 @@
             }
         };
     }
+
+    @Override
+    String getLocalizedMessage(String msg, Object... args) {
+        return messager.getText(msg, args);
+    }
 }
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolEnvironment.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolEnvironment.java	Mon Oct 10 06:47:47 2016 -0700
@@ -87,7 +87,7 @@
         return instance;
     }
 
-    private final Messager messager;
+    final Messager messager;
 
     /** Predefined symbols known to the compiler. */
     public final Symtab syms;
@@ -204,182 +204,8 @@
         return path != null;
     }
 
-    //---------------- print forwarders ----------------//
-
-    // ERRORS
     /**
-     * Print error message, increment error count.
-     *
-     * @param msg message to print.
-     */
-    public void printError(String msg) {
-        messager.printError(msg);
-    }
-
-//    /**
-//     * Print error message, increment error count.
-//     *
-//     * @param key selects message from resource
-//     */
-//    public void error(Element element, String key) {
-//        if (element == null)
-//            messager.error(key);
-//        else
-//            messager.error(element, key);
-//    }
-//
-//    public void error(String prefix, String key) {
-//        printError(prefix + ":" + messager.getText(key));
-//    }
-//
-//    /**
-//     * Print error message, increment error count.
-//     *
-//     * @param path the path to the source
-//     * @param key selects message from resource
-//     */
-//    public void error(DocTreePath path, String key) {
-//        messager.error(path, key);
-//    }
-//
-//    /**
-//     * Print error message, increment error count.
-//     *
-//     * @param path the path to the source
-//     * @param msg message to print.
-//     */
-//    public void printError(DocTreePath path, String msg) {
-//        messager.printError(path, msg);
-//    }
-//
-//    /**
-//     * Print error message, increment error count.
-//     * @param e the target element
-//     * @param msg message to print.
-//     */
-//    public void printError(Element e, String msg) {
-//        messager.printError(e, msg);
-//    }
-
-    /**
-     * Print error message, increment error count.
-     * @param key selects message from resource
-     * @param args replacement arguments
-     */
-    public void error(String key, String... args) {
-        error(null, key, args);
-    }
-
-    /**
-     * Print error message, increment error count.
-     *
-     * @param element the source element
-     * @param key selects message from resource
-     * @param args replacement arguments
-     */
-    public void error(Element element, String key, String... args) {
-        if (element == null)
-            messager.error(key, (Object[]) args);
-        else
-            messager.error(element, key, (Object[]) args);
-    }
-
-    // WARNINGS
-
-//    /**
-//     * Print warning message, increment warning count.
-//     *
-//     * @param msg message to print.
-//     */
-//    public void printWarning(String msg) {
-//        messager.printWarning(msg);
-//    }
-//
-//    public void warning(String key) {
-//        warning((Element)null, key);
-//    }
-
-    public void warning(String key, String... args) {
-        warning((Element)null, key, args);
-    }
-
-//    /**
-//     * Print warning message, increment warning count.
-//     *
-//     * @param element the source element
-//     * @param key selects message from resource
-//     */
-//    public void warning(Element element, String key) {
-//        if (element == null)
-//            messager.warning(key);
-//        else
-//            messager.warning(element, key);
-//    }
-//
-//    /**
-//     * Print warning message, increment warning count.
-//     *
-//     * @param path the path to the source
-//     * @param msg message to print.
-//     */
-//    public void printWarning(DocTreePath path, String msg) {
-//        messager.printWarning(path, msg);
-//    }
-//
-//    /**
-//     * Print warning message, increment warning count.
-//     *
-//     * @param e  the source element
-//     * @param msg message to print.
-//     */
-//    public void printWarning(Element e, String msg) {
-//        messager.printWarning(e, msg);
-//    }
-
-    /**
-     * Print warning message, increment warning count.
-     *
-     * @param e    the source element
-     * @param key  selects message from resource
-     * @param args the replace arguments
-     */
-    public void warning(Element e, String key, String... args) {
-        if (e == null)
-            messager.warning(key, (Object[]) args);
-        else
-            messager.warning(e, key, (Object[]) args);
-    }
-
-//    Note: no longer required
-//    /**
-//     * Print a message.
-//     *
-//     * @param msg message to print.
-//     */
-//    public void printNotice(String msg) {
-//        if (quiet) {
-//            return;
-//        }
-//        messager.printNotice(msg);
-//    }
-
-//  Note: no longer required
-//    /**
-//     * Print a message.
-//     *
-//     * @param e the source element
-//     * @param msg message to print.
-//     */
-//    public void printNotice(Element e, String msg) {
-//        if (quiet) {
-//            return;
-//        }
-//        messager.printNotice(e, msg);
-//    }
-
-    //  NOTICES
-    /**
-     * Print a message.
+     * Print a notice, iff <em>quiet</em> is not specified.
      *
      * @param key selects message from resource
      */
@@ -390,22 +216,8 @@
         messager.notice(key);
     }
 
-//    Note: not used anymore
-//    /**
-//     * Print a message.
-//     *
-//     * @param path the path to the source
-//     * @param msg message to print.
-//     */
-//    public void printNotice(DocTreePath path, String msg) {
-//        if (quiet) {
-//            return;
-//        }
-//        messager.printNotice(path, msg);
-//    }
-
     /**
-     * Print a message.
+     * Print a notice, iff <em>quiet</em> is not specified.
      *
      * @param key selects message from resource
      * @param a1 first argument
@@ -417,48 +229,6 @@
         messager.notice(key, a1);
     }
 
-//    Note: not used anymore
-//    /**
-//     * Print a message.
-//     *
-//     * @param key selects message from resource
-//     * @param a1 first argument
-//     * @param a2 second argument
-//     */
-//    public void notice(String key, String a1, String a2) {
-//        if (quiet) {
-//            return;
-//        }
-//        messager.notice(key, a1, a2);
-//    }
-//
-
-//    Note: not used anymore
-//    /**
-//     * Print a message.
-//     *
-//     * @param key selects message from resource
-//     * @param a1 first argument
-//     * @param a2 second argument
-//     * @param a3 third argument
-//     */
-//    public void notice(String key, String a1, String a2, String a3) {
-//        if (quiet) {
-//            return;
-//        }
-//        messager.notice(key, a1, a2, a3);
-//    }
-
-    /**
-     * Exit, reporting errors and warnings.
-     */
-    public void exit() {
-        // Messager should be replaced by a more general
-        // compilation environment.  This can probably
-        // subsume DocEnv as well.
-        throw new Messager.ExitJavadoc();
-    }
-
     TreePath getTreePath(JCCompilationUnit tree) {
         TreePath p = treePaths.get(tree);
         if (p == null)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolException.java	Mon Oct 10 06:47:47 2016 -0700
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.javadoc.internal.tool;
+
+import jdk.javadoc.internal.tool.Main.Result;
+
+/**
+ * Provides a mechanism for the javadoc tool to terminate execution.
+ * This class is constructed with a result and an error message,
+ * that can be printed out before termination, a cause can also
+ * be wrapped to supply extended information about the exception.
+ *
+ * <p><b>This is NOT part of any supported API.
+ * If you write code that depends on this, you do so at your own risk.
+ * This code and its internal interfaces are subject to change or
+ * deletion without notice.</b>
+ */
+
+class ToolException extends Exception {
+
+    private static final long serialVersionUID = 0;
+
+    final String message;
+
+    final Result result;
+
+    /**
+     * Constructs an object containing a result and a message to be
+     * printed out by the catcher.
+     * @param result the exit code
+     * @param message the detailed message
+     */
+    ToolException(Result result, String message) {
+        this.message = message;
+        this.result = result;
+        if (result == null || result.isOK() || message == null) {
+            throw new AssertionError("result == null || result.isOK() || message == null");
+        }
+    }
+
+    /**
+     * Constructs an object containing a result, a messages and an underlying cause.
+     * @param result the exit code
+     * @param message the detailed message
+     * @param cause the underlying cause
+     */
+    ToolException(Result result, String message, Throwable cause) {
+        super(cause);
+        this.message = message;
+        this.result = result;
+        if (result == null || message == null || cause == null || result.isOK()) {
+            throw new AssertionError("result == null || message == null"
+                    + " || cause == null || result.isOK()");
+        }
+    }
+}
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolOption.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/ToolOption.java	Mon Oct 10 06:47:47 2016 -0700
@@ -40,6 +40,7 @@
 import com.sun.tools.javac.util.Options;
 
 import static com.sun.tools.javac.main.Option.OptionKind.*;
+import static jdk.javadoc.internal.tool.Main.Result.*;
 
 /**
  * javadoc tool options.
@@ -226,63 +227,63 @@
 
     PACKAGE("-package", STANDARD) {
         @Override
-        public void process(Helper helper) {
+        public void process(Helper helper) throws OptionException {
             helper.setSimpleFilter("package");
         }
     },
 
     PRIVATE("-private", STANDARD) {
         @Override
-        public void process(Helper helper) {
+        public void process(Helper helper) throws OptionException {
             helper.setSimpleFilter("private");
         }
     },
 
     PROTECTED("-protected", STANDARD) {
         @Override
-        public void process(Helper helper) {
+        public void process(Helper helper) throws OptionException {
             helper.setSimpleFilter("protected");
         }
     },
 
     PUBLIC("-public", STANDARD) {
         @Override
-        public void process(Helper helper) {
+        public void process(Helper helper) throws OptionException {
             helper.setSimpleFilter("public");
         }
     },
 
     SHOW_MEMBERS("--show-members", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws OptionException {
             helper.setFilter(this, arg);
         }
     },
 
     SHOW_TYPES("--show-types", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws OptionException {
             helper.setFilter(this, arg);
         }
     },
 
     SHOW_PACKAGES("--show-packages", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws OptionException {
             helper.setShowPackageAccess(SHOW_PACKAGES, arg);
         }
     },
 
     SHOW_MODULE_CONTENTS("--show-module-contents", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws OptionException {
             helper.setShowModuleContents(SHOW_MODULE_CONTENTS, arg);
         }
     },
 
     EXPAND_REQUIRES("--expand-requires", STANDARD, true) {
         @Override
-        public void process(Helper helper, String arg) {
+        public void process(Helper helper, String arg) throws OptionException {
             helper.setExpandRequires(EXPAND_REQUIRES, arg);
         }
     },
@@ -342,19 +343,26 @@
         }
     },
 
+    DUMPONERROR("--dump-on-error", HIDDEN) {
+        @Override
+        public void process(Helper helper) {
+            helper.dumpOnError = true;
+        }
+    },
+
     // ----- help options -----
 
     HELP("--help -help", STANDARD) {
         @Override
-        public void process(Helper helper) {
-            helper.usage();
+        public void process(Helper helper) throws OptionException {
+            throw new OptionException(OK, helper::usage);
         }
     },
 
     X("-X", STANDARD) {
         @Override
-        public void process(Helper helper) {
-            helper.Xusage();
+        public void process(Helper helper) throws OptionException {
+           throw new OptionException(OK, helper::Xusage);
         }
     },
 
@@ -395,9 +403,9 @@
         this.hasSuffix = lastChar == ':' || lastChar == '=';
     }
 
-    void process(Helper helper, String arg) { }
+    void process(Helper helper, String arg) throws OptionException { }
 
-    void process(Helper helper) { }
+    void process(Helper helper) throws OptionException { }
 
     List<String> getNames() {
         return names;
@@ -451,6 +459,9 @@
         /** Javadoc tool options */
         final Map<ToolOption, Object> jdtoolOpts = new EnumMap<>(ToolOption.class);
 
+        /** dump stack traces for debugging etc.*/
+        boolean dumpOnError = false;
+
         /** Set by -breakiterator. */
         boolean breakiterator = false;
 
@@ -470,7 +481,8 @@
         abstract void usage();
         abstract void Xusage();
 
-        abstract void usageError(String msg, Object... args);
+        abstract String getLocalizedMessage(String msg, Object... args);
+
         abstract OptionHelper getOptionHelper();
 
         @SuppressWarnings("unchecked")
@@ -480,7 +492,7 @@
             jdtoolOpts.put(opt, list);
         }
 
-        void setExpandRequires(ToolOption opt, String arg) {
+        void setExpandRequires(ToolOption opt, String arg) throws OptionException {
             switch (arg) {
                 case "public":
                     jdtoolOpts.put(opt, AccessKind.PUBLIC);
@@ -489,11 +501,12 @@
                     jdtoolOpts.put(opt, AccessKind.PRIVATE);
                     break;
                 default:
-                    usageError("main.illegal_option_value", arg);
+                    String text = getLocalizedMessage("main.illegal_option_value", arg);
+                    throw new IllegalOptionValue(this::usage, text);
             }
         }
 
-        void setShowModuleContents(ToolOption opt, String arg) {
+        void setShowModuleContents(ToolOption opt, String arg) throws OptionException {
             switch (arg) {
                 case "api":
                     jdtoolOpts.put(opt, AccessKind.PUBLIC);
@@ -502,11 +515,12 @@
                     jdtoolOpts.put(opt, AccessKind.PRIVATE);
                     break;
                 default:
-                    usageError("main.illegal_option_value", arg);
+                    String text = getLocalizedMessage("main.illegal_option_value", arg);
+                    throw new IllegalOptionValue(this::usage, text);
             }
         }
 
-        void setShowPackageAccess(ToolOption opt, String arg) {
+        void setShowPackageAccess(ToolOption opt, String arg) throws OptionException {
             switch (arg) {
                 case "exported":
                     jdtoolOpts.put(opt, AccessKind.PUBLIC);
@@ -515,16 +529,17 @@
                     jdtoolOpts.put(opt, AccessKind.PRIVATE);
                     break;
                 default:
-                    usageError("main.illegal_option_value", arg);
+                    String text = getLocalizedMessage("main.illegal_option_value", arg);
+                    throw new IllegalOptionValue(this::usage, text);
             }
         }
 
 
-        void setFilter(ToolOption opt, String arg) {
+        void setFilter(ToolOption opt, String arg) throws OptionException {
             jdtoolOpts.put(opt, getAccessValue(arg));
         }
 
-        void setSimpleFilter(String arg) {
+        void setSimpleFilter(String arg) throws OptionException {
             handleSimpleOption(arg);
         }
 
@@ -532,7 +547,7 @@
             fileManagerOpts.put(opt, arg);
         }
 
-        void handleSimpleOption(String arg) {
+        void handleSimpleOption(String arg) throws OptionException {
             populateSimpleAccessMap(getAccessValue(arg));
         }
 
@@ -541,7 +556,7 @@
          * -private, so on, in addition to the new ones such as
          * --show-types:public and so on.
          */
-        private AccessKind getAccessValue(String arg) {
+        private AccessKind getAccessValue(String arg) throws OptionException {
             int colon = arg.indexOf(':');
             String value = (colon > 0)
                     ? arg.substring(colon + 1)
@@ -556,8 +571,8 @@
                 case "private":
                     return AccessKind.PRIVATE;
                 default:
-                    usageError("main.illegal_option_value", value);
-                    return null;
+                    String text = getLocalizedMessage("main.illegal_option_value", value);
+                    throw new IllegalOptionValue(this::usage, text);
             }
         }
 
--- a/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/resources/javadoc.properties	Mon Oct 10 06:47:47 2016 -0700
@@ -265,13 +265,13 @@
 main.Building_tree=Constructing Javadoc information...
 main.no_source_files_for_package=No source files for package {0}
 main.package_not_found=Package {0} not found
-main.fatal.error=fatal error
-main.fatal.exception=fatal exception
+main.fatal.error=fatal error encountered: {0}
 main.out.of.memory=java.lang.OutOfMemoryError: Please increase memory.\n\
 For example, on the JDK Classic or HotSpot VMs, add the option -J-Xmx\n\
 such as -J-Xmx32m.
 main.done_in=[done in {0} ms]
 main.more_than_one_doclet_specified_0_and_1=More than one doclet specified ({0} and {1}).
+main.doclet_could_not_get_location=Could not get location for {0}
 main.doclet_could_not_set_location=Could not set location for {0}
 main.doclet_no_classloader_found=Could not obtain classloader to load {0}
 main.could_not_instantiate_class=Could not instantiate class {0}
@@ -286,7 +286,15 @@
 main.release.bootclasspath.conflict=option {0} cannot be used together with -release
 main.unsupported.release.version=release version {0} not supported
 main.release.not.standard.file.manager=-release option specified, but the provided JavaFileManager is not a StandardJavaFileManager.
+main.file.manager.list=FileManager error listing files: "{0}"
+main.assertion.error=assertion failed: "{0}}"
 main.unknown.error=an unknown error has occurred
+main.internal.error=an internal error has occurred
+main.unexpected.exception=an unexpected exception was caught: {0}
+doclet.internal.report.bug=\
+Please file a bug against the javadoc tool via the Java bug reporting page\n\
+(http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com)\n\
+for duplicates. Include error messages and the following diagnostic in your report. Thank you.
 main.legacy_api=The old Doclet and Taglet APIs in the packages\n\
     com.sun.javadoc, com.sun.tools.doclets and their implementations\n\
     are planned to be removed in a future JDK release. These\n\
--- a/langtools/test/jdk/javadoc/doclet/T6735320/T6735320.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/T6735320/T6735320.java	Mon Oct 10 06:47:47 2016 -0700
@@ -42,7 +42,7 @@
     void test() {
         javadoc("-d", "out",
                 testSrc("SerialFieldTest.java"));
-        checkExit(Exit.FAILED);
+        checkExit(Exit.ERROR);
         checkOutput(Output.STDERR, false,
                 "OutOfBoundsException");
     }
--- a/langtools/test/jdk/javadoc/doclet/dupThrowsTags/TestDupThrowsTags.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/dupThrowsTags/TestDupThrowsTags.java	Mon Oct 10 06:47:47 2016 -0700
@@ -42,7 +42,7 @@
     void test() {
         javadoc("-d", "out",
                 testSrc("TestDupThrowsTags.java"));
-        checkExit(Exit.FAILED);
+        checkExit(Exit.ERROR);
 
         checkOutput("TestDupThrowsTags.html", true,
                 "Test 1 passes",
--- a/langtools/test/jdk/javadoc/doclet/lib/JavadocTester.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/lib/JavadocTester.java	Mon Oct 10 06:47:47 2016 -0700
@@ -327,15 +327,27 @@
         outputDirectoryCheck = c;
     }
 
+    /**
+     * The exit codes returned by the javadoc tool.
+     * @see jdk.javadoc.internal.tool.Main.Result
+     */
     public enum Exit {
-        OK(0),
-        FAILED(1);
+        OK(0),        // Javadoc completed with no errors.
+        ERROR(1),     // Completed but reported errors.
+        CMDERR(2),    // Bad command-line arguments
+        SYSERR(3),    // System error or resource exhaustion.
+        ABNORMAL(4);  // Javadoc terminated abnormally
 
         Exit(int code) {
             this.code = code;
         }
 
         final int code;
+
+        @Override
+        public String toString() {
+            return name() + '(' + code + ')';
+        }
     }
 
     /**
@@ -349,7 +361,7 @@
         if (exitCode == expected.code) {
             passed("return code " + exitCode);
         } else {
-            failed("return code " + exitCode +"; expected " + expected.code + " (" + expected + ")");
+            failed("return code " + exitCode +"; expected " + expected);
         }
     }
 
--- a/langtools/test/jdk/javadoc/doclet/testBadSourceFile/TestBadSourceFile.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testBadSourceFile/TestBadSourceFile.java	Mon Oct 10 06:47:47 2016 -0700
@@ -50,6 +50,6 @@
         javadoc("-Xdoclint:none",
                 "-d", "out",
                 testSrc("C2.java"));
-        checkExit(Exit.FAILED);
+        checkExit(Exit.ERROR);
     }
 }
--- a/langtools/test/jdk/javadoc/doclet/testConstantValuesPage/TestConstantValuesPage.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testConstantValuesPage/TestConstantValuesPage.java	Mon Oct 10 06:47:47 2016 -0700
@@ -45,7 +45,7 @@
         javadoc("-d", "out",
                 "-sourcepath", testSrc,
                 "foo");
-        checkExit(Exit.FAILED);
+        checkExit(Exit.CMDERR);
 
         checkOutput(Output.OUT, false,
                 "constant-values.html...");
--- a/langtools/test/jdk/javadoc/doclet/testDocErrorReporter/TestDocErrorReporter.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testDocErrorReporter/TestDocErrorReporter.java	Mon Oct 10 06:47:47 2016 -0700
@@ -52,6 +52,8 @@
                 "-encoding", "xyz",
                 testSrc("TestDocErrorReporter.java"));
 
-        checkExit(Exit.FAILED);
+        checkExit(Exit.ERROR);
+
+        checkOutput(Output.OUT, true, "error: unsupported encoding: xyz");
     }
 }
--- a/langtools/test/jdk/javadoc/doclet/testHelpOption/TestHelpOption.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testHelpOption/TestHelpOption.java	Mon Oct 10 06:47:47 2016 -0700
@@ -111,7 +111,7 @@
                 "-helpfile", testSrc("test-help.html"),
                 "-helpfile", testSrc("test-help.html"),
                 testSrc("Sample.java"));
-        checkExit(Exit.FAILED);
+        checkExit(Exit.ERROR);
     }
 
     @Test
@@ -121,7 +121,7 @@
                 "-helpfile", testSrc("test-help.html"),
                 "-nohelp",
                 testSrc("Sample.java"));
-        checkExit(Exit.FAILED);
+        checkExit(Exit.ERROR);
     }
 
     private void checkOutput(boolean withOption) {
--- a/langtools/test/jdk/javadoc/doclet/testIOException/TestIOException.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testIOException/TestIOException.java	Mon Oct 10 06:47:47 2016 -0700
@@ -57,7 +57,7 @@
         try {
             javadoc("-d", outDir.toString(),
                     new File(testSrc, "TestIOException.java").getPath());
-            checkExit(Exit.FAILED);
+            checkExit(Exit.ERROR);
             checkOutput(Output.OUT, true,
                 "Destination directory not writable: " + outDir);
         } finally {
@@ -85,7 +85,7 @@
             javadoc("-d", outDir.toString(),
                     new File(testSrc, "TestIOException.java").getPath());
 
-            checkExit(Exit.FAILED);
+            checkExit(Exit.ERROR);
             checkOutput(Output.OUT, true,
                 "Error writing file: " + index);
         } finally {
@@ -123,7 +123,7 @@
             setOutputDirectoryCheck(DirectoryCheck.NONE);
             javadoc("-d", outDir.toString(),
                     src_p_C.getPath());
-            checkExit(Exit.FAILED);
+            checkExit(Exit.ERROR);
             checkOutput(Output.OUT, true,
                 "Error writing file: " + new File(pkgOutDir, "C.html"));
         } finally {
@@ -167,7 +167,7 @@
             javadoc("-d", outDir.toString(),
                     "-sourcepath", srcDir.getPath(),
                     "p");
-            checkExit(Exit.FAILED);
+            checkExit(Exit.ERROR);
             checkOutput(Output.OUT, true,
                 "Error writing file: " + new File(docFilesOutDir, "info.txt"));
         } finally {
--- a/langtools/test/jdk/javadoc/doclet/testPackageHtml/TestPackageHtml.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testPackageHtml/TestPackageHtml.java	Mon Oct 10 06:47:47 2016 -0700
@@ -42,7 +42,7 @@
         javadoc("-d", "out-pkg-html",
                 "-sourcepath", testSrc,
                 "pkg1");
-        checkExit(Exit.FAILED);
+        checkExit(Exit.ERROR);
         checkOutput(Output.OUT, true, "package.html:10: error: bad use of '>'");
     }
 }
--- a/langtools/test/jdk/javadoc/doclet/testParamTaglet/TestParamTaglet.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testParamTaglet/TestParamTaglet.java	Mon Oct 10 06:47:47 2016 -0700
@@ -46,7 +46,7 @@
         javadoc("-d", "out",
                 "-sourcepath", testSrc,
                 "pkg");
-        checkExit(Exit.FAILED);
+        checkExit(Exit.ERROR);
 
         checkOutput("pkg/C.html", true,
                 //Regular param tags.
--- a/langtools/test/jdk/javadoc/doclet/testSearch/TestSearch.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testSearch/TestSearch.java	Mon Oct 10 06:47:47 2016 -0700
@@ -73,7 +73,7 @@
     void test2a() {
         javadoc("-d", "out-2a", "-Xdoclint:all", "-sourcepath", testSrc,
                 "-use", "pkg", "pkg1", "pkg2", "pkg3");
-        checkExit(Exit.FAILED);
+        checkExit(Exit.ERROR);
         checkDocLintErrors();
         checkSearchOutput(true);
         checkSingleIndex(true);
--- a/langtools/test/jdk/javadoc/doclet/testSerializedFormDeprecationInfo/TestSerializedFormDeprecationInfo.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testSerializedFormDeprecationInfo/TestSerializedFormDeprecationInfo.java	Mon Oct 10 06:47:47 2016 -0700
@@ -44,7 +44,7 @@
         javadoc("-d", "out-default",
                 "-sourcepath", testSrc,
                 "pkg1");
-        checkExit(Exit.FAILED); // TODO: should be OK
+        checkExit(Exit.OK);
 
         checkCommentDeprecated(true);
         checkNoComment(false);
@@ -56,7 +56,7 @@
                 "-nocomment",
                 "-sourcepath", testSrc,
                 "pkg1");
-        checkExit(Exit.FAILED); // TODO: should be OK
+        checkExit(Exit.OK);
 
         checkNoComment(true);
         checkCommentDeprecated(false);
@@ -68,7 +68,7 @@
                 "-nodeprecated",
                 "-sourcepath", testSrc,
                 "pkg1");
-        checkExit(Exit.FAILED); // TODO: should be OK
+        checkExit(Exit.OK);
 
         checkNoDeprecated(true);
         checkNoCommentNoDeprecated(false);
@@ -81,7 +81,7 @@
                 "-nodeprecated",
                 "-sourcepath", testSrc,
                 "pkg1");
-        checkExit(Exit.FAILED); // TODO: should be OK
+        checkExit(Exit.OK);
         checkNoCommentNoDeprecated(true);
         checkNoDeprecated(false);
     }
@@ -93,7 +93,7 @@
                 "<dl>\n"
                 + "<dt><span class=\"throwsLabel\">Throws:</span></dt>\n"
                 + "<dd><code>"
-                + "java.io.IOException</code></dd>\n"
+                + "java.io.IOException</code> - on error</dd>\n"
                 + "<dt><span class=\"seeLabel\">See Also:</span>"
                 + "</dt>\n"
                 + "<dd><a href=\"pkg1/C1.html#setUndecorated-boolean-\">"
@@ -121,7 +121,7 @@
                 + "<div class=\"block\">Reads the object stream.</div>\n"
                 + "<dl>\n"
                 + "<dt><span class=\"throwsLabel\">Throws:</span></dt>\n"
-                + "<dd><code>java.io.IOException</code></dd>\n"
+                + "<dd><code>java.io.IOException</code> - on error</dd>\n"
                 + "</dl>",
                 "<span class=\"deprecatedLabel\">Deprecated.</span>"
                 + "&nbsp;</div>\n"
--- a/langtools/test/jdk/javadoc/doclet/testSerializedFormDeprecationInfo/pkg1/C1.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testSerializedFormDeprecationInfo/pkg1/C1.java	Mon Oct 10 06:47:47 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -75,7 +75,6 @@
      * @param test boolean value
      * @exception IllegalArgumentException if the <code>owner</code>'s
      *     <code>GraphicsConfiguration</code> is not from a screen device
-     * @exception HeadlessException
      */
      public C1(String title, boolean test) {
 
@@ -98,6 +97,7 @@
     }
 
     /**
+     * @throws java.io.IOException on error
      * @see #setUndecorated(boolean)
      */
     public void readObject() throws IOException {
--- a/langtools/test/jdk/javadoc/doclet/testSerializedFormDeprecationInfo/pkg1/C2.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testSerializedFormDeprecationInfo/pkg1/C2.java	Mon Oct 10 06:47:47 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -74,7 +74,7 @@
      * Reads the object stream.
      *
      * @param s ObjectInputStream
-     * @throws IOException
+     * @throws IOException on error
      * @deprecated As of JDK version 1.5, replaced by
      * {@link C1#setUndecorated(boolean) setUndecorated(boolean)}.
      */
--- a/langtools/test/jdk/javadoc/doclet/testSinceTag/TestSinceTag.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testSinceTag/TestSinceTag.java	Mon Oct 10 06:47:47 2016 -0700
@@ -45,7 +45,7 @@
         javadoc("-d", "out-since",
                 "-sourcepath", testSrc,
                 "pkg1");
-        checkExit(Exit.FAILED); // TODO: investigate
+        checkExit(Exit.OK);
 
         checkSince(true);
     }
@@ -56,7 +56,7 @@
                 "-sourcepath", testSrc,
                 "-nosince",
                 "pkg1");
-        checkExit(Exit.FAILED); // TODO: investigate
+        checkExit(Exit.OK);
 
         checkSince(false);
     }
--- a/langtools/test/jdk/javadoc/doclet/testSinceTag/pkg1/C1.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testSinceTag/pkg1/C1.java	Mon Oct 10 06:47:47 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -72,7 +72,6 @@
      * @param test boolean value
      * @exception IllegalArgumentException if the <code>owner</code>'s
      *     <code>GraphicsConfiguration</code> is not from a screen device
-     * @exception HeadlessException
      */
     public C1(String title, boolean test) {
     }
@@ -93,6 +92,7 @@
     }
 
     /**
+     * @throws java.io.IOException on error
      * @see #setUndecorated(boolean)
      */
     public void readObject() throws IOException {
--- a/langtools/test/jdk/javadoc/doclet/testSupplementary/TestSupplementary.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testSupplementary/TestSupplementary.java	Mon Oct 10 06:47:47 2016 -0700
@@ -52,7 +52,7 @@
         javadoc("-locale", "en_US",
                 "-d", "out",
                 testSrc("C.java"));
-        checkExit(Exit.FAILED);
+        checkExit(Exit.ERROR);
 
         checkOutput(Output.OUT, true,
             "C.java:36: error: unexpected text",
--- a/langtools/test/jdk/javadoc/doclet/testThrowsTag/TestThrowsTag.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testThrowsTag/TestThrowsTag.java	Mon Oct 10 06:47:47 2016 -0700
@@ -45,7 +45,7 @@
         javadoc("-d", "out",
                 "-sourcepath", testSrc,
                 "pkg");
-        checkExit(Exit.FAILED);  // TODO: investigate why failed
+        checkExit(Exit.OK);
 
         checkOutput("pkg/C.html", true,
             "<dd><code><a href=\"../pkg/T1.html\" title=\"class in pkg\">T1</a></code> - the first throws tag.</dd>\n" +
--- a/langtools/test/jdk/javadoc/doclet/testThrowsTag/pkg/T1.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testThrowsTag/pkg/T1.java	Mon Oct 10 06:47:47 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,4 +23,4 @@
 
 package pkg;
 
-public class T1 extends Exception {}
+public class T1 extends RuntimeException {}
--- a/langtools/test/jdk/javadoc/doclet/testThrowsTag/pkg/T2.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testThrowsTag/pkg/T2.java	Mon Oct 10 06:47:47 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,4 +23,4 @@
 
 package pkg;
 
-public class T2 extends Exception {}
+public class T2 extends RuntimeException {}
--- a/langtools/test/jdk/javadoc/doclet/testThrowsTag/pkg/T3.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testThrowsTag/pkg/T3.java	Mon Oct 10 06:47:47 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,4 +23,4 @@
 
 package pkg;
 
-public class T3 extends Exception {}
+public class T3 extends RuntimeException {}
--- a/langtools/test/jdk/javadoc/doclet/testThrowsTag/pkg/T4.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testThrowsTag/pkg/T4.java	Mon Oct 10 06:47:47 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2004, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,4 +23,4 @@
 
 package pkg;
 
-public class T4 extends Exception {}
+public class T4 extends RuntimeException {}
--- a/langtools/test/jdk/javadoc/doclet/testValueTag/TestValueTag.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testValueTag/TestValueTag.java	Mon Oct 10 06:47:47 2016 -0700
@@ -46,7 +46,7 @@
                 "-sourcepath", testSrc,
                 "-tag", "todo",
                 "pkg1", "pkg2");
-        checkExit(Exit.FAILED);
+        checkExit(Exit.ERROR);
 
         checkOutput("pkg1/Class1.html", true,
                 // Base case:  using @value on a constant.
--- a/langtools/test/jdk/javadoc/doclet/testWarnings/TestWarnings.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/doclet/testWarnings/TestWarnings.java	Mon Oct 10 06:47:47 2016 -0700
@@ -47,7 +47,7 @@
         javadoc("-d", "out-default",
                 "-sourcepath", testSrc,
                 "pkg");
-        checkExit(Exit.FAILED);
+        checkExit(Exit.ERROR);
 
         checkOutput(Output.OUT, true,
                 "X.java:23: error: self-closing element not allowed");
@@ -75,7 +75,7 @@
                 "-private",
                 "-sourcepath", testSrc,
                 "pkg");
-        checkExit(Exit.FAILED);
+        checkExit(Exit.ERROR);
 
         checkOutput("pkg/X.html", true,
             "<a href=\"../pkg/X.html#m--\"><code>m()</code></a><br/>",
--- a/langtools/test/jdk/javadoc/tool/ReleaseOption.java	Tue Oct 11 00:28:49 2016 +0900
+++ b/langtools/test/jdk/javadoc/tool/ReleaseOption.java	Mon Oct 10 06:47:47 2016 -0700
@@ -28,7 +28,11 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.function.Predicate;
+
 import jdk.javadoc.internal.tool.Main;
+import jdk.javadoc.internal.tool.Main.Result;
+
+import static jdk.javadoc.internal.tool.Main.Result.*;
 
 /**
  * @test
@@ -43,13 +47,13 @@
     }
 
     void run() {
-        doRunTest(0, out -> out.contains("compiler.err.doesnt.exist: java.util.stream"), "--release", "7");
-        doRunTest(0, out -> !out.contains("compiler.err.doesnt.exist: java.util.stream"), "--release", "8");
-        doRunTest(1, out -> true, "--release", "7", "-source", "7");
-        doRunTest(1, out -> true, "--release", "7", "-bootclasspath", "any");
+        doRunTest(OK, out -> out.contains("compiler.err.doesnt.exist: java.util.stream"), "--release", "7");
+        doRunTest(OK, out -> !out.contains("compiler.err.doesnt.exist: java.util.stream"), "--release", "8");
+        doRunTest(CMDERR, out -> true, "--release", "7", "-source", "7");
+        doRunTest(CMDERR, out -> true, "--release", "7", "-bootclasspath", "any");
     }
 
-    void doRunTest(int expectedResult, Predicate<String> validate, String... args) {
+    void doRunTest(Result expectedResult, Predicate<String> validate, String... args) {
         System.err.println("running with args: " + Arrays.asList(args));
         List<String> options = new ArrayList<>();
         options.addAll(Arrays.asList(args));
@@ -60,7 +64,7 @@
         int actualResult = Main.execute(options.toArray(new String[0]), pw);
         System.err.println("actual result=" + actualResult);
         System.err.println("actual output=" + out.toString());
-        if (actualResult != expectedResult)
+        if (actualResult != expectedResult.exitCode)
             throw new Error("Exit code not as expected");
         if (!validate.test(out.toString())) {
             throw new Error("Output not as expected");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/jdk/javadoc/tool/exceptionHandling/TestExceptionHandling.java	Mon Oct 10 06:47:47 2016 -0700
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8151102
+ * @summary verify that option --dump-on-error functions correctly
+ * @library /tools/lib
+ * @modules
+ *      jdk.javadoc/jdk.javadoc.internal.api
+ *      jdk.javadoc/jdk.javadoc.internal.tool
+ *      jdk.compiler/com.sun.tools.javac.api
+ *      jdk.compiler/com.sun.tools.javac.main
+ * @build toolbox.ToolBox toolbox.TestRunner
+ * @run main TestExceptionHandling
+ */
+
+import java.io.File;
+import java.io.PrintStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+import toolbox.*;
+
+/**
+ * This class tests if stack traces printed when
+ * --dump-on-error. The standard doclet is used,
+ * to test the doclet as well as the tool.
+ */
+public class TestExceptionHandling  extends TestRunner {
+
+    final ToolBox tb;
+    final File testSrcFile;
+    final PrintStream ostream;
+    final JavadocTask cmdTask;
+    final JavadocTask apiTask;
+
+    public static void main(String... args) throws Exception {
+        TestExceptionHandling tester = new TestExceptionHandling();
+        tester.runTests();
+    }
+
+    TestExceptionHandling() {
+        super(System.err);
+        tb = new ToolBox();
+        ostream = System.err;
+        testSrcFile = new File(System.getProperty("test.src"), "TestExceptionHandling.java");
+        cmdTask = new JavadocTask(tb, Task.Mode.CMDLINE);
+        apiTask = new JavadocTask(tb, Task.Mode.API);
+    }
+
+    @Test
+    public void testDocletTrace() throws Exception {
+        Path out = Paths.get("out");
+        // create a file with the same name as the output
+        out.toFile().createNewFile();
+        cmdTask.outdir(out);
+        cmdTask.options("--dump-on-error");
+        cmdTask.files(testSrcFile.getAbsolutePath());
+        Task.Result tr = cmdTask.run(Task.Expect.FAIL);
+
+        String errString = "Destination directory is not a directory: " + out.toString();
+        // check the regular message
+        assertPresent("javadoc: error - " + errString, tr.getOutputLines(Task.OutputKind.DIRECT));
+        // check that first line of the stack trace is present
+        assertPresent("jdk.javadoc.internal.doclets.toolkit.util.SimpleDocletException: " +
+                errString, tr.getOutputLines(Task.OutputKind.STDERR));
+
+    }
+
+    @Test
+    public void testToolTrace() throws Exception {
+        Path out = Paths.get("out.dir");
+        cmdTask.options("--dump-on-error", "-doclet", "NonExistentDoclet");
+        cmdTask.outdir(out);
+        cmdTask.files(testSrcFile.getAbsolutePath());
+        Task.Result tr = cmdTask.run(Task.Expect.FAIL);
+
+        // check the regular message
+        assertPresent("javadoc: error - Cannot find doclet class NonExistentDoclet",
+                tr.getOutputLines(Task.OutputKind.DIRECT));
+
+        // check that first line of the stack trace is present
+        assertPresent("java.lang.ClassNotFoundException: NonExistentDoclet",
+                tr.getOutputLines(Task.OutputKind.STDERR));
+
+    }
+
+    @Test
+    public void testApiModeMissingDoclet() throws Exception {
+        apiTask.options("-doclet", "MissingDoclet");
+        try {
+            Task.Result result = apiTask.run(Task.Expect.FAIL);
+        } catch (IllegalArgumentException iae) {
+            // ok got the right exception
+            return;
+        }
+        throw new Exception("expected exception/error not found");
+    }
+
+    @Test
+    public void testApiModeMultipleDoclets() throws Exception {
+        apiTask.options("-doclet", "MissingDoclet",
+                "-doclet", "SomeDoclet");
+        try {
+            Task.Result result = apiTask.run(Task.Expect.FAIL);
+        } catch (IllegalArgumentException iae) {
+            // ok got the right exception
+            return;
+        }
+        throw new Exception("expected exception/error not found");
+    }
+
+    void assertPresent(String regex, List<String> output) throws Exception {
+        List<String> gresult = tb.grep(regex, output);
+        if (gresult.isEmpty()) {
+            ostream.println("Expected: " + regex);
+            ostream.println("Output: ");
+            output.forEach(s -> {
+                ostream.println(s);
+            });
+            throw new Exception("Test fails expected output not found: " + regex);
+        }
+    }
+}