Gradle standalone plugin unknown property when loaded from github repository - github

I've created a simple standalone Gradle plugin in Java and a Maven repository on GitHub for publishing the plugin. Everything works fine except retrieving project properties that should be available with the plugin. When I apply the plugin in another Gradle project, it can't find those properties.
It happens only when the plugin is loaded from the remote repository on GitHub. When loaded from a local repository, it works. Can the plugin implementation be somehow set to provide properties even from a remote repository? Or is this some GitHub feature that can't be hacked?
Details follow:
My build.gradle in the another project looks something like this:
buildscript {
repositories {
maven {
url "GITHUB_REPO_URL"
}
}
dependencies {
classpath "custom_plugin_group:custom_plugin:1.0"
}
}
apply plugin: "custom_plugin"
task propertyTest {
doLast {
println project.customProperty
}
}
When I try to run the the task propertyTest, it fails complaining that "Could not get unknown property 'customProperty' for root project 'another_project' of type org.gradle.api.Project."
I'm creating the property in the method apply in the main plugin class. I have tried following three approaches:
// First approach - adding a simple value to extensions
public class CustomPlugin implements Plugin<Project> {
#Override
public void apply(Project project) {
project.getExtensions().add("customProperty", "Custom property value");
}
}
// Second approach - setting extra property to extensions
public class CustomPlugin implements Plugin<Project> {
#Override
public void apply(Project project) {
project.getExtensions().getExtraProperties().set("customProperty", "Custom property value");
}
}
// Third approach - adding a property instance to extensions
public class CustomPlugin implements Plugin<Project> {
#Override
public void apply(Project project) {
Property<String> customProperty = project.getObjects().property(String.class);
customProperty.set("Custom property value");
project.getExtensions().add("customProperty ", customProperty);
}
}

For creating extensions for your Gradle plugin you need create POJO class with fields:
class YourExtension {
String customProperty
}
And then create extension in your plugin class:
public class CustomPlugin implements Plugin<Project> {
#Override
public void apply(Project project) {
project.getExtensions().create("extensionName", YourExtension.class);
}
}
Now you can use extension in build.gradle file:
extensionName {
customProperty = "value"
}

Related

A module can not access JpaRepository interface that is in the other module

I am using IntelliJ and Gradle for a sample project. There are two modules.
demo-core module
It has entity and repository classes. build.gradle file is like the below.
apply plugin: 'java'
group 'com.example'
version '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation 'com.h2database:h2:1.4.200'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.5.3'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
test {
useJUnitPlatform()
}
I added CustomerRepository class for Customer entity in demo-core module.
package example.springboot.entity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
}
demo-web module
This is a web module and uses the repository interface like the below.
#SpringBootApplication
public class DemoWebApp {
public static void main(String[] args) {
SpringApplication.run(DemoWebApp.class, args);
}
#Bean
public CommandLineRunner demo(CustomerRepository repository) {
return (args) -> {
// save a few customers
repository.save(new Customer("Jack", "Bauer"));
repository.save(new Customer("Chloe", "O'Brian"));
repository.save(new Customer("Kim", "Bauer"));
repository.save(new Customer("David", "Palmer"));
repository.save(new Customer("Michelle", "Dessler"));
};
}
}
This is build.gradle file for demo-web module.
plugins {
id 'org.springframework.boot' version '2.5.3'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group 'com.example'
version '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
implementation project(':demo-core')
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
}
test {
useJUnitPlatform()
}
but I can't use JpaRepsitory methods with this error.
/Users/dgkim/Downloads/demo/demo-web/src/main/java/example/springboot/DemoWebApp.java:19: error: cannot access JpaRepository
repository.save(new Customer("Jack", "Bauer"));
^
class file for org.springframework.data.jpa.repository.JpaRepository not found
So, I created a new interface wrapping the CustomerRepository in demo-core module like this.
#Service
public class CustomerFinder {
#Autowired
private CustomerRepository customerRepository;
public Optional<Customer> findCustomer(Long id) {
return customerRepository.findById(id);
}
}
My Controller class uses the wrapper interface like the below.
#RestController
public class CustomerController {
#Autowired
private CustomerFinder finder;
#GetMapping("/customer/{id}")
public String customer(#PathVariable Long id) {
Optional<Customer> customerOptional = finder.findCustomer(id);
if(customerOptional.isPresent()) return "find customer. " + customerOptional.get().getLastName();
else return "no entity";
}
}
It works. JpaRepository methods can be accessed in the same module but demo-web module that has a dependency on demo-core can not access it. DemoWebApp class can access CustomerRepository interface itself but can not access the super interface (JpaRepository).
How can I resolve this issue?
try to change core module dependencies from implementation to api
implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.5.3'
to
api 'org.springframework.boot:spring-boot-starter-data-jpa:2.5.3'
You can see the difference between api and implementation here

Replacing CastleWindsor with Autofac in .NETCore3.1

I was using CastleWindsor in my ASP.NETCore2.2 WebAPI project and was working fine. I'm migrating to ASP.NETCore3.1 now and it doesn't look like CastleWindor has offical support for that so I decided to move to Autofac with minimal changes but having some issues resolving the dependencies.
In my project, I've maintained very loose coupling between different layers in the application namely, business layer, data layer, and translation layer. All of those layers are in their own assemblies. And then in my main project, I've a folder say "dependencies" which will hold all the DLLs of differnet layers. Additionally, I've a separate project that lists all the interfaces that are implemented by the different layers and which needs to be resolved by the IoC container.
The project having all the interfaces looks like this:
namespace Shared.Interfaces
{
public interface IBusinessLayer<T>
{
....
}
public interface IDataLayer<T>
{
....
}
public interface ITranslationLayer<T>
{
....
}
}
The implementing projects looks like this:
namespace POC.Person.BusinessLayer
{
public class BusinessLayer<T> : IBusinessLayer<T> where T : Models.Person
{
...
}
}
namespace POC.Person.DataLayer
{
public class DataLayer<T> : IDataLayer<T> where T : Models.Person
{
...
}
}
namespace POC.Person.TranslationLayer
{
public class TranslationLayer<T> : ITranslationLayer<T> where T : Models.Person
{
...
}
}
Using Autofac in my migrated .netcore3.1 project, Startup.cs looks like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
//and other codes
}
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterModule(new DependencyResolver());
}
DependencyResolver is a class that inherits from Autofac.Module, which is again in a separate assembly in different project which looks like this:
namespace IOC.Autofac
{
public class DependencyResolver: Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
// get our path to dependencies folder in the main project
var path = Directory.GetCurrentDirectory() + "\\dependencies\\";
//get all the assemblies inside that folder
List<Assembly> assemblies = new List<Assembly>();
foreach (string assemblyPath in Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories))
{
var assembly = System.Runtime.Loader.AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
assemblies.Add(assembly);
}
// Register and resolve the types with the container
builder
.RegisterAssemblyTypes(assemblies.ToArray())
.AsClosedTypesOf(typeof(IBusinessLayer<>))
.AsClosedTypesOf(typeof(IDataLayer<>))
.AsClosedTypesOf(typeof(ITranslationLayer<>))
.AsImplementedInterfaces()
.InstancePerRequest();
}
}
}
I'm getting this error and I've not been able to fix it:
":"Unable to resolve service for type 'Shared.Interfaces.IBusinessLayer`1[Models.Person]' while attempting to activate 'POC.Person.Controllers.PersonController'.","
Inside my controller I've injection which looks like this:
namespace POC.Person.Controllers
{
public class PersonController : ControllerBase
{
private readonly IBusinessLayer<Models.Person> _bl;
public PersonController(IBusinessLayer<Models.Person> bl)
{
_bl = bl;
}
//other codes
}
}
Program.cs looks like this:
namespace POC.Person
{
public class Program
{
public static void Main(string[] args)
{
var host = BuildWebHost(args);
host.Build().Run();
}
public static IHostBuilder BuildWebHost(string[] args)
{
return Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel()
.UseStartup<Startup>()
.UseIIS()
.UseIISIntegration();
;
}).ConfigureAppConfiguration((context, config) =>
{
var builtConfig = config.Build();
});
}
}
}
It looks like with autofac involving generics, registering and resolving the type is not that straight forward?
Autofac does not currently support registering open generics whilst assembly scanning. It's a long-running known issue. You can do assembly scanning, you can register open generics, you can't do both at the same time. There are some ideas in that linked issue on ways some folks have solved it.
Out of the box, the scanning logic would, thus, be reduced to:
builder
.RegisterAssemblyTypes(assemblies.ToArray())
.AsImplementedInterfaces()
.InstancePerRequest();
You need to register generics separately, like:
builder
.RegisterGeneric(typeof(TranslationLayer<>))
.As(typeof(ITranslationLayer<>));

Trying to run a springboot war file with rest controller on tomcat 9 externally

I have been trying to deploy springboot apps. I tried one and could deploy the springboot with rest controller to embedded tomcat successfully. Now I am trying one more where I want to package it as a web app project and deploy to tomcat. I am able to deploy it but it doesn't resolve properly to rest controller paths and gives http 404 error. I am doing this with gradle build.
contextpath property didn't work.
My application starts up on http://localhost:8080/projectName/Welcome.jsp . I need to give this externally. It tries to move to http:localhost:8080/students on click of my link in jsp.
This gives the error - Type Status Report
Message /students
Description The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.
my gradle file :
/*
* This build file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java Library project to get you
started.
* For more details take a look at the Java Libraries chapter in the
Gradle
* user guide available at
https://docs.gradle.org/4.3/userguide/java_library_plugin.html
*/
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'java-library'
apply plugin: 'eclipse-wtp'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
repositories {
mavenCentral()
}
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.bmuschko:gradle-tomcat-plugin:2.5'
classpath("org.springframework.boot:spring-boot-gradle-
plugin:2.0.5.RELEASE")
//testImplementation 'junit:junit:4.12'
}
}
apply plugin: 'com.bmuschko.tomcat'
sourceCompatibility = 1.8
targetCompatibility = 1.8
bootWar{
mainClassName = 'org.sjsu.eds.student.main.StudentMain'
}
dependencies {
// This dependency is exported to consumers, that is to say found
on their compile classpath.
api 'org.apache.commons:commons-math3:3.6.1'
// This dependency is used internally, and not exposed to consumers
on their own compile classpath.
implementation 'com.google.guava:guava:23.0'
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
def tomcatVersion = '9.0.8'
tomcat "org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}",
"org.apache.tomcat.embed:tomcat-embed-logging-juli:9.0.0.M6",
"org.apache.tomcat.embed:tomcat-embed-jasper:${tomcatVersion}"
/*reference - https://github.com/bmuschko/gradle-tomcat-
plugin/blob/master/README.md*/
compile("org.springframework.boot:spring-boot-starter-web")
testCompile('org.springframework.boot:spring-boot-starter-test')
compile ("org.apache.httpcomponents:httpclient:4.5.7")
compile ("org.springframework:spring-webmvc:4.1.6.RELEASE")
}
tomcat {
httpProtocol = 'org.apache.coyote.http11.Http11Nio2Protocol'
ajpProtocol = 'org.apache.coyote.ajp.AjpNio2Protocol'
}
My spring main file
#SpringBootApplication
public class StudentMain extends SpringBootServletInitializer{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder
application) {
return application.sources(StudentMain .class);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
SpringApplication.run(StudentMain.class, args);
}
}
My rest controller simple
#RestController
#RequestMapping(value="/students")
public class StudentController {
private StudentServiceImplWithoutDB studentService;
#Autowired
public StudentController(StudentServiceImplWithoutDB studentService) {
this.studentService = studentService;
}
#GetMapping
public List<StudentVO> getAll(){
List<StudentVO> studentVO= studentService.getAllStudents();
return studentVO;
}
}
Do I need to set any path or properties for war? Almost the same application worked like a charm with embedded tomcat for simple java application
First you have to provide tomcat as provideRunTime in gradle after that change package type as war , and then extends SpringBootServletInitializer in main class , upload war file to tomcat web folder
According to these steps
First add in gradle
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
Change package type
apply plugin: "war"
Extends SpringBootServletInitializer class in main class
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Build war
gradle war

How to configure a Listener to override the behaviour of the class LoggingReporter in Citrus?

I'm looking for a way to remove stacktraces of fails assertions when using the framework Citrus.
This is done in testNg like this:
public class NoStackTraceListener implements ITestListener {
...
#Override
public void onTestFailure(ITestResult iTestResult) {
Throwable th = iTestResult.getThrowable();
if (Objects.nonNull(th)) {
System.out.println(th.getMessage());
iTestResult.setThrowable(null);
}
}
...
}
#Listeners({ NoStackTraceListener.class })
class A {...}
But I can't find any example of usgin the class 'TestListener' or others in order to override the supplied implementation of 'LoggingReporter'
Please do anyone has already overrided a Listener using framework citrus and could give the snippet to do so ?
Thanks
You need to add the custom reporter as bean to the Spring application context:
#Bean
public NoStackTraceReporter noStackTraceReporter() {
return new NoStackTraceReporter();
}
You can also overwrite the default logging reporter by choosing the bean name loggingReporter
#Bean
public NoStackTraceReporter loggingReporter() {
return new NoStackTraceReporter();
}
The NoStackTraceReporter implementation is then able to overwrite the specific event handler for failed tests:
public class NoStackTraceReporter extends LoggingReporter {
...
#Override
public void onTestFailure(TestCase test, Throwable cause) {
// do something customized
}
...
}
Also you may overwrite the generateTestResults() method in the reporter interface in order to customize logging results.
You can also follow the sample http://www.citrusframework.org/samples/reporting/ that demonstrates how to add customized reporters in Citrus.

AspectJ and Map<String,Object> Array

I am using AJDT 2.2.4 which is build on AspectJ 1.81.
Consider this simple aspect:
#Aspect
public class SampleAspect {
#Before("#annotation(logMe)")
public void beforeAdvice(JoinPoint joinPoint, LogMe logMe) {
System.out.println("Before the method");
}
}
It print some text before LogMe annotation which is :
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LogMe {}
Now, I apply this annotation to some method as:
public class DummyClass {
#LogMe
public void doSomething() {
SampleUtil sampleUtil = new SampleUtil();
//pass null for simplicity !
sampleUtil.sampleMethod(null);
System.out.println("Do Something");
}
}
The SampleUtil is
public class SampleUtil {
public void sampleMethod(
Map<String, Object>[] mapArray){
}
}
I get this warning:
can not resolve this member:
void foo.SampleUtil.sampleMethod(java.util.Map[]) [Xlint:unresolvableMember]
If I change the sampleMethod parameter to something else like Map<String, Object> aMap the error will go.
Why do I get this warning ?!
That warning means that it can't find foo.SampleUtil on the inpath. The inpath is similar to the classpath, and is used to determine what the aspects weave against.
I am guessing that foo.SampleUtil is in another project and this means that you need to explicitly add the project to your inpath.
Since you are using AJDT inside of Eclipse, you can go to the aspect project's properties page and select the AspectJ build path tab. Choose Inpath and add the other project.