From b56eef236e40ee0fa739957700fdfc9bb7a3bfad Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Fri, 15 Jan 2016 11:19:24 +0000 Subject: [PATCH] Close Reader used by MustacheViewResolver when compiling a Template Previously, MustacheViewResolver would create an InputStreamReader that wraps the template Resource's InputStream but would fail to close the Reader. When the InputStream was a FileInputStream, this caused the resolver to leak file handles. This commit updates the resolver to close the Reader once the Template has been compiled, thereby allowing any underlying resources to be cleaned up immediately, rather than having to wait for the JVM to exit. Closes gh-4921 --- .../mustache/web/MustacheViewResolver.java | 10 +++++-- .../web/MustacheViewResolverTests.java | 30 ++++++++++++++++++- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolver.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolver.java index c2acbc437b0..dbe66848ce7 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolver.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -106,7 +106,13 @@ public class MustacheViewResolver extends UrlBasedViewResolver { } private Template createTemplate(Resource resource) throws IOException { - return this.compiler.compile(getReader(resource)); + Reader reader = getReader(resource); + try { + return this.compiler.compile(reader); + } + finally { + reader.close(); + } } private Reader getReader(Resource resource) throws IOException { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolverTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolverTests.java index 8c3955d3e3c..7ed58b45448 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolverTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/mustache/web/MustacheViewResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2016 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. @@ -16,11 +16,14 @@ package org.springframework.boot.autoconfigure.mustache.web; +import java.io.InputStream; import java.util.Locale; +import org.fusesource.hawtbuf.ByteArrayInputStream; import org.junit.Before; import org.junit.Test; +import org.springframework.core.io.Resource; import org.springframework.mock.web.MockServletContext; import org.springframework.web.context.support.StaticWebApplicationContext; import org.springframework.web.servlet.View; @@ -29,11 +32,16 @@ import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; /** * Tests for {@link MustacheViewResolver}. * * @author Dave Syer + * @author Andy Wilkinson */ public class MustacheViewResolverTests { @@ -85,4 +93,24 @@ public class MustacheViewResolverTests { } + @Test + public void templateResourceInputStreamIsClosed() throws Exception { + final Resource resource = mock(Resource.class); + given(resource.exists()).willReturn(true); + InputStream inputStream = new ByteArrayInputStream(new byte[0]); + InputStream spyInputStream = spy(inputStream); + given(resource.getInputStream()).willReturn(spyInputStream); + this.resolver = new MustacheViewResolver(); + this.resolver.setApplicationContext(new StaticWebApplicationContext() { + + @Override + public Resource getResource(String location) { + return resource; + } + + }); + this.resolver.loadView("foo", null); + verify(spyInputStream).close(); + } + }