Compare commits

...

6 Commits

Author SHA1 Message Date
Olga MaciaszekSharma cdc052ce46 Add more logging. 1 year ago
Olga MaciaszekSharma 4833252b0f Add docs, javadocs and tests. 1 year ago
Olga MaciaszekSharma c4a4bc5590 Fix log level. 1 year ago
Olga MaciaszekSharma d1a7b9164e Rename property. 1 year ago
Olga MaciaszekSharma 9b3d806bae Fix property naming. 1 year ago
Olga MaciaszekSharma 89834505fa Refresh Scope on restart. 1 year ago
  1. 6
      docs/modules/ROOT/pages/spring-cloud-commons/application-context-services.adoc
  2. 1
      docs/modules/ROOT/partials/_configprops.adoc
  3. 8
      spring-cloud-context/src/main/java/org/springframework/cloud/autoconfigure/RefreshAutoConfiguration.java
  4. 77
      spring-cloud-context/src/main/java/org/springframework/cloud/context/refresh/RefreshScopeLifecycle.java
  5. 6
      spring-cloud-context/src/main/resources/META-INF/additional-spring-configuration-metadata.json
  6. 26
      spring-cloud-context/src/test/java/org/springframework/cloud/autoconfigure/RefreshAutoConfigurationTests.java
  7. 51
      spring-cloud-context/src/test/java/org/springframework/cloud/context/refresh/RefreshScopeLifecycleTests.java

6
docs/modules/ROOT/pages/spring-cloud-commons/application-context-services.adoc

@ -217,6 +217,12 @@ The configuration property must be present in order to update the value after a @@ -217,6 +217,12 @@ The configuration property must be present in order to update the value after a
a value in your application you might want to switch your logic to rely on its absence instead. Another option would be to rely
on the value changing rather than not being present in the application's configuration.
[refresh-scope-on-restart]
=== Refresh Scope on Restart
In order to allow seamlessly refreshing beans on restart, which is especially useful for applications running with JVM Checkpoint Restore (for example with https://github.com/CRaC[Project CRaC]), we now instantiate a `RefreshScopeLifecycle` bean that will trigger Context Refresh on restart, resulting in rebinding configuration properties and refreshing any `@RefreshScope`-annotated beans. This behaviour can be disabled by setting the value of `spring.cloud.refresh.on-restart.enabled` to `false`.
[[encryption-and-decryption]]
== Encryption and Decryption

1
docs/modules/ROOT/partials/_configprops.adoc

@ -75,6 +75,7 @@ @@ -75,6 +75,7 @@
|spring.cloud.refresh.enabled | `+++true+++` | Enables autoconfiguration for the refresh scope and associated features.
|spring.cloud.refresh.extra-refreshable | `+++true+++` | Additional class names for beans to post process into refresh scope.
|spring.cloud.refresh.never-refreshable | `+++true+++` | Comma separated list of class names for beans to never be refreshed or rebound.
|spring.cloud.refresh.on-restart.enabled | `+++true+++` | Enable refreshing context on start.
|spring.cloud.service-registry.auto-registration.enabled | `+++true+++` | Whether service auto-registration is enabled. Defaults to true.
|spring.cloud.service-registry.auto-registration.fail-fast | `+++false+++` | Whether startup fails if there is no AutoServiceRegistration. Defaults to false.
|spring.cloud.service-registry.auto-registration.register-management | `+++true+++` | Whether to register the management as a service. Defaults to true.

8
spring-cloud-context/src/main/java/org/springframework/cloud/autoconfigure/RefreshAutoConfiguration.java

@ -43,6 +43,7 @@ import org.springframework.boot.context.properties.bind.Binder; @@ -43,6 +43,7 @@ import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.cloud.context.refresh.ConfigDataContextRefresher;
import org.springframework.cloud.context.refresh.ContextRefresher;
import org.springframework.cloud.context.refresh.LegacyContextRefresher;
import org.springframework.cloud.context.refresh.RefreshScopeLifecycle;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.cloud.endpoint.event.RefreshEventListener;
import org.springframework.cloud.logging.LoggingRebinder;
@ -66,6 +67,7 @@ import org.springframework.util.StringUtils; @@ -66,6 +67,7 @@ import org.springframework.util.StringUtils;
*
* @author Dave Syer
* @author Venil Noronha
* @author Olga Maciaszek-Sharma
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RefreshScope.class)
@ -117,6 +119,12 @@ public class RefreshAutoConfiguration { @@ -117,6 +119,12 @@ public class RefreshAutoConfiguration {
return new ConfigDataContextRefresher(context, scope, properties);
}
@ConditionalOnProperty(value = "spring.cloud.refresh.on-restart.enabled", matchIfMissing = true)
@Bean
RefreshScopeLifecycle refreshScopeLifecycle(ContextRefresher contextRefresher) {
return new RefreshScopeLifecycle(contextRefresher);
}
@Bean
public RefreshEventListener refreshEventListener(ContextRefresher contextRefresher) {
return new RefreshEventListener(contextRefresher);

77
spring-cloud-context/src/main/java/org/springframework/cloud/context/refresh/RefreshScopeLifecycle.java

@ -0,0 +1,77 @@ @@ -0,0 +1,77 @@
/*
* Copyright 2012-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.context.refresh;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.Lifecycle;
/**
* A {@link Lifecycle} implementation that triggers {@link ContextRefresher#refresh()} to
* be called on restart.
*
* @author Olga Maciaszek-Sharma
* @since 4.1.0
*/
public class RefreshScopeLifecycle implements Lifecycle {
private static final Log LOG = LogFactory.getLog(RefreshScopeLifecycle.class);
private final ContextRefresher contextRefresher;
private final Object lifecycleMonitor = new Object();
private volatile boolean running = true;
public RefreshScopeLifecycle(ContextRefresher contextRefresher) {
this.contextRefresher = contextRefresher;
}
@Override
public void start() {
synchronized (lifecycleMonitor) {
if (!isRunning()) {
if (LOG.isInfoEnabled()) {
LOG.info("Refreshing context on restart.");
}
Set<String> keys = contextRefresher.refresh();
if(LOG.isInfoEnabled()){
LOG.info("Refreshed keys: " + keys);
}
}
running = true;
}
}
@Override
public void stop() {
synchronized (lifecycleMonitor) {
if (isRunning()) {
running = false;
}
}
}
@Override
public boolean isRunning() {
return running;
}
}

6
spring-cloud-context/src/main/resources/META-INF/additional-spring-configuration-metadata.json

@ -53,6 +53,12 @@ @@ -53,6 +53,12 @@
"type": "java.lang.Boolean",
"description": "Enable the DecryptEnvironmentPostProcessor.",
"defaultValue": true
},
{
"name": "spring.cloud.refresh.on-restart.enabled",
"type": "java.lang.Boolean",
"description": "Enable refreshing context on start.",
"defaultValue": true
}
]
}

26
spring-cloud-context/src/test/java/org/springframework/cloud/autoconfigure/RefreshAutoConfigurationTests.java

@ -22,11 +22,13 @@ import org.junit.jupiter.api.Test; @@ -22,11 +22,13 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.system.CapturedOutput;
import org.springframework.boot.test.system.OutputCaptureExtension;
import org.springframework.cloud.context.refresh.ContextRefresher;
@ -38,9 +40,10 @@ import static org.assertj.core.api.BDDAssertions.then; @@ -38,9 +40,10 @@ import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Dave Syer
* @author Olga Maciaszek-Sharma
*/
@ExtendWith(OutputCaptureExtension.class)
public class RefreshAutoConfigurationTests {
class RefreshAutoConfigurationTests {
private static ConfigurableApplicationContext getApplicationContext(WebApplicationType type, Class<?> configuration,
String... properties) {
@ -49,7 +52,7 @@ public class RefreshAutoConfigurationTests { @@ -49,7 +52,7 @@ public class RefreshAutoConfigurationTests {
}
@Test
public void noWarnings(CapturedOutput output) {
void noWarnings(CapturedOutput output) {
try (ConfigurableApplicationContext context = getApplicationContext(WebApplicationType.NONE, Config.class)) {
then(context.containsBean("refreshScope")).isTrue();
then(output.toString()).doesNotContain("WARN");
@ -57,7 +60,7 @@ public class RefreshAutoConfigurationTests { @@ -57,7 +60,7 @@ public class RefreshAutoConfigurationTests {
}
@Test
public void disabled() {
void disabled() {
try (ConfigurableApplicationContext context = getApplicationContext(WebApplicationType.SERVLET, Config.class,
"spring.cloud.refresh.enabled:false")) {
then(context.containsBean("refreshScope")).isFalse();
@ -65,7 +68,7 @@ public class RefreshAutoConfigurationTests { @@ -65,7 +68,7 @@ public class RefreshAutoConfigurationTests {
}
@Test
public void refreshables() {
void refreshables() {
try (ConfigurableApplicationContext context = getApplicationContext(WebApplicationType.NONE, Config.class,
"config.foo=bar", "spring.cloud.refresh.refreshable:" + SealedConfigProps.class.getName())) {
context.getBean(SealedConfigProps.class);
@ -84,7 +87,7 @@ public class RefreshAutoConfigurationTests { @@ -84,7 +87,7 @@ public class RefreshAutoConfigurationTests {
}
@Test
public void neverRefreshable() {
void neverRefreshable() {
try (ConfigurableApplicationContext context = getApplicationContext(WebApplicationType.NONE, Config.class,
"countingconfig.foo=bar",
"spring.cloud.refresh.never-refreshable:" + CountingConfigProps.class.getName())) {
@ -94,6 +97,19 @@ public class RefreshAutoConfigurationTests { @@ -94,6 +97,19 @@ public class RefreshAutoConfigurationTests {
}
}
@Test
void refreshScopeLifecylePresentByDefault() {
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class))
.run(context -> assertThat(context).hasBean("refreshScopeLifecycle"));
}
@Test
void refreshScopeLifecyleDisabledWithProp() {
new ApplicationContextRunner().withConfiguration(AutoConfigurations.of(RefreshAutoConfiguration.class))
.withPropertyValues("spring.cloud.refresh.on-restart.enabled=false")
.run(context -> assertThat(context).doesNotHaveBean("refreshScopeLifecycle"));
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration(exclude = DataSourceAutoConfiguration.class)
@EnableConfigurationProperties({ SealedConfigProps.class, CountingConfigProps.class })

51
spring-cloud-context/src/test/java/org/springframework/cloud/context/refresh/RefreshScopeLifecycleTests.java

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
/*
* Copyright 2012-2023 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.context.refresh;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* Tests for {@link RefreshScopeLifecycle}.
*
* @author Olga Maciaszek-Sharma
*/
class RefreshScopeLifecycleTests {
ContextRefresher contextRefresher = mock();
private final RefreshScopeLifecycle lifecycle = new RefreshScopeLifecycle(contextRefresher);
@Test
void shouldRefreshContextOnRestart() {
lifecycle.stop();
lifecycle.start();
verify(contextRefresher).refresh();
}
@Test
void shouldNotRefreshContextOnStart() {
lifecycle.start();
verifyNoInteractions(contextRefresher);
}
}
Loading…
Cancel
Save