Grails

CLONE -grails run-app: DefaultGrailsPlugin leaks filehandles in checkModified()

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: 1.0.4
  • Fix Version/s: 1.2-M2
  • Component/s: None
  • Labels:
    None
  • Environment:
  • Patch Submitted:
    Yes

Description

Clean grails 1.0.4 (no third-party plugins) in development mode (grails run-app) appears to leak filehandles in DefaultGrailsPlugin. To reproduce the problem:

$ grails create-app test
Welcome to Grails 1.0.4 - http://grails.org/
Licensed under Apache Standard License 2.0
Grails home is set to: /home/tecumseh/work/grails/grails

Base Directory: /home/tecumseh/scratch
Note: No plugin scripts found
Running script /home/tecumseh/work/grails/grails/scripts/CreateApp.groovy
[etc.]

$ cd test
$ grails run-app
[wait for it to start server]

and in another shell, with the only java process on the system being grails:

$ while true; do lsof -p `pidof java` | wc -l; sleep 1; done
[...]
551
581
596
611
626
656

and so on, until the number of open filehandles exceeds my ulimit -n of 1024, at which point grails run-app goes into death-throes with:

Error automatically restarting container: IO error scanning directory '/home/tecumseh/scratch/test/src/groovy' ...
: IO error scanning directory '/home/tecumseh/scratch/test/src/groovy'
at org.apache.tools.ant.DirectoryScanner.scandir(DirectoryScanner.java:1065)
at org.apache.tools.ant.DirectoryScanner.checkIncludePatterns(DirectoryScanner.java:856)
at org.apache.tools.ant.DirectoryScanner.scan(DirectoryScanner.java:817)
at org.apache.tools.ant.types.AbstractFileSet.getDirectoryScanner(AbstractFileSet.java:435)
at org.apache.tools.ant.taskdefs.MatchingTask.getDirectoryScanner(MatchingTask.java:192)
at org.codehaus.groovy.ant.Groovyc.execute(Groovyc.java:535)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:105)
at org.apache.tools.ant.Task.perform(Task.java:348)
at Package_groovy$_run_closure10.doCall(Package_groovy:349)
at RunApp_groovy$_run_closure3.doCall(RunApp_groovy:130)
at RunApp_groovy$_run_closure3.doCall(RunApp_groovy)
at RunApp_groovy$_run_closure1.doCall(RunApp_groovy:58)
at RunApp_groovy$_run_closure1.doCall(RunApp_groovy)
at gant.Gant.dispatch(Gant.groovy:271)
at gant.Gant.this$2$dispatch(Gant.groovy)
at gant.Gant.invokeMethod(Gant.groovy)
at gant.Gant.processTargets(Gant.groovy:436)
at gant.Gant.processArgs(Gant.groovy:372)

The time taken to exhaust the 1024 file-handle quota is usually within 5-10 minutes of doing a grails run-app, with no activity on the system (i.e., I am not editing files, or making web requests to the embedded jetty)

I've attached a patch that fixes the worst of the file handle leaks for me, but this is a quick hack that may not be the Right Thing (TM), and there might be more cases of the leak out there than I've met.

The problem seems to be that the OpenJDK URLConnection class (an instance of sun.net.www.protocol.file.FileURLConnection) opens an input stream to the file when urlConnection.lastModified() is called, and this input stream is not closed when the plugin checks for file modifications.

From a quick glance at the grails trunk source, the bug also seems to affect trunk, but I have not tested trunk.

I'm new to Grails and JIRA bug reporting, so if I'm doing something wrong, or filing bugs in the wrong place, please let me know.

Activity

Hide
Markus Mueller added a comment -

We're encountering same issue with Grails 1.1 and I just found out there's one place missing.

In GroovyPagesTemplateEngine.java replace method "establishLastModified" with the following code, so this should be fine:

private long establishLastModified(Resource resource) {
        if(resource ==null)return -1;
        long lastModified;
        URLConnection urlc = null;
		try {
            urlc = resource.getURL().openConnection();

            urlc.setDoInput(false);
            urlc.setDoOutput(false);

            lastModified = urlc.getLastModified();
        } catch (FileNotFoundException fnfe) {
            lastModified = -1;
        } catch (IOException e) {
            lastModified = -1;
        }
		finally {
			if (urlc != null) 			{
				try {
					InputStream is = urlc.getInputStream();
					if (is != null) {
						is.close();
					}
				}
				catch (IOException e) {
					// ignore
				}
			}
		}
        return lastModified;
    }

Sorry, I'm not very familiar with git yet so I'm posting the code here in jira. Hope it helps, too!

Kind regards,
Markus

Show
Markus Mueller added a comment - We're encountering same issue with Grails 1.1 and I just found out there's one place missing. In GroovyPagesTemplateEngine.java replace method "establishLastModified" with the following code, so this should be fine:
private long establishLastModified(Resource resource) {
        if(resource ==null)return -1;
        long lastModified;
        URLConnection urlc = null;
		try {
            urlc = resource.getURL().openConnection();

            urlc.setDoInput(false);
            urlc.setDoOutput(false);

            lastModified = urlc.getLastModified();
        } catch (FileNotFoundException fnfe) {
            lastModified = -1;
        } catch (IOException e) {
            lastModified = -1;
        }
		finally {
			if (urlc != null) 			{
				try {
					InputStream is = urlc.getInputStream();
					if (is != null) {
						is.close();
					}
				}
				catch (IOException e) {
					// ignore
				}
			}
		}
        return lastModified;
    }
Sorry, I'm not very familiar with git yet so I'm posting the code here in jira. Hope it helps, too! Kind regards, Markus

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: