You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tapestry.apache.org by hl...@apache.org on 2007/02/12 02:14:50 UTC

svn commit: r506201 - in /tapestry/tapestry5/tapestry-core/trunk/src: main/java/org/apache/tapestry/internal/services/ main/java/org/apache/tapestry/services/ main/java/org/apache/tapestry/test/ test/java/org/apache/tapestry/internal/services/

Author: hlship
Date: Sun Feb 11 17:14:49 2007
New Revision: 506201

URL: http://svn.apache.org/viewvc?view=rev&rev=506201
Log:
Move the logic for resolving component meta data up the containment tree to its own service, and introduce a system of defaults in that service's configuration.

Added:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MetaDataLocatorImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/MetaDataLocator.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/MetaDataLocatorImplTest.java
Modified:
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
    tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java
    tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PersistentFieldManagerImplTest.java

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MetaDataLocatorImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MetaDataLocatorImpl.java?view=auto&rev=506201
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MetaDataLocatorImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/MetaDataLocatorImpl.java Sun Feb 11 17:14:49 2007
@@ -0,0 +1,143 @@
+// Copyright 2007 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.internal.services;
+
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
+
+import java.util.Map;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.events.InvalidationListener;
+import org.apache.tapestry.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry.services.ComponentClassResolver;
+import org.apache.tapestry.services.MetaDataLocator;
+
+public class MetaDataLocatorImpl implements MetaDataLocator, InvalidationListener
+{
+    private final Map<String, Map<String, String>> _defaultsByFolder = newCaseInsensitiveMap();
+
+    private final Map<String, String> _cache = CollectionFactory.newThreadSafeMap();
+
+    private final ComponentClassResolver _componentClassResolver;
+
+    public MetaDataLocatorImpl(ComponentClassResolver componentClassResolver,
+            Map<String, String> configuration)
+    {
+        _componentClassResolver = componentClassResolver;
+
+        loadDefaults(configuration);
+    }
+
+    public void objectWasInvalidated()
+    {
+        _cache.clear();
+    }
+
+    private void loadDefaults(Map<String, String> configuration)
+    {
+        for (Map.Entry<String, String> e : configuration.entrySet())
+        {
+            String key = e.getKey();
+
+            int colonx = key.indexOf(':');
+
+            String folderKey = colonx < 0 ? "" : key.substring(0, colonx);
+
+            Map<String, String> forFolder = _defaultsByFolder.get(folderKey);
+
+            if (forFolder == null)
+            {
+                forFolder = CollectionFactory.newCaseInsensitiveMap();
+                _defaultsByFolder.put(folderKey, forFolder);
+            }
+
+            String defaultKey = colonx < 0 ? key : key.substring(colonx + 1);
+
+            forFolder.put(defaultKey, e.getValue());
+        }
+    }
+
+    public String findMeta(String key, ComponentResources resources)
+    {
+        // The component's complete id should be sufficient as locale-specific
+        // values don't enter into this.
+        
+        String cacheKey = resources.getCompleteId() + "/" + key;
+
+        if (_cache.containsKey(cacheKey))
+            return _cache.get(cacheKey);
+
+        String result = locate(key, resources);
+
+        _cache.put(cacheKey, result);
+
+        return result;
+    }
+
+    private String locate(String key, ComponentResources resources)
+    {
+        ComponentResources cursor = resources;
+
+        while (true)
+        {
+            String value = cursor.getComponentModel().getMeta(key);
+
+            if (value != null)
+                return value;
+
+            ComponentResources next = cursor.getContainerResources();
+
+            if (next == null)
+                return locateInDefaults(key, cursor);
+
+            cursor = next;
+        }
+    }
+
+    private String locateInDefaults(String key, ComponentResources pageResources)
+    {
+        String pageClassName = pageResources.getCompleteId();
+
+        String logicalName = _componentClassResolver.resolvePageClassNameToPageName(pageClassName);
+
+        // We're going to peel this apart, slash by slash. Thus for
+        // "mylib/myfolder/mysubfolder/MyPage" we'll be checking: "mylib/myfolder/mysubfolder",
+        // then "mylib/myfolder", then "mylib", then "".
+
+        String path = logicalName;
+
+        while (true)
+        {
+            int lastSlashx = path.lastIndexOf('/');
+
+            String folderKey = lastSlashx < 0 ? "" : path.substring(0, lastSlashx);
+
+            Map<String, String> forFolder = _defaultsByFolder.get(folderKey);
+
+            if (forFolder != null && forFolder.containsKey(key))
+                return forFolder.get(key);
+
+            if (lastSlashx < 0)
+                break;
+
+            path = path.substring(0, lastSlashx);
+        }
+
+        // Perhaps from here into the symbol sources? That may come later.
+
+        return null;
+    }
+
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java?view=diff&rev=506201&r1=506200&r2=506201
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/PersistentFieldManagerImpl.java Sun Feb 11 17:14:49 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 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.
@@ -14,6 +14,8 @@
 
 package org.apache.tapestry.internal.services;
 
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
+
 import java.util.Collection;
 import java.util.Map;
 
@@ -21,6 +23,7 @@
 import org.apache.tapestry.ioc.internal.util.CollectionFactory;
 import org.apache.tapestry.ioc.internal.util.InternalUtils;
 import org.apache.tapestry.model.ComponentModel;
+import org.apache.tapestry.services.MetaDataLocator;
 import org.apache.tapestry.services.PersistentFieldBundle;
 import org.apache.tapestry.services.PersistentFieldChange;
 import org.apache.tapestry.services.PersistentFieldManager;
@@ -28,15 +31,20 @@
 
 public class PersistentFieldManagerImpl implements PersistentFieldManager
 {
-    static final String META_KEY = "tapestry.persistence-strategy";
+    public static final String META_KEY = "tapestry.persistence-strategy";
+
+    public static final String DEFAULT_STRATEGY = "session";
 
-    static final String DEFAULT_STRATEGY = "session";
+    private final MetaDataLocator _metaDataLocator;
 
     private final Map<String, PersistentFieldStrategy> _strategies;
 
-    public PersistentFieldManagerImpl(final Map<String, PersistentFieldStrategy> strategies)
+    public PersistentFieldManagerImpl(MetaDataLocator locator,
+            Map<String, PersistentFieldStrategy> strategies)
     {
-        _strategies = strategies;
+        _metaDataLocator = locator;
+
+        _strategies = newCaseInsensitiveMap(strategies);
     }
 
     private PersistentFieldStrategy getStrategy(String strategyName)
@@ -44,13 +52,9 @@
         PersistentFieldStrategy result = _strategies.get(strategyName);
 
         if (result == null)
-        {
-            String catalog = InternalUtils.joinSorted(_strategies.keySet());
-
             throw new RuntimeException(ServicesMessages.unknownPersistentFieldStrategy(
                     strategyName,
-                    catalog));
-        }
+                    _strategies.keySet()));
 
         return result;
     }
@@ -85,26 +89,6 @@
         if (InternalUtils.isNonBlank(strategy))
             return strategy;
 
-        // OK, it isn't specified for the field itself, so work up the component hierarchy,
-        // checking to see if any component set a default persistent strategy.
-
-        ComponentResources search = resources;
-        while (search != null)
-        {
-            model = search.getComponentModel();
-
-            strategy = model.getMeta(META_KEY);
-
-            if (strategy != null)
-                return strategy;
-
-            // Work up the containment hierarchy. For a persistent field inside a mixin, the
-            // container is the component. From there we work up the normal page containment
-            // hierarchy.
-
-            search = search.getContainerResources();
-        }
-
-        return DEFAULT_STRATEGY;
+        return _metaDataLocator.findMeta(META_KEY, resources);
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java?view=diff&rev=506201&r1=506200&r2=506201
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/services/ServicesMessages.java Sun Feb 11 17:14:49 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 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.
@@ -210,9 +210,11 @@
         return MESSAGES.format("component-event-is-aborted", methodDescription);
     }
 
-    static String unknownPersistentFieldStrategy(String stategyName, String catalog)
+    static String unknownPersistentFieldStrategy(String stategyName,
+            Collection<String> strategyNames)
     {
-        return MESSAGES.format("unknown-persistent-field-strategy", stategyName, catalog);
+        return MESSAGES.format("unknown-persistent-field-strategy", stategyName, InternalUtils
+                .joinSorted(strategyNames));
     }
 
     static String couldNotResolvePageName(String pageName)
@@ -377,7 +379,7 @@
                 propertyName,
                 propertyExpression);
     }
-    
+
     static String writeOnlyProperty(String propertyName, Class clazz, String propertyExpression)
     {
         return MESSAGES.format(

Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/MetaDataLocator.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/MetaDataLocator.java?view=auto&rev=506201
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/MetaDataLocator.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/MetaDataLocator.java Sun Feb 11 17:14:49 2007
@@ -0,0 +1,41 @@
+// Copyright 2007 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.services;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.model.ComponentModel;
+
+/**
+ * Used to lookup meta data concerning a particular component. The primary source of meta data is
+ * the meta data defined for the component, accessed via {@link ComponentModel#getMeta(String)}.
+ * This includes meta data defined by base classes. When meta-data for a particular component can
+ * not be found, a search works up the containment hierarchy (to the component's container, and the
+ * container's containter, and so on). If <em>that</em> proves unfruitful, a system of defaults is
+ * provided by configuration and matched against the containing page's logical name.
+ */
+public interface MetaDataLocator
+{
+    /**
+     * Searches for the value for the corresponding key.
+     * 
+     * @param key
+     *            the key used to locate the meta data (case insensitive)
+     * @param resources
+     *            the resources of the initial component used in the search
+     * @return the value if found (in the component, the component's container, etc. or via a folder
+     *         default) or null if not found anywhere
+     */
+    String findMeta(String key, ComponentResources resources);
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java?view=diff&rev=506201&r1=506200&r2=506201
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/services/TapestryModule.java Sun Feb 11 17:14:49 2007
@@ -109,6 +109,7 @@
 import org.apache.tapestry.internal.services.LinkActionResponseGenerator;
 import org.apache.tapestry.internal.services.LinkFactory;
 import org.apache.tapestry.internal.services.MarkupWriterImpl;
+import org.apache.tapestry.internal.services.MetaDataLocatorImpl;
 import org.apache.tapestry.internal.services.MetaWorker;
 import org.apache.tapestry.internal.services.MixinAfterWorker;
 import org.apache.tapestry.internal.services.MixinWorker;
@@ -209,6 +210,10 @@
 
     private final StrategyBuilder _strategyBuilder;
 
+    // Primarily used as a InvalidationEventHub for service implementations
+    // that should clear their cache when the underlying component class loader
+    // is discarded.
+
     private final ComponentInstantiatorSource _componentInstantiatorSource;
 
     private final LinkFactory _linkFactory;
@@ -557,6 +562,7 @@
                 FieldValidatorSource.class,
                 GridDataModelSource.class,
                 MarkupWriterFactory.class,
+                MetaDataLocator.class,
                 PersistentFieldManager.class,
                 PropertyConduitSource.class,
                 Request.class,
@@ -972,9 +978,12 @@
 
     /** A public service since extensions may provide new persistent strategies. */
     public static PersistentFieldManager buildPersistentFieldManager(
+            @Inject("infrastructure:MetaDataLocator")
+            MetaDataLocator locator,
+
             Map<String, PersistentFieldStrategy> configuration)
     {
-        return new PersistentFieldManagerImpl(configuration);
+        return new PersistentFieldManagerImpl(locator, configuration);
     }
 
     /**
@@ -1393,5 +1402,24 @@
     {
         return new GridDataModelSourceImpl(_propertyAccess, _componentClassFactory,
                 _propertyConduitSource);
+    }
+
+    public MetaDataLocator buildMetaDataLocator(@Inject("infrastructure:ComponentClassResolver")
+    ComponentClassResolver componentClassResolver,
+
+    Map<String, String> configuration)
+    {
+        MetaDataLocatorImpl service = new MetaDataLocatorImpl(componentClassResolver, configuration);
+
+        _componentInstantiatorSource.addInvalidationListener(service);
+
+        return service;
+    }
+
+    public void contributeMetaDataLocator(MappedConfiguration<String, String> configuration)
+    {
+        configuration.add(
+                PersistentFieldManagerImpl.META_KEY,
+                PersistentFieldManagerImpl.DEFAULT_STRATEGY);
     }
 }

Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java?view=diff&rev=506201&r1=506200&r2=506201
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/test/TapestryTestCase.java Sun Feb 11 17:14:49 2007
@@ -822,4 +822,9 @@
     {
         return newMock(ValidationConstraintGenerator.class);
     }
+
+    protected final void train_getMeta(ComponentModel model, String key, String value)
+    {
+        expect(model.getMeta(key)).andReturn(value).atLeastOnce();
+    }
 }

Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/MetaDataLocatorImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/MetaDataLocatorImplTest.java?view=auto&rev=506201
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/MetaDataLocatorImplTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/MetaDataLocatorImplTest.java Sun Feb 11 17:14:49 2007
@@ -0,0 +1,255 @@
+// Copyright 2007 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.internal.services;
+
+import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newMap;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.tapestry.ComponentResources;
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.model.ComponentModel;
+import org.apache.tapestry.services.ComponentClassResolver;
+import org.apache.tapestry.services.MetaDataLocator;
+import org.testng.annotations.Test;
+
+public class MetaDataLocatorImplTest extends InternalBaseTestCase
+{
+    @Test
+    public void found_in_component()
+    {
+        ComponentResources resources = newComponentResources();
+        ComponentModel model = newComponentModel();
+        ComponentClassResolver resolver = newComponentClassResolver();
+
+        String key = "foo.bar";
+        String value = "zaphod";
+        String completeId = "foo.Bar:baz";
+
+        train_getCompleteId(resources, completeId);
+        train_getComponentModel(resources, model);
+        train_getMeta(model, key, value);
+
+        replay();
+
+        Map<String, String> configuration = Collections.emptyMap();
+
+        MetaDataLocator locator = new MetaDataLocatorImpl(resolver, configuration);
+
+        assertSame(locator.findMeta(key, resources), value);
+
+        verify();
+
+        // And check that it's cached:
+
+        train_getCompleteId(resources, completeId);
+
+        replay();
+
+        assertSame(locator.findMeta(key, resources), value);
+
+        verify();
+    }
+
+    @Test
+    public void found_in_container()
+    {
+        ComponentResources resources = newComponentResources();
+        ComponentResources containerResources = newComponentResources();
+        ComponentModel model = newComponentModel();
+        ComponentModel containerModel = newComponentModel();
+        ComponentClassResolver resolver = newComponentClassResolver();
+
+        String key = "foo.bar";
+        String value = "zaphod";
+        String completeId = "foo.Bar:baz";
+
+        train_getCompleteId(resources, completeId);
+        train_getComponentModel(resources, model);
+        train_getMeta(model, key, null);
+        train_getContainerResources(resources, containerResources);
+        train_getComponentModel(containerResources, containerModel);
+        train_getMeta(containerModel, key, value);
+
+        replay();
+
+        Map<String, String> configuration = Collections.emptyMap();
+
+        MetaDataLocator locator = new MetaDataLocatorImpl(resolver, configuration);
+
+        assertSame(locator.findMeta(key, resources), value);
+
+        verify();
+    }
+
+    @Test
+    public void found_via_high_level_default()
+    {
+        ComponentResources resources = newComponentResources();
+        ComponentModel model = newComponentModel();
+        ComponentClassResolver resolver = newComponentClassResolver();
+
+        String key = "foo.bar";
+        String value = "zaphod";
+        String completeId = "foo.Bar";
+
+        train_getCompleteId(resources, completeId);
+        train_getComponentModel(resources, model);
+        train_getMeta(model, key, null);
+        train_getContainerResources(resources, null);
+        train_resolvePageClassNameToPageName(resolver, completeId, "foo/Bar");
+
+        replay();
+
+        Map<String, String> configuration = newMap();
+        configuration.put(key, value);
+
+        MetaDataLocator locator = new MetaDataLocatorImpl(resolver, configuration);
+
+        assertSame(locator.findMeta(key, resources), value);
+
+        verify();
+
+        // And check that it's cached:
+
+        train_getCompleteId(resources, completeId);
+
+        replay();
+
+        assertSame(locator.findMeta(key, resources), value);
+
+        verify();
+    }
+
+    @Test
+    public void default_matching_is_case_insensitive()
+    {
+        ComponentResources resources = newComponentResources();
+        ComponentModel model = newComponentModel();
+        ComponentClassResolver resolver = newComponentClassResolver();
+
+        String key = "foo.bar";
+        String value = "zaphod";
+        String completeId = "foo.Bar";
+
+        train_getCompleteId(resources, completeId);
+        train_getComponentModel(resources, model);
+        train_getMeta(model, key, null);
+        train_getContainerResources(resources, null);
+        train_resolvePageClassNameToPageName(resolver, completeId, "foo/Bar");
+
+        replay();
+
+        Map<String, String> configuration = newMap();
+        configuration.put(key.toUpperCase(), value);
+
+        MetaDataLocator locator = new MetaDataLocatorImpl(resolver, configuration);
+
+        assertSame(locator.findMeta(key, resources), value);
+
+        verify();
+
+        // And check that it's cached:
+
+        train_getCompleteId(resources, completeId);
+
+        replay();
+
+        assertSame(locator.findMeta(key, resources), value);
+
+        verify();
+    }
+
+    @Test
+    public void subfolder_default_overrides_high_level_default()
+    {
+        ComponentResources resources = newComponentResources();
+        ComponentModel model = newComponentModel();
+        ComponentClassResolver resolver = newComponentClassResolver();
+
+        String key = "foo.bar";
+        String value = "zaphod";
+        String completeId = "foo.Bar";
+
+        train_getCompleteId(resources, completeId);
+        train_getComponentModel(resources, model);
+        train_getMeta(model, key, null);
+        train_getContainerResources(resources, null);
+        train_resolvePageClassNameToPageName(resolver, completeId, "foo/Bar");
+
+        replay();
+
+        Map<String, String> configuration = newMap();
+        configuration.put(key, "xxx");
+        configuration.put("foo:" + key, value);
+
+        MetaDataLocator locator = new MetaDataLocatorImpl(resolver, configuration);
+
+        assertSame(locator.findMeta(key, resources), value);
+
+        verify();
+
+        // And check that it's cached:
+
+        train_getCompleteId(resources, completeId);
+
+        replay();
+
+        assertSame(locator.findMeta(key, resources), value);
+
+        verify();
+    }
+
+    @Test
+    public void test_cache_cleared()
+    {
+        ComponentResources resources = newComponentResources();
+        ComponentModel model = newComponentModel();
+        ComponentClassResolver resolver = newComponentClassResolver();
+
+        String key = "foo.bar";
+        String value = "zaphod";
+        String completeId = "foo.Bar:baz";
+
+        train_getCompleteId(resources, completeId);
+        train_getComponentModel(resources, model);
+        train_getMeta(model, key, value);
+
+        replay();
+
+        Map<String, String> configuration = Collections.emptyMap();
+
+        MetaDataLocatorImpl locator = new MetaDataLocatorImpl(resolver, configuration);
+
+        assertSame(locator.findMeta(key, resources), value);
+
+        verify();
+
+        // And check that it's cached:
+
+        train_getCompleteId(resources, completeId);
+        train_getComponentModel(resources, model);
+        train_getMeta(model, key, value);
+
+        replay();
+
+        locator.objectWasInvalidated();
+
+        assertSame(locator.findMeta(key, resources), value);
+
+        verify();
+    }
+}

Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PersistentFieldManagerImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PersistentFieldManagerImplTest.java?view=diff&rev=506201&r1=506200&r2=506201
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PersistentFieldManagerImplTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/services/PersistentFieldManagerImplTest.java Sun Feb 11 17:14:49 2007
@@ -1,4 +1,4 @@
-// Copyright 2006 The Apache Software Foundation
+// Copyright 2006, 2007 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.
@@ -23,6 +23,7 @@
 import org.apache.tapestry.ComponentResources;
 import org.apache.tapestry.internal.test.InternalBaseTestCase;
 import org.apache.tapestry.model.ComponentModel;
+import org.apache.tapestry.services.MetaDataLocator;
 import org.apache.tapestry.services.PersistentFieldBundle;
 import org.apache.tapestry.services.PersistentFieldChange;
 import org.apache.tapestry.services.PersistentFieldManager;
@@ -51,7 +52,7 @@
 
         replay();
 
-        PersistentFieldManager manager = new PersistentFieldManagerImpl(strategies);
+        PersistentFieldManager manager = new PersistentFieldManagerImpl(null, strategies);
 
         try
         {
@@ -94,20 +95,19 @@
 
         replay();
 
-        PersistentFieldManager manager = new PersistentFieldManagerImpl(strategies);
+        PersistentFieldManager manager = new PersistentFieldManagerImpl(null, strategies);
 
         manager.postChange(pageName, resources, fieldName, value);
 
         verify();
     }
-
-    @Test
-    public void post_change_strategy_by_meta_data()
+    
+    public void strategy_name_is_case_insensitive()
     {
         String pageName = "foo.Bar";
         String nestedId = "nested";
         String fieldName = "field";
-        String strategyName = "foo";
+        String strategyName = "FOO";
 
         ComponentResources resources = newComponentResources();
         ComponentModel model = newComponentModel();
@@ -115,13 +115,11 @@
         Object value = new Object();
 
         Map<String, PersistentFieldStrategy> strategies = newMap();
-        strategies.put(strategyName, strat);
+        strategies.put("foo", strat);
 
         train_getComponentModel(resources, model);
 
-        train_getFieldPersistenceStrategy(model, fieldName, "");
-
-        train_getMeta(model, PersistentFieldManagerImpl.META_KEY, strategyName);
+        train_getFieldPersistenceStrategy(model, fieldName, strategyName);
 
         train_getNestedId(resources, nestedId);
 
@@ -129,15 +127,15 @@
 
         replay();
 
-        PersistentFieldManager manager = new PersistentFieldManagerImpl(strategies);
+        PersistentFieldManager manager = new PersistentFieldManagerImpl(null, strategies);
 
         manager.postChange(pageName, resources, fieldName, value);
 
-        verify();
+        verify();     
     }
 
     @Test
-    public void post_change_strategy_by_container_meta_data()
+    public void post_change_strategy_by_meta_data()
     {
         String pageName = "foo.Bar";
         String nestedId = "nested";
@@ -146,9 +144,9 @@
 
         ComponentResources resources = newComponentResources();
         ComponentModel model = newComponentModel();
-        ComponentResources containerResources = newComponentResources();
-        ComponentModel containerModel = newComponentModel();
         PersistentFieldStrategy strat = newPersistentFieldStrategy();
+        MetaDataLocator locator = newMetaDataLocator();
+
         Object value = new Object();
 
         Map<String, PersistentFieldStrategy> strategies = newMap();
@@ -158,12 +156,7 @@
 
         train_getFieldPersistenceStrategy(model, fieldName, "");
 
-        train_getMeta(model, PersistentFieldManagerImpl.META_KEY, null);
-
-        train_getContainerResources(resources, containerResources);
-        train_getComponentModel(containerResources, containerModel);
-
-        train_getMeta(containerModel, PersistentFieldManagerImpl.META_KEY, strategyName);
+        train_findMeta(locator, PersistentFieldManagerImpl.META_KEY, resources, strategyName);
 
         train_getNestedId(resources, nestedId);
 
@@ -171,7 +164,7 @@
 
         replay();
 
-        PersistentFieldManager manager = new PersistentFieldManagerImpl(strategies);
+        PersistentFieldManager manager = new PersistentFieldManagerImpl(locator, strategies);
 
         manager.postChange(pageName, resources, fieldName, value);
 
@@ -187,6 +180,8 @@
 
         ComponentResources resources = newComponentResources();
         ComponentModel model = newComponentModel();
+        MetaDataLocator locator = newMetaDataLocator();
+
         PersistentFieldStrategy strat = newPersistentFieldStrategy();
         Object value = new Object();
 
@@ -197,9 +192,11 @@
 
         train_getFieldPersistenceStrategy(model, fieldName, "");
 
-        train_getMeta(model, PersistentFieldManagerImpl.META_KEY, null);
-
-        train_getContainerResources(resources, null);
+        train_findMeta(
+                locator,
+                PersistentFieldManagerImpl.META_KEY,
+                resources,
+                PersistentFieldManagerImpl.DEFAULT_STRATEGY);
 
         train_getNestedId(resources, nestedId);
 
@@ -207,16 +204,22 @@
 
         replay();
 
-        PersistentFieldManager manager = new PersistentFieldManagerImpl(strategies);
+        PersistentFieldManager manager = new PersistentFieldManagerImpl(locator, strategies);
 
         manager.postChange(pageName, resources, fieldName, value);
 
         verify();
     }
 
-    protected final void train_getMeta(ComponentModel model, String key, String value)
+    protected void train_findMeta(MetaDataLocator locator, String key,
+            ComponentResources resources, String value)
+    {
+        expect(locator.findMeta(key, resources)).andReturn(value).atLeastOnce();
+    }
+
+    protected MetaDataLocator newMetaDataLocator()
     {
-        expect(model.getMeta(key)).andReturn(value).atLeastOnce();
+        return newMock(MetaDataLocator.class);
     }
 
     private PersistentFieldStrategy newPersistentFieldStrategy()
@@ -256,7 +259,7 @@
         strategies.put("alpha", strat1);
         strategies.put("beta", strat2);
 
-        PersistentFieldManager manager = new PersistentFieldManagerImpl(strategies);
+        PersistentFieldManager manager = new PersistentFieldManagerImpl(null, strategies);
 
         PersistentFieldBundle bundle = manager.gatherChanges("foo.Bar");