How to compile a .drl file through an ant build script - ant

I am new to Drools. I want to know if it is possible to compile a .drl file using some kind of a command that can be entered in the windows command line (shell/cmd). I looked through the binaries that come with the drools distribution but I am unable to figure out a way to compile a .drl file.
The reason I am interested in such a command is that I want to write an ant build file which will compile my java classes and rules and create a jar. This jar should be self sufficient, i.e running the jar from the command line should run the main program, which passes facts in the session causing the rules that operate on these facts to automatically be executed.

The DroolsCompilerAntTask used to be the way to do this. It would take all your various rule files and compile them into a serialized file. It appears to have some bugs in 5.3 though which I am currently trying to work out. In the meantime, here is an illustrative build file that can be used for creating an executable JAR based on Drools. The build will fail if the rules cannot be compiled.
<project name="DroolsProto" default="dist" basedir=".">
<property name="build.src" location="src"/>
<property name="build.target" location="target"/>
<property name="build.dist" location="dist"/>
<property name="build.artifact" value="droolsproto"/>
<property name="one-jar.dist.dir" value="~/Work/Data/Misc/OneJar"/>
<property name="one-jar.version" value="0.97"/>
<property name="one-jar.ant.jar" value="${one-jar.dist.dir}/one-jar-ant-task-${one-jar.version}.jar"/>
<path id="build.lib.path">
<fileset dir="${build.src}/lib">
<include name="**/*.jar"/>
</fileset>
</path>
<taskdef name="one-jar" classname="com.simontuffs.onejar.ant.OneJarTask"
classpath="${one-jar.ant.jar}" onerror="report"/>
<taskdef name="droolscompiler" classname="org.drools.contrib.DroolsCompilerAntTask">
<classpath refid="build.lib.path"/>
</taskdef>
<target name="clean">
<delete dir="${build.target}"/>
<delete dir="${build.dist}"/>
</target>
<target name="init">
<tstamp/>
<mkdir dir="${build.target}"/>
<mkdir dir="${build.dist}"/>
</target>
<target name="compile" depends="init">
<mkdir dir="${build.target}/classes"/>
<javac srcdir="${build.src}/main/java" destdir="${build.target}/classes">
<classpath refid="build.lib.path"/>
<include name="**/*.java"/>
<exclude name="**/*Test.java"/>
</javac>
</target>
<target name="verify-rules">
<droolscompiler srcDir="${build.src}/main/resources" toFile="${build.target}/classes/foo.foo">
<classpath refid="build.lib.path"/>
</droolscompiler>
</target>
<target name="verify-resources" depends="verify-rules"/>
<target name="bundle-resources" depends="verify-resources">
<copy todir="${build.target}/classes">
<fileset dir="${build.src}/main/resources"/>
</copy>
</target>
<target name="dist" depends="compile, bundle-resources">
<one-jar destfile="${build.dist}/${build.artifact}.jar">
<manifest>
<attribute name="One-Jar-Main-Class" value="org.drools.examples.HelloWorldExample"/>
</manifest>
<main>
<fileset dir="${build.target}/classes"/>
</main>
<lib>
<fileset dir="${build.src}/lib">
<include name="**/*.jar"/>
</fileset>
</lib>
</one-jar>
</target>
</project>
Note that the build uses One-Jar in order to create the self-contained executable, you may wish to substitute this with your 'Super Jar™' tool of choice. There is also a DroolsVerifierAntTask which allegedly can check logical errors in your rules (as opposed to syntactical ones), but I have no hands on experience with it.

You can use something like this:
private static void compile(final String srcFile, final String destFile) throws IOException {
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
URL src = FormChecker.class.getResource(srcFile);
Resource r = ResourceFactory.newInputStreamResource(src.openStream());
kbuilder.add(r, ResourceType.DRL);
if (kbuilder.hasErrors()) {
throw new IllegalStateException("Can not initialize Drools: " + kbuilder.getErrors().toString());
}
Collection<KnowledgePackage> kpackages = kbuilder.getKnowledgePackages();
File dest = new File(destFile);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(dest));
out.writeObject(kpackages);
out.close();
}

There's a drools-ant jar in the droolsjbpm-tools zip.

Related

Ant - Junit - eclipse - build.xml - java.lang.ClassNotFoundException error

I am new to Ant and getting java.lang.ClassNotFoundException. when running my Junit from eclipse / Ant build.xml, However, when running my unit test from eclipse by itself my test passes with no issue. There should be something wrong with my classpath which i cannot figure out.
My envrmnts are:
Java_home: C:/Program Files/Java/jdk1.7.0_25
Ant_home: C:/Users/Armen/javaFolder/apache-ant-1.9.2/bin
JUnit_home: C:/Users/Armen/javaFolder/apache-ant-1.9.2/bin/junit-4.10.jar
My Build.xml
<property name="junitLocation">C:/Users/Armen/javaFolder/apache-ant-1.9.2/bin/junit-4.10.jar</property>
<property name="antLocation2">C:/Users/Armen/JavaFolder/apache-ant-1.9.2.jar</property>
<property name="testCode">C:/Users/Armen/workspace/MockingObjects/test/demo</property>
<property name="srcCode">C:/Users/Armen/workspace/MockingObjects/src/demo</property>
<target name="compile" depends="clean">
<javac includeantruntime="false" srcdir="./src" destdir="./staging" ></javac>
</target>
<target name="run" depends="compile, unitTest">
<java classname="demo.AccountService"><classpath path="./staging"></classpath></java>
</target>
<target name="unitTest" depends="compile">
<junit printsummary="true" showoutput="true" haltonfailure="false" fork="yes">
<formatter type="plain" usefile="true"/>
<test name="demo.TestAccountService" outfile="./result" ></test>
<classpath>
<pathelement location="${junitLocation}"/>
<pathelement location="${antLocation}"/>
<pathelement location="${testCode}" />
<pathelement location="${srcCode}"/>
</classpath>
</junit>
</target>
<target name="clean">
<delete dir="staging"></delete>
<mkdir dir="./staging"/>
</target>
enter image description here
You don't have any step in your build script to compile the unit tests, and when you tell JUnit to run, you also don't include the compiled classes in the classpath - only the source files.
There are therefore 2 things you need to do:
Add an additional build step similar to the following
<target name="test-compile" depends="compile">
<javac includeantruntime="false" srcdir="./test" destdir="./test-classes" />
</target>
then change your current unitTest target to depend on test-compile rather than compile.
Tell JUnit where your classes are, not your source code. Change your testCode and srcCode properties to
<property name="testCode">C:/Users/Armen/workspace/MockingObjects/test-classes</property>
<property name="srcCode">C:/Users/Armen/workspace/MockingObjects/staging</property>
Note From your compile and run steps, it isn't clear if your code is properly structured in Java Packages or, if it is in packages, that you understand how these work. I've made the assumption that your code is in a package of demo and therefore stripped that part of the path out of your compiled class locations.

Ant With Internal Dependencies

I have a a jar right now that uses external dependencies. I'm trying to create a jar that packages all the external dependencies inside, and will just give me one jar. I saw this question asked multiple times, but I still can't figure it out. I'm using Ant, and copied some of the examples I saw on here. I'm using zipgroupfileset to reference the external(now internal) jars. As soon as I added the zipgroupfileset I got a runtime error that said my Runner class could not be found.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- WARNING: Eclipse auto-generated file.
Any modifications will be overwritten.
To include a user specific buildfile here, simply create one in the same
directory with the processing instruction <?eclipse.ant.import?>
as the first entry and export the buildfile again. -->
<project basedir="." default="build" name="ExcelDemo">
<property environment="env"/>
<property name="ECLIPSE_HOME" value="../../../../Program Files (x86)/eclipse"/>
<property name="debuglevel" value="source,lines,vars"/>
<property name="target" value="1.6"/>
<property name="source" value="1.6"/>
<property name="external-lib-dir" value="lib\poi-3.9" />
<property name="external-lib-dir2" value="lib\poi-3.9\lib" />
<property name="external-lib-dir3" value="lib\poi-3.9\ooxml-lib" />
<path id="ExcelDemo.classpath">
<pathelement location="bin"/>
</path>
<target name="init">
<mkdir dir="bin"/>
<copy includeemptydirs="false" todir="bin">
<fileset dir="src" excludes="**/*.launch, **/*.java"/>
</copy>
</target>
<target name="clean">
<delete dir="bin"/>
</target>
<target depends="clean" name="cleanall"/>
<target depends="build-subprojects,build-project" name="build"/>
<target name="build-subprojects"/>
<target depends="init" name="build-project">
<echo message="${ant.project.name}: ${ant.file}"/>
<javac debug="true" debuglevel="${debuglevel}" destdir="bin" source="${source}" target="${target}">
<src path="src"/>
<classpath refid="ExcelDemo.classpath"/>
</javac>
</target>
<target description="Build all projects which reference this project. Useful to propagate changes." name="build-refprojects">
<ant antfile="${ExcelSensitize.location}/build.xml" inheritAll="false" target="clean"/>
<ant antfile="${ExcelSensitize.location}/build.xml" inheritAll="false" target="build">
<propertyset>
<propertyref name="build.compiler"/>
</propertyset>
</ant>
</target>
<target description="copy Eclipse compiler jars to ant lib directory" name="init-eclipse-compiler">
<copy todir="${ant.library.dir}">
<fileset dir="${ECLIPSE_HOME}/plugins" includes="org.eclipse.jdt.core_*.jar"/>
</copy>
<unzip dest="${ant.library.dir}">
<patternset includes="jdtCompilerAdapter.jar"/>
<fileset dir="${ECLIPSE_HOME}/plugins" includes="org.eclipse.jdt.core_*.jar"/>
</unzip>
</target>
<target description="compile project with Eclipse compiler" name="build-eclipse-compiler">
<property name="build.compiler" value="org.eclipse.jdt.core.JDTCompilerAdapter"/>
<antcall target="build"/>
</target>
<target name="RunnerClass">
<java classname="runner.RunnerClass" failonerror="true" fork="yes">
<classpath refid="ExcelDemo.classpath"/>
</java>
</target>
<target name="jar" description="Create a jar for this project">
<manifestclasspath property="lib.list" jarfile="Test.jar">
<classpath refid="ExcelDemo.classpath" />
</manifestclasspath>
<jar jarfile="Test.jar" includes="*.class" basedir="bin">
<zipgroupfileset dir="${external-lib-dir}" includes="*.jar"/>
<zipgroupfileset dir="${external-lib-dir2}" includes="*.jar"/>
<zipgroupfileset dir="${external-lib-dir3}" includes="*.jar"/>
<manifest>
<attribute name="Class-Path" value="${lib.list}" />
<attribute name="Main-Class" value="runner.RunnerClass" />
</manifest>
</jar>
</target>
</project>
To make things simpler:
Create a separate sources jar for compilation. Then, have a separate compiled jar without the sources.
Don't include the third party jars. Instead, use Ivy with Ant. Ant will automatically download the required jars. In fact, I've see sources that just include the ivy.jar, so Ivy will automatically be configured when you unjar the sources. You type in ant, and everything just builds.
As an alternative, you can look at Maven which is how many projects are now packaged. In fact, if your jar is an open source project, you can probably host it on the OSS Maven repository. This way, no one even needs to manually download your compiled jar. If they want it, they configure their Maven project to do it for them.
i think the problem is that you use basedir="bin" in the your jar task. then path of your zipgroupfileset convert to bin/${external-lib-dir}

ant javac can't find other project compiled classes

The following is a simplified version of my ant script (it's got the project element etc).
I'm new to ant and unable to figure out why 'compileTests' doesn't compile, whereas 'compileFoo' does.
The error I get is 'package does not exist' as the class in the compileTests project can't find the compiled classes in the compileFoo project, even though they've compiled fine, i can see them on the file system and the path to them is listed in the classpath (i assume this is necessary?)
Clearly there is something basic I don't understand. Can someone please help by explaining?
<path id="build_classpath">
<fileset dir="${other_required_jars}" includes="**/*.jar" />
<fileset dir="${foo_build_location}" includes="**/*.class" />
</path>
<target name="compileFoo" description="compile">
<javac srcdir="${foo_source_directory}\test-src" includeantruntime="false" destdir="${foo_build_location}" includes="**/*.java" excludes="" debug="on" optimize="off" deprecation="on" verbose="on">
<classpath refid="build_classpath" />
</javac>
</target>
<target name="compileTests" description="compile">
<javac srcdir="${test_source_directory}\test-src" includeantruntime="false" destdir="${test_build_location}" includes="**/*.java" excludes="" debug="on" optimize="off" deprecation="on" verbose="on">
<classpath refid="build_classpath" />
</javac>
</target>
Your classpath is wrong. A classpath doesn't contain a set of .class files. It cntains a set of jar or directories, each containing the root of a package tree. So the classpath should simply contain one element : ${foo_build_location}:
<path id="build_classpath">
<fileset dir="${other_required_jars}" includes="**/*.jar" />
<pathelement location="${foo_build_location}"/>
</path>

ANT Generated jar: is it a namespace issue?

I have a Eclipse-Java-Project with an ANT-build-file. This build file exports a jar of the project without compiling it. So I only export the sources.
<target name="jar">
<mkdir dir="/jar"/>
<jar destfile="/jar/my_test_jarfile.jar" basedir="/src" />
</target>
I use this generated jar in another eclipse java project and set the path to the jar in the build-path-settings of the project. The problem is that eclipse says it cannot resolve the namespace of the imported classes of the jar.
If I export the jar manually by right clicking on the project and then "Export" and putting the jar to the build path of the other project, everything works fine and there are no errors. So the question is now, what am I doing wrong?
So here is my solution. It seems that you have to compile the source first and then pack it into a jar. I don't give a guarantee that this jar is exactly the same like the one you get from eclipse when you do the right click thing and export etc.
But it works for me, there are no namespace errors any longer. so here is a minimum version of my ant targets:
<project default="run" basedir=".">
<property name="src.dir" value="src" />
<property name="classes.dir" value="bin" />
<property name="build.dir" value="build" />
<path id="libs">
<fileset dir="lib">
<include name="*.jar"/>
</fileset>
<pathelement path="${basedir}\${classes.dir}"/>
</path>
<target name="run">
<antcall target="compile"/>
<antcall target="jar"/>
</target>
<target name="compile">
<javac debug="true" srcdir="${src.dir}" destdir="${classes.dir}" classpathref="libs" encoding="UTF-8" />
</target>
<target name="jar">
<jar destfile="${build.dir}/my_jar_file.jar" basedir="${classes.dir}">
</target>
</project>

ant + javac + properties

In an ant script, I would like to compile only certain packages e.g.
com.example.some_package.foo
com.example.some_package.bar
This is what I want to do, but it doesn't seem to work, because property substitution doesn't seem to work in the <include> tag:
<property name="ROOT_PKG_PATH" location="com/example/some_package"/>
...
<target name="compile-client" depends="init">
<javac srcdir="${srcDir}"
destdir="${buildDir}"
debug="on"
target="1.5"
classpathref="build.classpath">
<include name="${ROOT_PKG_PATH}/foo/**" />
<include name="${ROOT_PKG_PATH}/bar/**" />
</javac>
</target>
How can I get around this without having to retype the entire package path of each package?
Use the value attribute on the property, instead of location:
<property name="ROOT_PKG_PATH" value="com/example/some_package"/>
Example
I'm able to conditionally compile one of my java classes:
./src/some_package/demo1/Demo.java
./src/some_package/demo2/Demo.java
./build/classes/somepackage/demo1/Demo.class
./build.xml
Using the following ANT file:
<project name="demo" default="compile">
<property name="prop" value="some_package/demo1"/>
<target name="compile">
<mkdir dir="build/classes"/>
<javac srcdir="src" destdir="build/classes">
<include name="${prop}/**"/>
</javac>
</target>
</project>

Resources