I am working on a Java Spring Boot web application using HTML and Thymeleaf as my front end. The issue I am having is that my ThymeleafConfig class will randomly have an error when I try to run my project. I will explain in more detail below, but first here is my code.
Pom.xml dependency:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
ThymeleafConfig class:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;
#Configuration
public class ThymeleafConfig {
#Bean
public SpringSecurityDialect springSecurityDialect() {
return new SpringSecurityDialect();
}
}
So to explain further, my code will be good (according to my IDE), containing no errors on any of my lines, ready to run. I will run my application class to run my project, and I will get compilation errors.
IntelliJ will automatically open the file that has the error in it, which is my ThymeleafConfig class. When the error is present, the import statement import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect; line will be greyed out and not recognized. causing errors in the class.
To fix it, I right-click my pom.xml file -> Maven -> Reimport. This will reimport all of my dependencies and everything will go back to normal.
I also want to stress that this does not happen every time I run the application. Sometimes it will happen 3 times in a row, other times I will run it 5+ times before the error shows up again.
I have tried mvn clean which did not fix the issue. I have also moved the dependency to a different line in the pom.xml file.
edit:
Here is my Application.java class
#SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
To run the application I right click this file and click the run 'Application' button.
Spring boot already includes thymeleaf (a different version) and you are probably having multiple versions on the classpath. Spring Boot is pretty clear about thymeleaf on its documentation:
Spring boot 1.4.2's documentation on using a thymeleaf 3.0.0
By default, spring-boot-starter-thymeleaf uses Thymeleaf 2.1. If you
are using the spring-boot-starter-parent, you can use Thymeleaf 3 by
overriding the thymeleaf.version and thymeleaf-layout-dialect.version
properties, for example:
<properties>
<thymeleaf.version>3.0.0.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.0.3</thymeleaf-layout-dialect.version>
</properties>
...
If you are using any of the other auto-configured Thymeleaf Extras
(Spring Security, Data Attribute, or Java 8 Time) you should also
override each of their versions to one that is compatible with
Thymeleaf 3.0.
If you look at spring-boot-dependency's pom on github, the thymeleaf properties are:
<thymeleaf.version>2.1.5.RELEASE</thymeleaf.version>
<thymeleaf-extras-springsecurity4.version>2.1.2.RELEASE</thymeleaf-extras-springsecurity4.version>
<thymeleaf-extras-conditionalcomments.version>2.1.2.RELEASE</thymeleaf-extras-conditionalcomments.version>
<thymeleaf-layout-dialect.version>1.4.0</thymeleaf-layout-dialect.version>
<thymeleaf-extras-data-attribute.version>1.3</thymeleaf-extras-data-attribute.version>
<thymeleaf-extras-java8time.version>2.1.0.RELEASE</thymeleaf-extras-java8time.version>
Recap
I believe you would need to at least define the following properties (perhaps a few more to avoid a mismatch of versions?):
thymeleaf.version
thymeleaf-extras-springsecurity4.version
Related
I've read that I should not open an issue on github so I ask here. I've digged into the code and for example spring-boot-actuator-autoconfigure doesn't define the #Configuration\#AutoConfiguration classes inside META-INF/spring.factories follow the content of the file:
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.boot.actuate.autoconfigure.metrics.ValidationFailureAnalyzer
I've checked and ValidationFailureAnalyzer is not even annotated with #Configuration\#AutoConfiguration. Then I see the file META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports declaring all the classes #AutoConfiguration follow a little extraction of the file:
org.springframework.boot.actuate.autoconfigure.amqp.RabbitHealthContributorAutoConfiguration
org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration
org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration
org.springframework.boot.actuate.autoconfigure.availability.AvailabilityHealthContributorAutoConfiguration
...
all these classes are annotated with #AutoConfiguration. So far so good If we read the docs they say that:
Spring Boot checks for the presence of a META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports file within your published jar.
Indeed if we import:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.7.3</version>
</dependency>
everything works just fine. I'm not skilled with gradle but I don't see any special dependecy in spring-boot-actuator-starter or spring-boot-actuator-autoconfigure.
Searching on google I've found a discussion here where they say:
In Spring Boot v. 2.7 auto-configuration registration is moved from spring.factories to a new file named META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.
Each line contains the fully qualified name of the auto-configuration.
For backwards compatibility, entries in spring.factories will still be honored.
But honestly I've tryed to move the configuration classes in the new file but the configuration class is not loaded. I've writed an example here.
My org.springframework.boot.autoconfigure.AutoConfiguration.imports file:
com.example.springbootstarterexample.configuration.Config
If I move to the old configuration spring.factries everything works fine.
My #AutoConfiguration class:
#AutoConfiguration(after = JpaRepositoriesAutoConfiguration.class)
//#AutoConfigureAfter(JpaRepositoriesAutoConfiguration.class)
#EnableJpaRepositories(basePackages = "com.example.springbootstarterexample.repository")
#Import({SomeServiceImpl.class, SomeEntityController.class})
public class ExampleAutoConfiguration {
}
am I doing something wrong? why the spring-boot-starter-actuator works and my spring-boot-starter-example dosn't?
Your file is called org.springframework.boot.autoconfigure.AutoConfiguration.import,
and must be org.springframework.boot.autoconfigure.AutoConfiguration.imports (notice the extra s) at the end.
I have one spring boot project (jar) as dependent jar in another spring boot application, in the current project/application i dont want to have swagger sort of codes to be written or configured again, so is it possible to extend the spring boot application class from the dependent jar which has those configurations
There is a way to do it but I'd strongly recommend you do not do it. Spring boot applications are packaged differently to standard jars.. they're executable jars with other bits and bobs going on internally. If you plan on running the spring boot jar anywhere other than locally (eg within a container) it is likely to fail.
I'd recommend extracting out the common functionality to a shared module instead and add a dependency on the shared module to each parent module. This works well if you're using Gradle/maven.
Yes for a concept it can be done the following way , just make sure the the version of dependency match to that of one extending it
Project 1
Creating a Simple Hello World Spring Web project "projectone" using Spring starter Project
<groupId>com.harshit</groupId>
<artifactId>projectone</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>projectone</name>
#RestController with following API
#GetMapping("/one")
public String helloworld()
{
return "Project One Works";
}
#GetMapping("/alone")
public String checkmethod()
{
return "alone method also made its way into the second project";
}
Build / run /install. the project on server.port=8081
mvn clean install
Project 2
Creating another Simple Spring Web project "projecttwo" using Spring starter Project
<groupId>com.demo</groupId>
<artifactId>projecttwo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>projecttwo</name>
Add the previously created "projectione" as dependency in this project in the pom.xm of this project
<dependency>
<groupId>com.harshit</groupId>
<artifactId>projectone</artifactId>
<version>0.0.1-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
Create a #RestController with the following api extending the Controller class of the "projectione"
import com.harshit.controller.Controller;
#RestController
public class anothercontroller extends Controller{
#GetMapping("/two")
public String hellomultiverse()
{
return helloworld();
}
#GetMapping("/three")
public String multiverse()
{
return "threeworks";
}
#Override
public String helloworld()
{
return "done ";
}
Build / run /install. the project on server.port=9999
mvn clean install
Testing the Extensions
Project
API
Reachable
"projectone" method extended in "projecttwo"
Localhost:9999/alone
YES
"projectone" method extended + overridden in "projecttwo"
Localhost:9999/two
YES
"projecttwo" method
Locahost:9999/three
YES
"projectone" method extended + overridden in "projecttwo"
Localhost:9999/one
YES (Overridden output)
I am learning Spring Boot. I made a simple Spring Boot project that can output a hello world string at http://localhost:8080/welcome
I use Maven to build my project that would output a jar file.
To start up my spring boot app, I use the command as below
java -jar my-springboot-app.jar
My question is:
How is java smart enough to locate my main class and its main method (e.g. the application launcher)?
I checked the jar file and browsed those BOOT-INF & META-INF and could not find any clues.
Does the spring boot framework (#SpringBootApplication) or maven automatically do the magic for me?
In case of spring boot jar the things are little bit more complicated than regular jar. Mainly because spring boot applicaton jar is not really a JAR (by jar I mean something that has manifest and compiled classes). Regular JARs can be "recognized" and processed by jvm, however in Spring Boot there are also packed dependencies (take a look at BOOT-INF/lib) so its jars inside JARs. How to read this?
It turns out that spring boot always has its own main class that is indeed referred to in MANIFEST.MF and this a real entry point of the packaged application.
The manifest file contains the following lines:
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: com.example.demo.DemoApplication
Main-Class is a JVM entry point. This class, written by spring developers, basically does two things:
- Establishes a special class loader to deal with a "non-regular-jar" nature of spring boot application. Due to this special class loaders spring boot application that contains "jars" in BOOT-INF/lib can be processed, for example, regular java class loaders apparently cannot do this.
- Calls the main method of Start-Class value. The Start-Class is a something unique to spring boot applications and it denotes the class that contains a "main" method - the class you write and the class you think is an entry point :) But from the point of view of the spring boot infrastructure its just a class that has an "ordinary" main method - a method that can be called by reflection.
Now regarding the question "who builds the manifest":
This MANIFEST.MF is usually created automatically by plugins offered by Spring Developers for build systems like Maven or Gradle.
For example, the plugin looks like this:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
During its work, this plugin identifies your main class (com.example.demo.DemoApplication in my example). This class is marked with #SpringBootApplication annotation and has a public static void main method.
However, if you put many classes like this the plugin probably won't recognize the correct class so you'll need to configure the plugin properties in POM.xml to specify the right class.
Java classes are executed within a larger context,
you run java -jar somejar.jar the class in question will be selected in the .jar file's manifest.
#SpringBootApplication will have componentscan, enabling auto configuration(autowired)
componentscan - to identify all the controller, service and configuration classes within the package.
I have created my first spring boot project and I have copied the example verbatim from springs offical website.
However, when I try to start using mvn spring:run, it fails with the following exception:
org.springframework.context.ApplicationContextException: Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean.
You didn't say if your project is web project or not. Also since I don't see your application.properties or application.yaml. There could be so many reason and with limited amount of information on your question, I am trying to guess the answer.
If this doesn't work. update your question with more information.
If your project is not a web application project, try setting following on your application.properties
spring.main.web-application-type=none
And if you have application.yaml, set thefollowing
spring:
main:
web-application-type: none
With limited information, its hard to say whats wrong in your application. I had a similar issue and I believe it was due to corrupted jar files downloaded. I resolved referring this github issue.
There might be below reasons for ServletWebServerApplicationContext due to missing ServletWebServerFactory bean
You might have missing web dependency in you pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
You have not annoted #SpringBootApplication to your main class:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I've included a .jar in my maven project writing this in pom.xml:
<dependencies>
<dependency>
<groupId>org.loopingdoge.acme.model</groupId>
<artifactId>acme-model</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/src/main/webapp/WEB-INF/lib/acme-model.jar</systemPath>
</dependency>
</dependencies>
acme-model.jar contains org.loopingdoge.acme.model.House but this cast
public class HouseAdder implements JavaDelegate {
public void execute(DelegateExecution delegateExecution) throws Exception {
House house = (House) delegateExecution.getVariable("house");
}
}
gives me this error when deployed on a Wildfly server:
18:50:20,255 ERROR [org.camunda.bpm.engine.context] (default task-45) ENGINE-16004 Exception while closing command context:
org.loopingdoge.acme.model.House cannot be cast to org.loopingdoge.acme.model.House: java.lang.ClassCastException: org.loopingdoge.acme.model.House cannot be cast to org.loopingdoge.acme.model.House
at org.loopingdoge.acme.services.HouseAdder.execute(HouseAdder.java:13)
Such cases happens when a class will be loaded over different classloaders. java make them distinct even if package and classname are identical.
You need to find out on which ways this class will be loaded. As first step, find the jars which contains that class.
or/and read this on SO
Ran into the same problem. Further analysis showed that two different class loaders were used, as stated in this thread already. In my case the culprit was devtools of spring boot, a tool which likely many will have active in their pom. Seems that devtools doesn't play well with (in my case) camunda java delegates with embedded camunda engine.
I guess that you have the class in multiple places, e.g. packaged the jar within WildFly and the WAR you deploy. Check that you have the class only one time on the classpath.
By the way: Better not use system dependencies if not absolutely necessary. This is what maven repositories are for.