【问题标题】:Custom Ant Types with reference result in empty class具有空类引用结果的自定义 Ant 类型
【发布时间】:2012-11-22 14:30:36
【问题描述】:

我使用 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 的来源,它显示了我想要的行为。

但是,使用上述自定义任务创建目标后,输出为null。因此 SampleData 已实例化,但引用设置不正确。当我调试时,我发现 SampleData 在我的 ant 文件中正确地实例化了数据LOL,甚至参考设置为someid。此外,CustomTasksetDataref 方法确实传递了一个名为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

但是,当我检查引用对象封装的类时,它是正确的类型。 我现在对此感到非常厌倦。还有更多提示吗?

编辑 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);
}

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

【问题讨论】:

    标签: java ant customization


    【解决方案1】:

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

    您缺少自定义任务和类型的 loaderRef 指令。请参阅here:如果您正在定义与多个 taskdef 或 typedef 任务共享相同类路径的任务或类型,则相应的类将由不同的 Java 类加载器加载。从 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;
     }
    

    }

    【讨论】:

      【解决方案2】:

      我认为问题是你没有在你的班级中为 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());
           }
      }
      

      希望这会有所帮助..

      【讨论】:

      • 我尝试了你的建议,但使用调试我发现该方法永远不会被调用....太糟糕了。
      【解决方案3】:

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

      问题 1

      不知何故,typedeftaskdef 标签的 classpathref 参数不是要走的路。我将它们指向我在cls 目录中编译的类。

      所以我决定将我所有的文件打包并放在{ant.home}/lib 目录中,如下所示:

      <jar basedir="cls" destfile="${ant.home}/lib/demo.jar" />
      <delete dir="cls" />
      

      这样我可以删除classpathref 参数。 我认为这可能会解决它......但我错了,但它是实际的解决方案(如果你可以这么称呼它)。

      问题 2

      Eclipse... 这个程序使用它自己的 Ant 发行版,不知何故我自己生成的 jar 在运行 Eclipse 时没有添加到类路径中。这会导致以下错误:

      typedef class types.DemoType cannot be found
      

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

      底线

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-28
        • 1970-01-01
        • 1970-01-01
        • 2014-06-02
        相关资源
        最近更新 更多