2

I have a Grails 2.1 application in development, and I have a service that is calling Thread.currentThread().contextClassLoader.getResource(fileName) to load a configuration file in grails-app/conf (I'm hoping the location doesn't matter, though).

It works just fine, except that I have to restart the grails application to load changes to the config file. I'd really like to be able to read the file contents every single time I ask for the resource, at least during development.

Also, I'd like to avoid writing my own caching system if I can avoid it. The simpler the solution, the better.

My problem seems to be similar to this one with velocity but using getClass().getClassLoader().getResource(path).openStream() does not seem to work.

EDIT: I think I may have left out an important detail. The resource that I am retrieving is non-static. I am using the grails template engine to inject values into the resource like so:

Map bindings = [keys: 'values']
new SimpleTemplateEngine().createTemplate(resource).make(bindings).toString()

Using my debugger, I have found that the resource text isn't changing, but knowing the above may change potential answers.

EDIT #2: I tried using URLConnection.setUseCaches(false) like so with no luck:

URLConnection connection = Thread.currentThread().contextClassLoader.getResource(path).openConnection()
connection.setUseCaches(false)
connection.connect()
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))
String content = reader.readLines().join('\n')
reader.close()
Community
  • 1
  • 1
Eric Wendelin
  • 43,147
  • 9
  • 68
  • 92
  • 1
    Not sure about the original problem you're asking about, but be very careful with new'ing up `SimpleTemplateEngine` instances. It's possible it's been fixed, but I hit some very painful memory leakage issues with that in the past. If I'm remembering right, it was instantiating a new class in the classloader and eventually causing the system to run out of permgen. Caching templates that you've created is really the only way I'll use STE now. – Ted Naleid Aug 15 '12 at 04:22
  • Thanks, Ted. I'll definitely be caching the template at least in non-development envs. – Eric Wendelin Aug 15 '12 at 14:14

4 Answers4

3

If you're using a resource in this manner, you might consider moving it out of grails-app/conf and into /web-app.

When running grails run-app, a cached version of an exploded war is actually running in a container with some outside magic monitoring various paths for changes and re-compiling and re-copying updated files similar to how one might manually hot-swap .class files and other resources.

This monitoring happens out of the box for all files in /web-app. Once the files are there, you can use the ServletContext to get a handle to the file in question. For example, you might do something like:

def templateFile = new File(ServletContextHolder.servletContext.getRealPath('/templates/fileName.gsp')).text

and pass that to your templating engine. For environments that lock down the file system (e.g. Heroku), the getRealPath method will likely not work. You can, alternatively, use something like:

import org.apache.commons.io.IOUtils
def template =  IOUtils.toString(ServletContextHolder.servletContext.getResourceAsStream('/templates/fileName.gsp'), "UTF-8")

The downsides to this approach:

  • You'll need access to the ServletContext which will require you to have a container loaded. Therefore, no unit testing on this piece of coding; you'll want an integration test instead.
  • Putting resources like this in /web-app will make them public. Alternatively, you could place them in /web-app/WEB-INF if privacy is a concern.

Hope this helps!

Johnny Wey
  • 916
  • 7
  • 6
1

Could you use Spring's ResourceUtils to do this? http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/util/ResourceUtils.html

It'd look something like ResourceUtils.getFile("classpath:path/to/file.xyz")

marc esher
  • 4,871
  • 3
  • 36
  • 51
1

getClass().getClassLoader().getResource(path) returns a URL object, so the caching is likely being performed by URL/URLConnection.

Try using URLConnection.setDefaultUseCaches(false) when your application starts up (maybe only in dev environment for performance reasons). This can also be set on a per-URLConnection basis with the setUseCaches() method.

prunge
  • 22,460
  • 3
  • 73
  • 80
1

Just a thought, but Grails services are normally singletons. If you want the file re-read on every request, you might try changing the scope to prototype.

kousen
  • 2,897
  • 3
  • 28
  • 26
  • This doesn't fix the issue, but I'm giving you a karmic vote because you've answered other Grails questions in other places. :) – Eric Wendelin Aug 15 '12 at 15:17