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

时间:2015-11-09 17:40:17

标签: java error-handling

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

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

final Process p2 = builder.start();
BufferedReader in = new BufferedReader(new                                                 
        InputStreamReader(p2.getInputStream()));

每次成功,都会生成并显示输出。没问题。但我需要为每条错误消息显示自定义消息。

EG:

Error: Main method not found in class dummy.helloParse10.hello, please define the main method as:
public static void main(String[] args)
or a JavaFX application class must extend javafx.application.Application

可以自定义为:Error: Main method not found

我目前的方法非常难看且有限。我在寻找" Exception"字符串出现在错误流中,然后取出子字符串。如:

if(tError.contains("Exception"))            
      tError=tError.substring(tError.indexOf("main\"")+5,tError.indexOf("at"))
        + "( At Line: "+tError.substring(tError.indexOf(".java")+6);

但它并没有广泛地定制我的方法。

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

修改

我认为我的问题不清楚。基本上我正在通过执行Java程序 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  

过程的输出可能不是te java程序的结果,也不是来自进程steam的异常/错误,而是以String形式。需要操作这些错误。

更新:

我现在可以通过Java Compiler API解决编译时错误,如@Miserable Variable所示。我如何处理运行时异常?

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

4 个答案:

答案 0 :(得分:4)

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

我自己从未使用它,但它看起来非常简单。

答案 1 :(得分:3)

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

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

答案 2 :(得分:2)

对于运行时异常,您可以查看ProcessBuilder并流式传输错误异常,然后检查相同的内容。以下是一些可以帮助您的示例:

http://examples.javacodegeeks.com/core-java/lang/processbuilder/java-lang-processbuilder-example/

答案 3 :(得分:2)

我理解的方式是,

问题陈述:


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;
    }
}

感谢@Miserable Variable,我几乎准备为javac程序创建一个进程,但是你的回答为我节省了一些难看的代码。

** *已编辑**

命令行参数

编译:

//in the JreMain.compile()    
List<String> compilerOptionsList = Arrays.asList("-classpath", "jar/slf4j-api-1.7.10.jar", "-verbose");
JavaCompiler.CompilationTask compilationTask = compiler.getTask(null,
            fileManager, diagnostics, compilerOptionsList, null,
            compilationUnits);

对于Java运行时参数:

They will have to be passed to our JareMain program itself.
java -classpath "jar/slf4j-api-1.7.10.jar;" -verbose JreMain

新方案的主要方法:

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