I'm writing an Ant Task:
public class MyTask extends Task {
public void execute() {
....
}
}
Now I'm wondering whether it is possible for me to call a target that exists in another known xml file from within the above execute() method?
Something like:
public void execute() {
AntCaller.call("anotherBuildFile.xml", "someTarget");
}
You are on the right track. If you wanted to all another task from XML, you would use <ant> (since it is another file.) You can call a task from Java only if you have the .class file for it. Luckily, you do have the .class file for the Ant task itself so you can use the same technique as you would in a build xml:
Ant helper = new Ant();
helper.setTarget("someTarget");
helper.setAntFile("anotherBuildFile.xml");
helper.execute();
Related
I'm modifying someone else's code to implement a new functionality and I can't do it without changing the return of one of the functions. As I've said, this is not my code, so I can't change a single line of code.
The function itself is the following:
package me.Mohamad82.MineableGems.Core;
public class DropReader {
...
public DropReader() {}
public CustomDrop readCustomDrop(ConfigurationSection section, String mined, #Nullable String sectionNumber) {
...
}
}
And I'm trying to do something like this:
package com.rogermiranda1000.mineit;
public class DropReader extends me.Mohamad82.MineableGems.Core.DropReader {
public DropReader() {
super();
}
#Override
public CustomDrop readCustomDrop(ConfigurationSection section, String mined, #Nullable String sectionNumber) {
CustomDrop drop = super.readCustomDrop(section, mined, sectionNumber);
if (drop != null) {...}
return drop;
}
}
The problem is that I have no idea where to start. I can't change their code to call the other object, and I can't change the function call either.
The object is created every time (inside the functions that uses DropReader) so Reflection won't work, and searching I found something named Javassist but it won't work if the class is already loaded. I should be able to load by code before, but even with that I don't know if I'm approaching to the problem correctly, what do you think?
Edit: I'll try to explain better the situation. There's a class named Commands that runs the command new DropReader().readCustomDrop(section, mined, sectionNumber). The problem is that if section.getString("mine") != null I need to change the readCustomDrop return (I need to add an aditional property).
I can't change Commands's code, nor DropReader. Commands MUST get the modified object.
Edit2: It should work on both Java 8 and 17.
Probably the best way to do this is Java Instrumentation, but it was too complex for me so I did the following (assuming you have the .jar, and the .jar it's not already loaded):
Open the .jar as a Zip using ZipFile class
Send the .java to ClassFileToJavaSourceDecompiler (from JD-Core) and decompile it
Change the .java code
Compile the new code running javac with Runtime.getRuntime().exec (note: you'll probably need to add the dependencies using -classpath)
Add the compiled .java to the .jar
I'm creating a Gradle plugin with its corresponding objects for use in the Groovy DSL. I'm confused between the difference and extension and a task and how configuration should be passed between the two along with where the input and out annotations should be put. Here's my task
abstract public class UrlVerify extends DefaultTask {
#Input
abstract public Property<String> getUrl();
#TaskAction
public void verify() {
System.out.println(getUrl().get().toString());
}
}
Here's the extension
abstract public class UrlVerifierExtension {
abstract public Property<String> getUrl();
abstract public Property<Configuration> getConfiguration();
abstract public Property<Boolean> getIgnoreFailures();
public Set<ConflictCategory> getIncludeCategories() {
return includeCategories;
}
}
This plugin simply accepts a URL and validates it.
verification {
url = 'https://www.moooooereee.com/'
configuration = configurations.runtimeClasspath
ignoreFailures = false
}
I have the following plugin. I manually needed to pass the URL from the extension to the task and wondered if this is the correct way?
public class UrlVerifierPlugin implements Plugin<Project> {
#Override
public void apply(Project project) {
project.getPluginManager().apply(JavaLibraryPlugin.class);
UrlVerifierExtension extension = project.getExtensions().create("verification", UrlVerifierExtension.class);
UrlVerify verifyUrlTask = project.getTasks().create("verifyUrl", UrlVerify.class);
verifyUrlTask.getUrl().set(extension.getUrl());
}
}
Along with this, it is also unclear whether the #Input annotation belongs to the properties of the extension or the task?
You seem to have followed the examples from the Gradle documentation very precisely. This is the correct way to configure your custom tasks. The exact purpose of extensions is to have user-provided settings which are then consumed by your plugin to configure it and tasks.
Extensions are for the user to provide settings.
Tasks are for executing an action while Gradle is running.
The #Input annotation is used by Gradle to determine if the tasks needs to run. If the tasks has not run before, or if the input value has changed since the previous execution, then the tasks will run again.
Outputs declare some result produced by running the task. An example is a task that compiles Java files. The outputs would the the class files produced from the compilation process. If output files are modified or deleted by something other than the task that created them, then the task that created them is out-of-date, and Gradle will run it again.
Also, a task can declare the outputs of another task as its input. If task A creates some output files, and task B uses the outputs of task A as an input, then task B will be run when task A updates or creates its files.
In your case with the #Input annotation, my guess is that you do not want that in this case, because it tells Gradle that your tasks only needs to run once, then after that, only if the user updates the setting.
Say we are running mvn test.
I am wondering if there is a way to configure Maven to run some files before executing tests. In my case, I want to configure a library, but don't want to have to configure this library for every entrypoint in my app/tests. I am just looking to configure the lib for every mvn lifecycle hook which invokes a runtime.
Something like this:
#MavenRuntimeLifecycle
public class Whatever {
public void runtimeBegin(){
// right when the java process starts up
Mylib.configure("foo");
}
public void runtimeEnd(){
// right before the process shuts down
}
}
I assume this would be a Maven specific thing - not that it has to be in the same Java process as my server or tests etc.
Note that using Node.js, I would simply do it like so:
export class MyLib {
isConfigLoaded = false;
static loadConfig(){
// ...
}
static void run(){
if(!this.isConfigLoaded){
MyLib.loadConfig(require('../some/path/to/.mylib.config.js'));
this.isConfigLoaded = true;
}
this.doTheThing();
}
}
I could do the same thing with Java or Maven project, and just store a .java file in the resources directory. It's more manual, but it could be done.
Occasionally a slight modification to a Java source file like some additional explicit casts to help the compiler can improve compile time from 4 minutes to 3 seconds for a single java file (Especially in Java 8).
The problem is: In a large java project, how do you find which particular .java files are compiling slowly?
Is there a way to get Ant to time how long it takes to compile each individual .java file?
I think that this might be possible. Here's what I've found:
If you're using Java 8, you can register a Plugin with the compiler to add some additional functionality during compilation. The documentation has this to say about plugins:
It is expected that a typical plug-in will simply register a TaskListener to be informed of events during the execution of the compilation, and that the rest of the work will be done by the task listener.
So you can setup a plugin to use a TaskListener, and have the task listener log timestamps when class are being generated.
package xyz;
import com.sun.source.util.JavacTask;
import com.sun.source.util.Plugin;
public class TimestampPlugin implements Plugin {
#Override
public String getName() {
return "Timestamp_Plugin";
}
#Override
public void init(JavacTask task, String... strings) {
task.setTaskListener(new FileTimestampListener());
}
}
Documentation for TaskListener. A task listener is passed a TaskEvent, which has a Kind. In your case it sounds like you're interested in generation.
package xyz;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskListener;
import java.util.HashMap;
public class FileTimestampListener implements TaskListener {
HashMap<String, Long> timeStampMap = new HashMap<>();
#Override
public void started(TaskEvent taskEvent) {
if(TaskEvent.Kind.GENERATE.equals(taskEvent.getKind())) {
String name = taskEvent.getSourceFile().getName();
timeStampMap.put(name, System.currentTimeMillis());
}
}
#Override
public void finished(TaskEvent taskEvent) {
if(TaskEvent.Kind.GENERATE.equals(taskEvent.getKind())) {
String name = taskEvent.getSourceFile().getName();
System.out.println("Generated " + name + " over " + (System.currentTimeMillis() - timeStampMap.get(name)) + " milliseconds");
}
}
}
This is a simple example but it should be straightforward from here to set up something like a log file to store the information gathered. As you can see in the plugin's init function, arguments can be passed to the Plugin from the command line.
The plugin is configured by specifying it with the -Xplugin compiler argument. I'm not sure why but there doesn't appear to be any documentation on this page about it, but it can used by setting up a file called com.sun.source.util.Plugin (the FQ class name of the interface to implement) in your META-INF/services directory. So:
META-INF
|-- services
|-- com.sun.source.util.Plugin
And in that file list the FQ class name of your implementation of this class. So the file contents would be:
xyz.TimestampPlugin
In your Ant task you'll just need to specify a compiler flag -Xplugin:Timestamp_Plugin (note this is the name provided by the Plugin's getName() function). You'll also need to provide the compiled Plugin and runtime dependencies on the classpath, or the annotation processor path, if one is specified.
I need to have a jar file located in a main/assets directory within an Android project. It is important the jar file is located there.
With my main Android project is there a way to reference this jar file in my code and to use its classes?
To be clear I don't want to add the jar to the main project once compiled.
EDIT: I have tried the link below and it seems to load the Class file I've stated. But I'm strugging how to define constructor arguments for the dynamically loaded Class.
android-custom-class-loading-sample
EDIT2
Nearly there. I've confirmed the class is loaded from my classes.jar. I'm stuck instantiating it though.
On the licenseValidatorClazz.getConstructor line I get the error below. I'm guessing I'm missing something from my Interface file?
java.lang.NoSuchMethodException: [interface com.google.android.vending.licensing.Policy, interface com.google.android.vending.licensing.DeviceLimiter, interface com.google.android.vending.licensing.LicenseCheckerCallback, int, class java.lang.String, class java.lang.String]
public Class licenseValidatorClazz = null;
public LicenseValidator validator;
...
// Initialize the class loader with the secondary dex file.
DexClassLoader cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),
optimizedDexOutputPath.getAbsolutePath(),
null,
mContext.getClassLoader());
try {
// Load the library class from the class loader.
licenseValidatorClazz = cl.loadClass("com.google.android.vending.licensing.LicenseValidator");
validator = (LicenseValidator) licenseValidatorClazz.getConstructor(Policy.class,DeviceLimiter.class,LicenseCheckerCallback.class,int.class,String.class,String.class).newInstance(ddd, new NullDeviceLimiter(),
callback, generateNonce(), mPackageName, mVersionCode);
} catch (Exception exception) {
// Handle exception gracefully here.
exception.printStackTrace();
}
I have an Interface which contains the functions to pass to the loaded class.
public interface LicenseValidator
{
public LicenseCheckerCallback getCallback();
public int getNonce();
public String getPackageName();
public void verify(PublicKey publicKey, int responseCode, String signedData, String signature);
public void handleResponse(int response, ResponseData rawData);
public void handleApplicationError(int code);
public void handleInvalidResponse();
}
TO use an external jar to be associated with your application and use it during runtime, it needs to be in dalvik format since normal jars cannot work under dalvikVM.
Convert your files using the dx tool
using aapt cmd , add those classes.dex to your jar file.
Now this jar which contains files in dalvik format can be loaded into our project.
Here is a post which explains the procedure to accomplish it.
There are steps to accomplish this.
You have to make a copy of your JAR file into the private internal storage of your aplication.
Using the dx tool inside the android folder, you have to generate a classes.dex file associated with the JAR file. The dx tool will be at the location /android-sdks/build-tools/19.0.1 (this file is needed by the Dalvik VM, simply jar can not be read by the dalvik VM))
Using the aapt tool command which is also inside the same location, you have to add the classes.dex to the JAR file.
This JAR file could be loaded dynamically using DexClassLoader.
If you are making a JAR from any one your own library, you have to do this steps (1-4) every time when there is a change in your library source code. So you can automate this steps by creating a shell script(in Mac/Linux/Ubuntu) or batch scripts(in Windows). You can refere this link to understand how to write shell scripts.
Note : One situation for implementing this method is, when it is impossible to add the JAR files directly to the build path of core project and need to be loaded dynamically at run time. In normal cases the JAR files could be added to the build path.
please check this link for the detailed code and implementation.
How to load a jar file at runtime
Android: How to dynamically load classes from a JAR file?
Hope this helps!!
You should try out the Services API - java.util.ServiceLoader
You define a service interface and its implementations in your jar.
package com.my.project;
public interface MyService { ... }
public class MyServiceBarImpl implements MyService { ... }
public class MyServiceFooImpl implements MyService { ... }
Then you define the services contained within the jar file in the META-INF/services/ directory. For instance, in the file 'META-INF/services/com.my.project.MyService', you list the provider classes.
# Known MyService providers.
com.my.project.MyServiceBarImpl # The original implementation for handling "bar"s.
com.my.project.MyServiceFooImpl # A later implementation for "foo"s.
Then, in your main codebase, you can instantiate a MyService instance with the ServiceLoader:
for (MyService service : ServiceLoader.load(MyService.class)) {
//Perform some test to determine which is the right MyServiceImpl
//and then do something with the MyService instance
}
These examples are taken more-or-less straight from the API, although I've changed the package names to make them slightly less annoying to read.