Index: src/org/vfny/geoserver/wfs/responses/DescribeResponse.java
===================================================================
--- src/org/vfny/geoserver/wfs/responses/DescribeResponse.java	(revision 4345)
+++ src/org/vfny/geoserver/wfs/responses/DescribeResponse.java	(working copy)
@@ -11,6 +11,7 @@
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -69,6 +70,14 @@
     private String xmlResponse = new String();
 
     /**
+     * Returns any extra headers that this service might want to set in the HTTP response object.
+     * @see org.vfny.geoserver.Response#getResponseHeaders()
+     */
+    public HashMap getResponseHeaders() {
+    	return null;
+    }
+    
+    /**
      * Constructor with request.
      *
      * @param request The DescribeFeatureType request object.
Index: src/org/vfny/geoserver/wfs/responses/TransactionResponse.java
===================================================================
--- src/org/vfny/geoserver/wfs/responses/TransactionResponse.java	(revision 4345)
+++ src/org/vfny/geoserver/wfs/responses/TransactionResponse.java	(working copy)
@@ -87,6 +87,14 @@
     public TransactionResponse() {
         transaction = null;
     }
+    
+    /**
+     * Returns any extra headers that this service might want to set in the HTTP response object.
+     * @see org.vfny.geoserver.Response#getResponseHeaders()
+     */
+    public HashMap getResponseHeaders() {
+    	return null;
+    }
 
     public void execute(Request request) throws ServiceException, WfsException {
         if (!(request instanceof TransactionRequest)) {
Index: src/org/vfny/geoserver/wfs/responses/LockResponse.java
===================================================================
--- src/org/vfny/geoserver/wfs/responses/LockResponse.java	(revision 4345)
+++ src/org/vfny/geoserver/wfs/responses/LockResponse.java	(working copy)
@@ -6,6 +6,7 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -64,6 +65,14 @@
     LockRequest request;
 
     /**
+     * Returns any extra headers that this service might want to set in the HTTP response object.
+     * @see org.vfny.geoserver.Response#getResponseHeaders()
+     */
+    public HashMap getResponseHeaders() {
+    	return null;
+    }
+    
+    /**
      * Constructor
      *
      * @param gs DOCUMENT ME!
Index: src/org/vfny/geoserver/wfs/responses/FeatureResponse.java
===================================================================
--- src/org/vfny/geoserver/wfs/responses/FeatureResponse.java	(revision 4345)
+++ src/org/vfny/geoserver/wfs/responses/FeatureResponse.java	(working copy)
@@ -6,6 +6,7 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -87,6 +88,14 @@
         request = null;
         featureLock = null;
     }
+    
+    /**
+     * Returns any extra headers that this service might want to set in the HTTP response object.
+     * @see org.vfny.geoserver.Response#getResponseHeaders()
+     */
+    public HashMap getResponseHeaders() {
+    	return null;
+    }
 
     /**
      * DOCUMENT ME!
Index: src/org/vfny/geoserver/wfs/responses/WFSCapabilitiesResponse.java
===================================================================
--- src/org/vfny/geoserver/wfs/responses/WFSCapabilitiesResponse.java	(revision 4345)
+++ src/org/vfny/geoserver/wfs/responses/WFSCapabilitiesResponse.java	(working copy)
@@ -7,6 +7,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.HashMap;
 import java.util.logging.Logger;
 
 import javax.xml.transform.TransformerException;
@@ -38,6 +39,14 @@
     private byte[] rawResponse;
 
     /**
+     * Returns any extra headers that this service might want to set in the HTTP response object.
+     * @see org.vfny.geoserver.Response#getResponseHeaders()
+     */
+    public HashMap getResponseHeaders() {
+    	return null;
+    }
+    
+    /**
      * DOCUMENT ME!
      *
      * @param request DOCUMENT ME!
Index: src/org/vfny/geoserver/global/dto/FeatureTypeInfoDTO.java
===================================================================
--- src/org/vfny/geoserver/global/dto/FeatureTypeInfoDTO.java	(revision 4345)
+++ src/org/vfny/geoserver/global/dto/FeatureTypeInfoDTO.java	(working copy)
@@ -109,6 +109,18 @@
 
     /** Holds the location of the file that contains schema information.*/
     private File schemaFile;
+    
+    /**
+     * This value is added the headers of generated maps, marking them as being both
+     * "cache-able" and designating the time for which they are to remain valid.
+     *  The specific header added is "Cache-Control: max-age="
+     */
+    private String cacheMaxAge;
+    
+    /**
+     * Should we be adding the CacheControl: max-age header to outgoing maps which include this layer?
+     */
+    private boolean cachingEnabled;
 
     /**
      * FeatureTypeInfo constructor.
@@ -164,6 +176,9 @@
         dirName = dto.getDirName();
         schemaName = dto.getSchemaName();
         schemaBase = dto.getSchemaBase();
+        
+        cachingEnabled = dto.isCachingEnabled();
+        cacheMaxAge = dto.getCacheMaxAge();
     }
 
     /**
@@ -243,6 +258,9 @@
         r = r && (dirName == f.getDirName());
         r = r && (schemaName == f.getSchemaName());
         r = r && (schemaBase == f.getSchemaBase());
+        
+        r = r && (isCachingEnabled() == f.isCachingEnabled());
+        r = r && (getCacheMaxAge() != null && getCacheMaxAge().equals(f.getCacheMaxAge()));
 
         return r;
     }
@@ -278,6 +296,13 @@
         if (SRS != 0) {
             r = SRS % r;
         }
+        
+        if (cacheMaxAge != null) {
+        	r *= cacheMaxAge.hashCode();
+        }
+        
+        if (cachingEnabled)
+        	r += 1;
 
         return r;
     }
@@ -734,6 +759,23 @@
         + ", latLongBBOX: " + latLongBBox + "\n  SRS: " + SRS + ", schema:"
         + schema + ", schemaName: " + schemaName + ", dirName: " + dirName
         + ", title: " + title + "\n  definitionQuery: " + definitionQuery
-        + ", defaultStyle: " + defaultStyle + ", legend icon: " + legendURL;
+        + ", defaultStyle: " + defaultStyle + ", legend icon: " + legendURL
+        + ", caching?: " + cachingEnabled + ", max-age: " + cacheMaxAge;
     }
+
+	public boolean isCachingEnabled() {
+		return cachingEnabled;
+	}
+
+	public void setCachingEnabled(boolean cachingEnabled) {
+		this.cachingEnabled = cachingEnabled;
+	}
+
+	public String getCacheMaxAge() {
+		return cacheMaxAge;
+	}
+
+	public void setCacheMaxAge(String cacheMaxAge) {
+		this.cacheMaxAge = cacheMaxAge;
+	}
 }
Index: src/org/vfny/geoserver/global/FeatureTypeInfo.java
===================================================================
--- src/org/vfny/geoserver/global/FeatureTypeInfo.java	(revision 4345)
+++ src/org/vfny/geoserver/global/FeatureTypeInfo.java	(working copy)
@@ -166,6 +166,19 @@
     /** Holds the location of the file that contains schema information. */
     private File schemaFile;
     
+    
+    /**
+     * This value is added the headers of generated maps, marking them as being both
+     * "cache-able" and designating the time for which they are to remain valid.
+     *  The specific header added is "Cache-Control: max-age="
+     */
+    private String cacheMaxAge;
+    
+    /**
+     * Should we be adding the CacheControl: max-age header to outgoing maps which include this layer?
+     */
+    private boolean cachingEnabled;
+    
     /** 
      * dont use this unless you know what you're doing.  its for TemporaryFeatureTypeInfo.
      *
@@ -221,6 +234,8 @@
         schemaFile = dto.getSchemaFile();
         SRS = dto.getSRS();
         title = dto.getTitle();
+        cacheMaxAge = dto.getCacheMaxAge();
+        cachingEnabled = dto.isCachingEnabled();
     }
 
     /**
@@ -264,6 +279,8 @@
         dto.setSchemaName( getSchemaName() );        
         dto.setSRS(SRS);
         dto.setTitle(title);
+        dto.setCacheMaxAge(cacheMaxAge);
+        dto.setCachingEnabled(cachingEnabled);
 
         return dto;
     }
@@ -1060,6 +1077,38 @@
     	}
     	return result;
     }
+    
+    /**
+     * This value is added the headers of generated maps, marking them as being both
+     * "cache-able" and designating the time for which they are to remain valid.
+     *  The specific header added is "Cache-Control: max-age="
+     * @return a string representing the number of seconds to be added to the "Cache-Control: max-age=" header
+     */
+	public String getCacheMaxAge() {
+		return cacheMaxAge;
+	}
+    /**
+     * 
+     * @param cacheMaxAge a string representing the number of seconds to be added to the "Cache-Control: max-age=" header
+     */
+	public void setCacheMaxAge(String cacheMaxAge) {
+		this.cacheMaxAge = cacheMaxAge;
+	}
+	
+	/**
+	 * Should we add the cache-control: max-age header to maps containing this layer?
+	 * @return true if we should, false if we should omit the header
+	 */
+	public boolean isCachingEnabled() {
+		return cachingEnabled;
+	}
+	/**
+	 * Sets whether we should add the cache-control: max-age header to maps containing this layer
+	 * @param cachingEnabled true if we should add the header, false if we should omit the header
+	 */
+	public void setCachingEnabled(boolean cachingEnabled) {
+		this.cachingEnabled = cachingEnabled;
+	}
 
 }
  
Index: src/org/vfny/geoserver/global/xml/XMLConfigWriter.java
===================================================================
--- src/org/vfny/geoserver/global/xml/XMLConfigWriter.java	(revision 4345)
+++ src/org/vfny/geoserver/global/xml/XMLConfigWriter.java	(working copy)
@@ -826,6 +826,21 @@
                 m.put("default", ft.getDefaultStyle());
                 cw.attrTag("styles", m);
             }
+            
+            
+            
+            m = new HashMap();
+            if (ft.getCacheMaxAge() != null) {
+            	m.put("maxage",ft.getCacheMaxAge());
+            }
+            if (ft.isCachingEnabled()) {
+            	m.put("enabled", "true");
+            } else {
+            	m.put("enabled", "false");
+            }
+            cw.attrTag("cacheinfo", m);
+            
+            
 
             if (ft.getDefinitionQuery() != null) {
                 cw.openTag("definitionQuery");
Index: src/org/vfny/geoserver/global/xml/XMLConfigReader.java
===================================================================
--- src/org/vfny/geoserver/global/xml/XMLConfigReader.java	(revision 4345)
+++ src/org/vfny/geoserver/global/xml/XMLConfigReader.java	(working copy)
@@ -1096,6 +1096,12 @@
         if (tmp != null) {
             ft.setDefaultStyle(ReaderUtils.getAttribute(tmp, "default", false));
         }
+        
+        Element cacheInfo = ReaderUtils.getChildElement(fTypeRoot, "cacheinfo");
+        if (cacheInfo != null) {
+        	ft.setCacheMaxAge(ReaderUtils.getAttribute(cacheInfo, "maxage", true));
+        	ft.setCachingEnabled(Boolean.parseBoolean(ReaderUtils.getAttribute(cacheInfo, "enabled", true)));
+        }
 
         // Modif C. Kolbowicz - 06/10/2004
         Element legendURL = ReaderUtils.getChildElement(fTypeRoot, "LegendURL");
Index: src/org/vfny/geoserver/form/data/TypesEditorForm.java
===================================================================
--- src/org/vfny/geoserver/form/data/TypesEditorForm.java	(revision 4345)
+++ src/org/vfny/geoserver/form/data/TypesEditorForm.java	(working copy)
@@ -110,6 +110,11 @@
 
     /** FeatureType abstract */
     private String description;
+    /** The amount of time to use for the CacheControl: max-age parameter in maps generated from this featuretype **/
+    private String cacheMaxAge;
+    /** Should we add the CacheControl: max-age header to maps generated from this featureType? **/
+    private boolean cachingEnabled;
+    private boolean cachingEnabledChecked = false;
 
     /**
      * One of a select list - simplest is AbstractBaseClass.
@@ -172,13 +177,10 @@
      */
     public void reset(ActionMapping mapping, HttpServletRequest request) {
         super.reset(mapping, request);
-
-
-dataMinX="";
-dataMinY="";
-dataMaxX="";
-dataMaxY="";
-
+		dataMinX="";
+		dataMinY="";
+		dataMaxX="";
+		dataMaxY="";
         action = "";
 
         ServletContext context = getServlet().getServletContext();
@@ -199,6 +201,10 @@
         this.styleId = type.getDefaultStyle();
 
         description = type.getAbstract();
+        
+        this.cacheMaxAge = type.getCacheMaxAge();
+        this.cachingEnabled = type.isCachingEnabled();
+        cachingEnabledChecked = false;
 
         Envelope bounds = type.getLatLongBBox();
 
@@ -439,6 +445,14 @@
                     new ActionError("error.latLonBoundingBox.invalid", badNumber));
             }
         }
+        
+        try {
+        	Integer.parseInt(cacheMaxAge);
+        } catch (NumberFormatException nfe) {
+        	errors.add("cacheMaxAge", new ActionError("error.cacheMaxAge.malformed", nfe));
+        } catch (Throwable t) {
+        	errors.add("cacheMaxAge", new ActionError("error.cacheMaxAge.error", t));
+        }
 
         return errors;
     }
@@ -853,4 +867,25 @@
     {
     	return dataMaxY;
     }
+
+	public String getCacheMaxAge() {
+		return cacheMaxAge;
+	}
+
+	public void setCacheMaxAge(String cacheMaxAge) {
+		this.cacheMaxAge = cacheMaxAge;
+	}
+
+	public boolean isCachingEnabled() {
+		return cachingEnabled;
+	}
+
+	public void setCachingEnabled(boolean cachingEnabled) {
+		cachingEnabledChecked = true;
+		this.cachingEnabled = cachingEnabled;
+	}
+
+	public boolean isCachingEnabledChecked() {
+		return cachingEnabledChecked;
+	}
 }
Index: src/org/vfny/geoserver/config/FeatureTypeConfig.java
===================================================================
--- src/org/vfny/geoserver/config/FeatureTypeConfig.java	(revision 4345)
+++ src/org/vfny/geoserver/config/FeatureTypeConfig.java	(working copy)
@@ -123,6 +123,18 @@
      * The default style name.
      */
     private String defaultStyle;
+    
+    /**
+     * A featureType-specific override for the defaultMaxAge defined in WMSConfig.  This value is added the
+     * headers of generated maps, marking them as being both "cache-able" and designating the time for which
+     * they are to remain valid.  The specific header added is "CacheControl: max-age="
+     */
+    private String cacheMaxAge;
+    
+    /**
+     * Should we be adding the CacheControl: max-age header to outgoing maps which include this layer?
+     */
+    private boolean cachingEnabled;
 
     /**
      * Package visible constructor for test cases
@@ -187,6 +199,9 @@
         dirName = dataStoreId + "_" + name;
         schemaName = name + "_Type";        
         schemaBase = "gml:AbstractFeatureType";
+        
+        cachingEnabled = false;
+        cacheMaxAge = null;
     }
 
     /**
@@ -238,7 +253,10 @@
         defaultStyle = dto.getDefaultStyle();
         dirName = dto.getDirName();
         schemaName = dto.getSchemaName();
-        schemaBase = dto.getSchemaBase();        
+        schemaBase = dto.getSchemaBase();
+        
+        cachingEnabled = dto.isCachingEnabled();
+        cacheMaxAge = dto.getCacheMaxAge();
     }
 
     /**
@@ -286,6 +304,10 @@
         f.setDirName(dirName);
         f.setSchemaBase(schemaBase);
         f.setSchemaName(schemaName);
+        
+        f.setCachingEnabled(cachingEnabled);
+        f.setCacheMaxAge(cacheMaxAge);
+        
         return f;
     }
 
@@ -559,4 +581,20 @@
 	    + " SRS: " + SRS + " schemaAttributes: " + schemaAttributes + 
 	    " schemaBase " + schemaBase + "]";
     }
+
+	public boolean isCachingEnabled() {
+		return cachingEnabled;
+	}
+
+	public void setCachingEnabled(boolean cachingEnabled) {
+		this.cachingEnabled = cachingEnabled;
+	}
+
+	public String getCacheMaxAge() {
+		return cacheMaxAge;
+	}
+
+	public void setCacheMaxAge(String cacheMaxAge) {
+		this.cacheMaxAge = cacheMaxAge;
+	}
 }
Index: src/org/vfny/geoserver/wms/responses/WMSCapabilitiesResponse.java
===================================================================
--- src/org/vfny/geoserver/wms/responses/WMSCapabilitiesResponse.java	(revision 4345)
+++ src/org/vfny/geoserver/wms/responses/WMSCapabilitiesResponse.java	(working copy)
@@ -7,6 +7,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.HashMap;
 import java.util.logging.Logger;
 
 import javax.xml.transform.TransformerException;
@@ -42,7 +43,17 @@
      */
     private byte[] rawResponse;
 
+    
     /**
+     * Returns any extra headers that this service might want to set in the HTTP response object.
+     * @see org.vfny.geoserver.Response#getResponseHeaders()
+     */
+    public HashMap getResponseHeaders() {
+    	return null;
+    }
+    
+    
+    /**
      * DOCUMENT ME!
      *
      * @param request DOCUMENT ME!
Index: src/org/vfny/geoserver/wms/responses/GetLegendGraphicResponse.java
===================================================================
--- src/org/vfny/geoserver/wms/responses/GetLegendGraphicResponse.java	(revision 4345)
+++ src/org/vfny/geoserver/wms/responses/GetLegendGraphicResponse.java	(working copy)
@@ -6,6 +6,7 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
@@ -37,6 +38,14 @@
     public GetLegendGraphicResponse() {
         super();
     }
+    
+    /**
+     * Returns any extra headers that this service might want to set in the HTTP response object.
+     * @see org.vfny.geoserver.Response#getResponseHeaders()
+     */
+    public HashMap getResponseHeaders() {
+    	return null;
+    }
 
     /**
      * DOCUMENT ME!
Index: src/org/vfny/geoserver/wms/responses/GetFeatureInfoResponse.java
===================================================================
--- src/org/vfny/geoserver/wms/responses/GetFeatureInfoResponse.java	(revision 4345)
+++ src/org/vfny/geoserver/wms/responses/GetFeatureInfoResponse.java	(working copy)
@@ -6,6 +6,7 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
@@ -72,6 +73,14 @@
      */
     public GetFeatureInfoResponse() {
     }
+    
+    /**
+     * Returns any extra headers that this service might want to set in the HTTP response object.
+     * @see org.vfny.geoserver.Response#getResponseHeaders()
+     */
+    public HashMap getResponseHeaders() {
+    	return null;
+    }
 
     /**
      * Obtains a <code>GetFeatureInfoDelegate</code> for the requested output format,
Index: src/org/vfny/geoserver/wms/responses/featureInfo/HTMLTableFeatureInfoResponse.java
===================================================================
--- src/org/vfny/geoserver/wms/responses/featureInfo/HTMLTableFeatureInfoResponse.java	(revision 4345)
+++ src/org/vfny/geoserver/wms/responses/featureInfo/HTMLTableFeatureInfoResponse.java	(working copy)
@@ -9,6 +9,7 @@
 import java.io.PrintWriter;
 import java.nio.charset.Charset;
 import java.util.Collections;
+import java.util.HashMap;
 
 import org.geotools.data.FeatureReader;
 import org.geotools.data.FeatureResults;
@@ -43,6 +44,14 @@
         format = "text/html";
         supportedFormats = Collections.singletonList(format);
     }
+    
+    /**
+     * Returns any extra headers that this service might want to set in the HTTP response object.
+     * @see org.vfny.geoserver.Response#getResponseHeaders()
+     */
+    public HashMap getResponseHeaders() {
+    	return null;
+    }
 
     /**
      * Writes the image to the client.
Index: src/org/vfny/geoserver/wms/responses/featureInfo/GmlFeatureInfoResponse.java
===================================================================
--- src/org/vfny/geoserver/wms/responses/featureInfo/GmlFeatureInfoResponse.java	(revision 4345)
+++ src/org/vfny/geoserver/wms/responses/featureInfo/GmlFeatureInfoResponse.java	(working copy)
@@ -7,6 +7,7 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 
@@ -48,6 +49,14 @@
     }
 
     /**
+     * Returns any extra headers that this service might want to set in the HTTP response object.
+     * @see org.vfny.geoserver.Response#getResponseHeaders()
+     */
+    public HashMap getResponseHeaders() {
+    	return null;
+    }
+    
+    /**
      * Takes the <code>FeatureResult</code>s generated by the
      * <code>execute</code> method in the superclass and constructs a
      * <code>GetFeaturesResult</code> wich is passed to a
Index: src/org/vfny/geoserver/wms/responses/featureInfo/TextFeatureInfoResponse.java
===================================================================
--- src/org/vfny/geoserver/wms/responses/featureInfo/TextFeatureInfoResponse.java	(revision 4345)
+++ src/org/vfny/geoserver/wms/responses/featureInfo/TextFeatureInfoResponse.java	(working copy)
@@ -9,6 +9,7 @@
 import java.io.PrintWriter;
 import java.nio.charset.Charset;
 import java.util.Collections;
+import java.util.HashMap;
 
 import org.geotools.data.FeatureReader;
 import org.geotools.data.FeatureResults;
@@ -37,6 +38,14 @@
         format = "text/plain";
         supportedFormats = Collections.singletonList("text/plain");
     }
+    
+    /**
+     * Returns any extra headers that this service might want to set in the HTTP response object.
+     * @see org.vfny.geoserver.Response#getResponseHeaders()
+     */
+    public HashMap getResponseHeaders() {
+    	return null;
+    }
 
     /**
      * Writes the feature information to the client in text/plain format.
Index: src/org/vfny/geoserver/wms/responses/DescribeLayerResponse.java
===================================================================
--- src/org/vfny/geoserver/wms/responses/DescribeLayerResponse.java	(revision 4345)
+++ src/org/vfny/geoserver/wms/responses/DescribeLayerResponse.java	(working copy)
@@ -7,6 +7,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.HashMap;
 import java.util.logging.Logger;
 
 import javax.xml.transform.TransformerException;
@@ -51,7 +52,16 @@
     /** the raw XML content ready to be sent to the client */
     private byte[] content;
 
+    
     /**
+     * Returns any extra headers that this service might want to set in the HTTP response object.
+     * @see org.vfny.geoserver.Response#getResponseHeaders()
+     */
+    public HashMap getResponseHeaders() {
+    	return null;
+    }
+    
+    /**
      * DOCUMENT ME!
      *
      * @param request DOCUMENT ME!
Index: src/org/vfny/geoserver/wms/responses/GetMapResponse.java
===================================================================
--- src/org/vfny/geoserver/wms/responses/GetMapResponse.java	(revision 4345)
+++ src/org/vfny/geoserver/wms/responses/GetMapResponse.java	(working copy)
@@ -6,8 +6,11 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -66,15 +69,27 @@
      * WMS configuration
      */
     private WMSConfig config;
+    /**
+     * custom response headers
+     */
+    private HashMap responseHeaders;
     
     /**
      * Creates a new GetMapResponse object.
      */
     public GetMapResponse(WMSConfig config) {
         this.config = config;
+        responseHeaders = new HashMap();
     }
 
     /**
+     * Returns any extra headers that this service might want to set in the HTTP response object.
+     */
+    public HashMap getResponseHeaders() {
+    	return responseHeaders;
+    }
+
+    /**
      * DOCUMENT ME!
      *
      * @param req DOCUMENT ME!
@@ -126,8 +141,24 @@
          
         MapLayer layer;
 
+        //track the external caching strategy for any map layers
+        boolean cachingPossible = request.getHttpServletRequest().getMethod().equals("GET");
+        int maxAge = Integer.MAX_VALUE;
+        
         FeatureSource source;
         for (int i = 0; i < layers.length; i++) {
+        	if (cachingPossible) {
+        		if (layers[i].isCachingEnabled()) {
+        			int nma = Integer.parseInt(layers[i].getCacheMaxAge());
+        			//suppose the map contains multiple cachable layers...we can only cache the combined map for the
+        			//time specified by the shortest-cached layer.
+        			if (nma < maxAge)
+        				maxAge = nma;
+        		} else {
+        			//if one layer isn't cachable, then we can't cache any of them.  Disable caching.
+        			cachingPossible = false;
+        		}
+        	}
             Style style = styles[i];
 
             try {
@@ -154,6 +185,8 @@
         }
 
         this.delegate.produceMap(map);
+        if (cachingPossible)
+        	responseHeaders.put("Cache-Control: max-age",maxAge + "s" );
     }
 
     /**
Index: src/org/vfny/geoserver/servlets/AbstractService.java
===================================================================
--- src/org/vfny/geoserver/servlets/AbstractService.java	(revision 4345)
+++ src/org/vfny/geoserver/servlets/AbstractService.java	(working copy)
@@ -14,11 +14,14 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.PrintWriter;
 import java.io.Reader;
+import java.io.StringWriter;
 import java.net.SocketException;
 import java.nio.charset.Charset;
 import java.util.Enumeration;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -457,6 +460,9 @@
             return;
         } catch (Throwable t) {
             //we can safelly send errors here, since we have not touched response yet
+        	LOGGER.severe("Uncaught general exception while executing request: " + t.getMessage());
+        	if (LOGGER.isLoggable(Level.FINE))
+        		t.printStackTrace();
             serviceResponse.abort(s);
             sendError(response, t);
 
@@ -482,6 +488,19 @@
                 LOGGER.fine("content encoding is: " + encoding);
                 response.setHeader("content-encoding", encoding);
             }
+
+            // write any further custom headers
+        	if (serviceResponse.getResponseHeaders() != null) {
+        		HashMap headers = serviceResponse.getResponseHeaders();
+        		Iterator iHeaders = headers.keySet().iterator();
+        		String curKey;
+        		while (iHeaders.hasNext()) {
+        			curKey = iHeaders.next().toString();
+        			response.addHeader(curKey, headers.get(curKey).toString());
+        		}
+        			
+        	}
+
         } catch (SocketException socketException) {
             LOGGER.fine(
                 "it seems that the user has closed the request stream: "
@@ -743,7 +762,10 @@
         LOGGER.info("Had an undefined error: " + t.getMessage());
 
         //TODO: put the stack trace in the logger.
-        //t.printStackTrace();
+        StringWriter s = new StringWriter();
+        t.printStackTrace(new PrintWriter(s));
+        LOGGER.info(s.getBuffer().toString());
+        
         //String pre = "UNCAUGHT EXCEPTION";
         ExceptionHandler exHandler = getExceptionHandler();
         ServiceException se = exHandler.newServiceException(t);
Index: src/org/vfny/geoserver/action/data/TypesEditorAction.java
===================================================================
--- src/org/vfny/geoserver/action/data/TypesEditorAction.java	(revision 4345)
+++ src/org/vfny/geoserver/action/data/TypesEditorAction.java	(working copy)
@@ -245,6 +245,11 @@
         config.setTitle(form.getTitle());
         config.setLatLongBBox(getBoundingBox(form));
         config.setKeywords(keyWords(form));
+        config.setCacheMaxAge(form.getCacheMaxAge());
+        config.setCachingEnabled(form.isCachingEnabled());
+        if (!form.isCachingEnabledChecked()) {
+        	config.setCachingEnabled(false);
+        }
 
         String schemaBase = form.getSchemaBase();
 
Index: src/org/vfny/geoserver/Response.java
===================================================================
--- src/org/vfny/geoserver/Response.java	(revision 4345)
+++ src/org/vfny/geoserver/Response.java	(working copy)
@@ -6,6 +6,7 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.HashMap;
 
 import org.vfny.geoserver.global.GeoServer;
 import org.vfny.geoserver.global.Service;
@@ -172,6 +173,17 @@
      * @return the content encoding writeTo will encode with, or null if none
      */
     public String getContentEncoding();
+    
+    /**
+     * Returns any extra headers that this Response might wish to have set in the
+     * HTTP response object.
+     * 
+     * In particular, a WMS might wish to have some external caching information added
+     * to the HTTP response, so that caches can hang onto this map for a while and ligten
+     * the load on geoserver.
+     * @return
+     */
+    public HashMap getResponseHeaders();
 
     /**
      * Writes this respone to the provided output stream.

