You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@myfaces.apache.org by "Derek C. Ashmore (JIRA)" <de...@myfaces.apache.org> on 2007/06/29 05:56:05 UTC

[jira] Created: (TOMAHAWK-1040) Apparent File Handle Leak with Tomahawk 1.1.5 or 1.1.6 with MyFaces 1.1.5

Apparent File Handle Leak with Tomahawk 1.1.5 or 1.1.6 with MyFaces 1.1.5
-------------------------------------------------------------------------

                 Key: TOMAHAWK-1040
                 URL: https://issues.apache.org/jira/browse/TOMAHAWK-1040
             Project: MyFaces Tomahawk
          Issue Type: Bug
    Affects Versions: 1.1.6, 1.1.5
         Environment: Linux version 2.6.9-42.0.10.plus.c4smp
Java 1.5.0_09-b03
Tomcat 5.5.20

            Reporter: Derek C. Ashmore
            Priority: Critical


We experienced a file handle leak when using Tomahawk components that require static resources, such as the JSCookMenu, Calendar, and Tabbed Pane.  The handle allocations never go away.  Eventually, the container becomes inoperative and has to be restarted.

We experience this bug under Tomahawk 1.1.5 and 1.1.6 using Myfaces 1.1.5.  We do *not* experience the leak under Myfaces/Tomahawk 1.1.3.  I have no information about whether the bug appears under 1.1.4.

We detected the leak behavior by monitoring our container process using lsof.

Although it's a hack and I don't like it, I was able to avoid the leak by writing a custom AddResource class (and changing the configuration so that my custom class was used instead of the default).  My custom AddResource class works by caching request resources and preventing more than one I/O to a given resource.

My source code (for what it's worth) is attached below.  I'd be happy to provide additional information -- I would rather see this bug fixed in 1.1.6 then have my hack around for ever.

Thanks for looking at this.

---------------------------------------------------------------------------------------------------------
package com.ies.common.ui.common.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.myfaces.renderkit.html.util.DefaultAddResource;
import org.apache.myfaces.renderkit.html.util.DefaultResourceProvider;
import org.apache.myfaces.renderkit.html.util.MyFacesResourceLoader;

import com.ies.common.error.IESRuntimeException;

/**
 * Needed to prevent 'too many file handles' problem.
 * @author D. Ashmore
 *
 */
public class IesAddResource extends DefaultAddResource {
	
	private static final String MYFACES_RESOURCE = "MyFacesResourceLoader";
	private static final String MYFACES_PACKAGE_PREFIX = "org.apache.myfaces.custom.";
	private static Map<String, Resource> resourceMap = new ConcurrentHashMap<String, Resource>();

	public IesAddResource() {
		// No Op
	}

	@Override
	public void serveResource(ServletContext context, HttpServletRequest request, HttpServletResponse response) throws IOException {
		String pathInfo = request.getPathInfo();
        String uri = request.getContextPath() + request.getServletPath()
                + (pathInfo == null ? "" : pathInfo);
        
        if (uri.indexOf(MYFACES_RESOURCE) > 0) {
        	StringTokenizer token = new StringTokenizer(uri, "/");
        	if (token.countTokens() < 6) {
        		super.serveResource(context, request, response);
        		return;
        	}
        	
        	// Bypass the first five tokens as non-essential.
        	token.nextToken();
        	token.nextToken();
        	token.nextToken();
        	token.nextToken();
        	token.nextToken();
        	String className = token.nextToken();
        	Class componentClass = null;
        	try {
        		componentClass = Class.forName(MYFACES_PACKAGE_PREFIX + className);
        	}
        	catch (Exception e) {
        		IESRuntimeException ire = new IESRuntimeException("Class not found.");
				ire.addLabeledValue("className", className);
				
				throw ire;
        	}
        	
        	DefaultResourceProvider provider = new DefaultResourceProvider(componentClass);
        	String resource = uri.substring(
        			uri.lastIndexOf(className) + 
        			className.length() + 1);
        	String resourceKey = className + resource;
        	
        	Resource resourceData = (Resource)resourceMap.get(resourceKey);
        	InputStream is = null;
        	MyResourceLoader loader = new MyResourceLoader();
        	
        	if (resourceData == null) {    	
        		resourceData = new Resource();
        		try {
        			is = provider.getInputStream(context, resource);
        			if (is == null) {
        				IESRuntimeException ire = new IESRuntimeException("Resource not found.");
        				ire.addLabeledValue("resource", resource);
        				
        				throw ire;
        			}
        			resourceData.setData(inputStreamReader(is));
        			resourceData.setEncoding(provider.getEncoding(context, resource));
        			resourceData.setLastModified(provider.getLastModified(context, resource));
        			resourceData.setContentLength(provider.getContentLength(context, resource));
        		}
        		finally {if (is != null) is.close();}
        		resourceMap.put(resourceKey, resourceData);

        	}
        	is = new ByteArrayInputStream(resourceData.data);
        	
        	loader.defineContentHeaders(request, response, resource, resourceData.data.length, resourceData.encoding);
        	loader.defineCaching(request, response, resource, resourceData.lastModified);
        	loader.writeResource(request, response, is);
        }
        else super.serveResource(context, request, response);
	}
	
	private byte[] inputStreamReader(InputStream is) throws IOException {
		ByteArrayOutputStream outStream = new ByteArrayOutputStream(512000);
		
		byte[] bArray = new byte[8192];
		int nbrBytesRead = is.read(bArray);
		while (nbrBytesRead > 0) {
			outStream.write(bArray, 0, nbrBytesRead);
			nbrBytesRead = is.read(bArray);
		}
		
		return outStream.toByteArray();
	}
	
	private static class Resource {
		byte[] data;
		long lastModified;
		long contentLength;
		String encoding;
		
		public Resource() {
		}

		public byte[] getData() {
			return data;
		}

		public void setData(byte[] data) {
			this.data = data;
		}

		public String getEncoding() {
			return encoding;
		}

		public void setEncoding(String encoding) {
			this.encoding = encoding;
		}

		public long getLastModified() {
			return lastModified;
		}

		public void setLastModified(long lastModified) {
			this.lastModified = lastModified;
		}

		public long getContentLength() {
			return contentLength;
		}

		public void setContentLength(long contentLength) {
			this.contentLength = contentLength;
		}
	}
	
	private static class MyResourceLoader extends MyFacesResourceLoader {

		@Override
		public void defineCaching(HttpServletRequest request, HttpServletResponse response, String resource, long lastModified) {
			super.defineCaching(request, response, resource, lastModified);
		}

		@Override
		public void defineContentHeaders(HttpServletRequest request, HttpServletResponse response, String resource, int contentLength, String contentEncoding) {
			super.defineContentHeaders(request, response, resource, contentLength,
					contentEncoding);
		}

		@Override
		public void writeResource(HttpServletRequest request, HttpServletResponse response, InputStream in) throws IOException {
			super.writeResource(request, response, in);
		}
		
	}

}

-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.