我正在使用Apache Ant作为繁琐的数据收集和计算任务的工具,我必须一遍又一遍地做。我已经定义了一些自定义的Ant任务,它们的工作非常好。
但是,现在我想使用<typedef>
标记创建新的数据类型。我想在我的build.xml
开头定义一些数据,稍后我可以参考,就像我的一个Java项目的常规构建文件中的以下示例一样:
<path id="classpath.build">
<fileset dir="${dir.lib}">
<include name="**/*.jar" />
<exclude name="**/junit*" />
</fileset>
</path>
所以我创建了一个简单的HelloWorld示例,如下所示:
<sampledata data="LOL" id="someid" />
在自定义ant任务中,我想引用这种数据类型:
<customtask dataref="someid" />
这看似合理简单,因此在深入研究API文档后,我发现我的课程必须扩展org.apache.tools.ant.types.DataType
并且必须使用方法setRefid(org.apache.tools.ant.types.Reference r)
。
我的自定义Ant任务customtask对dataref属性使用以下代码:
public class CustomTask extends Task {
private SampleData data;
public void setDataref(Reference r) {
data = new SampleData(getProject());
data.setRefid(r);
}
public void execute() {
System.out.println(data.getData());
}
}
我的SampleData实现如下:
public class SampleData extends DataType {
private String data;
public SampleData(Project project) {
setProject(project);
}
public void setData(String data) {
this.data = data;
}
public String getData() {
return this.data;
}
public void setRefid(Reference r) {
super.setRefid(r);
}
}
请注意,这完全基于来自org.apache.tools.ant.types.Path
的来源,它显示了我想要的行为。
但是,在使用上面定义的customtask创建目标后,输出为null
。因此SampleData被实例化,但引用未正确设置。当我调试时,我发现SampleData在我的ant文件中使用数据LOL
正确实例化,甚至refence也设置为someid
。此外,CustomTask
类setDataref
方法确实传递了一个名为someid
的引用,所以在setDataref
方法中都出错了,但我不知道我有什么并且缺乏手册(或者我缺少一个重要的部分)。
我觉得我没有完全掌握id为自定义数据类型的生命周期。
编辑23-11-2012 9:24:
经过一些更多的摆弄并查看org.apache.tools.ant.types.Path
的来源后,我遵循了一些方法并将SampleData.getData更改为以下内容:
public String getData() {
if(isReference()) {
return ((SampleData)getCheckedRef()).getData();
}
return this.data;
}
我稍微进一步,但现在我在build.xml
中收到以下Ant错误:
/home/arjan/dev/so-demo/build.xml:9: someid doesn't denote a SampleData
但是,当我检查由Reference对象封装的类时,它是正确的类型。 我现在非常厌倦了。还有什么提示吗?
编辑23-11-2012 11:46:
我用一个明确的测试用例创建了一个Gist。我的Ant版本是1.8.4。 希望有人会提供一个解决方案,因为我已经查看了Sonatype Aether Antlib等其他库,并遵循他们的推理方式。
getCheckedRef
方法出错,特别是在Ant源文件src\main\org\apache\tools\ant\types\DataType.java:250
中:
if (!(requiredClass.isAssignableFrom(o.getClass()))) {
log("Class " + o.getClass() + " is not a subclass of " + requiredClass,
Project.MSG_VERBOSE);
String msg = ref.getRefId() + " doesn\'t denote a " + dataTypeName;
throw new BuildException(msg);
}
发生了什么事?这是我能想到的简单测试用例。
答案 0 :(得分:1)
我相信这可能会解决您的问题,我遇到了类似的错误:
您缺少自定义任务和类型的loaderRef指令。请参阅here:如果要定义与多个taskdef或typedef任务共享相同类路径的任务或类型,则相应的类将由不同的Java ClassLoader加载。从Java VM的角度来看,通过不同的ClassLoader加载的两个具有相同名称的类不是同一个类,它们不共享静态变量,并且这些类的实例不能访问由“”定义的实例的私有方法或属性。另一类“同名。它们甚至不属于同一个Java包,也不能访问包私有代码。
因此,当您通过typedef和taskdef定义自定义任务和自定义类型时,请使用loaderRef属性 - 这可以是任何内容(如customTaskLoader),只要它在您的任务和类型定义之间是相同的。
从那里,您可以进一步简化代码:
public class CustomTask extends Task {
private SampleData data;
public void execute() {
System.out.println(data.getData());
}
}
和
public class SampleData extends DataType {
private String data;
public SampleData(Project project) {
setProject(project);
}
public void setData(String data) {
this.data = data;
}
public String getData() {
if(isReference()) {
return ((SampleData)getCheckedRef()).getData();
}
return this.data;
}
}
答案 1 :(得分:0)
我认为问题是你没有为你的班级提供数据的setter ...这对于CustomTask是强制性的......
public class CustomTask extends Task {
private SampleData data;
public void setDataref(Reference r) {
data = new SampleData(getProject());
data.setRefid(r);
}
public void setData(SampleData data){
this.data = data;
}
public void execute() {
System.out.println(data.getData());
}
}
希望这会有所帮助..
答案 2 :(得分:0)
我使用上面的要点解决了它!问题是双重的,但我不知道如何解决第二个问题。
问题1
classpathref
和typedef
标记的taskdef
参数不是一种方法。我将它们导向cls
目录中的已编译类。
所以我决定将我的所有文件存放起来并将它们放在{ant.home}/lib
目录中,如下所示:
<jar basedir="cls" destfile="${ant.home}/lib/demo.jar" />
<delete dir="cls" />
这样我可以删除classpathref
个参数。
我认为这可能会解决它...但我错了,但是实际的解决方案(如果你可以称之为)可行。
问题2
Eclipse ...这个程序使用自己的Ant发行版,运行Eclipse时,我自己生成的jar不会添加到类路径中。这会导致以下错误:
typedef class types.DemoType cannot be found
从命令行运行证明没问题。
<强>底线强>
通过调整我的类型和任务并从命令行运行它来解决问题。将classpathref
与typedef
或taskdef
一起使用会导致someid doesn't denote a SampleData
错误。使用Eclipse导致class not found
错误。
奇怪的行为我必须说,我想知道如何让Eclipse正常使用自定义任务,因为这必须是可能的。
嗯......这花了我几个小时。