具有引用的自定义Ant类型导致空类



我正在使用ApacheAnt作为一种工具来完成我必须反复完成的乏味的数据收集和计算任务。我定义了一些自定义的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" />

在自定义蚂蚁任务中,我想引用这种数据类型:

<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。此外,CustomTasksetDataref方法确实传递了一个名为someid的Reference,所以在setDataref方法中一切都出错了,但我不知道我必须做什么,手册也缺少(或者我缺少了一个重要部分)。

我有一种感觉,我没有完全掌握带有id的自定义数据类型的生命周期。

2012年11月23日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对象封装的类时,它是正确的类型。我现在受够了。还有什么建议吗?

编辑:2012年11月23日11:46:

我创建了一个Gist,有一个清晰的测试用例。我的Ant版本是1.8.4。希望有人能找到一个解决方案,因为我查阅了其他库,如Sonatrype Aether Antlib,并遵循了他们的推理方式。

getCheckedRef方法出错,特别是在Ant源文件srcmainorgapachetoolsanttypesDataType.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);
}

发生了什么事?这是我能想到的最简单的测试用例。

我相信这可能会解决您的问题,我遇到了类似的错误:

您的自定义任务和类型缺少loaderRef指令。请参阅此处:如果您定义的任务或类型与多个taskdef或typedef任务共享同一类路径,则相应的类将由不同的Java ClassLoader加载。从Java虚拟机的角度来看,通过不同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;
}

}

我认为问题是您的类中没有为Data提供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());
}
}

希望这能有所帮助。。

我使用上面的Gist解决了它!问题有两个方面,但我不知道如何解决第二个问题。

问题1

不知怎的,typedeftaskdef标签的classpathref论点不是可行的。我将它们引导到cls目录中的已编译类中。

因此,我决定将我所有的文件进行jar,并将它们放在{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

从命令行运行证明没有问题。

底线

这个问题可以通过查找我的类型和任务并从命令行运行来解决。将classpathreftypedeftaskdef一起使用会导致someid doesn't denote a SampleData错误。使用Eclipse导致class not found错误。

奇怪的行为,我必须说,我想知道如何使Eclipse正确地处理自定义任务,因为这一定是可能的。

嗯。。。这花了我几个小时。

最新更新