myfaces-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Derek C. Ashmore (JIRA)" <...@myfaces.apache.org>
Subject [jira] Created: (TOMAHAWK-1040) Apparent File Handle Leak with Tomahawk 1.1.5 or 1.1.6 with MyFaces 1.1.5
Date Fri, 29 Jun 2007 03:56:05 GMT
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.


Mime
View raw message