Gradle gretty plugin:java.lang.NoClassDefFoundError when dependencies with mulit level project - gradle

When i use gretty to start my spring boot project , i got a NoClassDefFoundError,the project is structured as follows.ProjectA is a simple webapp project,ProjectB and ProjectC is simple java project.
projectA:
#RestController
public class AController {
#RequestMapping(value = "/print",method = RequestMethod.GET)
public String print(){
new BClass().print();
return this.getClass().getName();
}
}
apply plugin: 'org.akhikhl.gretty'
gretty{
springBoot = true
port = 8080
contextPath = '/'
servletContainer = 'tomcat8'
managedClassReload = true
}
dependencies{
compile "org.springframework.boot:spring-boot-starter-web"
compile project(":projectB")
}
ProjectB:
public class BClass {
public void print(){
new CClass().print();
System.out.println(this.getClass().getName());
}
}
dependencies{
compile project(":projectC")
}
ProjectC:
public class CClass {
public void print(){
System.out.println(this.getClass().getName());
}
}
start command:
gradle :projectA:appRun
then access : http://localhost:8080/print
I got java.lang.NoClassDefFoundError: com/project/c/CClass
it seems to be the ProjectA concise reference the class of ProjectC,and gretty how not include ProjectC to classpath.
How can i do for this?thanks
gretty plugin :https://github.com/akhikhl/gretty

I can't really help but I can confirm that I get the same problem. I'm also using Spring Boot as you are and wonder if that's part of the problem.

Related

404 on Controller Endpoint Spring while running on customer server

I'm getting 404 consistently for a Gradle spring project while running on tomcat 8.5.
I created a simple project from https://start.spring.io and imported it into the spring tool suite.
When I run it as spring boot app, I'm able to hit the endpoint http://localhost:8080/healthCheck but when I add it to server I created (tomcat 8.5), I'm getting a 404 error on the same endpoint.
This is my simple controller:
#RestController
public class HealthCheckController {
#GetMapping("/healthCheck")
public String healthCheck() {
return "API is accessible";
}
}
This is my build.gradle:
plugins {
id 'org.springframework.boot' version '2.5.4'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.demo'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-tomcat:2.5.4'
}
test {
useJUnitPlatform()
}
And this is my main class:
#SpringBootApplication
public class PracticeApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(PracticeApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(PracticeApplication.class);
}
}
I have changed the server location to use Tomcat Installation and hence am able to hit http://localhost:8080 and it comes up with tomcat homepage.
Here's my directory structure for reference:
I'm new to gradle and spring so apologies if its a stupid thing that I'm doing wrong.
Please change your code as follows:
#SpringBootApplication
public class PracticeApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(PracticeApplication.class, args);
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(PracticeApplication.class, HealthCheckController.class);
}
}
It seems that you need to list all the configuration classes and components in the application.
Still, I guess that overriding the configure() method is not necessary in your case, so I would also try:
#SpringBootApplication
public class PracticeApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(PracticeApplication.class, args);
}
}
So turns out Apache Tomcat does not take jar files and to run spring boot app on an external tomcat server (non-embedded ones), WAR file is needed to be created.
I added war to my build.gradle and gave it an alias as well
plugins {
id 'org.springframework.boot' version '2.5.4'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
id 'war'
}
war {
archiveName = 'practice.war'
}
After adding the above, I refreshed the Gradle project and restarted the server and then I hit the below endpoint:
http://localhost:8080/practice/healthCheck
This is different from how we hit the above endpoint on the embedded server which is:
http://localhost:8080/healthCheck
Another way to run this is to go to build/libs/ and copy the war into ROOT directory, then run startup.bat from apache/bin/

Configuring Camel-Spring for standalone Camel microservice in a multi-module Gradle project

Goal: Adapting Prototype microservice example from Ch7 of Camel in Action by Claus Ibsen into a Gradle multi-module project.
I followed the Spring Guide to 'Creating a Multi Module Project' among other resources.
Project Structure:
+ main-mm-build
|--+ src
|--+ main
|--+ java // Spring Boot microservice (A) in this tree
|--+ build.gradle
|--+ settings.gradle
|--+ contact-manager // standalone-camel-spring microservice (B)
|--+ src
|--+ main
|--+ java // standalone-camel-spring microservice (B) here
|--+ build.gradle
Got as far as:
microservice A (Spring Boot Rest controller) can invoke microservice B (standalone Camel exposing rest() on jetty, without Spring DI).
But what I really want is to use Spring DI (without Spring Boot) in microservice B.
After making changes for camel-spring I get an error.
It's probably mucked up Gradle config, but I could use some help.
main-mm-build/contact-manager$ ../gradlew build
main-mm-build/contact-manager$ java -jar build/libs/contact-manager-1.0.jar
.
.
.
Caused by: org.apache.camel.ResolveEndpointFailedException: Failed to resolve endpoint: rest://get:/contact-manager?consumerComponentName=jetty&consumes=application%2Fjson&outType=...dto.ContactDto%5B%5D&produces=application%2Fjson&routeId=route2 due to: null
at ...impl.engine.AbstractCamelContext.getEndpoint(AbstractCamelContext.java:801)
Caused by: java.lang.NullPointerException
at ...camel.spring.spi.ApplicationContextBeanRepository.lookupByNameAndType(Ap..j:45)
Root project Gradle file:
plugins {
id 'org.springframework.boot' version '2.3.1.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
sourceCompatibility = '11'
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-tomcat'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
allprojects {
group = 'el.cam'
repositories {
jcenter()
}
}
subprojects {
version = '1.0'
}
settings.gradle:
rootProject.name = 'main-mm-build'
include 'contact-manager'
Microservice B (contact-manager) build.gradle:
plugins {
id 'org.springframework.boot'
id 'io.spring.dependency-management'
id 'java'
id 'application'
}
sourceCompatibility = '11'
mainClassName = 'el.cam.contacts.ContactManagerApplication'
task fatJar(type: Jar) {
manifest {
attributes (
'Main-Class': mainClassName
)
}
baseName = 'contact-manager' + '-all'
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
dependencies {
implementation(platform("org.springframework.boot:spring-boot-dependencies:2.3.1.RELEASE"))
implementation( platform("org.apache.camel:camel-spring-boot-dependencies:3.0.0-RC3") )
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.apache.camel:camel-spring-boot-starter'
implementation 'org.apache.camel:camel-rest-starter'
implementation 'org.apache.camel:camel-jetty-starter'
implementation 'org.apache.camel:camel-jackson-starter'
implementation 'org.apache.camel:camel-swagger-java-starter'// '3.0.0-RC3'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}
ContactManagerApplication.java ( I wonder if I'm configuring Spring Camel incorrectly here. All the examples I found were based on Spring boot autoconfiguration, so I just figured it out as I went.)
import el.cam.contacts.configuration.ContactManagerConfiguration;
import org.apache.camel.spring.Main;
public class ContactManagerApplication {
private static final Logger LOG = LoggerFactory.getLogger(ContactManagerApplication.class);
public static void main(String[] args) throws Exception {
Main main = new Main();
main.setApplicationContext(createSpringApplicationContext());
// main.addRoutesBuilder(contactManagerController); // DI using Spring Autowiring
main.run();
}
private static AbstractApplicationContext createSpringApplicationContext() {
AnnotationConfigApplicationContext appContext = new AnnotationConfigApplicationContext();
appContext.register(ContactManagerConfiguration.class);
appContext.refresh();
return appContext;
}
}
Configuration class:
#Configuration
#ComponentScan(basePackages = "el.cam.contacts")
public class ContactManagerConfiguration {
#Autowired
ContactManagerController contactManagerController;
#Bean
public CamelContext camelContext() throws Exception {
SpringCamelContext camelContext = new SpringCamelContext();
camelContext.addRoutes(contactManagerController);
camelContext.setPropertiesComponent(properties());
camelContext.addComponent("rest", rest());
camelContext.addComponent("rest-api", restApi());
camelContext.addComponent("jetty", jetty());
return camelContext;
}
#Bean
public PropertiesComponent properties() throws Exception {
PropertiesComponent properties = new PropertiesComponent();
properties.setLocation("classpath:application.properties");
return properties;
}
#Bean
public RestComponent rest() {
RestComponent rest = new RestComponent();
return rest;
}
#Bean
public RestApiComponent restApi() {
RestApiComponent restApi = new RestApiComponent();
return restApi;
}
#Bean
public JettyHttpComponent jetty() {
JettyHttpComponent jettyHttpComponent = new JettyHttpComponent9();
return jettyHttpComponent;
}
Controller class:
#Component
public class ContactManagerController extends RouteBuilder {
#Autowired
ContactManagerService contactManagerService;
#Override
public void configure() throws Exception {
// before Camel-Spring, was using this to bind serviceBean in camel registry
// getContext().getRegistry().bind("contactManagerService", new ContactManagerService());
// TODO using default. camel property sources not picking up application.properties!
restConfiguration("jetty").port("{{port:8282}}").contextPath("api")
.bindingMode(RestBindingMode.json)
.dataFormatProperty("disableFeatures", "FAIL_ON_EMPTY_BEANS")
.apiContextPath("api-doc")
.enableCORS(true);
// define the rest service
rest("/contact-manager").consumes("application/json").produces("application/json")
.get().outType(ContactDto[].class)
.to("bean:contactManagerService?method=getContacts(${header.contactType})")
;
}
}

New Functional Web Framework with jetty

I wanted to setup an example for New in Spring 5: Functial Web Framework
So I set up a RouteConfiguration:
#Configuration
public class RouteConfiguration {
#Autowired
private MyService myService;
#Bean
public RouterFunction<?> routerFunction() {
return route(
GET("/first")
, myService::getItemsFirst)
.and(route(
GET("/second")
, myService::getItemsSecond));
}
}
I started my application using jetty and at first it seemed to work... until I wanted to call one of my methods: localhost:8080/first and it returned a 404.
Did I define my route configuration wrong or why arent the routes accessible?
EDIT
With netty you need to provide a Server Configuration Like the following:
#Configuration
public class HttpServerConfiguration {
#Autowired
private Environment environment;
#Bean
public HttpServer httpServer(final RouterFunction<?> routerFunction) {
final HttpHandler httpHandler = RouterFunctions.toHttpHandler(routerFunction);
final ReactorHttpHandlerAdapter adapter = new ReactorHttpHandlerAdapter(httpHandler);
final HttpServer server = HttpServer.create("localhost", Integer.valueOf(this.environment.getProperty("server.port")));
server.newHandler(adapter);
return server;
}
}
But I could not find something like this for jetty.
EDIT 2
My Dependencies:
repositories {
mavenCentral()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencyManagement {
dependencies {
dependency (group: 'org.springframework.cloud', name: 'spring-cloud-starter-consul-discovery', version: '2.0.0.M1')
dependencySet (group: 'org.hibernate', version: '5.2.8.Final') {
entry 'hibernate-core'
entry 'hibernate-entitymanager'
entry 'hibernate-spatial'
}
}
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-hateoas')
compile('org.springframework.boot:spring-boot-starter-jetty')
compile('org.springframework.boot:spring-boot-starter-webflux') {
exclude module: 'spring-boot-starter-reactor-netty'
}
compile('org.springframework.boot:spring-boot-starter-actuator')
compile('org.springframework.boot:spring-boot-autoconfigure')
compile('org.springframework.boot:spring-boot-actuator')
compile('org.springframework.cloud:spring-cloud-starter-consul')
compile('org.springframework.cloud:spring-cloud-starter-consul-discovery')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('junit:junit')
}
Spring-Boot Version: 2.0.0.M3
Reading the comments, it seems this was an issue with dependencies bringing spring-boot-starter-web; if it is present, a Spring MVC application is started by Spring Boot.
There's a way to explicitly tell Spring Boot the type of the application, in the main Application class:
public static void main(String[] args) {
SpringApplication application = new SpringApplication(AgentApplication.class);
application.setWebApplicationType(WebApplicationType.REACT‌​IVE);
application.run(args);
}

Gradle build with fatJar plugin and SpringBoot application gives 'Application startup failed'

Everything was working fine when starting my app using Intellij. But when I made fatJar (with gradle plugin: eu.appsatori.fatjar) and execute:
java -jar myapp.jar
I'm getting something like this:
11:41:01.224 [main] ERROR org.springframework.boot.SpringApplication - Application startup failed
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [my.testing.MyAppMain]; nested exception is java.lang.IllegalArgumentException: No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.
at org.springframework.context.annotation.ConfigurationClassParser.processDeferredImportSelectors(ConfigurationClassParser.java:482)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:184)
...
It looks like it didn't found auto configuration classes in META-INF/spring.factories.
How to add this file? And what should be the content of it?
I've got following build script:
apply plugin: "java";
apply plugin: "idea";
apply plugin: 'application'
apply plugin: 'eu.appsatori.fatjar'
apply plugin: 'org.springframework.boot'
repositories {
mavenCentral()
}
buildscript {
ext {
springBootVersion = '1.4.3.RELEASE'
}
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
classpath "eu.appsatori:gradle-fatjar-plugin:0.3"
}
}
sourceSets {
main {
java {
srcDir 'src/main/java'
}
resources {
srcDir 'src/main/resources'
}
}
test {
java {
srcDir 'src/test/java'
}
}
}
fatJar {
manifest {
attributes("Main-Class": 'my.testing.MyAppMain')
}
exclude 'META-INF/*.DSA'
exclude 'META-INF/*.SF'
exclude 'META-INF/*.RSA'
}
dependencies {
compile 'org.springframework.boot:spring-boot-starter-jdbc'
runtime 'mysql:mysql-connector-java'
testCompile 'org.springframework.boot:spring-boot-starter-test'
}
And my example code is:
package my.testing;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
#SpringBootApplication
public class MyAppMain {
private ConfigurableApplicationContext springContext;
#Autowired
private SimpleDao dao;
public static void main(String[] args) throws Exception {
MyAppMain test = new MyAppMain();
try {
test.init();
test.doWhatYouGotToDo();
} finally {
test.stop();
}
}
private void doWhatYouGotToDo() {
System.out.println("Auto-wired dao: " + dao.hashCode());
System.out.println("Auto-wired jdbcTemplate: " + dao.jdbcTemplate.hashCode());
}
private void init() throws Exception {
springContext = SpringApplication.run(MyAppMain.class);
springContext.getAutowireCapableBeanFactory().autowireBean(this);
}
private void stop() throws Exception {
springContext.close();
}
}
#Component
class SimpleDao {
#Autowired
JdbcTemplate jdbcTemplate;
}
application.properties file:
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.url = jdbc:mysql://localhost:3306/some_db?useSSL=false&serverTimezone=UTC
spring.datasource.username = some_user
spring.datasource.password = some_pass
NOTE: This question is based on SpringBoot - making jar files - No auto configuration classes found in META-INF/spring.factories
where are all answers are referring to building with Maven. Please put only answers related to Gradle here.
Although I mostly use Maven for Spring and Gradle for Android, but here is the gradle way for a Spring project:
gradle clean build
gradle bootRepackage
Result:
Here is my build.gradle file:

accessing the spring-restdocs generated content in a spring boot application

I'm using Spring Restdocs (v1.1.2) in a Spring Boot (v1.4.1) application.
In the jar task of the Gradle build file, I'm copying the generated output into public/docs:
jar {
dependsOn asciidoctor
from ("${asciidoctor.outputDir}/html5") {
into 'public/docs'
}
}
and I see in the generated JAR the document in
BOOT-INF/classes/public/docs/api-guide.html
However, when I run the JAR, I can't seem to address the api-guide.html at /docs, /public/docs, etc.
Can someone please explain what I'm doing wrong?
Thanks!
--john
buildscript {
ext {
springBootVersion = '1.4.1.RELEASE'
}
}
plugins {
id "org.asciidoctor.convert" version "1.5.3"
}
apply plugin: 'groovy'
apply plugin: 'spring-boot'
ext {
snippetsDir = file('build/generated-snippets')
springRestdocsVersion = '1.1.2.RELEASE'
}
test {
outputs.dir snippetsDir
}
asciidoctor {
attributes 'snippets': snippetsDir
inputs.dir snippetsDir
dependsOn test
}
jar {
dependsOn asciidoctor
from ("${asciidoctor.outputDir}/html5") {
into 'public/docs'
}
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-rest')
testCompile("org.springframework.restdocs:spring-restdocs-mockmvc:${springRestdocsVersion}")
}
=============================================================
here's the application config:
#SpringBootApplication
#EnableJpaRepositories
#EnableScheduling
class Application {
static void main(String[] args) {
SpringApplication.run Application, args
}
}
and the test config:
#RunWith(SpringJUnit4ClassRunner)
#SpringApplicationConfiguration(classes = Application)
class ApplicationTests {
...
}
OK, I finally figured out what I was doing wrong. I had the spring-boot-actuator-docs enabled:
compile('org.springframework.boot:spring-boot-actuator-docs')
and it was "taking over" the /docs path. As soon as I relocated the generated restdocs to a different path, e.g.
jar {
dependsOn asciidoctor
from ("${asciidoctor.outputDir}/html5") {
into 'static/api'
}
}
all was good.
Thanks Andy for your interest in my question and the very cool Spring REST Docs project!

Resources