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
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/
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})")
;
}
}
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.REACTIVE);
application.run(args);
}
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:
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!