Hot Reloading Thymeleaf Templates and Resource Bundles in Spring Boot 2.1 - java

I created a Spring Boot 2.1.3 application with i18n enabled, added devtools, and installed the Firefox LiveReload extension. Unfortunately, when I change my Thymeleaf templates or i18n messages, it doesn't change. The Spring Boot documentation seems to suggest all you need to do is install devtools, and it'll disable caching of static resources.
Here's what I did to create my app:
mkdir bootiful-i18n
cd bootiful-i18n
http https://start.spring.io/starter.zip dependencies==web,thymeleaf -d | tar xvz
Then I created a HomeController.java:
package com.example.demo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class HomeController {
#GetMapping("/")
String home() {
return "home";
}
}
I created a Thymeleaf template at src/main/resources/templates/home.html:
<html xmlns:th="http://www.thymeleaf.org">
<body>
<h1 th:text="#{title}"></h1>
<p th:text="#{message}"></p>
</body>
</html>
I added a messages.properties file in src/main/resources:
title=Welcome
message=Hello! I hope you're having a great day.
This all works great. To enable hot-reload, I added devtools to pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
And installed the LiveReload extension for Firefox.
I restarted my server, enabled LiveReload and navigated to http://localhost:8080. I tried changing and saving both home.html and messages.properties and refreshing my browser. The output in the browser does not change. Is there something else I need to do to disable caching of Thymeleaf templates and message bundles in Spring Boot 2.1?

Devtools will disable caching so that an update to the template file can be reconsidered when it needs to be rendered again.
When your application starts in a JVM process though, there is nothing pointing to your source files and it can only see the classpath. So whatever change you make to a source file needs to be updated to the classpath location, which usually happens by asking your IDE to "Build the project".
So the missing step for the template is that and if the documentation is not clear about that, feel free to open an issue with a suggestion as we've been iterating on that quite a lot.
There is nothing specific about i18n though. If you change them and update the classpath we'll restart the app rather than picking this up the same way we do for templates. We did try to support this once, see this issue for more details. Having said that, you shouldn't expect that a change to a resource file "will just work". A change to configuration will obviously require the app to restart for instance (again, by updating the classpath when you use devtools).
Some added note about IDEs support:
Eclipse incrementally compiles and copies changed files on "save". So if you save your template you should be good to go
IntelliJ IDEA Ultimate has dedicated devtools support as of 2018.1 - If you configure "On frame deactivation" to "Update resources" it will do the right thing when you switch to your browser. Before that support I had a special keystroke mapped to "Build Project" (incrementally compiles and copies resources) that I invoked whenever I wanted my changes to be reflected
If you're not using an IDE, you need to have something to update the classpath for you. We've tried to support that out-of-the-box but turned out to be quite complex. There is a comment to help you if you're using Gradle.

Add the following to your application.properties :
spring.thymeleaf.cache=false

Related

Vaadin 23 not resolving URL's, instead showing errorpage

Problem summary: we've upgraded our Java 17 monolith application with multiple modules from Vaadin 21.0.1 to 23.3.5 and now our application routes don't resolve anymore, instead resulting in 404 Whitelabel errorpages.
This not being our first Vaadin rodeo (originating from Vaadin 7), we followed the Vaadin upgrade guide generator and expanded accordingly upon that.
Steps we took, each having been validated seperately:
Upgraded our backend to use Spring 5.3.18 (coming from 5.3.10). No issue there
Upgraded our frontend to use Spring Boot 2.6.7 (coming from 2.5.4). No issue there
this step was project-specific and not in the guide Removed Vaadin-addons from pom.xml. Also removed those imports from our package.json. Naturally removed all addons-code from application (EnhancedDialog and MultiselectComboBox -> using v23's regular Dialog and MultiSelectComboBox)
Removed webpack.config.js, package-lock.json and the node_modules folder
Raised global (-g) npm version to 9.4.0 (coming from 8.3.3)
Cleared npm with commandline npm cache clear --force
Upgraded the Vaadin flow version to 23.3.5 (coming from 21.0.1)
added the following dependency and reloaded the pom.xml:
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-maven-plugin</artifactId>
<version>23.3.3</version>
</dependency>
Invalidated and restarted IntelliJ
ran clean-install Maven command through IntelliJ (with Production flag on/off makes no difference)
-> This results in a 404 whitelabel errorpage.
Next, I made sure that all our views annotated with com.vaadin.flow.router.#Route contained at least the javax.annotation.security.#PermitAll annotation.
I added spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration to our application.properties trying to get a more descriptive message.
-> This results in the Apache Tomcat errorpage with description: "The origin server did not find a current representation for the target resource or is not willing to disclose that one exists."
In both cases, the F12 DevTools are empty and showing nothing more than a 404 for the given URL.
So I continued debugging and validated that our custom security roles were validated correctly, a bit like this answer hinted at.
I'm getting into the breakpoints placed in the configure(HttpSecurity http) method from the WebSecurityConfigurerAdapter, but I'm unable to enter the serviceInit(ServiceInitEvent event) from the VaadinServiceInitListener.
I'm not a pro concerning servletRequests, but when hitting the isFrameworkInternalRequest(HttpServletRequest request) (called during the adapter's configuration), it seems only logical that for REQUEST_TYPE_PARAMETER a value of null is returned.
I was intrigued by these upgrade steps for an even higher Vaadin version, but nothing in there changed a bit for me.
When asked, I'd say that Spring isn't picking up on my views. Even the older #EnableVaadin() annotation did nothing. At this point I can't think straight anymore, even looking into the change of why Vaadin now uses vite.config.ts instead of webpack.config.js. Any pointers to where the issue might lay are immensely appreciated.
all credit to #Knoobie whose concise answer contained the correct solution.
Vaadin 23.3.x requires at least 2.7.x in order to work.
The Vaadin upgrade guide generator, when looking at the steps to migrate from v21 to v23, mistakenly referenced the minimum version of SpringBoot as 2.6.6. They will fix this in the immediate future.

Spring Boot resources not found

Ok, I developed a small spring boot website using thymleaf and now realized that I can't use the webapp folder if I want to package everything with the maven plugin.
To fix this I moved all my resources to src/main/resources. However, I keep getting FileNotFoundExceptions when I try to display any site (simple RequestMapping returning a String):
This is the error I get:
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/index.html]
at org.springframework.web.context.support.ServletContextResource.getInputStream(ServletContextResource.java:157) ~[spring-web-5.0.0.BUILD-SNAPSHOT.jar:5.0.0.BUILD-SNAPSHOT]
at org.thymeleaf.spring5.templateresource.SpringResourceTemplateResource.reader(SpringResourceTemplateResource.java:103) ~[thymeleaf-spring5-3.0.3.M1.jar:3.0.3.M1]
at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:223) ~[thymeleaf-3.0.3.RELEASE.jar:3.0.3.RELEASE]
... 75 common frames omitted
And then I get the same error again when Spring tries to load my error page.
Full http://pastebin.com/raw/Csw5akHJ
Explorer
(Yes I know that only the static folder is available. Good enough for testing.)
Can anyone help me? This is getting a bit frustrating.
If you are using Thymleaf as Template Engine you should add all .html files inside resources/templates
i am not sure if this is your problem but normally i would put all the html pages inside templates directory under resources and all js and css files under static directory.
by doing so js and css files can easily accessed. for eg if i have css directory and test.css inside it. i can simply access it doing
so coming to your problem on my controller i will return pages like this.
#RequestMapping(value="/viewusers",method = RequestMethod.GET)
public String viewUsers(){
return "users/viewusers";
}
in above sample i have viewusers.html under users directory. my users directory is inside templates directory.
OK, I made some headway. While it works fine if I use the default template Engine It stops working as soon as I start using the Thymeleaf one. Apparently the default template Engine can handle classpaths automatically while I needed to switch from SpringResourceTemplateResolver to ClassLoaderTemplateResolver if I want to use thymeleaf.
So far it looks like everything is working fine. Halleluja!

How to start with spring mvc and install dependencies

I am new to spring needed to know how to start and how to import dependencies in spring mvc. i am starting a project for web application possible with single page application
Best possible website to start is https://start.spring.io/ . On this select dependency package you need and get a kick start for your project. You can also install some dependency later as per your need in pom.xml file.
To start from scratch, move on to Build with Gradle.
If you know basics and want code template, do the following:
Download and unzip the source repository or clone it using Git: git clone https://github.com/spring-guides/gs-serving-web-content.git
cd into gs-serving-web-content/initial
Jump ahead to Create a web controller.
When you’re finished, you can check your results against the code in gs-serving-web-content/complete.
Build with Gradle
First you set up a basic build script. You can use any build system you like when building apps with Spring, but the code you need to work with Gradle is included here.
Create the directory structure
In a project directory of your choosing, create the following subdirectory structure; for example, with mkdir -p src/main/java/hello on *nix systems:
└── src
└── main
└── java
└── hello
Create a Gradle build file
Below is the initial Gradle build file.
build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.4.1.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
jar {
baseName = 'gs-serving-web-content'
version = '0.1.0'
}
repositories {
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
compile("org.springframework.boot:spring-boot-starter-thymeleaf")
compile("org.springframework.boot:spring-boot-devtools")
testCompile("junit:junit")
}
The Spring Boot gradle plugin provides many convenient features:
It collects all the jars on the classpath and builds a single, runnable "über-jar", which makes it more convenient to execute and transport your service.
It searches for the public static void main() method to flag as a runnable class.
It provides a built-in dependency resolver that sets the version number to match Spring Boot dependencies. You can override any version you wish, but it will default to Boot’s chosen set of versions.
**Create a web controller**
In Spring’s approach to building web sites, HTTP requests are handled by a controller. You can easily identify these requests by the #Controller annotation. In the following example, the GreetingController handles GET requests for /greeting by returning the name of a View, in this case, "greeting". A View is responsible for rendering the HTML content:
src/main/java/hello/GreetingController.java
package hello;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
#Controller
public class GreetingController {
#RequestMapping("/greeting")
public String greeting(#RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
return "greeting";
}
}
This controller is concise and simple, but there’s plenty going on. Let’s break it down step by step.
The #RequestMapping annotation ensures that HTTP requests to /greeting are mapped to the greeting() method.
The above example does not specify GET vs. PUT, POST, and so forth, because #RequestMapping maps all HTTP operations by default. Use #RequestMapping(method=GET) to narrow this mapping.
#RequestParam binds the value of the query String parameter name into the name parameter of the greeting() method. This query String parameter is not required; if it is absent in the request, the defaultValue of "World" is used. The value of the name parameter is added to a Model object, ultimately making it accessible to the view template.
The implementation of the method body relies on a view technology, in this case Thymeleaf, to perform server-side rendering of the HTML. Thymeleaf parses the greeting.html template below and evaluates the th:text expression to render the value of the ${name} parameter that was set in the controller.
src/main/resources/templates/greeting.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'Hello, ' + ${name} + '!'" />
</body>
</html>
Developing web apps
A common feature of developing web apps is coding a change, restarting your app, and refreshing the browser to view the change. This entire process can eat up a lot of time. To speed up the cycle of things, Spring Boot comes with a handy module known as spring-boot-devtools.
Enable hot swapping
Switches template engines to disable caching
Enables LiveReload to refresh browser automatically
Other reasonable defaults based on development instead of production
Make the application executable
Although it is possible to package this service as a traditional WAR file for deployment to an external application server, the simpler approach demonstrated below creates a standalone application. You package everything in a single, executable JAR file, driven by a good old Java main() method. Along the way, you use Spring’s support for embedding the Tomcat servlet container as the HTTP runtime, instead of deploying to an external instance.
src/main/java/hello/Application.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#SpringBootApplication is a convenience annotation that adds all of the following:
#Configuration tags the class as a source of bean definitions for the application context.
#EnableAutoConfiguration tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.
Normally you would add #EnableWebMvc for a Spring MVC app, but Spring Boot adds it automatically when it sees spring-webmvc on the classpath. This flags the application as a web application and activates key behaviors such as setting up a DispatcherServlet.
#ComponentScan tells Spring to look for other components, configurations, and services in the the hello package, allowing it to find the controllers.
The main() method uses Spring Boot’s SpringApplication.run() method to launch an application. Did you notice that there wasn’t a single line of XML? No web.xml file either. This web application is 100% pure Java and you didn’t have to deal with configuring any plumbing or infrastructure.
Build an executable JAR
You can run the application from the command line with Gradle or Maven. Or you can build a single executable JAR file that contains all the necessary dependencies, classes, and resources, and run that. This makes it easy to ship, version, and deploy the service as an application throughout the development lifecycle, across different environments, and so forth.
If you are using Gradle, you can run the application using ./gradlew bootRun. Or you can build the JAR file using ./gradlew build. Then you can run the JAR file:
java -jar build/libs/gs-serving-web-content-0.1.0.jar
If you are using Maven, you can run the application using ./mvnw spring-boot:run. Or you can build the JAR file with ./mvnw clean package. Then you can run the JAR file:
java -jar target/gs-serving-web-content-0.1.0.jar
The procedure above will create a runnable JAR. You can also opt to build a classic WAR file instead.
Logging output is displayed. The app should be up and running within a few seconds.
Test the App
Now that the web site is running, visit http://localhost:8080/greeting, where you see:
"Hello, World!"
Provide a name query string parameter with http://localhost:8080/greeting?name=User. Notice how the message changes from "Hello, World!" to "Hello, User!":
"Hello, User!"
This change demonstrates that the #RequestParam arrangement in GreetingController is working as expected. The name parameter has been given a default value of "World", but can always be explicitly overridden through the query string.
Add a Home Page
Static resources, like HTML or JavaScript or CSS, can easily be served from your Spring Boot application just be dropping them into the right place in the source code. By default Spring Boot serves static content from resources in the classpath at "/static" (or "/public"). The index.html resource is special because it is used as a "welcome page" if it exists, which means it will be served up as the root resource, i.e. at http://localhost:8080/ in our example. So create this file:
src/main/resources/static/index.html
<!DOCTYPE HTML>
<html>
<head>
<title>Getting Started: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p>Get your greeting here</p>
</body>
</html>
and when you restart the app you will see the HTML at http://localhost:8080/.
how to import dependencies in spring mvc?
Spring MVC or not, to manage dependencies you can use any of the Java packaging tools like Gradle or Maven (there are other option too , like ivy or ANT from ancient world :) )
How to start
https://start.spring.io/ Start any of the spring project from this website, select the dependencies you want. When you click on generate it will download a zip file which contains a project (gradle or maven) with all the dependencies you selected. And then, later you can add or change dependencies by editing your build.gradle (if you have selected gradle) or pom.xml (if you have selected maven).

Switch java spring maven project from linux to windows

I am new to spring development so i am stuck here. Is there any configuration while switching spring + maven project from linux to windows. I am running on tomcat server 7. The project is initially developed in linux. I have moved all the file from linux to wondows. While running on server, i get error The requested resource (/myproject/login/) is not available. Do i need to make any changes or add add in configuation.
Thanks in advance.
I think you have missed something.
Try this.but i'am not sure this is working or not.
Tomcat, by default invoker servlet disabled (commented out in the web.xml file). You have to create a 'servlet' and a 'servlet-mapping' entry in your web.xml.
Once you do, you can get rid of the "servlet/" part of your url.
Check out the following URL for more information regarding the invoker servlet:
http://faq.javaranch.com/view?InvokerServlet

Custom property editors not being registered for NetBeans 7.2 on JRE 1.7

I have a large desktop Java application that is being moved from JRE 1.6 to JRE 1.7. The application uses the NetBeans API for GUI and the SLF4J API with a Log4J backend. The application has two deployment methods, a standalone application (run either by Ant or a Launch4j exe) or a web start via a simple custom HTTP server (Jetty and JNLP servlet).
The application has several NetBeans ModuleInstall components that add property editors onto the Java PropertyEditorManager search path at startup. E.g.
ArrayList<String> editorPaths = new ArrayList<String>();
editorPaths.add(LocationEditor.class.getPackage().getName());
editorPaths.addAll(Arrays.asList(PropertyEditorManager.getEditorSearchPath()));
PropertyEditorManager.setEditorSearchPath(editorPaths.toArray(new String[editorPaths.size()]));
When I migrated to Java 1.7 (u9) these editors were no longer being found in the application, either standalone or web start deployment. The application started fine and the editors were accessable when started from Eclipse. Curious to see why the standalone failed I added some logging statements to report the search path and set the log4j.configuration property via the Ant startup script. The editors were now available. I then launched the .exe (which doesn't accept command line parameters so the log4j configuration wasn't set) and the editors were gone again.
So the differentiator appears to be setting the log4j.configuration parameter. I changed the build so the standlong Ant and .exe referenced this and they work. The web start though doesn't pass this value on (it is loaded internally in the code) and still fails to find the editors. I also removed the log4j setting from the Eclipse run command and the editors vanish again.
I tried registering against type specifically rather than using the search path but still had no success. I'm using the search path as one optional overrides another modules editors but I don't know the module startup order, so that one appends its path to the start of the lookup while the others append to the end.
I've also tried removing all SLF4J to Log4J mappings and using the SLF4J-Simple instead. This made no difference. Adding the parameter back in makes the code work again. The spring-aspects dependency is pulling in a single Log4J reference.
I have a working solution for standalone and I'm sure I can get the new trick working for the webstart, but this reeks of a bad hack to get things working. I rather figure out the cause for the editors going missing. No exceptions are reported and removing logging from all ModuleInstall objects does nothing. All the code still executes, and this includes other features the ModuleInstall executes so I know they are running.
I'm using NetBeans RELEASE72 and the other dependencies are:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.2</version>
</dependency>
Update 1
I've tracked the problem down to our custom logging setup. When this section was removed and the simple logger used the problem didn't occur. When this custom configuration was removed and the slf4j-log4j12 connected again I still got the property editors. However when I enabled the custom logging configuration again the editors failed.
The custom logging configuration ensures we have a logging setup even if nothing is specified by the user. There is also some weird statup process that other users activated where the logging can't be activated until after a weaving process. I suspect that maybe the custom logging setting doesn't work with Java 1.7, or something to do with replacement of default logging handlers redirecting to our custom logging.
In any case it looks like an internal mess, not much the internet can help with.
I'd left the problem and finally came back to it. The problem is that the PropertyEditorManager is now no longer a global singleton, but is a thread local singleton. The search path is registered in a ThreadLocalContext in JDK 1.7, presumably to prevent thread synchronisation issues. However the NetBeans ModuleInstall was being executed on the main thread, and then the property readers were being read on the AWT thread. This meant the AWT thread didn't find any of the property editors.
As a fix I just used the SwingUtilities.invokeLater() to register the property editor path.
I have no idea what caused the other symptons to show.
I have just been tracing a similiar problem;
when I run my NetBeans RCP 7.3 app under java 1.7.0_25
org.netbeans.beaninfo.editors.DateEditor is no longer available to my GUI Property Sheets.
As you suggest, I have worked around the problem by including this code into my GUI module's Installer
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run() {
NodeOp.registerPropertyEditors();
}
});
This solves the problem for now, but it feels kind of hacky ...

Categories