2010-09-16 14 views
8

build.xml chứa <scp><sshexec> tác vụ, vì vậy tôi cung cấp jsch.jar và các thư viện khác trong cùng một thư mục cùng với build.xml.Có cách nào để xác định vị trí của một jsch.jar cục bộ từ bên trong build.xml không?

Các taskdef sau:

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

ném một lỗi

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

tôi không thể thay đổi cài đặt Ant tiêu chuẩn (ví dụ như đặt jsch.jar trong kiến ​​lib thư mục, hoặc loại bỏ ant-jsch. jar) hoặc thêm cờ dòng lệnh hoặc sửa đổi biến môi trường hệ thống, v.v.: tập lệnh phải chạy với Ant mặc định trên các hệ thống khác nhau.

Tôi thực sự reposting câu hỏi ban đầu hỏi ở đây: http://ant.1045680.n5.nabble.com/specifying-location-of-an-external-library-within-build-xml-td1344969.html

nhưng không thể có được câu trả lời về classloader để làm việc.

Trả lời

17

Cuối cùng tôi tìm thấy một giải pháp làm việc (đối với 1.7.1 Ant ít nhất). Trước tiên, bạn phải xóa ant-jsch.jar khỏi ANT_HOME/lib vì Ant phàn nàn về điều đó và bị nhầm lẫn. Sau đó tải thư viện từ chính dự án:

<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" /> 
+1

Có vẻ như đây là một vấn đề thường gặp và tôi nghĩ rằng đây là giải pháp tốt nhất, ngoại trừ thay vì thất bại, tôi sẽ chỉ đi trước và xóa thư mục/lib/ant -jsch.jar. Trong trường hợp nó giúp người khác, Paulo đưa ra một giải thích về vấn đề cốt lõi trong câu trả lời của mình [câu trả lời cho câu hỏi SCP liên quan] (http://stackoverflow.com/questions/5796587/problems-with-ant-optional-tasks-sshexec- và-scp-classpath-issue) và [cùng một vấn đề xảy ra với junit và được giải thích ở đây] (http://ant.apache.org/faq.html#delegating-classloader) – gMale

+1

Nhưng điều này đòi hỏi phải sửa đổi cài đặt Ant chuẩn . –

+0

Tôi đã vật lộn với vấn đề này trong nhiều ngày và giải pháp này là giải pháp duy nhất tôi có thể làm việc! –

0

Tạo một tài liệu tham khảo đường dẫn và sau đó sử dụng nó trong định nghĩa nhiệm vụ của bạn:

<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" /> 
3

Vì vậy, câu hỏi này là cũ, nhưng tôi nghĩ ra một cách tiếp cận khác có thể giúp người khác. Chúng ta có thể sinh ra Ant từ một nhiệm vụ <java> với classpath thích hợp để chạy <scp>. Đây tránh classpath rò rỉ vấn đề, và không đòi hỏi phải thay đổi Ant cài đặt trong bất kỳ cách nào:

<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="[email protected]:${todir}" 
     trust="true" port="22" password="${PASSWORD}"/> 
</target> 

Tham số port là không cần thiết, nhưng nó ở đây như là một lời nhắc nhở cho các cổng SSH tuỳ biến. Mật khẩu là tài sản được lưu trữ trên /tmp/passwordfile, như PASSWORD=mysecretpassword. Thay đổi những thứ này cho phù hợp với nhu cầu của bạn. Ở đây sau một ví dụ sử dụng:

<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> 
2

Để tham khảo, một cách tiếp cận mà tôi thấy hữu ích là để đóng gói lại các lọ, vì vậy họ không xung đột - bạn có thể làm điều này trong Ant sử dụng Jarjar như thế này:

<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="[email protected]"/> 
     <rule pattern="org.apache.tools.ant.taskdefs.optional.ssh.**" result="[email protected]"/> 
    </jarjar> 
</target> 
0

Tạo ~/.ant/lib và sao chép jsch.jar trong đó như là một phần của khởi tạo xây dựng.

<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> 
1

tôi đã có thể giải quyết vấn đề này bài sau đây từ đây https://stackoverflow.com/a/858744/3499805 và sau đó

<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"/> 
0

Có một nổi tiếng trick với URLClassLoader. Bằng cách sử dụng nó, chúng tôi có thể làm cho jsch có thể truy cập được ant-jsch.

Tôi tự hỏi làm thế nào classloadertask từ câu trả lời của @ user3499805 hoạt động.

<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"; 
}