【问题标题】:What is a good approach/tool to embed a mini-language in Java app?在 Java 应用程序中嵌入迷你语言的好方法/工具是什么?
【发布时间】:2012-08-10 20:07:00
【问题描述】:

我正在尝试解决以下问题:

  • 我有一个 Java 应用程序(不是我编写的),其目的是获取 2 个报告文件(只是逗号分隔的表格输出),并将文件的每一行和每一列逐个单元格地相互比较 -基本上,用于回归测试。

  • 我想用以下功能增强它:

    • 假设我做了一个软件更改,导致 C1 列中的所有值都增加了 100%。

    • 在比较特定列“C1”时,软件当前会报告 C1 中 100% 的值发生了变化。

    • 我想要做的是 能够将回归测试器配置为不简单地比较“第 1 行中的 C1 在两个文件中是否相同”,而是应用预先配置的比较规则/公式到现场,例如"确保文件#2 中的 C1 值是文件#1 中 C1 值的 2 倍"。这样,我不仅可以抑制 100% 的虚假回归错误,即每行的 C1 列不匹配,而且还可以使用新软件捕获 C1 列不完全 100% 大的任何实际错误。

      李>

以前在 Perl 中编写此类功能时,解决方案非常简单 - 只需将自定义的每列比较器配置编码到存储在配置文件中的 Perl 哈希中,哈希键是列和哈希值是 Perl 子例程,用于以任何我想要的复杂方式比较 2 个值。

显然,这种方法不适用于 Java,因为我无法在 Java 中编写自定义比较器逻辑,并让 Java 在不同的运行时评估/编译/执行这些比较器。

这意味着我需要提出一些特定领域的可解释语言,我的不同之处将解释/评估/执行。

由于我对 Java 生态系统和库不是很熟悉,所以我问:

为可配置的比较器逻辑实现此 DSL 有什么好的解决方案?

我的要求是:

  • 解决方案必须“像啤酒一样免费”

  • 解决方案必须是“收缩包装”。例如。一个现有的库,我可以简单地放入我的代码中,添加配置文件,然后让它工作。

    要求我编写自己的 BNF 语法并提供我必须编写自己的解释器的通用语法解析器的东西是不可接受的。

  • 就数据处理和语法丰富而言,解决方案必须相当灵活。例如:

    • 您至少应该能够从 DSL 中传入和引用/寻址一整行数据作为哈希

    • 它应该有相当完整的语法;至少进行基本的字符串操作(连接、子字符串,理想情况下是某种级别的正则表达式匹配和替换);基本算术,包括在浮点 #s 上执行 abs(val1 - val2) > tolerance 的能力;以及基本的流程控制/逻辑,例如条件和理想循环。

  • 解决方案应该相当快,并且必须是可扩展的。例如。比较 100x100 大小的文件与 10-20 个自定义列不应该花费 10 分钟。

如果重要的话,目标环境是 Java 1.6

【问题讨论】:

  • 尝试一种可嵌入的脚本语言,例如 SISC 或 Jython。

标签: java interpreter dsl evaluation


【解决方案1】:

有多种动态 JVM 编程语言可以轻松集成到 Java 应用程序中,而无需付出太多努力。我认为值得研究Groovy 和/或Scala

另一个可能的选择是使用 XTextXTend 创建您自己的 DSL。

【讨论】:

  • 1. xText 是否可以支持我能够传入 HashMap 或类似的东西并访问单个值的要求?有条件或循环控制结构?
  • 2.您能否详细说明将 Groovy/Scala 集成到 Java 中?我知道它们是基于 Java 的,但是是否有标准工具可以在 Java 代码中运行 Groovy/Scala 子例程并将它们的数据结构作为参数传递?
  • 你检查过我为 Groovy 和 Scala 添加的链接吗?他们应该向您展示从 Java 调用 Groovy/Scala 函数的可能方式以及如何在它们之间传递参数。我对 XText 不太确定。我认为这应该是可能的,但是对于您的目标来说它可能有点过大。
【解决方案2】:

每当谈到 Java 中的动态特性时,我都会想到 Janino,这是一个迷人的运行时和内存编译器。在您的情况下,它会为您提供类似于纯 Java 的 eval(...) 的内容,请参阅:http://docs.codehaus.org/display/JANINO/Basic#Basic-expressionevaluator

这里的重点是您的测试配置没有 DSL,但您可以使用纯 Java 语法在测试配置中编写自定义表达式。

下面提出的解决方案无法满足的唯一要求是您可以从配置文件中处理整行。该解决方案假定您编写了一个 Java 测试类,它逐个值(或更好地逐对)遍历您的测试数据,并使用您配置的表达式来比较单个值。所以动态部分是表达式,静态部分是测试数据的迭代。

但是所需的代码非常小而且简单,如下所示:

配置文件(Java属性语法,key为列名,value为测试表达式):

# custom expression for column C1
C1  = a == 2 * b           
# custom expression for column C4
C4  = a == b ^ 2           
# custom expression for column C47
C47 = Math.abs(a - b) < 1  

测试代码草图:

// read config file into Properties
Properties expressions = Properties.load(....);

// Compile expressions, this could also be done lazily
HashMap<String, ExpressionEvaluator> evals = new HashMap<String, ExpressionEvaluator>();
for (String column : expressions.stringPropertyNames()) {
    String expression = expressions.getProperty(column);
    ExpressionEvaluator eval = new ExpressionEvaluator(
        expression,                  // expression
        boolean.class,               // expressionType
        new String[] { "a", "b" },   // parameterNames
        new Class[] { int.class, int.class } // parameterTypes, depends on your data
    );
    evals.put(column, eval);
}

// Now for every value pair (a, b) check if a custom expression is defined 
// for the according column and if yes execute: 
Boolean correct = (Boolean) theEvalForTheColumn.evaluate(
    new Object[] { a, b }          // parameterValues
);
if (!correct) throw Exception("Wrong values...");

正如 Janino 页面上所说,编译表达式的性能非常好(它们是真正的 java 字节码),只有编译会减慢进程。因此,如果您有许多自定义表达式,但它应该随着值的增加而很好地扩展,这可能是一个问题。 hth.

【讨论】:

  • 我还不确定在没有原型设计的情况下它是否对这项任务有用,但无论如何这是一个很好的信息! +1
  • “原型设计”是什么意思?
  • 构建一个可以调用 Janino 的迷你差异
  • 嗯,好的。仅供参考:我花了大约 10 分钟来设置一个运行示例。
【解决方案3】:

不需要嵌入式语言。将您的比较器定义为接口。

您可以使用 class.forName(name) 加载在运行时定义接口的类, 其中 name 可以通过命令行参数或任何其他方便的方式指定。

你的比较器类看起来像

class SpecialColumn3 implements ColumnCompare
{ boolean compare(String a,String b) {...}
}

【讨论】:

    猜你喜欢
    • 2010-10-09
    • 2012-09-02
    • 1970-01-01
    • 2016-02-12
    • 1970-01-01
    • 2012-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多