简体   繁体   English

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

[英]Best way to process/handle Error stream messages

I need to process different Error/Exception messages generated compile/run time. 我需要处理生成编译/运行时的不同错误/异常消息。

I execute a Java program an read the stream thus generated : 我执行一个Java程序读取由此生成的流:

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

For each success , the output is generated and shown. 每次成功,都会生成并显示输出。 No problem there. 没问题。 But I need to show custom message for every Error message. 但我需要为每条错误消息显示自定义消息。

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

May be customized as : Error: Main method not found 可以自定义为: Error: Main method not found

My current approach is very ugly and limited . 我目前的方法非常丑陋且有限。 I am looking if "Exception" string is present in error stream and then taking out the sub- strings.Something like : 我正在查看错误流中是否存在“Exception”字符串,然后取出子字符串。如下所示:

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

But it do not customize my approach in a broad way. 但它并没有广泛地定制我的方法。

What is the best thing I could do? 我能做的最好的事情是什么?

Edit : 编辑

I think my question is unclear. 我认为我的问题不清楚。 Basically I am executing a Java program via ProcessBuilder . 基本上我是通过ProcessBuilder执行Java程序的。

    //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  

No the output of the process can be the result of te java program or an exception/error which is taken from the process steam an is in String form.Need to manipulate those Errors. 这个过程的输出可能不是te java程序的结果,也不是从进程steam获取的异常/错误,而是以String形式。需要操作那些错误。

Update : 更新:

I can now solve the compile time errors via Java Compiler API as suggested by @Miserable Variable .How can I deal with Runtime Exceptions similarly ? 我现在可以通过@Miserable Variable建议的Java Compiler API来解决编译时错误。我如何处理Runtime Exceptions?

EDIT : In actual it is not possible to modify the program to be run in a new process.They are user specific. 编辑:实际上,无法修改要在新进程中运行的程序。它们是特定于用户的。

For identifying errors in compilation, instead of running javac using ProcessBuilder a better alternative might be to use Java Compiler API . 为了识别编译中的错误,可以使用Java Compiler API ,而不是使用ProcessBuilder运行javac

I have never used it myself but it seems pretty straightforward. 我自己从未使用它,但它看起来非常简单。

It would be much easier IMO if your program relies on a logging framework to log its errors in a certain log format. 如果您的程序依赖于日志记录框架以某种日志格式记录其错误,那么IMO会容易得多。 You can benefit from this by using a log parser. 您可以通过使用日志解析器来从中受益。 It should be simple to write your own and maybe you can define a dictionary of common errors patterns that are assigned to your specific program. 编写自己的应该很简单,也许你可以定义一个分配给你的特定程序的常见错误模式的字典。

If your program does not follow a well-defined log pattern and you want a scalable approach, then one possible way is to implement a Grok -based solution. 如果您的程序没有遵循明确定义的日志模式并且您想要一种可扩展的方法,那么一种可能的方法是实现基于Grok的解决方案。 Grok is used in powerful tools like Logstash. Grok用于Logstash等强大的工具。 See this post for how to do this in Java. 有关如何在Java中执行此操作,请参阅此文章

For Runtime exceptions ,you could look into ProcessBuilder and streaming the error exceptions and then checking for the same.Here are some examples that can help you: 对于运行时异常,您可以查看ProcessBuilder并流式传输错误异常,然后检查相同的内容。以下是一些可以帮助您的示例:

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

The way I understand it is, 我理解的方式是,

Problem statement: 问题陈述:

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

Given above problem statement I have produced a solution. 鉴于上述问题陈述,我已经提出了解决方案。 Before code I think it's a good idea to explain what it does, in the order it does it. 在编码之前,我认为按照它的顺序解释它的作用是个好主意。

Solution steps: 解决步骤:

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.

Working code: 工作代码:

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

Thanks to @Miserable Variable, I was almost set to create a process for the javac program, but your answer saved me some ugly code. 感谢@Miserable Variable,我几乎已经开始为javac程序创建一个进程,但是你的回答为我节省了一些丑陋的代码。

** *Edited ** ** *已编辑**

Command line arguments 命令行参数

For compilation: 编译:

//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);

For Java runtime arguments: 对于Java运行时参数:

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

For main method of new program: 对于新程序的主要方法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 处理错误和成功消息的最佳方法是从后端复制文本? - Best way to handle error and success messages copy texts from back-end? 调试改进错误消息的最佳方法 - The Best Way to Debug Retrofit Error Messages 套接字输入流在最终读取时挂起。 最好的处理方法? - Socket input stream hangs on final read. Best way to handle this? 关闭在解析过程中正在读取SAX解析器的流的最佳方法? - Best way to close a stream an SAX parser is reading during the parsing process? 呈现特定于组件的错误消息的最佳方法是什么? - What's the best way of rendering component specific error messages? 将 Google Pub/Sub 消息直接“流式传输”到前端的最佳方式? 服务器发送的事件? - Best way to "stream" Google Pub/Sub messages directly to frontend? Server-sent events? 处理注释的最佳方法 - Best way to process the annotations 处理此“ try-error-clean -retry”情况的最佳方法是什么? - What's the best way to handle this “try - error - clean -retry” case? Apache Camel - 处理 404 错误的最佳方法是什么? - Apache Camel - What is the best way to handle a 404 error? 编码简约消息的最佳方法 - Best way to encode minimalistic messages
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM