Refreshing static content with Spring MVC and Boot - java

I'm evaluating Spring MVC & Boot and AngularJs for building web applications. I've run into the problem that when I make modifications to my static content (html, js, css), I have to restart the application every time. I hope there is a some way of solving that because restarting the whole application for static content changes is not efficient. Every other web app framework I've tried allows updating static content files on the fly(even just Spring MVC and plain old WAR application).
I've setup my project from "Building a RESTful Web Service with Spring Boot Actuator" guide (http://spring.io/guides/gs/actuator-service/). Basically it uses Spring Boot and MVC controllers to create a REST service. In addition, I've used "Consuming a RESTful Web Service with AngularJS" guide (http://spring.io/guides/gs/consuming-rest-angularjs/) to build a frontend with AngularJS. It creates a web page that displays the response from the REST service. The only change I've made is that the requests are made to my application instead of "http://rest-service.guides.spring.io/greeting". My static content is stored in "src/main/resources/public" folder. This setup works correctly except it doesn't reload static content.

A recap of the original problem
I've run into the problem that when I make modifications to my static content (html, js, css), I have to restart the application every time
I had the same problem and finally solved it by adding
<configuration>
<addResources>true</addResources>
</configuration>
to spring-boot-maven-plugin in the pom.xml
I got confused by this spring-boot-devtools thing, but it had no effect whatever I did.
My static content is stored in "src/main/resources/public" folder.
Your path is just fine. src/main/resources/static is also fine.

Ah ... I came across this issue too.
Instead of putting your static content in the classpath src/main/resources/public folder, put them in src/main/webapp, the same as you would any other Java web app. The embedded Tomcat will automatically reload them whenever they change.
As mentioned in the comments, the default configuration will not include the resources that are in src\main\webapp. To get around this issue, you can just add the following to your pom.xml <build> node:
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>validate</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/classes/static</outputDirectory>
<resources>
<resource>
<directory>src/main/webapp</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
By using the resources plugin, you are able to do your local development by running the executable JAR:
java -jar target/.jar
While that is running you can use Chrome Dev Tools or whatever IDE you like for modifying the files, without restarts. However, whenever you run your build, then the package generated will include all of the files under src\main\webapp in src\main\resources\static.

The docs say "all modern IDEs allow reloading of static resources and usually also hot-swapping of Java class changes" (https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/howto.html#howto-hotswapping). It's true. Eclipse does it more or less by default, and I'm not an IntelliJ user, but from what I understand you can configure it to build automatically as well.

A colleague and I came across this issue as well. We found the answer in the IntelliJ documentation...
On the main menu, choose Run | Reload Changed Classes

My solution (written in Kotlin but is quite obvious):
#Controller
class WebController : WebMvcConfigurerAdapter() {
override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
System.getProperty("resources.local.path")?.let {
registry.addResourceHandler("/**").addResourceLocations(it)
}
}
...
}
Main idea is you can add your own resource handler conditionally. E.g. if some system property is set (resources.local.path) then add resource location with value from the property. Then you set this property in development with some reasonable value like '-Dresources.local.path=file:/Users/andrey/Projects/gsp-test/src/main/resources/static/'.
Do not forget trailing slash.

I am using 1.5.8.RELEASE.
It instantly updates my changes especially static files or jsp files.
If you are using Maven. You need to add this in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
And you have to start Spring Boot with this:
mvn clean spring-boot:run
Full example and more detail here https://www.surasint.com/spring-boot-with-auto-update-changed-files-example/

#eigil metioned addResources config for maven build. I'm using spring-boot-gradle-plugin in a gradle build, and I found this Spring Boot github issue
, and the Spring Boot doc mentioned this option too. Just add this directive to build.gradle and run Gradle task bootRun, then resource file refreshes immediately when saved. FYI.

I had the same issue , the solution proposed here seems logical and worked for me
in breif :
1- ctrl+shift+A
2- search for registry
3- in the opened dialogue search for "compiler.automake.allow.when.app.running"
and check it
http://garywaddell.com/2015/11/20/spring-boot-intellij-idea-not-reloading-static-content/

For eclipse you have to activate the Project -> "Build Automatically" option as a minimum configuration.

What I ended up using was Browsersync with grunt. browsersync and grunt watches your static resources on disk and updates the browser when you edit the files. It acts as a kind of proxy. This way you can see changes in UI immediately without building or restarting anything.
Grunt, browsersync, spring boot and angularjs are configured for you if you use JHipster which I used to setup my project.
Granted this requires a lot more tools than just an IDE and is a lot more complicated so I wouldn't recommend this for every project.

spring-boot-devtools is not the solution to "hot deploy" of edited static htm/js
I configured a web facet inside intellij so that when I use it to edit html/js file inside resources/static, intellij then knows to copy the updated file to ./target and the spring boot application I have launched inside the automatically displays that content
see
https://www.jetbrains.com/help/idea/2016.2/configuring-static-content-resources.html

The Java version of #viator 's answer:
#Configuration
class WebMvcConfigurer extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/dist/*.js").addResourceLocations(
"file:src/main/typescript/dist/"
);
}
}

You can do it by just adding one more dependency
you Gradle
compile group: 'org.springframework.boot', name: 'spring-boot-devtools', version: '1.3.0.RELEASE'
In you Pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<version>1.3.0.RELEASE</version>
</dependency>

You have two possebilities how to serve static webcontent
From the classpath (per default src/main/resources/static
or src/main/resources/public or META-INF/resources/)
From the file system (per default src/main/webapp)
If you pick solution 1) - you can safely copy the jar around as the static web content is within that jar. If you want that the server picks up changes, you need to do (auto)hotswapping.
If you pick solution 2) - everything will work out of the box, every change will be automatically picked up. HOWEVER - if you copy the final jar to a different location - things will stop working. That is unless you specify an absolute path in application.properties. For example:
spring.resources.static-locations=file:///C:/myspringbootapp/src/main/webapp
So solution 2) is easier but less portable. Solution 1) is portable but more difficult to use(ide config).

For Spring Boot 2+ with gradle Kotlin dsl:
tasks.bootRun {
sourceResources(sourceSets.getAt(SourceSet.MAIN_SOURCE_SET_NAME))
}
thanks to #briskr's answer for the gradle dsl version :)

I had the same problem with live reloading of static contents in my SpringBoot porject: Now from various solutions posted in StackOverflow, I am able to get the solution. Following are the tools I used for development: IntelliJ Idea & Google Chrome in Ubuntu 18.04
I did the following:
Kept the templates folder in resourses folder itself. (Some solutions I found it to be kept in webapp folder under the main, but I did not get the result)
Add this configuration
<addResources>true</addResources>
</configuration>
to spring-maven-plugin in your POM file.
3.Please don't forget to add this dependency to POM file
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
Add the Live Reload extension to your web browser.
Restart the server using ' mvn clean spring-boot:run ' (only then the changes will be reflected in the build). During server startup you can see the message Live Server started at ....
Load the page using localhost:8080/... and click the LiveReload extension to connect it with the server.
Make any change to your static HTML file in the resources/ templates folder. Save it and check the webbrowser again, it will be reflected there...

Related

Read POM values in external application properties

I have the following properties in the pom file
<name>DemoApplication</name>
<description>Demo spring project</description>
<version>1.0.0-SNAPSHOT</version>
And I have a class that reads the properties from application.yml
But instead of using the application.yml under src/main/resources I am specifying the properties through an external file as follows
java -jar DemoApplication-1.0.0-SNAPSHOT.jar --spring.config.location=application.yml
In this external application properties, I have the following attributes
swagger:
apiTitle: '#project.name#'
apiDescription: '#project.description#'
apiVersion: '#project.version#'
The issue is that the #project.name# and other properties are not being replaced as expected, but are read as-is.
How should the problem be approached?
According that section of the official documentation of Spring Boot v2, you can configure it with :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.7</version>
<configuration>
<delimiters>
<delimiter>#</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>
With useDefaultDelimiters set to false or to true depending on your configuration.
The others sections of that official documentation will be helpful for your use case, especially these one : "77.5 Use YAML for External Properties".
If nothing is working, why don't you are loading a custom Properties file ? It could be loaded as you need without any problem. Just reference it with the correct path when you are starting your program, and inside your program, test if your file config.properties is available and contains what you need to work with.
Of course, the Maven way of loading resources files is the best easy way to go, and it should be a simple Properties file too. I have done exactly that way inside the software I am released to manage my configuration :
Writing a app.properties
Loading that file with Maven at runtime with resource configuration
Expanding properties with classical syntax ${my.prop}
Run the program with a Maven task.
Of course, when you distribute your app as a jar, it is a bit different.
Maybe you can try to write your properties files within a Maven goal.

How to set GOOGLE_APPLICATION_CREDENTIALS in spring boot app

I am attempting to use the google vision library in java. The steps specify that I need to setup my auth credentials in order to start using the this library . I was able to generate my json property file from API Console Credentials page and I placed it in my spring boot app in the resources folder.
I think updated my application.properties file to include the value like so:
GOOGLE_APPLICATION_CREDENTIALS=datg-avatar-generator-9dc9155cd5bd.json
I'm also setting my property source in my controller like so:
#PropertySource("${GOOGLE_APPLICATION_CREDENTIALS}")
However, after doing that I'm still getting an error saying:
java.io.IOException: The Application Default Credentials are not available. They are available if running in Google Compute Engine. Otherwise, the environment variable GOOGLE_APPLICATION_CREDENTIALS must be defined pointing to a file defining the credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
I was able to configure this property using Spring Cloud GCP spring-cloud-gcp-starter-data-datastore, creating a service account project owner, copy the JSON private key to the main resources directory, and setting the following properties in the application.properties
spring.cloud.gcp.project-id=<project-id>
spring.cloud.gcp.credentials.location=classpath:<credentials-private-key>.json
from the documentation
You can find the project id by visiting this page https://support.google.com/googleapi/answer/7014113?hl=en
Go to the API Console.
From the projects list, select Manage all projects. The names and IDs for all the projects you're a member of are displayed.
You can also select the project go the settings and see the project ID
You can use application properties, but you need to use a different StorageOptions builder.
You are probably using
private static Storage storage = StorageOptions.getDefaultInstance().getService();
But if you want to skip the environment variable you need to use:
Credentials credentials = GoogleCredentials
.fromStream(new FileInputStream("path/to/file"));
Storage storage = StorageOptions.newBuilder().setCredentials(credentials)
.setProjectId("my-project-id").build().getService();
Note that the default builder (using environment variables) is better if you are going to deploy your applications to cloud, because then this is automatically filled for you.
You need to set the shell variable. Run this command before mvn run.
export GOOGLE_APPLICATION_CREDENTIALS="/Users/ronnyshibley/Dev/eddress-service-key.json"
For authentication using the service account key, you can set the Environment Variable in your shell.
export GOOGLE_APPLICATION_CREDENTIALS="/Users/username/directory/service-key-file-name.json"
Then you need to start your IDE from the same session. I was stuck after exporting and setting up the environment variable and was still unable to use it.
I tried quitting the current IDE window and restarted the IDE again from the same session.
In my case it was Intellij, so in the terminal itself,
cd project directory
idea .
Or you can also add the environment variable in your bash profile and then source it.
I have tried several ways to do this, and none of them worked. Maven plugin
environmentVariables is the last thing that worked without any problem.
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
<executable>true</executable>
<environmentVariables>
<GOOGLE_APPLICATION_CREDENTIALS>/path/to/the/service-account.json</GOOGLE_APPLICATION_CREDENTIALS>
</environmentVariables>
</configuration>
</plugin>
</plugins>
</build>
You need to add env variable
GOOGLE_APPLICATION_CREDENTIALS=<path to google project json file >
If you are using IntelliJ idea, Edit the Project configuration and add the Environment variable
For images check this
https://www.twilio.com/blog/set-up-env-variables-intellij-idea-java

How to locate Thymeleaf template from spring boot

I am trying to follow this tutorial at http://www.thymeleaf.org/doc/articles/springmvcaccessdata.html to learn how to send responses to Thymeleaf template. But I get this error: Cannot find template location: classpath:/templates/ (please add some templates or check your Thymeleaf configuration)
I put the message.html file in Other Sources directory and inside src/main/resources under <default package>.
So the structure looks like :
SomeProject
-Other Sources
--src/main/resources
---<default package>
----message.html
I was wondering why it shows under <default package> but not under <template> ? Could it be the problem? If so how am I supposed to change it? I am using netbeans and maven. Any ideas please? These are the dependencies I have in my pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
In the controller I have
#RequestMapping(value = "message", method = RequestMethod.GET)
public String messages(Model model) {
model.addAttribute("messages", messageRepository.findAll());
return "message";
}
And in the view:
<ul th:each="message : ${messages}">
<li th:text="${message.id}">1</li>
<li>Title ...</li>
<li th:text="${message.text}">Text ...</li>
</ul>
Spring Boot includes auto-configuration support for the thymeleaf templating engines, your templates will be picked up automatically from src/main/resources/templates.
if you are customize you template location then use below thymeleaf property configuration available in Spring Boot.
spring.thymeleaf.check-template=true # Check that the template exists before rendering it.
spring.thymeleaf.check-template-location=true # Check that the templates location exists.
spring.thymeleaf.enabled=true # Enable MVC Thymeleaf view resolution.
spring.thymeleaf.prefix=classpath:/templates/ # Prefix that gets prepended to view names when building a URL.
spring.thymeleaf.suffix=.html # Suffix that gets appended to view names when building a URL.
The default directory location for thymeleaf templates is:
src/main/resources/templates
The other paths are standard conventions for Spring Boot.
2 things here :
1. If you are using Maven, and I assume no customizations to folder names. Then the folder name should be src instead of source.
2. Once the folder has been renamed move your templates into 'templates' folder inside src/resources this should run fine.
For Thymeleaf template files put in folder src/main/resources/templates/ and it will work for you also do check out http://www.mkyong.com/spring-boot/spring-boot-hello-world-example-thymeleaf/ . Hope you will find it easy to kick start thymeleaf with spring boot.
I had the same problem with Spring boot + Gradle and these are the steps that I followed to resolve it:
Print out the classpath
Once I established that the resources folder was not being included in the classpath, I reloaded the application from disk:
Gradle clean the project
I see a lot of issues regarding the ThymeLeaf /templates not found problem.
For everyone using Eclipse or STS the solution is maybe simpler then any type of configuration.
In the course of the project operation with maven, that is compile, clean, build and others, the final deployment Output Directory on your eclipse workspace (or staging area)
[eclipseprojectrootfolder]/target/
can erase some of the output directory structure and not re-created them it properly.
In my case I went for all sorts of solutions on application.properties, changing the autoconfig on the Spring app to no avail.
The solution was simple:
Just look inside
[eclipseprojectrootfolder]/target/classes
and see if in that directory you can find the directories
[eclipseprojectrootfolder]/target/classes/templates/
and
[eclipseprojectrootfolder]/target/classes/static/
Those are the folders inside the resources directory:
[eclipseprojectrootfolder]/src/main/resources
simply Copy the folders inside [eclipseprojectrootfolder]/src/main/resources, that is the
/templates
/static
into [eclipseprojectrootfolder]/target/classes
That is the Solution.
Spring is complaining exactly because of that ..can not find /templates ... because the directory on the Output deployment folder/directory may indeed not be there ...
Just recreate the directory manually and all will be fine.
Important note, Of course when I mention that a manual copy of the directories may be necessary this means we need to recreate the directories And the files inside that are the templates and static content for thymeleaf.

Externalize Spring Libraries Web Application

Good morning # Everybody,
i would like to know if it is possible to externalize Spring libraries. We have a lot of Applications / Webservices which all use the same Spring - libs and now we are forced to deploy our applications on a Websphere (8.5.0.1) server. Since Websphere and Classloading is a bit different than everything else i knew before ...
Well ...
The Idea is the following:
Create a mavenproject containing all Springlibraries and add this jar-file to the server. We would like to keep developing on a tomcat server and just deploy on a websphere without having to much trouble...
But if i move all my springlibs to an external file, it seems that the application cannot be loaded properly (Classloader issue?).
So if anybody of you could give me a hint or a good suggestion, i would really appreciate that.
And of course, if i miss a fact, why it is not possible?
Edit:
This is how my pom.xml actually looks like (a part of course) and i really, really do not like it ...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<packagingExcludes>WEB-INF/lib/hibernate*.jar,
WEB-INF/lib/*spring*.jar,
WEB-INF/lib/xml-apis*.jar,
WEB-INF/lib/xpp3_min*.jar,
WEB-INF/lib/xstream*.jar,
WEB-INF/lib/antlr*.jar,
WEB-INF/lib/webservices-api*.jar,
WEB-INF/lib/commons-collections*.jar,
WEB-INF/lib/dom4j*.jar,
WEB-INF/lib/javassist*.jar,
WEB-INF/lib/jboss-logging*.jar,
WEB-INF/lib/jboss-transaction-api*.jar,
WEB-INF/lib/primefaces*.jar,
WEB-INF/lib/redmond*.jar,
WEB-INF/lib/sunny*.jar,
WEB-INF/lib/akka*.jar,
WEB-INF/lib/scala*.jar,
WEB-INF/lib/scala*.jar,
WEB-INF/lib/jsf-impl*.jar,
WEB-INF/lib/jsf-api*.jar,
WEB-INF/lib/config-0.3.1.jar,
WEB-INF/lib/commons-collections*.jar,
WEB-INF/lib/commons-lang*.jar,
WEB-INF/lib/cglib-nodep*.jar,
WEB-INF/lib/slf4j-api*.jar,
WEB-INF/lib/quartz*.jar,
WEB-INF/lib/c3p0*.jar,
WEB-INF/lib/ojdbc6*.jar,
WEB-INF/lib/mysql-connector-java*.jar,
WEB-INF/lib/aopalliance-1.0.jar,
WEB-INF/lib/aspectjrt-1.6.8.jar,
WEB-INF/lib/cdi-api-1.0.jar,
WEB-INF/lib/commons-cli-1.2.jar,
WEB-INF/lib/commons-io-2.3.jar,
WEB-INF/lib/commons-logging-1.1.1.jar,
WEB-INF/lib/freemarker-2.3.9.jar,
WEB-INF/lib/javax.inject-1.jar,
WEB-INF/lib/jboss-interceptor-api-1.1.jar,
WEB-INF/lib/jcl-over-slf4j-1.6.1.jar,
WEB-INF/lib/jettison-1.0.jar,
WEB-INF/lib/jsr250-api-1.0.jar,
WEB-INF/lib/validation-api-1.0.0.GA.jar
</packagingExcludes>
</configuration>
</plugin>
but if i do it like that, add this libraries as external, my application works (but JUST on a Websphere, which is absolutely not my intention)
try keeping the spring libraries inside the WEB-INF/lib folder as suggested by #Dhanush Gopinath and than change the classloader to parent last.
To change the classloader go in the Domain Manager console to.
Servers > WebSphere Application Servers > $Server Name > Servers Specific Application Settings
Change the ClassLoader policy to "Single" and class loading mode to "parent last". Check if this helps.

Adding classpath to jetty running in maven integration-test

I'm trying to set up integration tests for a Maven project that produces a war file. (As seen here http://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin/.) However I the war file requires a bunch of .properties files on the classpath, that I don't want to bundle in the war.
Is there a way (preferably through plugin configuration) to add a folder to the classpath used by jetty?
I Googled this and found http://markmail.org/message/awtqrgxxttra3uxx but this, as far as I can tell, does not actually work at all. The .properties files are not found.
This should be possible using the webAppConfig configuration element (sample below taken from this thread):
<webAppConfig>
<contextPath>/nportal</contextPath>
<!-- All I want to do here is add in the /etc/jetty/classes for runtime files. For some reason I have to also add back in the /target/classes directory -->
<extraClasspath>${basedir}/target/classes/;${basedir}/etc/jetty/classes/</extraClasspath>
</webAppConfig>
If you find that the above solution doesn't work for you, consider including the test classpath into your Jetty configuration.
<configuration>
<useTestClasspath>true</useTestClasspath>
...
</configuration>
This will then allow you to place all manner of resources/classes on the test classpath and have them visible to the Jetty server without them creeping into the production code.
You can place your additional configuration files under /src/test/resources and set a property <useTestScope>true</useTestScope> in the plugin configuration as specified here:
useTestScope
If true, the classes from testClassesDirectory and dependencies of scope "test" are placed first on the classpath. By default this is false.

Categories

Resources