1717
1818package io .microsphere .spring .config .context .annotation ;
1919
20+ import io .microsphere .lang .function .ThrowableAction ;
21+ import io .microsphere .spring .config .env .event .PropertySourcesChangedEvent ;
2022import org .junit .After ;
2123import org .junit .Before ;
2224import org .junit .Test ;
2325import org .springframework .beans .factory .BeanDefinitionStoreException ;
26+ import org .springframework .context .ApplicationListener ;
2427import org .springframework .context .ConfigurableApplicationContext ;
2528import org .springframework .context .annotation .AnnotationConfigApplicationContext ;
2629import org .springframework .core .env .CompositePropertySource ;
2730import org .springframework .core .env .ConfigurableEnvironment ;
2831import org .springframework .core .env .MutablePropertySources ;
2932import org .springframework .core .env .PropertySource ;
33+ import org .springframework .core .io .Resource ;
3034
35+ import java .io .File ;
36+ import java .io .FileOutputStream ;
37+ import java .io .IOException ;
38+ import java .io .OutputStream ;
3139import java .util .Collection ;
3240import java .util .Iterator ;
3341import java .util .ListIterator ;
42+ import java .util .Map ;
43+ import java .util .Properties ;
44+ import java .util .concurrent .atomic .AtomicBoolean ;
3445import java .util .function .BiConsumer ;
46+ import java .util .function .Consumer ;
3547
48+ import static io .microsphere .io .FileUtils .forceDelete ;
49+ import static io .microsphere .lang .function .ThrowableAction .execute ;
50+ import static java .lang .Thread .sleep ;
51+ import static java .util .UUID .randomUUID ;
3652import static org .junit .Assert .assertEquals ;
3753import static org .junit .Assert .assertNotNull ;
3854import static org .junit .Assert .assertSame ;
3955import static org .junit .Assert .assertTrue ;
4056import static org .springframework .core .env .StandardEnvironment .SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME ;
4157import static org .springframework .core .env .StandardEnvironment .SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME ;
58+ import static org .springframework .core .io .support .PropertiesLoaderUtils .loadProperties ;
4259
4360/**
4461 * {@link ResourcePropertySourceLoader} Test
@@ -52,6 +69,10 @@ public class ResourcePropertySourceLoaderTest {
5269
5370 static final Class <ResourcePropertySource > RESOURCE_PROPERTY_SOURCE_CLASS = ResourcePropertySource .class ;
5471
72+ static final String PROPERTIES_DIRECTORY_RESOURCE_LOCATION = "classpath:/META-INF/test/" ;
73+
74+ static final String PROPERTIES_RESOURCE_LOCATION = PROPERTIES_DIRECTORY_RESOURCE_LOCATION + "*.properties" ;
75+
5576 @ Before
5677 public void before () {
5778
@@ -120,6 +141,63 @@ public void testOnAutoRefreshedConfig() {
120141 }, AutoRefreshedConfig .class );
121142 }
122143
144+ @ Test
145+ public void testOnFileCreated () {
146+ testInSpringContainer ((context , environment ) -> {
147+ execute (() -> {
148+ Resource propertiesDirectoryResource = context .getResource (PROPERTIES_DIRECTORY_RESOURCE_LOCATION );
149+ assertTrue (propertiesDirectoryResource .exists ());
150+ File propertiesDirectory = propertiesDirectoryResource .getFile ();
151+ File cPropertiesFile = new File (propertiesDirectory , "c.properties" );
152+ try {
153+ testOnFileCreated (context , cPropertiesFile );
154+ } finally {
155+ // recovery
156+ delete (cPropertiesFile );
157+ }
158+ });
159+ }, AutoRefreshedConfig .class );
160+ }
161+
162+ @ Test
163+ public void testOnFileModified () {
164+ testInSpringContainer ((context , environment ) -> {
165+ execute (() -> {
166+ Resource bPropertiesResource = context .getResource (PROPERTIES_DIRECTORY_RESOURCE_LOCATION + "b.properties" );
167+ assertTrue (bPropertiesResource .exists ());
168+ Properties bProperties = loadProperties (bPropertiesResource );
169+ File bPropertiesResourceFile = bPropertiesResource .getFile ();
170+ try {
171+ testOnFile (context , bPropertiesResourceFile , new Properties (bProperties ));
172+ } finally {
173+ // recovery
174+ writePropertiesFile (bPropertiesResourceFile , bProperties );
175+ }
176+ });
177+ }, AutoRefreshedConfig .class );
178+ }
179+
180+ @ Test
181+ public void testOnFileDeleted () {
182+ testInSpringContainer ((context , environment ) -> {
183+ execute (() -> {
184+ Resource aPropertiesResource = context .getResource (PROPERTIES_DIRECTORY_RESOURCE_LOCATION + "a.properties" );
185+ assertTrue (aPropertiesResource .exists ());
186+ Properties aProperties = loadProperties (aPropertiesResource );
187+ File aPropertiesResourceFile = aPropertiesResource .getFile ();
188+ try {
189+ testOnFileDeleted (context , aPropertiesResourceFile , removedProperties -> {
190+ assertEquals ("1" , removedProperties .get ("a" ));
191+ assertEquals ("3" , removedProperties .get ("b" ));
192+ });
193+ } finally {
194+ // recovery
195+ writePropertiesFile (aPropertiesResourceFile , aProperties );
196+ }
197+ });
198+ }, AutoRefreshedConfig .class );
199+ }
200+
123201 void assertDefaultPropertySource (ConfigurableEnvironment environment , Class <?> introspectedClass ) {
124202 PropertySource <?> propertySource = assertLastPropertySource (environment );
125203 assertDefaultPropertySourceName (propertySource , introspectedClass );
@@ -225,33 +303,95 @@ void testInSpringContainer(BiConsumer<ConfigurableApplicationContext, Configurab
225303 context .close ();
226304 }
227305
228- @ ResourcePropertySource ("classpath*:/META-INF/test/*.properties" )
306+ void testOnFileCreated (ConfigurableApplicationContext context , File newPropertiesFile ) throws Throwable {
307+ testOnFile (context , newPropertiesFile , new Properties ());
308+ }
309+
310+ void testOnFile (ConfigurableApplicationContext context , File propertiesFile , Properties properties ) throws Throwable {
311+
312+ // watches the properties file
313+ AtomicBoolean notified = new AtomicBoolean ();
314+
315+ String propertyName = propertiesFile .getName ();
316+ String propertyValue = randomUUID ().toString ();
317+
318+ context .addApplicationListener ((ApplicationListener <PropertySourcesChangedEvent >) event -> {
319+ notified .set (true );
320+ ConfigurableEnvironment environment = context .getEnvironment ();
321+ assertEquals (propertyValue , environment .getProperty (propertyName ));
322+ });
323+
324+ // appends the new content
325+ properties .setProperty (propertyName , propertyValue );
326+
327+ // waits for being notified
328+ waits (notified , () -> writePropertiesFile (propertiesFile , properties ));
329+ }
330+
331+ void testOnFileDeleted (ConfigurableApplicationContext context , File deletedPropertiesFile , Consumer <Map <String , Object >> removedPropertiesConsumer ) throws Throwable {
332+ // watches the properties file
333+ AtomicBoolean notified = new AtomicBoolean ();
334+
335+ context .addApplicationListener ((ApplicationListener <PropertySourcesChangedEvent >) event -> {
336+ notified .set (true );
337+ Map <String , Object > removedProperties = event .getRemovedProperties ();
338+ removedPropertiesConsumer .accept (removedProperties );
339+ });
340+
341+ // waits for being notified
342+ waits (notified , () -> delete (deletedPropertiesFile ));
343+ }
344+
345+ void writePropertiesFile (File propertiesFile , Properties properties ) throws IOException {
346+ try (OutputStream outputStream = new FileOutputStream (propertiesFile )) {
347+ properties .store (outputStream , null );
348+ }
349+ }
350+
351+ void waits (AtomicBoolean notified , ThrowableAction action ) throws Throwable {
352+ // waits for being notified
353+ for (int i = 0 ; i < 100 ; i ++) {
354+ if (notified .get ()) {
355+ break ;
356+ }
357+ action .execute ();
358+ sleep (500 );
359+ }
360+ }
361+
362+ static void delete (File file ) throws IOException {
363+ if (file .exists ()) {
364+ forceDelete (file );
365+ }
366+ }
367+
368+ @ ResourcePropertySource (PROPERTIES_RESOURCE_LOCATION )
229369 static class DefaultConfig {
230370 }
231371
232372 @ ResourcePropertySource (
233373 name = "test-property-source" ,
234- value = "classpath*:/META-INF/test/*.properties"
374+ value = PROPERTIES_RESOURCE_LOCATION
235375 )
236376 static class NamedConfig {
237377 }
238378
239379 @ ResourcePropertySource (
240- value = "classpath*:/META-INF/test/*.properties" ,
380+ value = PROPERTIES_RESOURCE_LOCATION ,
241381 first = true
242382 )
243383 static class FirstConfig {
244384 }
245385
246386 @ ResourcePropertySource (
247- value = "classpath*:/META-INF/test/*.properties" ,
387+ value = PROPERTIES_RESOURCE_LOCATION ,
248388 before = SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME
249389 )
250390 static class BeforeConfig {
251391 }
252392
253393 @ ResourcePropertySource (
254- value = "classpath*:/META-INF/test/*.properties" ,
394+ value = PROPERTIES_RESOURCE_LOCATION ,
255395 after = SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME
256396 )
257397 static class AfterConfig {
@@ -269,7 +409,7 @@ static class NotFoundConfig {
269409 }
270410
271411 @ ResourcePropertySource (
272- value = "classpath*:/META-INF/test/*.properties" ,
412+ value = PROPERTIES_RESOURCE_LOCATION ,
273413 autoRefreshed = true
274414 )
275415 static class AutoRefreshedConfig {
0 commit comments