Spring Loaded is an open source JVM agent that improves on the JVMs hot-code replace functionality. It does for instance make it possible to add, remove and rename methods and add annotations or change their values. Using it while developing means you can avoid restarting the application and save a lot of time.
Since Blossom makes heavy use of annotations Spring Loaded can be a real time saver.
Spring Loaded has basic support for Spring built-in but it's not enough to cover what we need for Blossom. To find templates, dialogs, etc, Blossom scans the beans available and introspects their classes to find the information it needs in their annotations. This needs to be done again every time Spring Loaded reloads a class. For this we need a custom plugin.
The Spring Loaded plugin for Blossom version 1.0 was released today and is available in the Magnolia CMS maven repository.
When added to your project it will automatically find all BlossomDispatcherServlets and refresh their ApplicationContexts. Refreshing an ApplicationContext makes it dispose of all beans its currently managing and creating new ones. It's a pretty costly operation but it's what's needed to make Blossom see the changes. Most applications have their main infrastructure beans that takes time to start in the root web application context and only simpler beans in DispatcherServlets so this should be fine.
It will by default only refresh if it sees a class reloaded that has a Blossom annotation or the @Controller annotation. This can be changed so it refreshes on any change by setting the BlossomSpringLoadedPlugin.refreshOnAnyChange system property to true.
-DBlossomSpringLoadedPlugin.refreshOnAnyChange=true
If you have standard DispatcherServlets in your projects it can be useful to have those refreshed as well. If you set the BlossomSpringLoadedPlugin.refreshAllDispatcherServlets system property to true the plugin will refresh those too.
-DBlossomSpringLoadedPlugin.refreshAllDispatcherServlets=true
To use the plugin start by downloading the Spring Loaded JAR. Then add it to the JVM using these JVM arguments:
-javaagent:<path to the JAR> -noverify
Then add the plugin dependency to your project.
After this you can start the application in debug mode and reload changes using hot-swap. But since Magnolia projects are multi module maven projects its unfortunately not this easy. Spring Loaded 1.2.3 doesn't reload classes from JAR files. It's been said this is coming in the next release, 1.2.4.
Until 1.2.4 is out you need to reconfigure your development environment to place classes inside WEB-INF/classes instead of inside JAR files in WEB-INF/lib.
I don't know how to do it in Eclipse but I suspect its possible to configure in project properties in the Build Path and Deployment Assembly views. Please leave a comment if you know how to do this.
In IntelliJ you open Project Structure, select Artifacts, click on your artifact (the one that ends in war exploded), then on the Output Layout tab you expand WEB-INF/lib and remove your module. Then scroll back up and add it to WEB-INF/classes.
If you later do reimport of your maven projects in IDEA you'll need to do this again because IDEA will restore the settings.
Hopefully, this should save you a bunch of time!
A huge credit to Thomas Kratz who shared his work on using Magnolia with Blossom and Spring Loaded on his blog and for his ideas on how to improve it!
Thomas also wrote a module for using Thymeleaf with Blossom, great work!
Since Blossom makes heavy use of annotations Spring Loaded can be a real time saver.
Spring Loaded has basic support for Spring built-in but it's not enough to cover what we need for Blossom. To find templates, dialogs, etc, Blossom scans the beans available and introspects their classes to find the information it needs in their annotations. This needs to be done again every time Spring Loaded reloads a class. For this we need a custom plugin.
The Spring Loaded plugin for Blossom version 1.0 was released today and is available in the Magnolia CMS maven repository.
When added to your project it will automatically find all BlossomDispatcherServlets and refresh their ApplicationContexts. Refreshing an ApplicationContext makes it dispose of all beans its currently managing and creating new ones. It's a pretty costly operation but it's what's needed to make Blossom see the changes. Most applications have their main infrastructure beans that takes time to start in the root web application context and only simpler beans in DispatcherServlets so this should be fine.
It will by default only refresh if it sees a class reloaded that has a Blossom annotation or the @Controller annotation. This can be changed so it refreshes on any change by setting the BlossomSpringLoadedPlugin.refreshOnAnyChange system property to true.
-DBlossomSpringLoadedPlugin.refreshOnAnyChange=true
If you have standard DispatcherServlets in your projects it can be useful to have those refreshed as well. If you set the BlossomSpringLoadedPlugin.refreshAllDispatcherServlets system property to true the plugin will refresh those too.
-DBlossomSpringLoadedPlugin.refreshAllDispatcherServlets=true
To use the plugin start by downloading the Spring Loaded JAR. Then add it to the JVM using these JVM arguments:
-javaagent:<path to the JAR> -noverify
Then add the plugin dependency to your project.
<dependency> <groupId>info.magnolia.blossom</groupId> <artifactId>magnolia-blossom-spring-loaded</artifactId> <version>1.0.3</version> </dependency>
After this you can start the application in debug mode and reload changes using hot-swap. But since Magnolia projects are multi module maven projects its unfortunately not this easy. Spring Loaded 1.2.3 doesn't reload classes from JAR files. It's been said this is coming in the next release, 1.2.4.
Until 1.2.4 is out you need to reconfigure your development environment to place classes inside WEB-INF/classes instead of inside JAR files in WEB-INF/lib.
I don't know how to do it in Eclipse but I suspect its possible to configure in project properties in the Build Path and Deployment Assembly views. Please leave a comment if you know how to do this.
In IntelliJ you open Project Structure, select Artifacts, click on your artifact (the one that ends in war exploded), then on the Output Layout tab you expand WEB-INF/lib and remove your module. Then scroll back up and add it to WEB-INF/classes.
If you later do reimport of your maven projects in IDEA you'll need to do this again because IDEA will restore the settings.
Hopefully, this should save you a bunch of time!
A huge credit to Thomas Kratz who shared his work on using Magnolia with Blossom and Spring Loaded on his blog and for his ideas on how to improve it!
Thomas also wrote a module for using Thymeleaf with Blossom, great work!
Thank you for all your work Tobias! I am not a programmer, however I have been evaluating Magnolia as a CMS solution for my new project. Cheers!
ReplyDeleteHey Tobias, awesome plug-in! Since we are using JRebel for most projects, we wrote a port of the plug-in. Will try to keep it up to date with the spring loaded plug-in! Regards, Arne.
ReplyDeleteNice work Arne! That should be really helpful to a lot of people!
DeleteThis comment has been removed by the author.
ReplyDeleteHi Tobias,
ReplyDeletethanks for you great work.
I'm currently trying to get spring-loaded work.
Spring loaded seems to support add/modify/delete methods/fields/constructors.
However I didn't get this part working with intellij and tomcat.
Field, Methods, Constructors are not reloaded when they are added or deleted.
Annotation Reloading an new registering of Templates is working.
Is this expected behaviour or am I doing something wrong?
Alex
It still does not work for me. I get the same error as before
ReplyDeleteHere is my config in pom.xml. I am using springloaded-1.2.7.RELEASE
-javaagent:${settings.localRepository}/org/springframework/springloaded/1.2.7.RELEASE/springloaded-1.2.7.RELEASE.jar
-noverify
-Xdebug
-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9000
-Xnoagent
-Djava.compiler=NONE
-agentpath:${project.build.directory}/../lib/lib/${jrebel.agent}
-DBlossomSpringLoadedPlugin.refreshOnAnyChange=true
-DBlossomSpringLoadedPlugin.refreshAllDispatcherServlets=true
-Drebel.log=trace