I am attempting to create an android library that checks if internet is available before executing a method that has a custom annotation I have defined. I'm using AspectJ to accomplish this.
My annotation is as follows :
#Target({METHOD}) #Retention(RUNTIME)
public #interface InternetRequired {
}
Now for my aspect:
#Aspect
public class CilantroAspect
{
private static final String POINTCUT_METHOD = "execution(#com.cilantro.service.InternetRequired * *(..))";
private static final String POINTCUT_METHOD2 ="#annotation(com.cilantro.service.InternetRequired)";
;
#Pointcut(POINTCUT_METHOD2)
public void internetAnnotatedMethod() {
}
#Around("internetAnnotatedMethod()")
public void checkInternetConnectivity(ProceedingJoinPoint joinPoint) throws Throwable {
Log.v("Aspect","advice being triggered");
if (Cilantro.isConnected()) {
joinPoint.proceed();
} else {
Cilantro.Toast("Internet not available");
}
}
}
Snippet of my activity with the annotated method.
....
Cilantro.init(this);
test();
}
#InternetRequired
public void test()
{
Toast.makeText(this,"Test method",Toast.LENGTH_LONG).show();
}
When I run my android app the around advice is not being triggered. I tried using POINTCUT_METHOD and POINTCUT_METHOD2. Still no luck.
My android app is configured to use Aspect J so I know that's not the problem because if I make errors within the pointcut definitions it's detected..but just to be sure let me share.
Main build script
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
classpath 'com.uphyca.gradle:gradle-android-aspectj-plugin:0.9.+'
}
....
module containing Aspects
apply plugin: 'com.android.library'
apply plugin: 'android-aspectj'
....
The around advice wasn't being triggered because I was using annotations related to the Aspect from the app level while the aspect was contained in the library. For the aspects to be weaved into the app module at compile time I had to simply publish the library to maven (local for testing and maven central for distribution) and then include the library as a project dependency in a gradle plugin which contains the AspectJ weaving tasks.The plugin is then applied to the app's module.
Here's a snippet of my plugin that's written in groovy. I add my library that contains my Aspects and then run the weaving tasks on the app's module.
project.dependencies {
compile 'com.github.jd-alexander:flender-runtime:1.0'
// TODO this should come transitively
compile 'org.aspectj:aspectjrt:1.8.5'
}
variants.all { variant ->
variant.dex.doFirst {
String[] args = [
"-showWeaveInfo",
"-1.5",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)
]
log.debug "ajc args: " + Arrays.toString(args)
MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler);
For a complete example of how this is done you can check my library out on github https://github.com/jd-alexander/flender
Related
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
I am trying to implement a simple modular application with a self written plugin loader. For that I created a gradle multiproject build. The main project is going to be the started application. In the subproject spi is a module containing the Plugin interface, while in the subproject simple-plugin I wanted to create a plugin to test the plugin loading.
The Plugin interface is very simple:
package io.github.zeroneca.orgascreen.spi;
public interface Plugin {
String getId();
}
module-info.java:
module io.github.zeroneca.orgascreen.spi {
exports io.github.zeroneca.orgascreen.spi;
}
SimplePlugin.java:
package io.github.zeroneca.orgascreen.plugins.simple;
import io.github.zeroneca.orgascreen.spi.Plugin;
public class SimplePlugin implements Plugin {
private final String id = "SimplePlugin";
#Override
public String getId() { return id; }
}
module-info.java
module io.github.zeroneca.orgascreen.plugins.simple {
requires io.github.zeroneca.orgascreen.spi;
provides io.github.zeroneca.orgascreen.spi.Plugin with io.github.zeroneca.orgascreen.plugins.simple.SimplePlugin;
}
Now in the class PluginLoader I'm loading all jars from a directory and want to add all Plugins to a List:
package io.github.zeroneca.orgascreen;
// several imports
public class PluginLoader {
private static final List<Plugin> PLUGINS = new ArrayList<>();
public static void load() {
URL[] pluginURLs = getPluginURLs(); // returns all URLs of jar files in a directory
URLClassLoader urlClassLoader = URLClassLoader.newInstance(pluginURLs);
ServiceLoader<Plugin> serviceLoader = ServiceLoader.load(Plugin.class, urlClassLoader);
for (Plugin plugin : serviceLoader) {
PLUGINS.add(plugin);
}
}
// some more methods hidden here
}
module-info.java
module io.github.zeroneca.orgascreen {
requires io.github.zeroneca.orgascreen.spi;
uses io.github.zeroneca.orgascreen.spi.Plugin;
}
Now after building with gradle and copying the resulting jar simple-plugin-0.1.jar into the directory, I can see, that the URL of the file is in pluginJars but the List PLUGINS remains empty. I'm not sure what I'm doing wrong, online searches didn't help, I cannot see a difference to working solution, but the use of gradle.
gradle 6.5.1 is in use and modularity.inferModulePath is set to true.
Help is greatly appreciated!
Note: when adding META-INF/services/io.github.zeroneca.orgascreen.spi.Plugin to the jar of SimplePlugin it works, so something must be wrong with the module declarations. But I just can't see what.
I'm setting up a SpringBoot application, which should be executed on the commandline.
The application contains multiple CommandLineRunners, which run their specific logic if the needed option was passed by through the commandline execution.
I use Gradle as build-tool.
There was one initialization-method in every runner to parse the options and print out the help-statement if no option was passed by, which I had outsourced into an aspect-class.
And that's how the problem starts.
At first, I had already tried it successfully with Spring AOP, but this isn't an option, because I want to bind also private methods with my aspects.
So I must use the more powerful AspectJ to achieve this.
Unfortunately, AspectJ Load-Time Weaving isn't an option for me, because it requires to pass in jvm-arguments to the commandline when the jar is getting executed, which I want to avoid.
So from what I have seen now, there's only AspectJ Compile-Time Weaving and AspectJ Post-Compile-Time Weaving left.
The AspectJ Compile-Time Weaving looks the best fitting for me, but unfortunately one has to apply a plugin like the following from io.freefair.
Link: https://plugins.gradle.org/plugin/io.freefair.aspectj.compile-time-weaving
I've tried that and it had worked properly,
but my goal is to avoid adding external dependencies to my project, hence I'm trying to find a solution from the Spring or standard Java family.
Same for the Post-Compile-Time Weaving.
Every tutorial or answer concerning Compile-Time Weaving I've read was about applying a plugin.
I am wondering that I found no method to resolve my use case, except for the io.freefair plugin above.
The MainClass:
#SpringBootApplication
#ImportResource("classpath:spring/applicationContext.xml")
#EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class })
public class MainClass {
/**
* The main method.
*
* #param args the arguments
*/
public static void main(String[] args) {
SpringApplication.run(MainClass.class, args);
}
}
My aspect class looks like this:
#Aspect
public class ConfigurationsAspect {
private static final Logger logger = Logger.getLogger(ConfigurationsAspect.class);
#Autowired
private ConnectionWrapper connection;
#Autowired
private OptionHandler optionHandler;
/**
* Closes the connection to the Server if there were an initialized connection.
*/
#After("execution(void tools.cli.MainClass.main(*))")
public void closeConnection() {
if (this.connection.isConnected()) {
logger.debug("Closing connection");
this.connection.close();
}
}
/**
* Parses the options if not parsed before.
*
* #param jp The actual point of execution.
*/
#Before("execution(* tools.runners.tasks.*.run(*))")
public void parseOptions(JoinPoint jp) {
if (!this.optionHandler.isParsed()) {
String[] args = (String[]) jp.getArgs()[0];
this.optionHandler.parseParameters(args);
}
}
}
The involved beans are described in the applicationContext.xml like (Snippet):
<bean class="tools.aspects.ConfigurationsAspect" factory-method="aspectOf"/>
<bean class="tools.cli.options.OptionHandler"/>
<bean class="tools.connection.ConnectionWrapper" />
My build.gradle looks like this (Snippet):
plugins {
id 'org.springframework.boot' version '2.1.3.RELEASE'
id 'java'
}
apply plugin: 'application'
apply plugin: 'io.spring.dependency-management'
dependencies {
compile('commons-io:commons-io:2.5')
compile('commons-cli:commons-cli:1.4')
compile('org.springframework.boot:spring-boot-starter')
compile('org.springframework:spring-oxm')
testCompile('org.springframework:spring-oxm')
compile 'org.aspectj:aspectjrt:1.9.2'
compile 'org.aspectj:aspectjweaver:1.9.2'
}
I'm getting a following error message when I don't apply the io.freefair plugin.
ERROR org.springframework.boot.SpringApplication: 858 - Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'tools.aspects.ConfigurationsAspect#0' defined in class path resource [spring/applicationContext.xml]: No matching factory method found: factory method 'aspectOf()'. Check that a method with the specified name exists and that it is static.
AspectJ should create an instance of my ConfigurationsAspect and inserted the aspectOf()-method, which Spring is searching for if it had worked properly.
Leading to my Question:
Is there an opportunity to achieve the AspectJ Compile-Time (or Post-Compile-Time) Weaving without applying external dependencies?
If not then, even though I prefer Compile-Time Weaving, is there an opportunity to achieve the Load-Time Weaving without the obligation of passing in JVM arguments to run the jar correctly?
Thanks and Greetings.
I was learning how to use Micronaut to create REST services but something I can't see is wrong with my tests settings and whatever I change in my code or in gradle I always get an annoying 'Page not found' error when running the tests but not when the application is running normally.
I have a class named App in the package br.com.myproject. In this class I have only the main method, as follows:
public static void main(final String[] args) {
Micronaut.run(App.class);
}
In a subpackage br.com.myproject.controllers I have a class HelloController annotated with #Get("/hello") that should respond with a single "Hello, World!" text and it does normally when I access it through the browser:
package br.com.myproject.controllers;
#Controller("/hello")
public class HelloController {
#Get("/")
#Produces(MediaType.TEXT_PLAIN)
public String index() {
return "Hello, World!";
}
}
In the tests directory, I have the class HelloControllerTest that should assure my /hello endpoint works properly. But unfortunately, my test is failing with a PageNotFound exception.
My class test is as follows:
package br.com.myproject.controllers;
public class HelloControllerTest {
private static EmbeddedServer server;
private static HttpClient client;
#BeforeClass
public static void setupServer() {
server = ApplicationContext.run(EmbeddedServer.class);
client = server
.getApplicationContext()
.createBean(HttpClient.class, server.getURL());
}
#AfterClass
public static void stopServer() {
if (client != null)
client.stop();
if (server != null)
server.stop();
}
#Test
public void testHello() throws Exception {
HttpRequest<String> httpRequest = HttpRequest.GET("/hello");
String body = client.toBlocking().retrieve(httpRequest);
Assert.assertNotNull(body);
Assert.assertEquals(body, "Hello, World!");
}
}
And, for last, my gradle settings:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.3'
classpath 'io.spring.gradle:dependency-management-plugin:1.0.6.RELEASE'
classpath 'net.ltgt.gradle:gradle-apt-plugin:0.15'
}
}
plugins {
id 'io.spring.dependency-management' version '1.0.6.RELEASE'
id 'com.github.johnrengelman.shadow' version '4.0.3'
id 'application'
id 'java'
id 'net.ltgt.apt-idea' version '0.15'
}
group 'br.com.myproject'
version '1.0.0-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenLocal()
mavenCentral()
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
annotationProcessor 'io.micronaut:micronaut-inject-java:1.0.0'
compile 'io.micronaut:micronaut-http-client:1.0.0'
compile 'io.micronaut:micronaut-http-server-netty:1.0.0'
compile 'io.micronaut:micronaut-inject:1.0.0'
compile 'io.micronaut:micronaut-runtime:1.0.0'
compileOnly 'io.micronaut:micronaut-inject-java:1.0.0'
runtime 'ch.qos.logback:logback-classic:1.2.3'
testCompile 'junit:junit:4.12'
testCompile 'io.micronaut:micronaut-inject-java:1.0.0'
}
shadowJar {
mergeServiceFiles()
}
mainClassName = 'br.com.myproject.App'
Both my tests and my gradle settings were written based on a code example in Micronaut documentation (this one). And somehow the tests in the code example works properly.
For instance, these are what I tried before asking here:
Changed the versions of dependencies to match versions in code example: it didn't work;
Put all classes under same package (br.com.myproject): it didn't work;
Copy/paste the gradle settings of the example code into my gradle settings and changing the package names properly: it didn't work;
Print out the URL used in the test just to check if it is correct: it is correct, but still 'Page not found' is thrown.
Everything works fine, however, when I run the application and type the address into the browser.
I have googled the keywords "micronaut test page not found" and similars but I found no useful articles that helped me to fix this error.
Just to mention: I am not completely familiar with gradle configuration and thus I suspect I might be lacking something here.
Does anyone have any idea what I may be missing?
I appreciate any tip.
Thanks =)
See the project at https://github.com/jeffbrown/jonathansouzanotfound. I have pasted your code directly into that project (and added missing import statements) and the test passes.
$ ./gradlew clean test
> Task :compileJava
...
BUILD SUCCESSFUL in 4s
5 actionable tasks: 5 executed
#Get("/")
#Produces (MediaType.TEXT_PLAIN)
public String index(){
return "Hello World";
}
Referred from Jeff's comment and answered it here to make it more visible
Make sure annotation processing is enabled in the IDE if you running the tests through IDE.
What I'm trying to do is to write some Maven plugin which scans application classes looking for implementation of a particular interface (it might be classes with some annotation as well) and basis on the result generating some code. I've successfully implemented plugin running in the generate-sources phase and writing source code to the generated-sources directory.
The problem is with scanning classpath for the particular interface implementations/classes with some annotation.
I am using the Reflections library to scan classes in the following way:
private Set<Class< ? extends MyInterface >> scan(final String packageName) {
final Reflections reflections = new Reflections(packageName);
return reflections.getSubTypesOf(MyInterface.class);
}
Unfortunately, this method returns empty set. When I print my classpath in the class extending org.apache.maven.plugin.AbstractMojo (the same within which I'm using Reflections) I get the following result:
/home/pd5108/apache-maven-2.2.1/boot/classworlds-1.1.jar
The classes I want to find using Reflections exists in dependend JARs as well as in the module within which plugin is configured. Looking at the classpath printed out it seems that at this point (generate-sources phase) dependencies defined in maven all not available on classpath yet - probably they are added in the next phases. Is that true? Is there any other approach I can use?
Here is the way how classpath is printed out:
URL[] urls = ((URLClassLoader)sysClassLoader).getURLs();
for(int i=0; i< urls.length; i++) {
System.out.println(urls[i].getFile());
}
Required MOJO class fields:
/**
* The project currently being built.
*
* #parameter expression="${project}"
* #readonly
* #required
*/
private MavenProject project;
/** #parameter expression="${localRepository}" */
protected ArtifactRepository m_localRepository;
/**#parameter default-value="${localRepository}" */
private org.apache.maven.artifact.repository.ArtifactRepository
localRepository;
/** #parameter default-value="${project.remoteArtifactRepositories}" */
private java.util.List remoteRepositories;
/** #component */
private org.apache.maven.artifact.factory.ArtifactFactory artifactFactory;
/** #component */
private org.apache.maven.artifact.resolver.ArtifactResolver resolver;
Resolution of all dependencies JARs:
final List<Dependency> dependencies = project.getDependencies();
for (Dependency d : dependencies) {
final Artifact artifact =
artifactFactory.createArtifactWithClassifier(d.getGroupId(),
d.getArtifactId(), d.getVersion(), d.getType(),
d.getClassifier());
try {
resolver.resolve(artifact, remoteRepositories,
localRepository );
} catch (ArtifactResolutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ArtifactNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
File artifactFile = artifact.getFile();
System.out.println(artifactFile.getAbsolutePath());
}
And now we need to scan these JARs using reflection API looking for the appropriate classes.
At this point I think that there's no other way, since I work in generate-sources phase and artifact values for the next phases are not computed yet.
There are artifact dependencies defined in <dependencies> section and plugin dependencies defined under <plugin><dependencies>.
Plugin dependencies are added to the classpath while I am not sure about the artifact dependencies. Did you try to add your plugin dependencies under the <plugin><dependencies>?
Might be quicker/safer/easier to just have the plugin config list the classes you want to generate code based on. Then you just add it to the pom and you're done. No reflection needed, and it'd certainly speed up the running of the plugin.