Java 处理/处理错误流消息的最佳方法

Java 处理/处理错误流消息的最佳方法,java,error-handling,Java,Error Handling,我需要处理编译/运行时生成的不同错误/异常消息 我执行一个Java程序,读取由此生成的流: final Process p2 = builder.start(); BufferedReader in = new BufferedReader(new InputStreamReader(p2.getInputStream())); 对于每次成功,都会生成并显示输出。没问题。但是我需要

我需要处理编译/运行时生成的不同错误/异常消息

我执行一个Java程序,读取由此生成的流:

final Process p2 = builder.start();
BufferedReader in = new BufferedReader(new                                                 
        InputStreamReader(p2.getInputStream()));
对于每次成功,都会生成并显示输出。没问题。但是我需要为每个错误消息显示自定义消息

例如:

可以自定义为:
错误:找不到主方法

我目前的做法是非常丑陋和有限的。我正在查看错误流中是否存在“Exception”字符串,然后取出子字符串。类似于:

if(tError.contains("Exception"))            
      tError=tError.substring(tError.indexOf("main\"")+5,tError.indexOf("at"))
        + "( At Line: "+tError.substring(tError.indexOf(".java")+6);
但它并没有广泛地定制我的方法

我能做的最好的事情是什么

编辑

我想我的问题不清楚。基本上我是通过
ProcessBuilder

    //Compile the program 
  Process p = Runtime.getRuntime().exec("javac filename ");

    // Now get the error stream if available :
  BufferedReader in = new BufferedReader(new            
                    InputStreamReader(p.getOutputStream()));

 String line = null;
 while ((line = in.readLine()) != null) {

       //process error in compilation.
       }
    ...
    ...
  // ProcessBuilder to execute the program.java
  //Read the output or runtime Exception  
否进程的输出可以是java程序的结果,也可以是以字符串形式从进程中获取的异常/错误。需要处理这些错误

更新:

Write a Java program which should be able to 1. Take an input as Java program file 2. Compile it and produce errors if can't compile 3. Run the class file generated in previous step 4. Produce the run time exceptions generated by the invocation if any Assumptions: 1. Class would contain a "main" method else it can't be run using "java" program 1. Compile the Java code using the compiler API (using ToolProvider.getSystemJavaCompiler()) 2. Use DiagnosticCollector to collect for any compilation errors that might have ocurred. 3. If compilation is successful then load the generated class in byte array. 4. Use ClassLoader.defineClass() to load class file from byte array into JVM runtime. 5. Once class is loaded, use reflection to find the main method, if not present throw main not found related exceptions. 6. Run main method, and report back any runtime exceptions produced. Note: If needed Standard input and output streams can be redirected for the new program and original objects can be saved as originals for main program. I haven't done it, but it's trivial to do.
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class JreMain {
    private static final String PATH_TO_JAVA_FILE = "src/main/java/MyProgram.java";

    public static void main(String[] args) {
        JreMain main = new JreMain();
        System.out.println("Running a java program");

        String filePath = PATH_TO_JAVA_FILE;


        File javaFile = new File(filePath);
        /*compiling the file */
        List<String> errorList = main.compile(Arrays.asList(javaFile));

        if(errorList.size() != 0) {
            System.out.println("file could not be compiled, check below for errors");

            for(String error : errorList) {
                System.err.println("Error : " + error);
            }
        } else {
            main.runJavaClass(filePath, new String[] {});
        }
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private void runJavaClass(String filePath, String[] mainArguments) {

        System.out.println("Running " + filePath);

        ClassLoader classLoader = getClass().getClassLoader();
        Class klass = null;
        String fileNameWithoutExtension = filePath.substring(0, filePath.length() - ".java".length());
        String className = getClassName(fileNameWithoutExtension);
        /* loading defineClass method in Classloader through reflection, since it's 'protected' */
        try {
            /* signature of defineClass method: protected final Class<?> defineClass(String name, byte[] b, int off, int len)*/

            Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE);
            defineClassMethod.setAccessible(true);

            /* attempting to load our class in JVM via byte array */

            byte[] classBytes = getClassBytes(fileNameWithoutExtension + ".class");
            klass = (Class)defineClassMethod.invoke(classLoader, className, classBytes, 0, classBytes.length);

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        /* checking if main method exists, in the loaded class, and running main if exists*/

        if(klass != null) {
            try {
                Method mainMethod = klass.getMethod("main", String[].class);
                Class returnType = mainMethod.getReturnType();

                /*Checking for main method modifiers and return type*/

                if( !Modifier.isStatic(mainMethod.getModifiers()) || !Modifier.isPublic(mainMethod.getModifiers()) || !(returnType.equals(Void.TYPE) || returnType.equals(Void.class))) {
                    throw new RuntimeException("Main method signature incorrect, expected : \"public static void main(String[] args)\",");
                }

                /* finally invoking the main method **/
                mainMethod.invoke(null, new Object[]{mainArguments});

            } catch (NoSuchMethodException e) {
                throw new RuntimeException("Class " + klass.getCanonicalName() + " does not declare main method");
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                /*e.printStackTrace();*/
                System.err.println("Exception in main :");
                throw new RuntimeException(e.getCause());
            }
        }
    }

    private String getClassName(String fileNameWithoutExtension) {
        String className = null;
        int lastIndex = -1;
        if( ( lastIndex = fileNameWithoutExtension.lastIndexOf(File.separator)) != -1) {
            className = fileNameWithoutExtension.substring(lastIndex + 1);
        } if( ( lastIndex = fileNameWithoutExtension.lastIndexOf("\\")) != -1) {
            className = fileNameWithoutExtension.substring(lastIndex + 1);
        } else if( ( lastIndex = fileNameWithoutExtension.lastIndexOf("/")) != -1) {
            className = fileNameWithoutExtension.substring(lastIndex + 1);
        }
        return className;
    }

    private byte[] getClassBytes(String classFilePath) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        File classFile = new File(classFilePath);
        if(!classFile.exists()) {
            throw new RuntimeException("Class file does not exist : " + classFile.getAbsolutePath());
        }

        byte[] buffer = new byte[2048];
        int readLen = -1;
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(classFile);
            while( (readLen = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, readLen);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return baos.toByteArray();
    }

    @SuppressWarnings("restriction")
    public List<String> compile (List<File> javaFileList) {
        System.out.println("Started compilation");
        List<String> errorList = new ArrayList<String>();
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(
                diagnostics, null, null);

        Iterable<? extends JavaFileObject> compilationUnits = fileManager
                .getJavaFileObjectsFromFiles(javaFileList);
        compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits)
                .call();

        for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics
                .getDiagnostics()) {
            String diagnosticMessage = String.format("Error on line %d in %s%n",
                    diagnostic.getLineNumber(), diagnostic.getSource().toUri() + " : \n\t" + diagnostic.getMessage(null));

            /*Following gives out of box good message, but I used above to show the custom use of diagnostic
             * String diagnosticMessage = diagnostic.toString();*/

            errorList.add(diagnosticMessage);
        }
        try {
            fileManager.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return errorList;
    }
}
我现在可以通过@spuble Variable建议的方法解决编译时错误。我如何类似地处理运行时异常

编辑:
实际上,不可能修改要在新进程中运行的程序。它们是特定于用户的。

为了识别编译中的错误,而不是使用
ProcessBuilder
运行
javac
,更好的选择可能是使用


我自己从来没有用过,但它看起来很简单

如果您的程序依赖于日志框架,以特定的日志格式记录其错误,那么IMO会容易得多。通过使用日志解析器,您可以从中受益。它应该很简单,也许你可以定义一个字典的常见错误模式,分配给你的具体程序


如果您的程序没有遵循定义良好的日志模式,并且您想要一种可伸缩的方法,那么一种可能的方法是实现基于日志的解决方案。Grok用于强大的工具,如Logstash。有关如何在Java中执行此操作,请参阅。

有关运行时异常,您可以查看ProcessBuilder并对错误异常进行流式处理,然后检查错误异常。以下是一些可以帮助您的示例:


按照我的理解

问题陈述:

Write a Java program which should be able to 1. Take an input as Java program file 2. Compile it and produce errors if can't compile 3. Run the class file generated in previous step 4. Produce the run time exceptions generated by the invocation if any Assumptions: 1. Class would contain a "main" method else it can't be run using "java" program 1. Compile the Java code using the compiler API (using ToolProvider.getSystemJavaCompiler()) 2. Use DiagnosticCollector to collect for any compilation errors that might have ocurred. 3. If compilation is successful then load the generated class in byte array. 4. Use ClassLoader.defineClass() to load class file from byte array into JVM runtime. 5. Once class is loaded, use reflection to find the main method, if not present throw main not found related exceptions. 6. Run main method, and report back any runtime exceptions produced. Note: If needed Standard input and output streams can be redirected for the new program and original objects can be saved as originals for main program. I haven't done it, but it's trivial to do.
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

public class JreMain {
    private static final String PATH_TO_JAVA_FILE = "src/main/java/MyProgram.java";

    public static void main(String[] args) {
        JreMain main = new JreMain();
        System.out.println("Running a java program");

        String filePath = PATH_TO_JAVA_FILE;


        File javaFile = new File(filePath);
        /*compiling the file */
        List<String> errorList = main.compile(Arrays.asList(javaFile));

        if(errorList.size() != 0) {
            System.out.println("file could not be compiled, check below for errors");

            for(String error : errorList) {
                System.err.println("Error : " + error);
            }
        } else {
            main.runJavaClass(filePath, new String[] {});
        }
    }

    @SuppressWarnings({"rawtypes", "unchecked"})
    private void runJavaClass(String filePath, String[] mainArguments) {

        System.out.println("Running " + filePath);

        ClassLoader classLoader = getClass().getClassLoader();
        Class klass = null;
        String fileNameWithoutExtension = filePath.substring(0, filePath.length() - ".java".length());
        String className = getClassName(fileNameWithoutExtension);
        /* loading defineClass method in Classloader through reflection, since it's 'protected' */
        try {
            /* signature of defineClass method: protected final Class<?> defineClass(String name, byte[] b, int off, int len)*/

            Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, Integer.TYPE, Integer.TYPE);
            defineClassMethod.setAccessible(true);

            /* attempting to load our class in JVM via byte array */

            byte[] classBytes = getClassBytes(fileNameWithoutExtension + ".class");
            klass = (Class)defineClassMethod.invoke(classLoader, className, classBytes, 0, classBytes.length);

        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        /* checking if main method exists, in the loaded class, and running main if exists*/

        if(klass != null) {
            try {
                Method mainMethod = klass.getMethod("main", String[].class);
                Class returnType = mainMethod.getReturnType();

                /*Checking for main method modifiers and return type*/

                if( !Modifier.isStatic(mainMethod.getModifiers()) || !Modifier.isPublic(mainMethod.getModifiers()) || !(returnType.equals(Void.TYPE) || returnType.equals(Void.class))) {
                    throw new RuntimeException("Main method signature incorrect, expected : \"public static void main(String[] args)\",");
                }

                /* finally invoking the main method **/
                mainMethod.invoke(null, new Object[]{mainArguments});

            } catch (NoSuchMethodException e) {
                throw new RuntimeException("Class " + klass.getCanonicalName() + " does not declare main method");
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                /*e.printStackTrace();*/
                System.err.println("Exception in main :");
                throw new RuntimeException(e.getCause());
            }
        }
    }

    private String getClassName(String fileNameWithoutExtension) {
        String className = null;
        int lastIndex = -1;
        if( ( lastIndex = fileNameWithoutExtension.lastIndexOf(File.separator)) != -1) {
            className = fileNameWithoutExtension.substring(lastIndex + 1);
        } if( ( lastIndex = fileNameWithoutExtension.lastIndexOf("\\")) != -1) {
            className = fileNameWithoutExtension.substring(lastIndex + 1);
        } else if( ( lastIndex = fileNameWithoutExtension.lastIndexOf("/")) != -1) {
            className = fileNameWithoutExtension.substring(lastIndex + 1);
        }
        return className;
    }

    private byte[] getClassBytes(String classFilePath) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        File classFile = new File(classFilePath);
        if(!classFile.exists()) {
            throw new RuntimeException("Class file does not exist : " + classFile.getAbsolutePath());
        }

        byte[] buffer = new byte[2048];
        int readLen = -1;
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(classFile);
            while( (readLen = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, readLen);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return baos.toByteArray();
    }

    @SuppressWarnings("restriction")
    public List<String> compile (List<File> javaFileList) {
        System.out.println("Started compilation");
        List<String> errorList = new ArrayList<String>();
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(
                diagnostics, null, null);

        Iterable<? extends JavaFileObject> compilationUnits = fileManager
                .getJavaFileObjectsFromFiles(javaFileList);
        compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits)
                .call();

        for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics
                .getDiagnostics()) {
            String diagnosticMessage = String.format("Error on line %d in %s%n",
                    diagnostic.getLineNumber(), diagnostic.getSource().toUri() + " : \n\t" + diagnostic.getMessage(null));

            /*Following gives out of box good message, but I used above to show the custom use of diagnostic
             * String diagnosticMessage = diagnostic.toString();*/

            errorList.add(diagnosticMessage);
        }
        try {
            fileManager.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return errorList;
    }
}
对于新程序的主要方法:

//second argument here is going to main method
 main.runJavaClass(filePath, new String[] {});

我不知道你在找什么。如果您试图使用自定义异常,那么您可以创建自定义异常(它应该扩展异常),将您的逻辑放在try块中,“在catch块中,始终使用自定义消息创建自定义异常并将其抛出。@bakki我知道用户定义的异常。这些异常是在新进程中执行Java程序时生成的。我手头只有错误流。也许您希望实现自定义异常。@jewelsea我已进行了编辑。可能问题更多。”现在可以理解了。你看过了吗?我无法更改我要运行的程序。否则会有很多选择。在本例中,如果Grok选项适合您的需要,您可能会探索它。谢谢您的回答!我可以通过这种方法传递命令行参数吗?我还没看过密码。我已经给了赏金,因为它即将到期。感谢faith,更新了我的答案,包括如何添加命令行选项。