Custom Gradle Plugin executed only once in Multiproject build - java

I have written a gradle plugin in Java, which I want to use in a multiproject gradle setup. The task which comes with the plugin is only executed once and I don't understand why.
The structure of the project looks like this:
Root project
|- settings.gradle
|- build.gradle
|- project1
| |- build.gradle
|- project2
| |- build.gradle
|- project3
| |- build.gradle
...
The root build.gradle looks like this
buildscript {
repositories {
jcenter()
maven { url "https://repo.spring.io/plugins-snapshot" }
maven { url "https://repo.spring.io/plugins-release" }
maven{
url = "https://my-personal-nexus.de/content/repositories/release/"
}
....
dependencies {
....
classpath "de.mystuff:myCustomPlugin:1.0.0"
}
}
}
subprojects {
apply plugin: "myCustomPlugin"
....
}
The plugin "myCustomPlugin" provides a Task called myTask. Since the Plugin is applied in die subprojects part of the build.gradle, I assumed that when I call:
./gradlew myTask
the task would be executed for project1, project2, project3.
In reality the task only executes in project1 and not in project2 or project3. If I call
./gradlew -p "project2" myTask
the task is executed in project 2 just fine.
Any hints on why the task is not executed for every subproject?
When I call
./gradlew tasks --all
The result shows the task for every subproject.
Thanks!
EDIT:
The Plugin code of the Task:
public class MyGeneratorTask extends DefaultTask {
#TaskAction
public void genMyCode() throws Exception {
MyGeneratorExtension extension = getProject().getExtensions().getByType(MyGeneratorExtension.class);
MyGeneratorConfig.setPath(extension.getPath());
MyGeneratorConfig.setGendir(extension.getGendir());
MyCodeGeneratorApplication.generateMyClasses();
}
}
And the plugin definition itself:
public class MyPlugin implements Plugin<Project> {
static final String GEN_TASK_CONFIG = "myConfig";
static final String GEN_TASK_NAME = "myTask";
#Override
public void apply(Project target) {
target.getExtensions().add(GEN_TASK_CONFIG, MyGeneratorExtension.class);
target.getTasks().create(GEN_TASK_NAME, MyGeneratorTask.class);
}
}
And the code of the extension:
import lombok.Data;
import lombok.EqualsAndHashCode;
#Data
#EqualsAndHashCode(callSuper = false)
public class MyGeneratorExtension {
private String path;
private String gendir;
}

Solution:
It Seems that gradle does not like it when you call a static Method in a Task.
After I switched to class-initialization it worked.
Thanks #M.Ricciuti

Related

Spring Profiles in Cucumber Testing and Gradle

I have an automation test suite that is using Cucumber and Spring running with a Gradle build. I am facing an issue where depending on environment the property file used will be different. I know there is a way to use Profile annotations to specify ContextConfiguration but I can not find any resources on how to get started on this.
I believe I have to create a ProfileManager Class but I am not sure how to connect this to the #ContectConfiguration annotating my Cucumber Step Definitions and the Property Sources in my Configuration Class.
Here are some of my code samples.
StepDefintions.java
#CucumberContextConfiguration
#ContextConfiguration(classes = {ApplicationConfiguration.class, DynamoDBConfiguration.class})
#TestPropertySource("classpath:application-test-local.properties")
//#TestPropertySource("classpath:application-test.properties")
public class StepDefinition {
#Given("I set stuff up")
public void myGivenStep() {
app.assertTheThings()
#When("I do things")
public void myWhenStep() {
app.checksStuff();
}
.....
}
ApplicationConfiguraton.java
#Configuration
public class ApplicationConfiguration {
#Autowired
private Environment env;
.....
}
build.gradle
{
.....
task cucumber() {
dependsOn assemble, testClasses
doLast {
javaexec {
main='io.cucumber.core.cli.Main'
classpath=configurations.cucumberRuntime + sourceSets.main.output + sourceSets.test.output
def env = System.getProperty('testingENV')
println("Testing in " + env.toUpperCase() + ' Enviornment')
systemProperties = System.properties
def feature = ''
if (project.hasProperty('feature')) {
def arg1 = project.getProperty('feature')
feature = arg1
}
args = [
'--plugin', 'pretty',
'--plugin', 'json:target/cucumber-report.json',
'--glue', 'com.testautomation.cucumber',
"src/test/resources/com/testautomation/cucumber/feature/${feature}"
]
}
}
}
}
Basically I would like to use my testingENV system prop to define the profile and change which property source is being used without having to comment lines out everytime I am switching envs.

Gradle 'implementation' dependency isn't compile with correct code

I have a compilation issue with implementation dependency when compiling with Gradle the following project that contains of 3 modules:
test-impl
test-lib
my-test
My error:
/Users/igor/projects/my-test/src/main/java/MyTest.java:4: error: cannot access TestImpl
lib.foo("");
^
class file for TestImpl not found
It is compiled when I change implemention to api or if I rename any of foo methods in TestLib class.
Gradle 6.0.1, Java 1.8.0_271-b09, OSX
Doesn't it look like a bug? Where to report?
All build.gradle files:
Module test-impl build.gradle:
apply plugin: 'java-library'
Module test-lib build.gradle:
apply plugin: 'java-library'
dependencies {
implementation project(':test-impl')
}
Module my-test build.gradle:
apply plugin: 'java-library'
dependencies {
api project(':test-lib')
}
All source codes:
TestImpl.java in test-impl module:
public class TestImpl {
}
TestLib.java in test-lib module:
public class TestLib {
public void foo(String s) {
}
private void foo(TestImpl impl) {
}
}
MyTest.java in my-test module:
public class MyTest {
public void test() {
TestLib lib = new TestLib();
lib.foo("");
}
}

how to capture Build Info using Gradle and Spring Boot

I am trying to get access to build info values such as version in my Java main application using Spring Boot and Gradle.
I can't find any documentation / examples of how to configure the
build.gradle
application.yml (if required)
Java main class
could someone please help with a small code example for the above files.
In my build.gradle file I will have the version entry, so how to get this into Java main class using Spring Boot and Gradle.
build.gradle
version=0.0.1-SNAPSHOT
I've tried adding
build.gradle
apply plugin: 'org.springframework.boot'
springBoot {
buildInfo()
}
but the buildInfo() isn't recognised as a keyword in Intellij
In my Java main class I have the following:
public class MyExampleApplication implements CommandLineRunner {
#Autowired
private ApplicationContext context;
public static void main(String[] args) {
SpringApplication.run(MyExampleApplication.class, args);
}
#Override
public void run(String[] args) throws Exception{
Environment env = (Environment) context.getBean("environment");
displayInfo(env);
}
private static void displayInfo(Environment env) {
log.info("build version is <" + env.getProperty("version")
}
But when I run this - the output from env.getProperty("version") is showing as null.
Spring Boot auto-configures a BuildProperties bean with the information generated by buildInfo().
So to get the information use context.getBean(BuildProperties.class).getVersion();.
I managed to get it working now - using the help pointer that Vampire gave below and some other sources. The key was adding the actuator class to the project dependency.
Note: Intellj doesn't seem to recognise buildInfo() in the springBoot tag - but it does run ok - so don't be put off.
build.gradle
buildscript {
ext {
springBootVersion = '1.5.6.RELEASE'
gradleVersion = '3.3'
}
repositories {
mavenLocal()
maven { url "http://cft-nexus.ldn.xxxxxxxxx.com:8081/nexus/content/groups/public/" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'application'
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'org.springframework.boot'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
springBoot{
buildInfo {
additionalProperties = [
'foo': 'bar'
]
}
}
compile "org.springframework.boot:spring-boot-starter-actuator"
MyExampleApplication
#Slf4j
#EnableIntegration
#EnableLoaderApplication
#SpringBootApplication
#EnableDiscoveryClient
public class MyExampleApplication implements CommandLineRunner {
private static final String SYSTEM_NAME_INFO = "My Example Application";
private static final String VERSION="0.0.1";
#Autowired
private ApplicationContext context;
public static void main(String[] args) {
SpringApplication.run(MyExampleApplication.class, args);
}
#Override
public void run(String[] args) throws Exception{
BuildProperties buildProperties = context.getBean(BuildProperties.class);
displayInfo(buildProperties);
}
private static void displayInfo(BuildProperties buildProperties) {
log.info("build version is <" + buildProperties.getVersion() + ">");
log.info("value for custom key 'foo' is <" + buildProperties.get("foo") + ">");
}
}
Screenshot of Console output when running the Application in Intellj
pasting the output as well incase the image doesn't display
> 2017-11-14 14:35:47.330 INFO 22448 --- [ main]
> o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase
> 2147483647 2017-11-14 14:35:47.448 INFO 22448 --- [ main]
> s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s):
> 8780 (http) 2017-11-14 14:35:47.451 INFO 22448 --- [ main]
> c.u.o.metrics.MyExampleApplication : build version is
> <0.0.1-SNAPSHOT> 2017-11-14 14:35:47.451 INFO 22448 --- [
> main] c.u.o.myexample.MyExampleApplication : value for custom key
> 'foo' is <bar>
UPDATE
After reviewing this with my colleague we decided to move the some of the build properties, e.g. version (above) out of the build.gradle file and into gradle.properties file. This gives us a cleaner separation for build details and properties. When you run Gradle build it automatically pulls these values in and they are available in the BuildProperties bean in the Java main class as shown in the example above.
gradle.properties
group=com.xxx.examplesource
version=0.0.1-SNAPSHOT
gradleVersion=3.3
add following to your Gradle script.It inserts the version into the jar manifest correctly, as shown here:
version = '1.0'
jar {
manifest {
attributes 'Implementation-Title': 'Gradle Quickstart',
'Implementation-Version': version
}
}
Your code will be able to pick up the version from that jar manifest file:
public class BuildVersion {
public static String getBuildVersion(){
return BuildVersion.class.getPackage().getImplementationVersion();
}
}
Refer the link below for more details: https://github.com/akhikhl/wuff/wiki/Manifest-attributes-in-build.gradle
Easy way to get version number in Spring boot
#Controller
#RequestMapping("/api")
public class WebController {
private final BuildProperties buildProperties;
public WebController(BuildProperties properties) {
buildProperties = properties;
}
#GetMapping("/test")
public String index() {
System.out.println(buildProperties.getVersion());
System.out.println(buildProperties.getArtifact());
System.out.println(buildProperties.getGroup());
System.out.println(buildProperties.getName());
System.out.println(buildProperties.getTime());
return "index";
}
}
And dont forget generate application-build.properties
springBoot {
buildInfo()
}

Cannot resolve symbol generated AutoValue_Foo class inside pure java module in android project

I am trying to use AutoValue in a module that is written in pure Java that acts the domain module for my Android application. My application is composed of 3 layers, presentation, domain and data. Presentation and Data both have android dependencies, but domain does not.
Here is the AutoValue class data implementation :
import com.google.auto.value.AutoValue;
import org.jetbrains.annotations.Nullable;
import java.util.Date;
#AutoValue
public abstract class Trip {
public abstract int id();
public abstract String name();
public abstract int totalDistance();
#Nullable public abstract Date startDate();
#Nullable public abstract Date endDate();
public static Builder builder() {
return new AutoValue_Trip.Builder().totalDistance(0);
}
#AutoValue.Builder
public abstract static class Builder {
public abstract Builder id(int id);
public abstract Builder name(String name);
public abstract Builder totalDistance(int totalDistance);
public abstract Builder startDate(Date startDate);
public abstract Builder endDate(Date endDate);
public abstract Trip build();
}
}
Android Studio cannot find the generated AutoValue_Trip class, so it marks it as an error however, the project builds and runs just fine.
Here is my build.gradle of the domain module
buildscript {
repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath 'me.tatarka:gradle-retrolambda:3.2.3'
classpath "net.ltgt.gradle:gradle-apt-plugin:0.12"
}
}
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: "net.ltgt.apt"
apply plugin: 'me.tatarka.retrolambda'
//noinspection GroovyUnusedAssignment
sourceCompatibility = 1.8
//noinspection GroovyUnusedAssignment
targetCompatibility = 1.8
configurations {
provided
}
sourceSets {
main {
compileClasspath += configurations.provided
}
}
dependencies {
def domainDependencies = rootProject.ext.domainDependencies
def domainTestDependencies = rootProject.ext.domainTestDependencies
apt domainDependencies.autoValue
compileOnly domainDependencies.autoValue
provided domainDependencies.javaxAnnotation
compile domainDependencies.javaxInject
compile domainDependencies.rxJava
compile domainDependencies.arrow
testCompile domainTestDependencies.junit
testCompile domainTestDependencies.mockito
testCompile domainTestDependencies.assertj
}
I have tried using a sourceSet as follows to add the generated build files and folders to the source path:
main {
java {
srcDirs += 'src/../build/generated'
}
}
But then I was getting compileJava errors.
Has anyone ran into this issue and how did you correct it? Additional info will be provided if necessary.

Dagger2 component is not created

Here is the code....
build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.1'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
MainActivity
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import javax.inject.Inject;
public class MainActivity extends AppCompatActivity {
#Inject
SampleModule sampleModule;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((SampleApp)getApplication()).getSampleComponent().inject(this);
sampleModule.simpleModel.setX(10);
}
}
SampleApp
import android.app.Application;
/**
* Created by pavan on 4/17/2017.
*/
public class SampleApp extends Application {
private SampleComponent sampleComponent;
#Override
public void onCreate() {
super.onCreate();
sampleComponent = DaggerSampleComponent.builder()
.sampleModule(new SampleModule(this))
.build();
}
public SampleComponent getSampleComponent(){
return sampleComponent;
}
}
SampleComponent
import javax.inject.Singleton;
import dagger.Component;
/**
* Created by pavan on 4/17/2017.
*/
#Singleton
#Component(modules = {SampleModule.class})
public interface SampleComponent {
void inject(MainActivity activity);
}
SampleModule
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
/**
* Created by pavan on 4/17/2017.
*/
#Module
public class SampleModule {
SimpleModel simpleModel;
SampleApp sampleApp;
public SampleModule(SampleApp sampleApp){
this.sampleApp = sampleApp;
}
#Provides
#Singleton
public SampleApp provideApplication(){
return sampleApp;
}
#Provides
#Singleton
public SimpleModel provideSimpleModelObj() {
return new SimpleModel();
}
}
SimpleModel
public class SimpleModel {
private int x;
private int y;
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
Gradle Log
Error:(13, 10) error: com.uvr.organizer.myfilesorganizer.FileOrganizerModule cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method.
com.uvr.organizer.myfilesorganizer.FileOrganizerModule is injected at
com.uvr.organizer.myfilesorganizer.LoginActivity.appScope
com.uvr.organizer.myfilesorganizer.LoginActivity is injected at
com.uvr.organizer.myfilesorganizer.AppComponent.inject(loginActivity)
Error:(14, 10) error: com.uvr.organizer.myfilesorganizer.FileOrganizerModule cannot be provided without an #Inject constructor or from an #Provides- or #Produces-annotated method.
com.uvr.organizer.myfilesorganizer.FileOrganizerModule is injected at
com.uvr.organizer.myfilesorganizer.MainActivity.appScope
com.uvr.organizer.myfilesorganizer.MainActivity is injected at
com.uvr.organizer.myfilesorganizer.AppComponent.inject(mainActivity)
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
> Compilation failed; see the compiler error output for details.
Information:BUILD FAILED
Information:Total time: 8.136 secs
Information:3 errors
Information:0 warnings
Information:See complete output in console
All my problem is about the file "DaggerSampleComponent" which is not at all getting created unless i delete all inject in interface.
Java version : 1.8
The same code seems working on my office Mac but not in my Windows. struggling a lot for this.
Can someone help me!!!
Thanks in advance.
When something goes wrong with your Dagger 2 setup, you will get a compile-time message in the Gradle console when you try and build (it's in the bottom right corner of Android Studio). The message will tell you what is wrong and give you a clue for how to fix it.
In your case, it looks like you have something like this inside your LoginActivity:
#Inject FileOrganiserModule fileOrganiserModule;
protected void onCreate(Bundle savedInstanceState) {
Dagger 2 modules and components are like scaffolding that helps you to request injection for the dependencies in your project. You normally shouldn't request injection of them by putting #Inject annotations on them.
If you have to create your module with a reference of the current Activity, you normally just create an instance of the module using the constructor:
void injectMembers() {
DaggerLoginComponent.builder().loginModule(new LoginModule(this));
}
Or you can use the new dagger.android classes to do that for you. A good example project for you to follow is in the Google Android Architecture Blueprints repo here

Categories