I've got a simple test case in /build directory that I'm trying to compile and run with Ant. Compilation is fine, but I can't figure out how to run this. The class is:
package money;
import org.junit.*;
import static org.junit.Assert.*;
public class MoneyTest {
#Test
public void testAmount() {
Money m = new Money(20);
System.out.println("AMOUNT: " + m.amount());
System.out.println("DOUBLE AMOUNT: " + m.doubleAmount());
assertEquals(21, m.amount());
}
}
and the buildfile goes like this:
<?xml version="1.0"?>
<project name="Money" default="build-source" basedir=".">
<description>The Money project build file.</description>
<property name="src" location="."/>
<property name="build" location="build"/>
<property name="junit" location="lib/junit-4.8.2.jar"/>
<path id="_classpath">
<pathelement path="${junit}"/>
<pathelement path="${build}"/>
</path>
<target name="prepare">
<mkdir dir="${build}"/>
</target>
<target name="build-source" depends="prepare" description="compile the source ">
<javac srcdir="${src}" destdir="${build}">
<classpath refid="_classpath"/>
</javac>
</target>
<target name="run" depends="build-source">
<java classname="${build}/MoneyTest">
<classpath refid="_classpath"/>
</java>
</target>
</project>
when I run "ant run" from the Terminal, it says that it cannot find the class at that path.
Thanks beforehand!
You've specified a location as a classname. The name of the class would be money.MoneyTest, not /home/build/money/MoneyTest.
However, you should be using the junit task instead of the java task anyway, given that it's a JUnit test rather than a Java app itself. (It doesn't have a main method, for example.)
Related
I've started using Mockito to mock some classes in my unit tests, for various reasons, and as I was developing, I got everything to work. Once I finished my changes, I pushed to our build server, and discovered that the unit tests are broken when run through Ant. I've been able to reproduce this in a simple stripped-down case, in which I'm using Mockito to test the mock the static method of a dependency of the class under test. Below I've pasted the source files, the test file, the Ant script, and the Ivy dependencies file describing the versions of each library I'm using (all of them up to date at this time), followed by the verbose error I'm seeing.
But if I create these several files in a directory (with the two classes under src/com/example and the unit test under tests/com/example and open it up as an IntelliJ IDEA project, using all the same dependencies (including JUnit) and run all tests, it succeeds. The only change from the standard module template is specifying the JDK path and Java level to 1.8. That's part of what's making it so puzzling. For what it's worth, Ant acts the same whether invoked from within IntelliJ IDEA's built-in Ant integration, or separately on the command line.
BusinessClass.java
package com.example;
public class BusinessClass {
public String returnString() {
StaticDependency.DoSomething();
return "Answer";
}
}
StaticDependency.java
package com.example;
public class StaticDependency {
public static void DoSomething() {
throw new RuntimeException("Real code causes side effects I'm isolating from");
}
}
BusinessClassTest.java
package com.example;
import org.junit.Test;
import org.mockito.MockedStatic;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mockStatic;
public class BusinessClassTest {
#Test
public void returnString() {
final BusinessClass classUnderTest = new BusinessClass();
try (final MockedStatic<StaticDependency> ignored = mockStatic(StaticDependency.class)) {
assertEquals("Answer", classUnderTest.returnString());
}
}
}
ivy.xml
<ivy-module version="2.0">
<info organisation="com.example" module="MockitoTest"/>
<dependencies>
<dependency org="junit" name="junit" rev="4.13.2"/>
<!-- Mockito and dependencies -->
<dependency org="org.mockito" name="mockito-inline" rev="4.2.0"/>
<dependency org="net.bytebuddy" name="byte-buddy-dep" rev="1.12.6"/>
<dependency org="org.objenesis" name="objenesis" rev="3.2"/>
</dependencies>
</ivy-module>
build.xml
<project default="all" xmlns:ivy="antlib:org.apache.ivy.ant">
<property name="lib" location="Lib"/>
<property name="src" location="src"/>
<property name="tests" location="tests"/>
<property name="build-test" location="build-test"/>
<property name="build" location="build"/>
<path id="build.classpath">
<fileset dir="${lib}">
<include name="**/*.jar"/>
</fileset>
</path>
<target name="all" depends="clean, resolve, compile, test"/>
<target name="clean">
<delete dir="${build}"/>
<delete dir="${build-test}"/>
<delete dir="${lib}"/>
</target>
<target name="resolve" description="Download dependencies">
<ivy:resolve file="ivy.xml"/>
<mkdir dir="${lib}"/>
<ivy:retrieve pattern="${lib}/[artifact]-[type].[ext]"/>
</target>
<target name="compile" description="Compile the source code">
<mkdir dir="${build}"/>
<javac destdir="${build}">
<src path="${src}"/>
<classpath refid="build.classpath"/>
</javac>
<mkdir dir="${build-test}"/>
<javac destdir="${build-test}">
<src path="${src}"/>
<src path="${tests}"/>
<classpath refid="build.classpath"/>
</javac>
</target>
<target name="test" depends="compile" description="Run all unit tests">
<junit>
<classpath>
<path refid="build.classpath"/>
<path path="${build-test}"/>
</classpath>
<formatter type="plain" usefile="false"/>
<test name="com.example.BusinessClassTest" />
</junit>
</target>
</project>
JUnit output
Testsuite: com.example.BusinessClassTest
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.889 sec
Testcase: returnString took 0.779 sec
Caused an ERROR
Could not modify all classes [class com.example.StaticDependency]
org.mockito.exceptions.base.MockitoException: Could not modify all classes [class com.example.StaticDependency]
at com.example.BusinessClassTest.returnString(Unknown Source)
Caused by: java.lang.IllegalStateException:
Byte Buddy could not instrument all classes within the mock's type hierarchy
This problem should never occur for javac-compiled classes. This problem has been observed for classes that are:
- Compiled by older versions of scalac
- Classes that are part of the Android distribution
at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:280)
at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.mockClassStatic(InlineBytecodeGenerator.java:221)
at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClassStatic(TypeCachingBytecodeGenerator.java:63)
at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createStaticMock(InlineDelegateByteBuddyMockMaker.java:560)
at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createStaticMock(InlineByteBuddyMockMaker.java:83)
at org.mockito.internal.util.MockUtil.createStaticMock(MockUtil.java:147)
at org.mockito.internal.MockitoCore.mockStatic(MockitoCore.java:142)
at org.mockito.Mockito.mockStatic(Mockito.java:2164)
at org.mockito.Mockito.mockStatic(Mockito.java:2101)
Caused by: java.lang.AbstractMethodError: org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator$ParameterWritingVisitorWrapper.wrap(Lnet/bytebuddy/description/type/TypeDescription;Lorg/objectweb/asm/ClassVisitor;Lnet/bytebuddy/implementation/Implementation$Context;Lnet/bytebuddy/pool/TypePool;Lnet/bytebuddy/description/field/FieldList;Lnet/bytebuddy/description/method/MethodList;II)Lorg/objectweb/asm/ClassVisitor;
at net.bytebuddy.asm.AsmVisitorWrapper$Compound.wrap(AsmVisitorWrapper.java:746)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining$WithFullProcessing$RedefinitionClassVisitor.visit(TypeWriter.java:4906)
at org.objectweb.asm.ClassReader.accept(ClassReader.java:569)
at org.objectweb.asm.ClassReader.accept(ClassReader.java:424)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForInlining.create(TypeWriter.java:3951)
at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:2213)
at net.bytebuddy.dynamic.scaffold.inline.RedefinitionDynamicTypeBuilder.make(RedefinitionDynamicTypeBuilder.java:224)
at net.bytebuddy.dynamic.scaffold.inline.AbstractInliningDynamicTypeBuilder.make(AbstractInliningDynamicTypeBuilder.java:123)
at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3661)
at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.transform(InlineBytecodeGenerator.java:394)
at sun.instrument.TransformerManager.transform(TransformerManager.java:188)
at sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:428)
at sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
at sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:144)
at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:276)
It turns out my problem was the counterproductive byte-buddy-dep dependency in my ivy.xml file. As #temp-droid pointed out in a GitHub issue I raised on the Mockito project posing this same problem, replacing that with byte-buddy and byte-buddy-agent would work instead.
I then realized that Ivy doesn't require me to explicitly list sub-dependencies (I wasn't aware it knew how to resolve them), and so I was able to update to this much simpler ivy.xml, which works in Ant and IntelliJ alike:
<ivy-module version="2.0">
<info organisation="com.example" module="MockitoTest"/>
<dependencies>
<dependency org="junit" name="junit" rev="4.13.2"/>
<dependency org="org.mockito" name="mockito-inline" rev="4.2.0"/>
</dependencies>
</ivy-module>
I am having an issue for a homework project where we are creating a MVC like package of classes. Using a Debian 9.x.x virtual box, we are to use an ant file to build/compile/execute.
I am including a screenshot of both the overall directory structure mandated, as well as the source directory structure mandated.
We are specifically to have a source files in the structure:
With that in mind, we are supposed to package the source files as ser321.assign3.kajeffr1.client. I have tried this as relative (which I didn't think made sense since the local path doesn't include what is desired in the class package path), and as absolute. However, my classes don't seem to recognize each other. I get the following error.
It is my understanding that the package paths shouldn't really matter because it can compile to where it wants. I tried imports from the other classes and it didn't seem to help, not to mention they are all in the same directory so I don't know why Message seems to be invisible to the other classes. I am including my build.xml and beginning of the source files since it seems like my error is in packaging/importing, but if the rest of the code is needed I can provide that as well.
build.xml:
<project name="Ser321 Spring 2019 Assign2" default="targets" basedir=".">
<!-- set global properties for this build -->
<property name="src" value="./src" />
<property name="build" value="./classes" />
<property name="docs" value="./docs" />
<property name="lib" value="./lib" />
<property name="userId" value="kajeffr1" />
<property name="parentDir" value="${user.dir}${file.separator}" />
<path id="compile.classpath">
<pathelement location="${build}/"/>
<fileset dir="${lib}">
<include name="*.jar"/>
</fileset>
</path>
<target name="targets">
<echo message="targets are clean, prepare, compile, and execute. "/>
</target>
<target name="prepare">
<!-- Create the time stamp -->
<tstamp/>
<!-- Create the build directory structure used by compile -->
<echo message="home defined as: ${home}" />
<mkdir dir="${build}" />
</target>
<target name="compile" depends="prepare"
description="Compile Java sources">
<javac srcdir="${src}/client"
includeantruntime="false"
destdir="${build}">
<classpath refid="compile.classpath"/>
</javac>
</target>
<target name="execute" depends="compile"
description="execute the execute mail client">
<java classname="ser321.assign2.kajeffr1.client.MessageController"
fork="yes">
<arg value="kajeffr1"/>
<arg value="localhost:8080"/>
<classpath refid="compile.classpath"/>
</java>
</target>
<target name="clean">
<!-- Delete the ${build} and ${docs} directory trees -->
<delete dir="${build}" />
</target>
</project>
Message.java:
package ser321.assign2.kajeffr1.client;
import java.io.Serializable;
import org.json.JSONObject;
public class Message implements Serializable { ... }
MessageLibary.java:
package ser321.assign2.kajeffr1.src.client;
public interface MessageLibrary { ... }
MessageLibraryImpl.java:
package ser321.assign2.kajeffr1.src.client;
import org.json.*;
import java.io.*;
import java.util.*;
public class MessageLibraryImpl implements Serializable, MessageLibrary { ... }
MessageController.java:
package ser321.assign2.kajeffr1.src.client;
//other java imports
public class MessageController extends MessageGui //MessageGUI is provided in jar files { ... }
The ant file is building the "classes" file and then failing to compile anything into it.
I was just missing the ".src." in my Message.java. I can't believe how many times I must have missed that.
in my project there are - among others - two classes: TheProblem and Server.
public class TheProblem {
public static void main (String args []) throws ClassNotFoundException {
ClassLoader.getSystemClassLoader().loadClass("Server");
}
}
When I execute the code from the command line, everything works just fine.
But when I use ANT to execute the code, I get a ClassNotFoundException - although both Server.class and TheProblem.class are inside of the same directory.
The directory structure of my project is fairly simple - I will try to illustrate it here:
root_folder/
- build.xml
- src/
- TheProblem.java
- Server.java
- build/
- TheProblem.class
- Server.class
Here is an excerpt of my build.xml file:
<?xml version="1.0" encoding="UTF-8" ?>
<project name="JAXB" default="compile">
<path id="project.class.path">
<pathelement path="${java.class.path}" />
<pathelement location="build" />
</path>
<target name="init" >
<mkdir dir="build" />
</target>
<target name="compile" depends="init" >
<javac classpathref="project.class.path" srcdir="src" destdir="build"
includeAntRuntime="false" />
</target>
<target name="execute-problem" depends="compile">
<java classpathref="project.class.path" classname="TheProblem" />
</target>
<target name="clean" depends="init">
<delete dir="build" />
</target>
</project>
When I execute ant compile, everything compiles, but when I execute ant execute-problem, the ClassLoader cannot find the Server class and throws a ClassNotFoundException.
When I navigate into the build directory and call java TheProblem, it works just fine. I really have no clue, why it doesn't work using ANT.
Thank you very much for taking the time to read this post.
Instead of
<target name="execute-problem" depends="compile">
<java classpathref="project.class.path" classname="TheProblem" />
</target>
try to use this
<target name="execute-problem" depends="compile">
<java fork="true" dir="." classname="TheProblem">
<classpath>
<path refid="project.class.path" />
</classpath>
</java>
</target>
I need to execute in parallel an automation suite on three different applications. For that I have created a main class in which I am passing brand value as an arg to execute for each specific brand. Here are the following details:
PROJECT STRUCTURE:
POM PACKAGE- contains all java files of application pages
UTILITY PACKAGE - contains the common utility java classes for performing common operations like filling the form
TESTSUITE PACKAGE - contains test case class and main class.
This is the compile and run part of build file I have created for performing parallel execution:
<!-- building all java file 3 times in different target for separate jars to class binary -->
<target name="compile" depends="init">
<javac srcdir="${basedir}/src/" destdir="${basedir}/build" classpathref="classpath" includeantruntime="true" />
</target>
<target name="compile2" depends="init">
<javac srcdir="${basedir}/src/" destdir="${basedir}/buildB" classpathref="classpath" includeantruntime="true" />
</target>
<target name="compile3" depends="init">
<javac srcdir="${basedir}/src/" destdir="${basedir}/buildC" classpathref="classpath" includeantruntime="true"/>
</target>
<target name="jar" depends="compile,compile2,compile3">
<jar destfile = "${basedir}/ClassFiles/A.jar" basedir = "${basedir}/build/" />
<jar destfile = "${basedir}/ClassFiles/B.jar" basedir = "${basedir}/buildB/" />
<jar destfile = "${basedir}/ClassFiles/C.jar" basedir = "${basedir}/buildC/" />
</target>
<!-- execute generated jar -->
<target name="Run" depends="jar" >
<parallel>
<java fork="true" classname="com.TestSuitePackage.MainClass">
<arg value="brand1"/>
<classpath>
<path refid="classpath"/>
<path location="${basedir}/ClassFiles/classes.jar"/>
</classpath>
</java>
<java fork="true" classname="com.TestSuitePackage.MainClass">
<arg value="brand2"/>
<classpath>
<path refid="classpath"/>
<path location="${basedir}/ClassFiles/B.jar"/>
</classpath>
</java>
<java fork="true" classname="com.TestSuitePackage.MainClass">
<arg value="brand3"/>
<classpath>
<path refid="classpath"/>
<path location="${basedir}/ClassFiles/C.jar"/>
</classpath>
</java>
</parallel>
</target>
In the above build file, I have compiled the same code three times to create diff jars for the same class with diff argument. Running this build, separate jar files get created however independent JVM is not getting invoked.
Please suggest the changes I need to make and inputs on the approach I am using.
build.xml contains <scp> and <sshexec> tasks, so I provide jsch.jar and
other libraries in the same directory together with build.xml.
The following taskdef:
<taskdef name="scp"
classname="org.apache.tools.ant.taskdefs.optional.ssh.Scp"
classpath="WebContent/WEB-INF/lib/jsch-0.1.43.jar" />
throws an error
A class needed by class org.apache.tools.ant.taskdefs.optional.ssh.Scp
cannot be found: com/jcraft/jsch/UserInfo
I cannot modify the standard Ant installation (e.g. put jsch.jar in ant lib
directory, or remove ant-jsch.jar), or add command-line flags, or modify
system environment variables, etc.: the script has to run with default Ant
on different systems.
I'm actually reposting the question originally asked here:
http://ant.1045680.n5.nabble.com/specifying-location-of-an-external-library-within-build-xml-td1344969.html
but could not get the answer about classloader to work.
Finally I found a working solution (for Ant 1.7.1 at least). First you have to remove ant-jsch.jar from ANT_HOME/lib as Ant complains about it and gets confused. Then load libraries from the project itself:
<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" />
So, this question is old, but I devised another approach which may help others. We can spawn Ant from a <java> task with the proper classpath to run <scp>. This avoid the classpath leaking problem, and doesn't requires changing Ant install in any way:
<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>
The port parameter isn't needed, but it's here as a reminder for custom SSH ports. The password is a property stored on /tmp/passwordfile, like PASSWORD=mysecretpassword. Change these to suit your needs. Here follows an usage example:
<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>
For reference, an approach that I find useful is to repackage the jars, so they don't conflict - you can do this in Ant using JarJar like this:
<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>
I was able to solve this issue following post from here
https://stackoverflow.com/a/858744/3499805
and then
<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"/>
Create a path reference and then use it in your task definition:
<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" />
Create ~/.ant/lib and copy jsch.jar in there as part of the build initialisation.
<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>
There's a well-known trick with URLClassLoader. By using it we can make jsch accessible to ant-jsch.
I wonder how classloadertask from the answer by #user3499805 works.
<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";
}