Author: jkuhnert
Date: Tue Dec 27 16:07:47 2005
New Revision: 359328

URL: http://svn.apache.org/viewcvs?rev=359328&view=rev
Log:
Initial work on adding unprotected asset resources to AssetService, allowing 
more dynamic options for third party libraries/css asset includes/etc.

Added:
    
jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ResourceMatcher.java
    
jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ResourceMatcherImpl.java
    
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/TestUnprotectedAsset.java
    
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/subresource/
    
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/subresource/sub-resource.txt
Modified:
    jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.asset.xml
    
jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.services.xml
    
jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetService.java
    
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/ClasspathAssetFactoryTest.java
    
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/engine/encoders/AssetEncoderTest.java
    jakarta/tapestry/trunk/status.xml

Modified: 
jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.asset.xml
URL: 
http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.asset.xml?rev=359328&r1=359327&r2=359328&view=diff
==============================================================================
--- jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.asset.xml 
(original)
+++ jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.asset.xml 
Tue Dec 27 16:07:47 2005
@@ -137,4 +137,46 @@
   <contribution configuration-id="tapestry.Infrastructure">
     <property name="assetFactory" object="service:LookupAssetFactory"/>
   </contribution>
+  
+  <configuration-point id="UnprotectedAssets">
+    
+    Maps asset path regexp patterns to assets that won't be required
+    to have md5sum digests, making them visible and accessible to 
unauthenticated
+    url requests. Care should be taken contributing here. Most things should 
remain
+    protected, like .class files, database configuration files, etc.
+    
+    This configuration is mainly intended to help some resources that require 
being
+    able to dynamically load content on the browser client-side. One such 
resource
+    is the http://dojotoolkit.org javascript library, which loads 
javascript/css/other 
+    resources dynamically via XmlHttpRequests for resources that haven't been 
previously
+    digested.
+    
+    <schema>
+      <element name="unprotected-resource">
+        <attribute name="contains" required="true" unique="true">
+          The regexp pattern to match against.
+        </attribute>
+      </element>
+    </schema>    
+  </configuration-point>
+  
+  <contribution configuration-id="UnprotectedAssets">
+   List of unprotected asset regexps for ResourceMatcher service.
+    
+  </contribution>
+  
+  <service-point id="UnprotectedResourceMatcher">
+    
+    Uses the UnprotectedAssets configuration point to dynamically determine
+    which assets managed by the AssetService should be protected. The default
+    is to protect all resources.
+    
+    <invoke-factory>
+      <construct class="ResourceMatcherImpl">
+        <event-listener service-id="tapestry.ResetEventHub"/>
+        <set-configuration configuration-id="UnprotectedAssets" 
property="contributions" />
+      </construct>
+    </invoke-factory>
+  </service-point>  
+  
 </module>

Modified: 
jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.services.xml
URL: 
http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.services.xml?rev=359328&r1=359327&r2=359328&view=diff
==============================================================================
--- 
jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.services.xml 
(original)
+++ 
jakarta/tapestry/trunk/framework/src/descriptor/META-INF/tapestry.services.xml 
Tue Dec 27 16:07:47 2005
@@ -77,6 +77,8 @@
         <set-service property="context" 
service-id="tapestry.globals.WebContext"/>
         <set-object property="response" 
value="service:tapestry.globals.WebResponse"/>
         <set-service property="digestSource" 
service-id="tapestry.asset.ResourceDigestSource"/>
+        <set-service property="unprotectedMatcher" 
+                     service-id="tapestry.asset.UnprotectedResourceMatcher" />
       </construct>
     </invoke-factory>
   </service-point>  

Modified: 
jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetService.java
URL: 
http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetService.java?rev=359328&r1=359327&r2=359328&view=diff
==============================================================================
--- 
jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetService.java
 (original)
+++ 
jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/AssetService.java
 Tue Dec 27 16:07:47 2005
@@ -76,6 +76,9 @@
     /** @since 4.0 */
     private ResourceDigestSource _digestSource;
 
+    /** @since 4.1 */
+    private ResourceMatcher _unprotectedMatcher;
+    
     /**
      * Defaults MIME types, by extension, used when the servlet container 
doesn't provide MIME
      * types. ServletExec Debugger, for example, fails to provide these.
@@ -131,7 +134,7 @@
      */
 
     public static final String DIGEST = "digest";
-
+    
     /**
      * Builds a [EMAIL PROTECTED] ILink}for a [EMAIL PROTECTED] PrivateAsset}.
      * <p>
@@ -142,19 +145,19 @@
     public ILink getLink(boolean post, Object parameter)
     {
         Defense.isAssignable(parameter, String.class, "parameter");
-
+        
         String path = (String) parameter;
-
+        
         String digest = _digestSource.getDigestForResource(path);
-
+        
         Map parameters = new HashMap();
-
+        
         parameters.put(ServiceConstants.SERVICE, getName());
         parameters.put(PATH, path);
         parameters.put(DIGEST, digest);
-
+        
         // Service is stateless, which is the exception to the rule.
-
+        
         return _linkFactory.constructLink(this, post, parameters, false);
     }
 
@@ -199,23 +202,23 @@
                         .md5Mismatch(path));
                 return;
             }
-
+            
             // If they were vended an asset in the past then it must be up-to 
date.
             // Asset URIs change if the underlying file is modified.
-
+            
             if (_request.getHeader("If-Modified-Since") != null)
             {
                 _response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
                 return;
             }
-
+            
             URL resourceURL = _classResolver.getResource(path);
-
+            
             if (resourceURL == null)
                 throw new 
ApplicationRuntimeException(AssetMessages.noSuchResource(path));
-
+            
             URLConnection resourceConnection = resourceURL.openConnection();
-
+            
             writeAssetContent(cycle, path, resourceConnection);
         }
         catch (Throwable ex)
@@ -231,7 +234,7 @@
             URLConnection resourceConnection) throws IOException
     {
         InputStream input = null;
-
+        
         try
         {
             // Getting the content type and length is very dependant
@@ -246,13 +249,13 @@
 
             _response.setDateHeader("Last-Modified", _startupTime);
             _response.setDateHeader("Expires", _expireTime);
-
+            
             // Set the content type. If the servlet container doesn't
             // provide it, try and guess it by the extension.
-
+            
             if (contentType == null || contentType.length() == 0)
                 contentType = getMimeType(resourcePath);
-
+            
             OutputStream output = _response.getOutputStream(new 
ContentType(contentType));
 
             input = new 
BufferedInputStream(resourceConnection.getInputStream());
@@ -319,5 +322,11 @@
     public void setRequest(WebRequest request)
     {
         _request = request;
+    }
+    
+    /** @since 4.1 */
+    public void setUnprotectedMatcher(ResourceMatcher matcher)
+    {
+        _unprotectedMatcher = matcher;
     }
 }

Added: 
jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ResourceMatcher.java
URL: 
http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ResourceMatcher.java?rev=359328&view=auto
==============================================================================
--- 
jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ResourceMatcher.java
 (added)
+++ 
jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ResourceMatcher.java
 Tue Dec 27 16:07:47 2005
@@ -0,0 +1,38 @@
+// Copyright 2004, 2005 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package org.apache.tapestry.asset;
+
+/**
+ * Responsible for determining if the path string of a [EMAIL PROTECTED] 
Reasource} -
+ * typically involving [EMAIL PROTECTED] IAsset} resources being examined by 
the 
+ * [EMAIL PROTECTED] AssetService} - match the configured resource match 
strings
+ * of this service.
+ *
+ * <p/>Currently used by [EMAIL PROTECTED] AssetService} to enable having 
unprotected 
+ * resources based off of regexp patterns. 
+ *
+ * @author jkuhnert
+ */
+public interface ResourceMatcher {
+
+    /**
+     * Checks for the existance of a resource pattern that matches the incoming
+     * path string.
+     * @param path The path to a resource
+     * @return True if the path is matched against one of the patterns 
configured
+     *         for this matcher, false otherwise.
+     */
+    
+    public boolean containsResource(String path);
+}

Added: 
jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ResourceMatcherImpl.java
URL: 
http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ResourceMatcherImpl.java?rev=359328&view=auto
==============================================================================
--- 
jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ResourceMatcherImpl.java
 (added)
+++ 
jakarta/tapestry/trunk/framework/src/java/org/apache/tapestry/asset/ResourceMatcherImpl.java
 Tue Dec 27 16:07:47 2005
@@ -0,0 +1,80 @@
+// Copyright 2004, 2005 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package org.apache.tapestry.asset;
+
+import java.util.List;
+
+import org.apache.tapestry.event.ResetEventListener;
+import org.apache.tapestry.util.RegexpMatcher;
+
+
+/**
+ * Implementation of [EMAIL PROTECTED] ResourceMatcher}.
+ *
+ * @author jkuhnert
+ */
+public class ResourceMatcherImpl implements ResetEventListener, 
ResourceMatcher {
+    
+    /** regexp matcher engine */
+    protected RegexpMatcher matcher;
+    /** Resource match configuration regexp strings */
+    protected List contributions;
+    
+    /** no args constructor */
+    public ResourceMatcherImpl() { }
+    
+    /**
+     * Invoked by hivemind by default to initialize
+     * service.
+     */
+    public void initializeService() 
+    {
+        matcher = new RegexpMatcher();
+    }
+    
+    /**
+     * [EMAIL PROTECTED]
+     */
+    public synchronized void resetEventDidOccur()
+    {
+        matcher.clear();
+    }
+    
+    /**
+     * [EMAIL PROTECTED]
+     */
+    public boolean containsResource(String path)
+    {
+        if (contributions == null || contributions.size() < 1)
+            return false;
+        
+        for (int i = 0; i < contributions.size(); i++) {
+            String pattern = (String)contributions.get(i);
+            if (matcher.contains(pattern, path))
+                return true;
+        }
+        
+        return false;
+    }
+    
+    /**
+     * The set of contributed regexp strings that will positively
+     * match incoming path strings for this matcher.
+     * @param contributions
+     */
+    public void setContributions(List contributions)
+    {
+        this.contributions = contributions;
+    }
+}

Modified: 
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/ClasspathAssetFactoryTest.java
URL: 
http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/ClasspathAssetFactoryTest.java?rev=359328&r1=359327&r2=359328&view=diff
==============================================================================
--- 
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/ClasspathAssetFactoryTest.java
 (original)
+++ 
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/ClasspathAssetFactoryTest.java
 Tue Dec 27 16:07:47 2005
@@ -142,6 +142,33 @@
         verifyControls();
     }
 
+    /**
+     * Tests relative sub-directory paths.
+     */
+    public void testRelativeDirectoryPath()
+    {
+        IEngineService assetService = newService();
+        Location l = newLocation();
+        
+        replayControls();
+        
+        ClasspathAssetFactory factory = new ClasspathAssetFactory();
+        factory.setClassResolver(getClassResolver());
+        factory.setAssetService(assetService);
+        factory.setLocalizer(new DefaultResourceLocalizer());
+        
+        Resource subResource = new ClasspathResource(getClassResolver(),
+                "/org/apache/tapestry/asset/subresource/sub-resource.txt");
+        IAsset asset = factory.createAsset(subResource, l);
+        
+        assertTrue(asset instanceof PrivateAsset);
+        assertEquals("/org/apache/tapestry/asset/subresource/sub-resource.txt",
+                asset.getResourceLocation().getPath());
+        assertSame(l, asset.getLocation());
+        
+        verifyControls();
+    }
+    
     private ClasspathResource newBaseResource()
     {
         return new ClasspathResource(getClassResolver(),

Added: 
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/TestUnprotectedAsset.java
URL: 
http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/TestUnprotectedAsset.java?rev=359328&view=auto
==============================================================================
--- 
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/TestUnprotectedAsset.java
 (added)
+++ 
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/TestUnprotectedAsset.java
 Tue Dec 27 16:07:47 2005
@@ -0,0 +1,116 @@
+// Copyright 2004, 2005 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package org.apache.tapestry.asset;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.hivemind.impl.DefaultClassResolver;
+import org.apache.hivemind.test.HiveMindTestCase;
+import org.apache.oro.text.regex.MalformedPatternException;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.PatternCompiler;
+import org.apache.oro.text.regex.PatternMatcher;
+import org.apache.oro.text.regex.Perl5Compiler;
+import org.apache.oro.text.regex.Perl5Matcher;
+
+
+/**
+ * Tests for unprotected resource contributions.
+ *
+ * @author jkuhnert
+ */
+public class TestUnprotectedAsset extends HiveMindTestCase {
+    
+    PatternCompiler compiler = new Perl5Compiler();
+    PatternMatcher matcher = new Perl5Matcher();
+    
+    /**
+     * Tests for regexp patterns describing unprotected resources.
+     */
+    public void testUnProtectedMatch()
+    {
+        Pattern pr = newPattern("org/apache/tapestry/asset/.*.txt");
+        
+        assertFalse(matcher.contains("org/apache/tapestry/foobar.png", pr));
+        
assertTrue(matcher.contains("org/apache/tapestry/asset/base-resource.txt", pr));
+        assertFalse(matcher.contains("org/apache/tapestry/asset/foobar.png", 
pr));
+    }
+    
+    /**
+     * Creates [EMAIL PROTECTED] Pattern} objects for regexp matching.
+     * @param pattern
+     * @return
+     */
+    protected Pattern newPattern(String pattern)
+    {
+        Pattern pr = null;
+        try {
+            pr = compiler.compile("org/apache/tapestry/asset/.*.txt",
+                    Perl5Compiler.READ_ONLY_MASK | 
Perl5Compiler.CASE_INSENSITIVE_MASK
+                    | Perl5Compiler.MULTILINE_MASK);
+        } catch (MalformedPatternException e) {
+            unreachable();
+        }
+        
+        return pr;
+    }
+    
+    /**
+     * Tests and asserts that it doesn't take ~longer~ to work with undigested
+     * resources using patterns than normal digested resources.
+     */
+    public void testResourcePerformanceComparison()
+    {
+        Pattern pr = 
newPattern("/org/apache/tapestry/asset/tapestry-in-action.png");
+        
+        ResourceDigestSourceImpl s = new ResourceDigestSourceImpl();
+        s.setClassResolver(new DefaultClassResolver());
+        
+        assertEquals("a5f4663532ea3efe22084df086482290", s
+                
.getDigestForResource("/org/apache/tapestry/asset/tapestry-in-action.png"));
+        
+        long currTime = System.currentTimeMillis();
+        
s.getDigestForResource("/org/apache/tapestry/asset/tapestry-in-action.png");
+        long drtime = System.currentTimeMillis() - currTime;
+        
+        currTime = System.currentTimeMillis();
+        matcher.contains("/org/apache/tapestry/asset/tapestry-in-action.png", 
pr);
+        long urtime = System.currentTimeMillis() - currTime;
+        
+        assertFalse("Urtime > drtime: " + urtime + " > " + drtime, urtime < 
drtime);
+    }
+    
+    /**
+     * Tests the implementation of [EMAIL PROTECTED] ResourceMatcher}.
+     */
+    public void testResourceMatcher()
+    {
+        ResourceMatcherImpl rm = new ResourceMatcherImpl();
+        List patterns = new ArrayList();
+        patterns.add("/org/apache/tapestry/asset/.*.txt");
+        patterns.add("/org/apache/tapestry/asset/.*.css");
+        patterns.add("/org/apache/tapestry/asset/.*.js");
+        patterns.add("/org/apache/tapestry/asset/[%$4]rew\\invalidpattern");
+        rm.setContributions(patterns);
+        rm.initializeService();
+        
+        
assertFalse(rm.containsResource("/org/apache/tapestry/asset/AbstractAsset.class"));
+        assertFalse(rm.containsResource("/org/apache/tapestry/.*.class"));
+        
assertTrue(rm.containsResource("/org/apache/tapestry/asset/assetBuilder.js"));
+        assertTrue(rm.containsResource("/org/apache/tapestry/asset/foo.txt"));
+        assertFalse(rm.containsResource("/org/apache/tapestry/asset/foo.TXT"));
+        
assertTrue(rm.containsResource("/org/apache/tapestry/asset/subdirectory/foo.css"));
+    }
+}

Added: 
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/subresource/sub-resource.txt
URL: 
http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/subresource/sub-resource.txt?rev=359328&view=auto
==============================================================================
--- 
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/subresource/sub-resource.txt
 (added)
+++ 
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/asset/subresource/sub-resource.txt
 Tue Dec 27 16:07:47 2005
@@ -0,0 +1 @@
+sub-resource value
\ No newline at end of file

Modified: 
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/engine/encoders/AssetEncoderTest.java
URL: 
http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/engine/encoders/AssetEncoderTest.java?rev=359328&r1=359327&r2=359328&view=diff
==============================================================================
--- 
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/engine/encoders/AssetEncoderTest.java
 (original)
+++ 
jakarta/tapestry/trunk/framework/src/test/org/apache/tapestry/engine/encoders/AssetEncoderTest.java
 Tue Dec 27 16:07:47 2005
@@ -31,16 +31,16 @@
     public void testWrongService()
     {
         ServiceEncoding encoding = newEncoding();
-
+        
         trainGetParameterValue(encoding, ServiceConstants.SERVICE, "foo");
-
+        
         replayControls();
-
+        
         new AssetEncoder().encode(encoding);
-
+        
         verifyControls();
     }
-
+    
     protected void trainGetParameterValue(ServiceEncoding encoding, String 
name, String value)
     {
         encoding.getParameterValue(name);
@@ -67,7 +67,7 @@
 
         verifyControls();
     }
-
+    
     protected void trainGetServletPath(ServiceEncoding encoding, String 
servletPath)
     {
         encoding.getServletPath();
@@ -77,18 +77,18 @@
     public void testEncode()
     {
         ServiceEncoding encoding = newEncoding();
-
+        
         trainGetParameterValue(encoding, ServiceConstants.SERVICE, 
Tapestry.ASSET_SERVICE);
         trainGetParameterValue(encoding, AssetService.PATH, 
"/foo/bar/Baz.gif");
         trainGetParameterValue(encoding, AssetService.DIGEST, "12345");
-
+        
         encoding.setServletPath("/assets/12345/foo/bar/Baz.gif");
         encoding.setParameterValue(AssetService.PATH, null);
         encoding.setParameterValue(AssetService.DIGEST, null);
         encoding.setParameterValue(ServiceConstants.SERVICE, null);
-
+        
         replayControls();
-
+        
         AssetEncoder encoder = new AssetEncoder();
         encoder.setPath("/assets");
 
@@ -100,14 +100,14 @@
     public void testDecode()
     {
         ServiceEncoding encoding = newEncoding();
-
+        
         trainGetServletPath(encoding, "/assets");
         trainGetPathInfo(encoding, "/12345/foo/bar/Baz.gif");
-
+        
         encoding.setParameterValue(ServiceConstants.SERVICE, 
Tapestry.ASSET_SERVICE);
         encoding.setParameterValue(AssetService.DIGEST, "12345");
         encoding.setParameterValue(AssetService.PATH, "/foo/bar/Baz.gif");
-
+        
         replayControls();
 
         AssetEncoder encoder = new AssetEncoder();

Modified: jakarta/tapestry/trunk/status.xml
URL: 
http://svn.apache.org/viewcvs/jakarta/tapestry/trunk/status.xml?rev=359328&r1=359327&r2=359328&view=diff
==============================================================================
--- jakarta/tapestry/trunk/status.xml (original)
+++ jakarta/tapestry/trunk/status.xml Tue Dec 27 16:07:47 2005
@@ -57,6 +57,9 @@
   <changes>
     <release version="4.1-alpha-1" date="unreleased">
       <action type="update" dev="JK" >Cleaned up warnings/imports showing on 
eclipse problem pane.</action>
+      <action type="add" dev="JK" fixes-bug="TAPESTRY-821">ResourceMatcher 
service added with configurable regexp pattern 
+      hivemind contributions to conditionally allow some resources to not
+      require md5sum digest checks.</action>
     </release>
     <release version="4.0-rc-2" date="unreleased">
       <action type="fix" dev="HLS" 
fixes-bug="TAPESTRY-789">DefaultResourceLocalizerTest is under framework/src 
not framework/test</action>



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to