【问题标题】:Is there a way to specify the location of a local jsch.jar from within build.xml?有没有办法从 build.xml 中指定本地 jsch.jar 的位置?
【发布时间】:2011-04-13 04:48:29
【问题描述】:

build.xml 包含 <scp><sshexec> 任务,所以我提供了 jsch.jar 和 同一目录中的其他库以及 build.xml。

以下任务定义:

<taskdef name="scp"
    classname="org.apache.tools.ant.taskdefs.optional.ssh.Scp"
    classpath="WebContent/WEB-INF/lib/jsch-0.1.43.jar" />

抛出错误

A class needed by class org.apache.tools.ant.taskdefs.optional.ssh.Scp
cannot be found: com/jcraft/jsch/UserInfo

我无法修改标准 Ant 安装(例如,将 jsch.jar 放入 ant lib 目录,或者删除 ant-jsch.jar),或者添加命令行标志,或者修改 系统环境变量等:脚本必须使用默认的 Ant 运行 在不同的系统上。

我实际上是在重新发布最初在这里提出的问题: http://ant.1045680.n5.nabble.com/specifying-location-of-an-external-library-within-build-xml-td1344969.html

但无法得到关于类加载器工作的答案。

【问题讨论】:

    标签: java ant scp


    【解决方案1】:

    创建一个路径引用,然后在您的任务定义中使用它:

    <path id="ssh.path">
       <pathelement location="${lib1.dir}/helloworld.jar"/>
       <fileset dir="${lib2.dir}">
           <include name="*.jar"/>
       </fileset>
    </path>
    
    <taskdef name="mytask" classname="org.mytask" classpathref="ssh.path" />
    

    【讨论】:

      【解决方案2】:

      最后我找到了一个可行的解决方案(至少对于 Ant 1.7.1)。首先,您必须从 ANT_HOME/lib 中删除 ant-jsch.jar,因为 Ant 抱怨它并感到困惑。然后从项目本身加载库:

      <available property="ant-jsch.present" file="${ant.home}/lib/ant-jsch.jar"/>
      <fail if="ant-jsch.present" message="Please remove ant-jsch.jar from ANT_HOME/lib see [http://ant.apache.org/faq.html#delegating-classloader]"/>
      
      <path id="jsch.path">
          <pathelement location="lib/ant-jsch.jar" />
          <pathelement location="lib/jsch-0.1.44.jar" />
      </path>
      
      <taskdef name="scp" classname="org.apache.tools.ant.taskdefs.optional.ssh.Scp" classpathref="jsch.path" />
      <taskdef name="sshexec" classname="org.apache.tools.ant.taskdefs.optional.ssh.SSHExec" classpathref="jsch.path" />
      

      【讨论】:

      • 这似乎是一个常见问题,我认为这是最好的解决方案,除了失败,我将继续删除 /lib/ant-jsch.jar。如果它对其他人有帮助,Paulo 在他的 SO answer to a related SCP questionthe same problem occurs with junit and is explained here 中解释了核心问题
      • 但这需要修改标准的Ant安装。
      • 我一直在努力解决这个问题好几天了,这个解决方案是我唯一可以开始工作的解决方案!
      【解决方案3】:

      所以,这个问题很老,但我设计了另一种可能对其他人有所帮助的方法。我们可以从具有正确类路径的&lt;java&gt; 任务中生成Ant 来运行&lt;scp&gt;。这避免了类路径泄漏问题,并且不需要以任何方式更改 Ant 安装:

      <target name="sendfile">
          <!-- file: local file to send -->
          <!-- todir: remote directory -->
          <java classname="org.apache.tools.ant.launch.Launcher"
              fork="true" dir="${basedir}" taskname="ant+scp">
              <classpath>
                  <pathelement location="/where/is/jsch-0.1.49.jar"/>
                  <pathelement location="${ant.home}/lib/ant-launcher.jar"/>
              </classpath>
              <arg value="-buildfile"/>
              <arg file="${ant.file}"/>
              <arg value="-Dfile=${file}"/>
              <arg value="-Dtodir=${todir}"/>
              <arg value="sendfile.scp"/>
          </java>
      </target>
      
      <target name="sendfile.scp">
          <echo message="Sending ${file} to ${todir}"/>
          <property file="/tmp/passwordfile"/>
          <scp file="${file}" todir="username@11.22.33.44:${todir}"
              trust="true" port="22" password="${PASSWORD}"/>
      </target>
      

      port 参数不是必需的,但它在这里是为了提醒自定义 SSH 端口。密码是存储在/tmp/passwordfile 上的属性,例如PASSWORD=mysecretpassword。更改这些以满足您的需求。下面是一个用法示例:

      <ant target="sendfile">
          <!-- Example: send /etc/os-release file to remote dir /home/myself -->
          <property name="file" value="/etc/os-release"/>
          <property name="todir" value="/home/myself"/>
      </ant>
      

      【讨论】:

        【解决方案4】:

        作为参考,我发现一种有用的方法是重新打包 jar,这样它们就不会发生冲突 - 您可以在 Ant 中使用 JarJar 执行此操作,如下所示:

        <taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask" classpath="${basedir}/lib/build/jar/jarjar-1.4.jar"/>
        
        <taskdef name="scp" classname="repackaged.scp.org.apache.tools.ant.taskdefs.optional.ssh.Scp" classpath="${basedir}/lib/build/jar/repackaged-scp.jar"/>
        
        <target name="repackage.scp" description="Repackages Ant's optional SCP task and the JSch implementation to avoid conflicting with one on Ant's classpath">
            <delete file="${basedir}/lib/build/jar/repackaged-scp.jar" failonerror="false"/>
            <jarjar basedir="." jarfile="${basedir}/lib/build/jar/repackaged-scp.jar" includes="nothing">
                <zipfileset src="${basedir}/lib/build/jar/ant-jsch-1.9.1.jar"/>
                <zipfileset src="${basedir}/lib/build/jar/jsch-0.1.50.jar"/>
                <rule pattern="com.jcraft.jsch.**" result="repackaged.scp.com.jcraft.jsch.@1"/>
                <rule pattern="org.apache.tools.ant.taskdefs.optional.ssh.**" result="repackaged.scp.org.apache.tools.ant.taskdefs.optional.ssh.@1"/>
            </jarjar>
        </target>
        

        【讨论】:

          【解决方案5】:

          创建 ~/.ant/lib 并在其中复制 jsch.jar 作为构建初始化的一部分。

          <target name="init">
            <property name="user.ant.lib" location="${user.home}/.ant/lib"/>
            <mkdir dir="${user.ant.lib}"/>
            <copy todir="${user.ant.lib}">
              <fileset dir="${basedir}/build/tools" includes="jsch-*.jar"/>
            </copy>
          </target>
          

          【讨论】:

            【解决方案6】:

            我能够从这里的帖子中解决这个问题 https://stackoverflow.com/a/858744/3499805 然后

            <taskdef resource="net/jtools/classloadertask/antlib.xml" classpath="${basedir}/ant-lib/ant-classloadertask.jar" />
            <classloader loader="system" classpath="${basedir}/ant-lib/jsch-0.1.54.jar"/>
            

            【讨论】:

              【解决方案7】:

              有一个众所周知的trickURLClassLoader。通过使用它,我们可以使jsch 可以访问ant-jsch

              我想知道@user3499805 的回答中的classloadertask 是如何工作的。

              <target name="injectJsch" description="inject jsch jar">
                  <makeurl file="${acdc.java.tools}/lib/jsch-0.1.50.jar" property="jsch.jar.url"/>
                  <taskdef name="injectJsch"
                      classname="tools.deployments.ant.InjectJsch"
                      classpath="${basedir}/jars/ajwf_deploytools.jar"
                  />
                  <injectJsch jarLocation="${jsch.jar.url}"/>
              </target>
              

              _

              package tools.deployments.ant;
              
              import java.lang.reflect.Method;
              import java.net.URL;
              import java.net.URLClassLoader;
              
              import org.apache.tools.ant.BuildException;
              import org.apache.tools.ant.Task;
              import org.apache.tools.ant.taskdefs.optional.ssh.LogListener;
              
              public class InjectJsch extends Task {
              
                  public void setJarLocation(final String jarLocation) {
                      this.jarLocation = jarLocation;
                  }
              
                  @Override
                  public void execute() throws BuildException {
                      try {
                          injectJsch(new URL(jarLocation));
                      } catch (final Exception e) {
                          throw new BuildException(e);
                      }
                  }
              
                  public static void injectJsch(final URL jarLocation) throws Exception {
                      ClassLoader parent = LogListener.class.getClassLoader();
                      try {
                          parent.loadClass(TESTCLASS);
                      } catch (final ClassNotFoundException e) {
                          final Method addURLmethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
                          addURLmethod.setAccessible(true);
                          ClassLoader cl;
                          do {
                              cl = parent;
                              if (cl instanceof URLClassLoader) {
                                  addURLmethod.invoke(cl, jarLocation);
                                  break;
                              }
                              parent = cl.getParent();
                          } while (parent != cl && parent != null);
                          LogListener.class.getClassLoader().loadClass(TESTCLASS);
                      }
              
                  }
              
                  private String jarLocation;
              
                  private static final String TESTCLASS = "com.jcraft.jsch.UserInfo";
              }
              

              【讨论】:

                猜你喜欢
                • 2011-07-11
                • 1970-01-01
                • 2019-10-21
                • 2011-02-21
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-12-17
                • 1970-01-01
                相关资源
                最近更新 更多