4

I find that the basic example setup of Spring Boot with Spring Loaded does not work according to the documentation. I followed the instructions at http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#getting-started-first-application to create a basic application, the only difference is that I put my class into a package and used the release version of the parent pom. Application works fine. Then I added spring loaded to the pom file as described in the doco.

I run the application with mvn spring-boot:run.

Changing my controller output e.g. "Hello World" to "Hello me" does not hot reload.

I tried running as a java application with the javaagent command line parameter described in other places, but that doesn't work either.

The pom looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>myproject</artifactId>
<version>0.0.1-SNAPSHOT</version>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.0.RELEASE</version>
</parent>

<!-- Additional lines to be added here... -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <dependencies>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>springloaded</artifactId>
                    <version>1.2.1.RELEASE</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

Compiled classes are found here: project_dir\target\classes (I'm using Eclipse and the m2e plugin).

Startup log:

[INFO] Scanning for projects...
[INFO] 
[INFO] Using the builder org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder with a thread count of 1
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building myproject 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] >>> spring-boot-maven-plugin:1.2.0.RELEASE:run (default-cli) @ myproject >>>
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ myproject ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 0 resource
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ myproject ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to C:\dev\src\scrapi\target\classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ myproject ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:\dev\src\scrapi\src\test\resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ myproject ---
[INFO] No sources to compile
[INFO] 
[INFO] <<< spring-boot-maven-plugin:1.2.0.RELEASE:run (default-cli) @ myproject <<<
[INFO] 
[INFO] --- spring-boot-maven-plugin:1.2.0.RELEASE:run (default-cli) @ myproject ---

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.2.0.RELEASE)

2015-01-02 13:23:25.311  INFO 2180 --- [.Example.main()] first.Example                            : Starting Example on DEFRAVD804016 with PID 2180 (C:\dev\src\scrapi\target\classes started by john in C:\dev\src\scrapi)
2015-01-02 13:23:25.359  INFO 2180 --- [.Example.main()] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@44354005: startup date [Fri Jan 02 13:23:25 CET 2015]; root of context hierarchy
2015-01-02 13:23:26.193  INFO 2180 --- [.Example.main()] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2015-01-02 13:23:27.121  INFO 2180 --- [.Example.main()] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8182/http
2015-01-02 13:23:27.689  INFO 2180 --- [.Example.main()] o.apache.catalina.core.StandardService   : Starting service Tomcat
2015-01-02 13:23:27.691  INFO 2180 --- [.Example.main()] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.0.15
2015-01-02 13:23:27.814  INFO 2180 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2015-01-02 13:23:27.814  INFO 2180 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2457 ms
2015-01-02 13:23:28.653  INFO 2180 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2015-01-02 13:23:28.657  INFO 2180 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2015-01-02 13:23:28.657  INFO 2180 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'characterEncodingFilter' to: [/*]
2015-01-02 13:23:28.883  INFO 2180 --- [.Example.main()] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@44354005: startup date [Fri Jan 02 13:23:25 CET 2015]; root of context hierarchy
2015-01-02 13:23:28.995  INFO 2180 --- [.Example.main()] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto java.lang.String first.Example.home()
2015-01-02 13:23:28.997  INFO 2180 --- [.Example.main()] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2015-01-02 13:23:28.998  INFO 2180 --- [.Example.main()] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[text/html],custom=[]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
2015-01-02 13:23:29.112  INFO 2180 --- [.Example.main()] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2015-01-02 13:23:29.112  INFO 2180 --- [.Example.main()] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2015-01-02 13:23:29.160  INFO 2180 --- [.Example.main()] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2015-01-02 13:23:29.268  INFO 2180 --- [.Example.main()] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2015-01-02 13:23:29.394  INFO 2180 --- [.Example.main()] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8182/http
2015-01-02 13:23:29.399  INFO 2180 --- [.Example.main()] first.Example                            : Started Example in 4.417 seconds (JVM running for 11.803)

What do I need to do to make Spring Loaded work in this case?

Fletch
  • 4,829
  • 2
  • 41
  • 55
  • Make sure the class is compiled when you change it and copied to the target directory (usually the default with Eclipse, but check your project settings for "build automatically"). – Dave Syer Dec 31 '14 at 14:49
  • @DaveSyer yes, "build automatically" is on and the the timestamp on the class under target/classes gets updated when I save. But no hot reload. – Fletch Jan 02 '15 at 11:22
  • Do you see the agent being added in logs as the app starts? I assume the controller is working before the change? – Dave Syer Jan 02 '15 at 11:26
  • Thanks for the help @DaveSyer. Yes, the controller is working fine. No, I don't see the agent in the logs. I've edited the question to include the startup logs. – Fletch Jan 02 '15 at 12:32
  • I can see the log is missing. It's like you are not using the pom you posted (Eclipse playing tricks?). Works for me on the command line. Did you try that? – Dave Syer Jan 02 '15 at 13:48
  • @DaveSyer I downloaded the latest version of maven and ran it like this: "C:\dev\tools\maven-3.2.5\bin\mvn clean spring-boot:run" with no change to the results. Nevertheless I've now got an interesting workaround, see my answer below. – Fletch Jan 03 '15 at 11:19

3 Answers3

2

I have never get spring loaded working this way. Personaly I use the "old-way" which worked every time for me. Please try to follow this:

Open run/debug configuration in eclipse.

Debug configuration dialog

In the left side expand Java Application option and select your run configuration. App in my case. It is common mistake setting wrong configuration so please make sure that project and main class is correct.

Main settings

Edit VM arguments and set it to -javaagent:<spring_loaded_jar_file_path> -noverify. If you dont have spring loaded localy, simplest way to obtain it is checkout their official repo and run ./gradlew.

VM arguments

A gradle build script is included, run './gradlew build' to rebuild the agent - it will be created as something like: springloaded/build/libs/springloaded-1.1.5.BUILD-SNAPSHOT.jar

Now you can just launch your new config. Before you check things in browser, please check your configuration in eclipse. Right click your java application a select properties.

Debug - thread view

Then you should see that your application is running with javaagent.

JVM properties

Please let us know if this works for you.

svobol13
  • 1,842
  • 3
  • 25
  • 40
1

OK I have at least a workaround which I stumbled upon by accident. Hopefully this will help someone to work out exactly what is going on. Summary: it works when a debugger is configured in the pom.

I wanted to enable debugging, so I added the following to the pom as described in the plugin doco:

<configuration>
  <jvmArguments>
    -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005
  </jvmArguments>
</configuration>

Then I restarted the application, and lo and behold, I get this in the logs before the spring logo:

[INFO] Attaching agents: [C:\dev\maven_local\org\springframework\springloade
d\1.2.1.RELEASE\springloaded-1.2.1.RELEASE.jar]
Listening for transport dt_socket at address: 5005

Startup then ceases, the application will not begin to service requests until a debugger is attached (is this normal? EDIT: rereading the page I linked to, I see that this is expected. It would be great if there were a way for startup to proceed without needing to attach the debugger, but leaving the debug port open). Once a debugger is attached, the Spring logo and the rest of the startup logs appear. Then I detach the debugger (just to prove that reloading is not via the debugger), the application continues servicing requests, and sure enough, hot reloading works!

Thanks a lot to @DaveSyer for helping to discard various other possibilities. This will do for now, though as it's not documented, I guess this requirement is not by design and it would be fantastic if it were to work in all cases.

Fletch
  • 4,829
  • 2
  • 41
  • 55
  • I can only think it's something weird with the windows platform. BTW it's the `suspend=y` that makes the JVM pause on startup. – Dave Syer Jan 03 '15 at 13:02
  • Hmm, I guess so. But then you would think that more people would be complaining. Oh well, let's see if it comes up more frequently. Thanks for the suspend tip - yep it works with suspend=n. – Fletch Jan 04 '15 at 11:06
0

Try adding Spring Loaded as a dependency to the Spring Boot Maven plugin:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <dependencies>
                <dependency>
                    <groupId>org.springframework</groupId>
                    <artifactId>springloaded</artifactId>
                    <version>${spring-loaded.version}</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

I've Spring Loaded running with a simple Spring Cloud application.

joran
  • 2,815
  • 16
  • 18