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()
4

4 に答える 4

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!

于 2012-08-15T04:43:12.413 に答える
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")

于 2012-08-14T23:03:15.163 に答える
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.

于 2012-08-15T00:28:04.290 に答える
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.

于 2012-08-15T02:03:57.527 に答える