Skip to content

Commit

Permalink
Allow Devtools to be enabled irrespective of the launching ClassLoader
Browse files Browse the repository at this point in the history
Closes gh-21424
  • Loading branch information
wilkinsona committed Nov 12, 2020
1 parent 30bc0ad commit 6df1084
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 7 deletions.
Expand Up @@ -43,14 +43,38 @@ public URL[] getInitialUrls(Thread thread) {
}

/**
* Returns if the thread is for a main invocation. By default checks the name of the
* thread and the context classloader.
* Returns if the thread is for a main invocation. By default {@link #isMain(Thread)
* checks the name of the thread} and {@link #isDevelopmentClassLoader(ClassLoader)
* the context classloader}.
* @param thread the thread to check
* @return {@code true} if the thread is a main invocation
* @see #isMainThread
* @see #isDevelopmentClassLoader(ClassLoader)
*/
protected boolean isMain(Thread thread) {
return thread.getName().equals("main")
&& thread.getContextClassLoader().getClass().getName().contains("AppClassLoader");
return isMainThread(thread) && isDevelopmentClassLoader(thread.getContextClassLoader());
}

/**
* Returns whether the given {@code thread} is considered to be the main thread.
* @param thread the thread to check
* @return {@code true} if it's the main thread, otherwise {@code false}
* @since 2.4.0
*/
protected boolean isMainThread(Thread thread) {
return thread.getName().equals("main");
}

/**
* Returns whether the given {@code classLoader} is one that is typically used during
* development.
* @param classLoader the ClassLoader to check
* @return {@code true} if it's a ClassLoader typically used during development,
* otherwise {@code false}
* @since 2.4.0
*/
protected boolean isDevelopmentClassLoader(ClassLoader classLoader) {
return classLoader.getClass().getName().contains("AppClassLoader");
}

/**
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2019 the original author or authors.
* Copyright 2012-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -64,7 +64,24 @@ private void onApplicationStartingEvent(ApplicationStartingEvent event) {
// It's too early to use the Spring environment but we should still allow
// users to disable restart using a System property.
String enabled = System.getProperty(ENABLED_PROPERTY);
if (enabled == null || Boolean.parseBoolean(enabled)) {
RestartInitializer restartInitializer = null;
if (enabled == null) {
restartInitializer = new DefaultRestartInitializer();
}
else if (Boolean.parseBoolean(enabled)) {
restartInitializer = new DefaultRestartInitializer() {

@Override
protected boolean isDevelopmentClassLoader(ClassLoader classLoader) {
return true;
}

};
logger.info(LogMessage.format(
"Restart enabled irrespective of application packaging due to System property '%s' being set to true",
ENABLED_PROPERTY));
}
if (restartInitializer != null) {
String[] args = event.getArgs();
DefaultRestartInitializer initializer = new DefaultRestartInitializer();
boolean restartOnInitialize = !AgentReloader.isActive();
Expand Down
Expand Up @@ -88,6 +88,14 @@ void disableWithSystemProperty(CapturedOutput output) {
assertThat(output).contains("Restart disabled due to System property");
}

@Test
void enableWithSystemProperty(CapturedOutput output) {
System.setProperty(ENABLED_PROPERTY, "true");
testInitialize(false);
assertThat(Restarter.getInstance()).hasFieldOrPropertyWithValue("enabled", true);
assertThat(output).contains("Restart enabled irrespective of application packaging due to System property");
}

private void testInitialize(boolean failed) {
Restarter.clearInstance();
RestartApplicationListener listener = new RestartApplicationListener();
Expand Down
Expand Up @@ -542,7 +542,10 @@ To include devtools support, add the module dependency to your build, as shown i

NOTE: Developer tools are automatically disabled when running a fully packaged application.
If your application is launched from `java -jar` or if it is started from a special classloader, then it is considered a "`production application`".
If that does not apply to you (i.e. if you run your application from a container), consider excluding devtools or set the `-Dspring.devtools.restart.enabled=false` system property.
You can control this behavior by using the `spring.devtools.restart.enabled` system property.
To enable devtools, irrespective of the classloader used to launch your application, set the `-Dspring.devtools.restart.enabled=true` system property.
This must not be done in a production environment where running devtools is a security risk.
To disable devtools, exclude the dependency or set the `-Dspring.devtools.restart.enabled=false` system property.

TIP: Flagging the dependency as optional in Maven or using the `developmentOnly` configuration in Gradle (as shown above) prevents devtools from being transitively applied to other modules that use your project.

Expand Down

0 comments on commit 6df1084

Please sign in to comment.