Rust代码如何检查对Java代码的调用引发的异常?

时间:2019-07-01 21:20:04

标签: rust java-native-interface

我有Rust代码调用Java方法:

let cls = je.find_class("com/purplefrog/batikExperiment/ToPixels")?;

let width = 400;
let height = 400;
let rgbs = je.new_byte_array(width*height*3)?;
let rgbs2:JObject = JObject::from(rgbs);

let result = je.call_static_method(cls, "renderTo", "(II[B)V", &[
    JValue::from(width),
    JValue::from(height),
    JValue::from(rgbs2),
])?;

运行它时,会收到很多警告,例如:

WARNING in native method: JNI call made without checking exceptions when required to from CallVoidMethod
    at sun.dc.pr.PathStroker.dispose(Native Method)
    at sun.dc.DuctusRenderingEngine.createStrokedShape(DuctusRenderingEngine.java:108)
    at java.awt.BasicStroke.createStrokedShape(BasicStroke.java:301)
    at org.apache.batik.gvt.StrokeShapePainter.getPaintedArea(StrokeShapePainter.java:125)
    at org.apache.batik.gvt.StrokeShapePainter.getPaintedBounds2D(StrokeShapePainter.java:134)
    at org.apache.batik.gvt.CompositeShapePainter.getPaintedBounds2D(CompositeShapePainter.java:156)
    at org.apache.batik.gvt.ShapeNode.getPrimitiveBounds(ShapeNode.java:238)
    at org.apache.batik.gvt.AbstractGraphicsNode.getTransformedPrimitiveBounds(AbstractGraphicsNode.java:854)
    at org.apache.batik.gvt.AbstractGraphicsNode.getTransformedBounds(AbstractGraphicsNode.java:820)
    at org.apache.batik.gvt.CompositeGraphicsNode.getPrimitiveBounds(CompositeGraphicsNode.java:224)
    at org.apache.batik.gvt.CompositeGraphicsNode.getTransformedPrimitiveBounds(CompositeGraphicsNode.java:295)
    at org.apache.batik.gvt.AbstractGraphicsNode.getTransformedBounds(AbstractGraphicsNode.java:820)
    at org.apache.batik.gvt.CompositeGraphicsNode.getPrimitiveBounds(CompositeGraphicsNode.java:207)
    at org.apache.batik.gvt.AbstractGraphicsNode.getBounds(AbstractGraphicsNode.java:768)
    at org.apache.batik.gvt.AbstractGraphicsNode.paint(AbstractGraphicsNode.java:443)
    at com.purplefrog.batikExperiment.ToPixels.renderTo(ToPixels.java:49)

由于E_net4可能会由于核心调用之外的因素导致此问题,所以我提供了更完整的源代码:

main.rs:

use jni::{InitArgsBuilder, JNIVersion, JavaVM, AttachGuard, JNIEnv};
use jni::objects::{JValue, JObject, AutoLocal};

fn main() -> Result<(), jni::errors::Error>
{
    let jvm_args = InitArgsBuilder::new()
            .version(JNIVersion::V8)
            .option("-Xcheck:jni")
            .option(&format!("-Djava.class.path={}", heinous_classpath()))
            .build()
            .expect("impossible, failed to construct JVM initialization args");

    let jvm:JavaVM = JavaVM::new(jvm_args)?;

    let env:AttachGuard = jvm.attach_current_thread()?;
    let je:&JNIEnv = &env; // this is just so intellij's larval rust plugin can give me method name completion

    let cls = je.find_class("com/purplefrog/batikExperiment/ToPixels")?;

    je.exception_check()?;

    let width = 400;
    let height = 400;
    let rgbs = AutoLocal::new(&je,
                              JObject::from(je.new_byte_array(width*height*3)? ) );
    je.exception_check()?;

    println!("before calling renderTo()");

    let result = je.call_static_method(cls, "renderTo", "(II[B)V", &[
        JValue::from(width),
        JValue::from(height),
        JValue::from(rgbs.as_obj()),
    ])?;
    je.exception_check()?;

    println!("{:?}", result);

    let blen = je.get_array_length(*rgbs.as_obj())? as usize;
    je.exception_check()?;
    let mut rgbs3:Vec<i8> = vec![0; blen];
    println!("byte array length = {}", blen);

    je.get_byte_array_region(*rgbs.as_obj(), 0, &mut rgbs3)?;
    je.exception_check()?;

    save_as_PPM(width, height, &rgbs3, "/tmp/x.ppm").expect("failed to save PPM");

    return Ok(());
}

pub fn save_as_PPM(width: i32, height: i32, rgbs3: & Vec<i8>, file_name: &str)-> Result<(),std::io::Error>
{
    use std::fs::File;
    use std::path::Path;
    use std::io::Write;
    let mut f = File::create(Path::new(file_name))?;
    f.write_all(format!("P6\n{} {} 255\n", width, height).as_bytes())?;
    let tmp: &[u8] = unsafe { &*(rgbs3.as_slice() as *const _ as *const [u8]) };
    f.write_all(tmp)?;
    println!("wrote {}", file_name);

    Ok(())
}

fn heinous_classpath() -> String
{
    let x = "/home/thoth/src/batik-experiment/target/appassembler/etc\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-rasterizer/1.11/batik-rasterizer-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-svgrasterizer/1.11/batik-svgrasterizer-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-parser/1.11/batik-parser-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-awt-util/1.11/batik-awt-util-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/xmlgraphics-commons/2.3/xmlgraphics-commons-2.3.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/commons-io/commons-io/1.3.1/commons-io-1.3.1.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-xml/1.11/batik-xml-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/xml-apis/xml-apis-ext/1.3.04/xml-apis-ext-1.3.04.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-transcoder/1.11/batik-transcoder-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-anim/1.11/batik-anim-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-css/1.11/batik-css-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-ext/1.11/batik-ext-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-svg-dom/1.11/batik-svg-dom-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-bridge/1.11/batik-bridge-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-script/1.11/batik-script-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-dom/1.11/batik-dom-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/xalan/xalan/2.7.2/xalan-2.7.2.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/xalan/serializer/2.7.2/serializer-2.7.2.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/xml-apis/xml-apis/1.3.04/xml-apis-1.3.04.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-gvt/1.11/batik-gvt-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-svggen/1.11/batik-svggen-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-util/1.11/batik-util-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-constants/1.11/batik-constants-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-i18n/1.11/batik-i18n-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/org/apache/xmlgraphics/batik-codec/1.11/batik-codec-1.11.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/com/purplefrog/penrose/1.0-SNAPSHOT/penrose-1.0-SNAPSHOT.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/com/purplefrog/knotwork/1.0-SNAPSHOT/knotwork-1.0-SNAPSHOT.jar\
:/home/thoth/src/batik-experiment/target/appassembler/repo/com/purplefrog/batikExperiment/batikExperiment/1.0-SNAPSHOT/batikExperiment-1.0-SNAPSHOT.jar";
    return String::from(x);
}

Cargo.toml:

[package]
name = "rust_call_jni"
version = "0.1.0"
authors = ["Robert Forsman <git@thoth.purplefrog.com>"]
edition = "2018"


[dependencies.jni]
version="0.12.3"
features=["invocation"]

ToPixels.java:

package com.purplefrog.batikExperiment;

import org.apache.batik.anim.dom.*;
import org.apache.batik.bridge.*;
import org.apache.batik.gvt.*;
import org.apache.batik.gvt.renderer.*;
import org.apache.batik.util.*;
import org.w3c.dom.*;

import javax.imageio.*;
import java.awt.*;
import java.awt.color.*;
import java.awt.geom.*;
import java.awt.image.*;
import java.io.*;
import java.util.List;

public class ToPixels
{
    public static void main(String[] args)
        throws IOException
    {
        Document document = ultraflower();

        // create the GVT
        GraphicsNode gn = parseSVG(document);

        BufferedImage bi = renderImageTest1(gn);

        ImageIO.write(bi, "PNG", new File("/tmp/x.png"));
    }

    public static void renderTo(int width, int height, byte[] rgbPixels)
    {
        System.out.println("renderTo");
        try {
            List<GraphicsNode> gns = sampleGN1();
            System.out.println("graphics node "+gns);
            BufferedImage bi = rgbToWritableRaster(width, height, rgbPixels);
            System.out.println("BufferedImage "+bi);
            Graphics2D g = bi.createGraphics();
            System.out.println(g);

            double s = width / 1920.;

            g.scale(s, s);
            g.translate(0, (height/s-1080)/2.0);
            for (Object child : gns) {
                ((GraphicsNode) child).paint(g);
            }
            System.out.println("painted");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static BufferedImage rgbToWritableRaster(int width, int height, byte[] rgbPixels)
    {
        ColorModel cm = new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB),
            new int[] {8,8,8}, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
        System.out.println("ColorModel "+cm);
        System.out.println(rgbPixels.length +" vs "+(width*height*3));
        DataBuffer buffer = new DataBufferByte(rgbPixels, width*height*3);
        System.out.println("databuffer "+buffer);
        WritableRaster wr = Raster.createWritableRaster(cm.createCompatibleSampleModel(width, height), buffer, new Point());
        System.out.println("writableraster "+wr);
        return new BufferedImage(cm, wr, false, null);
    }

    public static class Exp2 {
        public static void main(String[] args)
            throws IOException
        {
            int width = 400;
            int height = 600;
            byte[] rgbs = new byte[width * height *3];
            renderTo(width, height, rgbs);
            String fname = "/tmp/x.ppm";
            OutputStream ostr = new FileOutputStream(fname);
            ostr.write(("P6\n"+width+" "+height+" 255\n").getBytes());
            ostr.write(rgbs);
            ostr.close();
        }
    }

    public static List<GraphicsNode> sampleGN1()
        throws IOException
    {
        GraphicsNode gn = parseSVG(ultraflower());
        CanvasGraphicsNode canvas = (CanvasGraphicsNode) gn.getRoot().getChildren().get(0);
        return canvas.getChildren();
    }

    public static BufferedImage renderImageTest1(GraphicsNode gn)
    {
        BufferedImage bi;
        if (false) {
            StaticRenderer sr = new StaticRenderer();
            sr.setTree(gn);

            bi = sr.getOffScreen();
        } else {
            bi = new BufferedImage(400,400, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g = bi.createGraphics();
            if (false) {
                g.scale(0.25, 0.25);
                gn.paint(g);
            } else {
                RootGraphicsNode rootGN = (RootGraphicsNode) gn;
                rootGN.getChildren();
                CanvasGraphicsNode canvasGN = (CanvasGraphicsNode) rootGN.getChildren().get(0);
                double s = 400 / 1920.;
                if (false) {
                    AffineTransform at = new AffineTransform(s, 0, 0, s, 0, 0);
                    //canvasGN.setViewingTransform(at);
                    canvasGN.setPositionTransform(at);
                    canvasGN.paint(g);
                    // this appears to clip according to the viewBox
                } else {
                    g.scale(s, s);
                    g.translate(0, (1920-1080)/2.0);
                    for (Object child : canvasGN.getChildren()) {
                        ((GraphicsNode) child).paint(g);
                    }
                }
            }
        }
        return bi;
    }

    public static GraphicsNode parseSVG(Document document)
    {
        UserAgent userAgent = new UserAgentAdapter();
        DocumentLoader loader = new DocumentLoader(userAgent);
        BridgeContext bctx = new BridgeContext(userAgent, loader);
        bctx.setDynamicState(BridgeContext.STATIC);
        GVTBuilder builder = new GVTBuilder();
        return builder.build(bctx, document);
    }

    public static Document ultraflower()
        throws IOException
    {
        String fname = "/home/thoth/art/ultraflower/ultraflower3.svg";

        // create the document
        String parser = XMLResourceDescriptor.getXMLParserClassName();
        SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
        return f.createDocument(fname, new FileInputStream(fname));
    }

    public static class Madness1
    {
        public static void main(String[] args)
            throws IOException
        {
            String resource = "example1.svg";
            String parser = XMLResourceDescriptor.getXMLParserClassName();
            SAXSVGDocumentFactory f = new SAXSVGDocumentFactory(parser);
            Document doc = f.createDocument(resource, Madness1.class.getResourceAsStream(resource));

            GraphicsNode gn = parseSVG(doc);

            RootGraphicsNode rootGN = (RootGraphicsNode)gn;
            CanvasGraphicsNode canvasGN = (CanvasGraphicsNode) rootGN.getChildren().get(0);

            CompositeGraphicsNode gn1 = (CompositeGraphicsNode) canvasGN.getChildren().get(1);
            System.out.println(gn1.getOutline());
        }
    }
}

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.purplefrog.batikExperiment</groupId>
    <artifactId>batikExperiment</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>batikExperiment</name>
    <url>http://maven.apache.org</url>

    <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.5.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>

                <plugin>
                  <groupId>org.codehaus.mojo</groupId>
                  <artifactId>appassembler-maven-plugin</artifactId>
                  <version>1.10</version>
                  <configuration>
                    <programs>
                      <program>
                        <mainClass>com.purplefrog.batikExperiment.PathConverter</mainClass>
                        <id>pathConverter</id>
                      </program>
                    </programs>
                  </configuration>
                </plugin>

            </plugins>
        </build>

    <dependencies>
        <dependency>
            <groupId>org.apache.xmlgraphics</groupId>
            <artifactId>batik-rasterizer</artifactId>
            <version>1.11</version>
        </dependency>
        <dependency>
            <groupId>com.purplefrog</groupId>
            <artifactId>penrose</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

如何修改我的代码以检查异常,以使其不会显示数百种此类警告?

2 个答案:

答案 0 :(得分:1)

在JNI下进行开发时,可能引发异常的函数必须紧随ExceptionCheckExceptionOccurred原语之后才能正确处理异常。在jni板条箱中,它们被转换为方法exception_occurredexception_check。方法call_static_methodcall_method本身不会执行异常处理。

let result = je.call_static_method(cls, "renderTo", "(II[B)V", &[
    JValue::from(width),
    JValue::from(height),
    JValue::from(rgbs2),
])?;
je.exception_check()?;

另请参阅:

答案 1 :(得分:0)

另一个类似异常的来源是Java的事件处理循环

WARNING in native method: JNI call made without checking exceptions when required to from CallStaticVoidMethod
    at sun.awt.X11.XToolkit.waitForEvents(Native Method)
    at sun.awt.X11.XToolkit.run(XToolkit.java:574)
    at sun.awt.X11.XToolkit.run(XToolkit.java:538)
    at java.lang.Thread.run(Thread.java:748)

这使我研究了禁用Java的GUI事件循环的可能性。通过修改我的代码以将-Djava.awt.headless=true传递给JVM,我消除了警告流。

pub fn jvm8_args_with_classpath(classpath: &&str) -> InitArgs {
    let jvm_args = InitArgsBuilder::new()
        .version(JNIVersion::V8)
        .option("-Xcheck:jni")
        .option(&format!("-Djava.class.path={}", classpath))
        .option("-Djava.awt.headless=true")
        .build()
        .expect("impossible, failed to construct JVM initialization args");
    jvm_args
}