Logging in custom ant tasks - java

I'm creating a custom ant task, which performs an IO tasks based on the user received param(like an file write/append)
I wanted to write the task so as if the developer using it in the ant task runs it with a -v or -d flag, will output more,
I'm wondering how are the core ant tasks doing it. Are they checking the output level before printing to console or is it just done by using java.util.logging.Logger

Follow this tutorial.
Extract :
Integration with TaskAdapter
Our class has nothing to do with Ant. It extends no superclass and
implements no interface. How does Ant know to integrate? Via name
convention: our class provides a method with signature public void
execute(). This class is wrapped by Ant's
org.apache.tools.ant.TaskAdapter which is a task and uses reflection
for setting a reference to the project and calling the execute()
method.
Setting a reference to the project? Could be interesting. The Project
class gives us some nice abilities: access to Ant's logging facilities
getting and setting properties and much more. So we try to use that
class:
import org.apache.tools.ant.Project;
public class HelloWorld {
private Project project;
public void setProject(Project proj) {
project = proj;
}
public void execute() {
String message = project.getProperty("ant.project.name");
project.log("Here is project '" + message + "'.", Project.MSG_INFO);
} }
[...]

Related

Getting the resource path of the java project from a custom gradle plugin

Trying to create a custom gradle plugin in java, how do i get the resources path from inside the task class?
public class MyCustomPlugin implements Plugin<Project> {
#Override
public void apply(Project project) {
project.getTasks().register("doStuff", CustomTask.class);
}
}
public class CustomTask extends DefaultTask {
// How do I get java project resources dir from here?
#Inject
public CustomTask(ProjectLayout projectLayout) {
directoryProperty = projectLayout.getBuildDirectory();
}
#TaskAction
public void execute() {
...
}
}
I would recommend to not get the directory inside the task, because the plugin that provides it might not be applied. Instead I would do it from within your plugin that registers the task, this way you can also ensure that the necessary plugin is actually applied. Gradle will display an error if the task is used without a value being assigned to the input that explains that nothing was assigned.
With the kotlin-dsl:
#CacheableTask
abstract class CustomTask : DefaultTask() {
#get:InputFiles
abstract val resources: FileCollection
//...
}
I cannot answer if #InputFiles is the right annotation for your use case, because I don't know what you want to do with the resource. Refer to the Gradle documentation for more information on the available annotations, and what they do.
plugins {
java
}
tasks.register<CustomTask>("customTask") {
resources.set(sourceSets.main.map { it.resources })
}
Notice the map {} which ensures that our task has a dependency on the processResources task, this is done automatically for us because we stick to the provider API of Gradle for everything.
Note that the resources are by default in one directory, but they don't have to be. This is why the resources are defined as SourceDirectorySet and not as Provider<Directory>. The same is true for anything that originates from the SourceSetContainer. It is easier to explain with Java source code: imagine you have Java and Kotlin, then you will have src/main/java and src/main/kotlin, hence, 2 directories. The former will have a **/*.java include filter, whereas the latter has a **/*.kt includes filter. If we just want to get all sources then we use sourceSets.main.map { it.java.sourceDirectories }, and if we want to get one of both it gets complicated. 😝
First, you'd have to ensure this is a Java project: either applying the "java" plugin from your plugin (project.getPluginManager().apply("java")), or only registering the task when the "java" plugin has been applied by the user (project.getPluginManager().withPlugin("java", ignored -> { project.getTasks().register(…); });).
You could then get the resources from the main source set:
SourceSetContainer sourceSets = project.getExtensions().getByType(SourceSetContainer.class);
// Use named() instead of get() if you prefer/need to use providers
SourceSet mainSourceSet = sourceSets.get(SourceSet.MAIN_SOURCE_SET_NAME);
SourceDirectorySet resources = mainSourceSet.getResources();
BTW, the best practice is to have tasks only declare their inputs and outputs (e.g. I need a set of directories, or files, as inputs, and my outputs will be one single file, or in one single directory) and have the actual wiring with default values be done by the plugin.
You could have the plugin unconditionally register the task, then conditionally when the "java" plugin is applied configure its inputs to the project resources; or conditionally register the task or unconditionally apply the "java" plugin, as I showed above.
You can access the sources through the project.sourceSets.
#Inject
public CustomTask(Project project) {
directoryProperty = project.projectLayout.getBuildDirectory();
sourceSet = project.sourceSets.main
}
See also the reference documentation here: https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_project_layout

Creating an 'Empty Shell' Jar

I'm looking on guidance on how I can essentially create an 'empty shell' jar with maven. The idea is I have a java project, and I want to export the my.project.api classes (with package) into its own jar without saving the methods / constructors actual code inside.
For example, lets say I have the following:
public class Test {
public void doSomething(String message) {
System.out.println(message);
}
}
I want to export a separate jar which would keep its package declaration, and export as:
public class Test {
public void doSomething(String message) {}
}
The reasoning for this is the project itself is exclusive, but I want to allow other developers to make their own integrations without the need of the physical product / project. This way by them hooking into say my.project.api.Test, they'd be able to see the methods and do as they wish.
Hopefully this clarifies enough, it would export as a separate jar maybe as 'MyProject-API.jar' or something.
Thanks!
This very much looks like a use case for interfaces.

cleanup after execution of all tests ( spock framework )

i'm looking for a solution that allow me to handle the setup and the cleanup of my test environment at the launch and the end of my test framework execution.
The setup is not a problem but the cleanup imply to know when the test framework has finished to work or the index of the current test in execution queue.
Has someone a solution to implement this?
You can use org.spockframework.runtime.extension.IGlobalExtension to achieve this, as Spock extensions have callbacks for both BEFORE all specs start, and AFTER all specs end.
public interface IGlobalExtension {
void start();
void visitSpec(SpecInfo spec);
void stop();
}
So, implement stop() in your case to do whatever you need to do.
Spock finds extensions via Java's ServiceLoader, so make sure to add a META-INF/services file (pre-Java9) or declare it in your module-info.java file (post Java9), as explained here: http://spockframework.org/spock/docs/1.1/extensions.html#_writing_custom_extensions
One solution is to create an abstract class that all your specs extend:
class AbstractSpec extends Specification
then inside AbstractSpec find out the classes that are going to be run(for example if you're using spring framework):
private static final synchronized TEST_CLASSES = new ClassPathScanningCandidateComponentProvider(false).with {
addIncludeFilter(new AssignableTypeFilter(AbstractSpec.class))
findCandidateComponents("com/example/test").findAll { it.beanClassName != AbstractSpec.class.canonicalName }
.collect { Class.forName(it.beanClassName) }
}
then inside AbstractSpec do the actual clean up after all classes are run:
def synchronized cleanupSpec() {
TEST_CLASSES.remove(getClass())
if (TEST_CLASSES.isEmpty()) {
// clean up here
}
}
The problem with this solution is that if you run a subset of tests or classes in your IDE; you might have all the test classes in the classpath so TEST_CLASSES won't be emptied so the clean up won't execute.

Correct way to define a Gradle plugin property extension with Java?

I'm trying to create a Gradle plugin in Java that has property extensions (not conventions, as this is apparently the old, wrong way). For the record, I'm working with Gradle 1.6 on a Linux machine (Ubuntu 12.04).
I've gotten as far as figuring out that the this should be done in the Plugin class definition. Here is A way of adding an extension. Create an extension class that contains your properties:
public class MyPluginExtensions {
File sourceDir;
File outputDir;
public MyPluginExtensions(Project project) {
this.project = project;
sourceDir = new File(project.getProjectDir(), "src");
outputDir = new File(project.getBuildDir(), "out");
}
}
Now add these extensions to the project in the main plugin class:
public class MyPlugin implements Plugin<Project> {
#Override
public void apply(Project project) {
Map<String,Object> taskInfo = new HashMap<String,Object>();
taskInfo.put("type", MyPluginTask.class);
taskInfo.put("description", "Generates blah from blah.");
taskInfo.put("group", "Blah");
Task myPluginTask = project.task(taskInfo, "myPluginTask");
// Define conventions and attach them to tasks
MyPluginExtensions extensions = new MyPluginExtensions(project);
myPluginTask.getExtensions().add(
"sourceDir",
extensions.sourceDir);
myPluginTask.getExtensions().add(
"outputDir",
extensions.outputDir);
}
}
This approach, however, doesn't seem to be correct. A new project property is shows up in the project.ext namespace. I expect to be able to address the plugin extensions as:
in my build.gradle:
myPluginTask.sourceDir = file('the/main/src')
myPluginTask.outputDir = file('the/output')
However, when I put such things in a gradle script that uses my plugin and try to set this property, gradle tells me I can't set it:
* What went wrong:
A problem occurred evaluating script.
> There's an extension registered with name 'sourceDir'. You should not reassign it via a property setter.
So, what's the right way to add property extensions for a task in a Java-based Gradle plugin?
EDIT:
Based on some other SO posts, I tried just adding my extensions object in one shot:
// first attempt:
//myPluginTask.getExtensions().add("sourceDir", extensions.sourceDir);
//myPluginTask.getExtensions().add("outputDir",extensions.outputDir);
// second attempt
myPluginTask.getExtensions().add("myPluginTask", extensions);
This appears to work. However, Gradle is now complaining that I've added a dynamic property:
Deprecated dynamic property: "sourceDir" on "task ':myPluginTask'", value: "/Users/jfer...".
So, again, what's the right way to add a plugin extension property?
EDIT 2
So, taking yet another shot at this, I'm adding the extension to the project object and using the create method instead:
// first attempt:
//myPluginTask.getExtensions().add("sourceDir", extensions.sourceDir);
//myPluginTask.getExtensions().add("outputDir",extensions.outputDir);
// second attempt
// myPluginTask.getExtensions().add("myPluginTask", extensions);
// third attempt
project.getExtensions().create("myPluginTask", MyPluginExtensions.class, project);
However, this fails for a couple of reasons:
Creating a properties extension with the same name ("myPluginTask") as the task creates a collision between the task name and extension name, causing the task to disappear from gradle's perspective (and throw oblique errors, such as "No such property: dependsOn for class ...MyPluginExtensions").
If I provide a name that does not collide with a task name (e.g., "myPluginPropExt"), the create() method works, but DOES NOT add the extension in its own namespace as expected (e.g., project.myPluginPropExt.propertyName and instead adds it in the project namespace (e.g., project.propertyName) which is not correct and causes Gradle to throw a bunch of "deprecated dynamic property" warnings.
So here is a solution to my problem:
public class MyPlugin implements Plugin<Project> {
#Override
public void apply(Project project) {
Map<String,Object> taskInfo = new HashMap<String,Object>();
taskInfo.put("type", MyPluginTask.class);
taskInfo.put("description", "Generates blah from blah.");
taskInfo.put("group", "Blah");
Task myPluginTask = project.task(taskInfo, "myPluginTask");
// Define conventions and attach them to tasks
MyPluginExtensions extensions = new MyPluginExtensions(project);
// the magic extension code:
project.getExtensions().add("myPluginName", extensions);
}
}
Now I can set a value for one of the extension properties in my gradle.build file like so (and I don't get a warning about adding deprecated dynamic properties):
myPluginName.sourceDir = file('the/main/src')
The final trick is to get this value in my Plugin's task:
public class MyPluginTask extends DefaultTask {
#TaskAction
public void action() {
MyPluginExtensions extensions = (MyPluginExtensions) getProject()
.getExtensions().findByName("myPluginName");
System.out.println("sourceDir value: " + extensions.sourceDir);
}
}
This works, but what annoys me about this solution is that I want to be able to put the extension properties in the same namespace as the task (e.g., myPluginTask.sourceDir) which I have seen in groovy-based plugins, but this apparently is not supported or just doesn't work.
In the meantime, hope this helps someone else.
The code is adding an extension to a task (rather than a project), which is rarely useful. After that, it tries to set myPluginTask.sourceDir = file('the/main/src'), which isn't possible because the extension was just registered under that same name.
When your task and your extension have the same name, you can do this in your build.gradle:
myPluginTask.sourceDir = file('the/main/src')
project.tasks.myPluginTask.dependsOn clean

How to run ajc from Java?

I'm trying to run ajc compiler from Java (not from Maven or Ant!). The question is which Maven dependency do I need and which class is an entry point? The best option I have now is org.aspectj.tools.ajc.Main from org.aspectj:aspectjtools:1.7.2. Am I right?
Yes. In your Java project you need aspectjrt.jar (for the runtime) and aspectjtools.jar (for the compiler) on the class path. Then you can build an AspectJ project and create a JAR file containing aspects and classes like this:
import org.aspectj.tools.ajc.Main;
public class AjcRunner {
public static void main(String[] args) throws Exception {
String[] ajcArgs = {
"-sourceroots", "c:\\my\\aspectj_project\\src",
"-outjar", "my_aspects.jar"
};
Main.main(ajcArgs);
}
}
Afterwards you can test the result on the console like this, assuming you have a class Application with a main method:
java -cp C:\path\to\aspectjrt.jar;my_aspects.jar Application

Categories